001    /**
002     *
003     * Copyright 2003-2004 The Apache Software Foundation
004     *
005     *  Licensed under the Apache License, Version 2.0 (the "License");
006     *  you may not use this file except in compliance with the License.
007     *  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    
018    package javax.activation;
019    
020    import java.awt.datatransfer.DataFlavor;
021    import java.awt.datatransfer.Transferable;
022    import java.awt.datatransfer.UnsupportedFlavorException;
023    import java.io.IOException;
024    import java.io.InputStream;
025    import java.io.OutputStream;
026    import java.io.PipedInputStream;
027    import java.io.PipedOutputStream;
028    import java.net.URL;
029    
030    /**
031     * @version $Rev: 376811 $ $Date: 2006-02-10 11:40:13 -0800 (Fri, 10 Feb 2006) $
032     */
033    public class DataHandler implements Transferable {
034        private final DataSource ds;
035        private final DataFlavor flavor;
036    
037        private CommandMap commandMap;
038        private DataContentHandler dch;
039    
040        public DataHandler(DataSource ds) {
041            this.ds = ds;
042            this.flavor = new ActivationDataFlavor(ds.getContentType(), null);
043        }
044    
045        public DataHandler(Object data, String type) {
046            this.ds = new ObjectDataSource(data, type);
047            this.flavor = new ActivationDataFlavor(data.getClass(), null);
048        }
049    
050        public DataHandler(URL url) {
051            this.ds = new URLDataSource(url);
052            this.flavor = new ActivationDataFlavor(ds.getContentType(), null);
053        }
054    
055        public DataSource getDataSource() {
056            return ds;
057        }
058    
059        public String getName() {
060            return ds.getName();
061        }
062    
063        public String getContentType() {
064            return ds.getContentType();
065        }
066    
067        public InputStream getInputStream() throws IOException {
068            return ds.getInputStream();
069        }
070    
071        public void writeTo(OutputStream os) throws IOException {
072            if (ds instanceof ObjectDataSource) {
073                ObjectDataSource ods = (ObjectDataSource) ds;
074                DataContentHandler dch = getDataContentHandler();
075                if (dch == null) {
076                    throw new UnsupportedDataTypeException(ods.mimeType);
077                }
078                dch.writeTo(ods.data, ods.mimeType, os);
079            } else {
080                byte[] buffer = new byte[1024];
081                InputStream is = getInputStream();
082                try {
083                    int count;
084                    while ((count = is.read(buffer)) != -1) {
085                        os.write(buffer, 0, count);
086                    }
087                } finally {
088                    is.close();
089                }
090            }
091        }
092    
093        public OutputStream getOutputStream() throws IOException {
094            return ds.getOutputStream();
095        }
096    
097        public synchronized DataFlavor[] getTransferDataFlavors() {
098            return getDataContentHandler().getTransferDataFlavors();
099        }
100    
101        public boolean isDataFlavorSupported(DataFlavor flavor) {
102            DataFlavor[] flavors = getTransferDataFlavors();
103            for (int i = 0; i < flavors.length; i++) {
104                DataFlavor dataFlavor = flavors[i];
105                if (dataFlavor.equals(flavor)) {
106                    return true;
107                }
108            }
109            return false;
110        }
111    
112        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
113            DataContentHandler dch = getDataContentHandler();
114            if (dch != null) {
115                return dch.getTransferData(flavor, ds);
116            } else if (this.flavor.match(flavor)) {
117                if (ds instanceof ObjectDataSource) {
118                    return ((ObjectDataSource) ds).data;
119                } else {
120                    return ds.getInputStream();
121                }
122            } else {
123                throw new UnsupportedFlavorException(flavor);
124            }
125        }
126    
127        public CommandInfo[] getPreferredCommands() {
128            return getCommandMap().getPreferredCommands(ds.getContentType());
129        }
130    
131        public CommandInfo[] getAllCommands() {
132            return getCommandMap().getAllCommands(ds.getContentType());
133        }
134    
135        public CommandInfo getCommand(String cmdName) {
136            return getCommandMap().getCommand(ds.getContentType(), cmdName);
137        }
138    
139        public Object getContent() throws IOException {
140            if (ds instanceof ObjectDataSource) {
141                return ((ObjectDataSource) ds).data;
142            } else {
143                DataContentHandler dch = getDataContentHandler();
144                if (dch != null) {
145                    return dch.getContent(ds);
146                } else {
147                    return ds.getInputStream();
148                }
149            }
150        }
151    
152        public Object getBean(CommandInfo cmdinfo) {
153            try {
154                return cmdinfo.getCommandObject(this, this.getClass().getClassLoader());
155            } catch (IOException e) {
156                return null;
157            } catch (ClassNotFoundException e) {
158                return null;
159            }
160        }
161    
162        /**
163         * A local implementation of DataSouce used to wrap an Object and mime-type.
164         */
165        private class ObjectDataSource implements DataSource {
166            private final Object data;
167            private final String mimeType;
168    
169            public ObjectDataSource(Object data, String mimeType) {
170                this.data = data;
171                this.mimeType = mimeType;
172            }
173    
174            public String getName() {
175                return null;
176            }
177    
178            public String getContentType() {
179                return mimeType;
180            }
181    
182            public InputStream getInputStream() throws IOException {
183                final DataContentHandler dch = getDataContentHandler();
184                if (dch == null) {
185                    throw new UnsupportedDataTypeException(mimeType);
186                }
187                final PipedInputStream is = new PipedInputStream();
188                final PipedOutputStream os = new PipedOutputStream(is);
189                Thread thread = new Thread("DataHandler Pipe Pump") {
190                    public void run() {
191                        try {
192                            try {
193                                dch.writeTo(data, mimeType, os);
194                            } finally {
195                                os.close();
196                            }
197                        } catch (IOException e) {
198                            // ignore, per spec - doh!
199                        }
200                    }
201                };
202                thread.start();
203                return is;
204            }
205    
206            public OutputStream getOutputStream() throws IOException {
207                return null;
208            }
209        }
210    
211        public synchronized void setCommandMap(CommandMap commandMap) {
212            this.commandMap = commandMap;
213            this.dch = null;
214        }
215    
216        private synchronized CommandMap getCommandMap() {
217            return commandMap != null ? commandMap : CommandMap.getDefaultCommandMap();
218        }
219    
220        /**
221         * Search for a DataContentHandler for our mime type.
222         * The search is performed by first checking if a global factory has been set using
223         * {@link #setDataContentHandlerFactory(DataContentHandlerFactory)};
224         * if found then it is called to attempt to create a handler.
225         * If this attempt fails, we then call the command map set using {@link #setCommandMap(CommandMap)}
226         * (or if that has not been set, the default map returned by {@link CommandMap#getDefaultCommandMap()})
227         * to create the handler.
228         *
229         * The resulting handler is cached until the global factory is changed.
230         *
231         * @return
232         */
233        private synchronized DataContentHandler getDataContentHandler() {
234            DataContentHandlerFactory localFactory;
235            synchronized (DataHandler.class) {
236                if (factory != originalFactory) {
237                    // setDCHF was called - clear our cached copy of the DCH and DCHF
238                    dch = null;
239                    originalFactory = factory;
240                }
241                localFactory = originalFactory;
242            }
243            if (dch == null) {
244                // get the main mime-type portion of the content.
245                String mimeType = getMimeType(ds.getContentType());
246                if (localFactory != null) {
247                    dch = localFactory.createDataContentHandler(mimeType);
248                }
249                if (dch == null) {
250                    if (commandMap != null) {
251                        dch = commandMap.createDataContentHandler(mimeType);
252                    } else {
253                        dch = CommandMap.getDefaultCommandMap().createDataContentHandler(mimeType);
254                    }
255                }
256            }
257            return dch;
258        }
259    
260        /**
261         * Retrieve the base MIME type from a content type.  This parses
262         * the type into its base components, stripping off any parameter
263         * information.
264         *
265         * @param contentType
266         *               The content type string.
267         *
268         * @return The MIME type identifier portion of the content type.
269         */
270        private String getMimeType(String contentType) {
271            try {
272                MimeType mimeType = new MimeType(contentType);
273                return mimeType.getBaseType();
274            } catch (MimeTypeParseException e) {
275            }
276            return contentType;
277        }
278    
279        /**
280         * This is used to check if the DataContentHandlerFactory has been changed.
281         * This is not specified behaviour but this check is required to make this work like the RI.
282         */
283        private DataContentHandlerFactory originalFactory;
284    
285        {
286            synchronized (DataHandler.class) {
287                originalFactory = factory;
288            }
289        }
290    
291        private static DataContentHandlerFactory factory;
292    
293        /**
294         * Set the DataContentHandlerFactory to use.
295         * If this method has already been called then an Error is raised.
296         *
297         * @param newFactory the new factory
298         * @throws SecurityException if the caller does not have "SetFactory" RuntimePermission
299         */
300        public static synchronized void setDataContentHandlerFactory(DataContentHandlerFactory newFactory) {
301            if (factory != null) {
302                throw new Error("javax.activation.DataHandler.setDataContentHandlerFactory has already been defined");
303            }
304            SecurityManager sm = System.getSecurityManager();
305            if (sm != null) {
306                sm.checkSetFactory();
307            }
308            factory = newFactory;
309        }
310    }