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