View Javadoc

1   /**
2    *
3    * Copyright 2003-2004 The Apache Software Foundation
4    *
5    *  Licensed under the Apache License, Version 2.0 (the "License");
6    *  you may not use this file except in compliance with the License.
7    *  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  
18  package javax.activation;
19  
20  import java.awt.datatransfer.DataFlavor;
21  import java.awt.datatransfer.Transferable;
22  import java.awt.datatransfer.UnsupportedFlavorException;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.OutputStream;
26  import java.io.PipedInputStream;
27  import java.io.PipedOutputStream;
28  import java.net.URL;
29  
30  /**
31   * @version $Rev: 376811 $ $Date: 2006-02-10 11:40:13 -0800 (Fri, 10 Feb 2006) $
32   */
33  public class DataHandler implements Transferable {
34      private final DataSource ds;
35      private final DataFlavor flavor;
36  
37      private CommandMap commandMap;
38      private DataContentHandler dch;
39  
40      public DataHandler(DataSource ds) {
41          this.ds = ds;
42          this.flavor = new ActivationDataFlavor(ds.getContentType(), null);
43      }
44  
45      public DataHandler(Object data, String type) {
46          this.ds = new ObjectDataSource(data, type);
47          this.flavor = new ActivationDataFlavor(data.getClass(), null);
48      }
49  
50      public DataHandler(URL url) {
51          this.ds = new URLDataSource(url);
52          this.flavor = new ActivationDataFlavor(ds.getContentType(), null);
53      }
54  
55      public DataSource getDataSource() {
56          return ds;
57      }
58  
59      public String getName() {
60          return ds.getName();
61      }
62  
63      public String getContentType() {
64          return ds.getContentType();
65      }
66  
67      public InputStream getInputStream() throws IOException {
68          return ds.getInputStream();
69      }
70  
71      public void writeTo(OutputStream os) throws IOException {
72          if (ds instanceof ObjectDataSource) {
73              ObjectDataSource ods = (ObjectDataSource) ds;
74              DataContentHandler dch = getDataContentHandler();
75              if (dch == null) {
76                  throw new UnsupportedDataTypeException(ods.mimeType);
77              }
78              dch.writeTo(ods.data, ods.mimeType, os);
79          } else {
80              byte[] buffer = new byte[1024];
81              InputStream is = getInputStream();
82              try {
83                  int count;
84                  while ((count = is.read(buffer)) != -1) {
85                      os.write(buffer, 0, count);
86                  }
87              } finally {
88                  is.close();
89              }
90          }
91      }
92  
93      public OutputStream getOutputStream() throws IOException {
94          return ds.getOutputStream();
95      }
96  
97      public synchronized DataFlavor[] getTransferDataFlavors() {
98          return getDataContentHandler().getTransferDataFlavors();
99      }
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 }