|
1 /* |
|
2 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Sun designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Sun in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
22 * CA 95054 USA or visit www.sun.com if you need additional information or |
|
23 * have any questions. |
|
24 */ |
|
25 |
|
26 package javax.activation; |
|
27 |
|
28 import java.io.InputStream; |
|
29 import java.io.IOException; |
|
30 import java.io.OutputStream; |
|
31 import java.io.PipedInputStream; |
|
32 import java.io.PipedOutputStream; |
|
33 import java.io.OutputStreamWriter; |
|
34 import java.net.URL; |
|
35 import java.awt.datatransfer.Transferable; |
|
36 import java.awt.datatransfer.DataFlavor; |
|
37 import java.awt.datatransfer.UnsupportedFlavorException; |
|
38 |
|
39 /** |
|
40 * The DataHandler class provides a consistent interface to data |
|
41 * available in many different sources and formats. |
|
42 * It manages simple stream to string conversions and related operations |
|
43 * using DataContentHandlers. |
|
44 * It provides access to commands that can operate on the data. |
|
45 * The commands are found using a CommandMap. <p> |
|
46 * |
|
47 * <b>DataHandler and the Transferable Interface</b><p> |
|
48 * DataHandler implements the Transferable interface so that data can |
|
49 * be used in AWT data transfer operations, such as cut and paste and |
|
50 * drag and drop. The implementation of the Transferable interface |
|
51 * relies on the availability of an installed DataContentHandler |
|
52 * object corresponding to the MIME type of the data represented in |
|
53 * the specific instance of the DataHandler.<p> |
|
54 * |
|
55 * <b>DataHandler and CommandMaps</b><p> |
|
56 * The DataHandler keeps track of the current CommandMap that it uses to |
|
57 * service requests for commands (<code>getCommand</code>, |
|
58 * <code>getAllCommands</code>, <code>getPreferredCommands</code>). |
|
59 * Each instance of a DataHandler may have a CommandMap associated with |
|
60 * it using the <code>setCommandMap</code> method. If a CommandMap was |
|
61 * not set, DataHandler calls the <code>getDefaultCommandMap</code> |
|
62 * method in CommandMap and uses the value it returns. See |
|
63 * <i>CommandMap</i> for more information. <p> |
|
64 * |
|
65 * <b>DataHandler and URLs</b><p> |
|
66 * The current DataHandler implementation creates a private |
|
67 * instance of URLDataSource when it is constructed with a URL. |
|
68 * |
|
69 * @see javax.activation.CommandMap |
|
70 * @see javax.activation.DataContentHandler |
|
71 * @see javax.activation.DataSource |
|
72 * @see javax.activation.URLDataSource |
|
73 * |
|
74 * @since 1.6 |
|
75 */ |
|
76 |
|
77 public class DataHandler implements Transferable { |
|
78 |
|
79 // Use the datasource to indicate whether we were started via the |
|
80 // DataSource constructor or the object constructor. |
|
81 private DataSource dataSource = null; |
|
82 private DataSource objDataSource = null; |
|
83 |
|
84 // The Object and mimetype from the constructor (if passed in). |
|
85 // object remains null if it was instantiated with a |
|
86 // DataSource. |
|
87 private Object object = null; |
|
88 private String objectMimeType = null; |
|
89 |
|
90 // Keep track of the CommandMap |
|
91 private CommandMap currentCommandMap = null; |
|
92 |
|
93 // our transfer flavors |
|
94 private static final DataFlavor emptyFlavors[] = new DataFlavor[0]; |
|
95 private DataFlavor transferFlavors[] = emptyFlavors; |
|
96 |
|
97 // our DataContentHandler |
|
98 private DataContentHandler dataContentHandler = null; |
|
99 private DataContentHandler factoryDCH = null; |
|
100 |
|
101 // our DataContentHandlerFactory |
|
102 private static DataContentHandlerFactory factory = null; |
|
103 private DataContentHandlerFactory oldFactory = null; |
|
104 // the short representation of the ContentType (sans params) |
|
105 private String shortType = null; |
|
106 |
|
107 /** |
|
108 * Create a <code>DataHandler</code> instance referencing the |
|
109 * specified DataSource. The data exists in a byte stream form. |
|
110 * The DataSource will provide an InputStream to access the data. |
|
111 * |
|
112 * @param ds the DataSource |
|
113 */ |
|
114 public DataHandler(DataSource ds) { |
|
115 // save a reference to the incoming DS |
|
116 dataSource = ds; |
|
117 oldFactory = factory; // keep track of the factory |
|
118 } |
|
119 |
|
120 /** |
|
121 * Create a <code>DataHandler</code> instance representing an object |
|
122 * of this MIME type. This constructor is |
|
123 * used when the application already has an in-memory representation |
|
124 * of the data in the form of a Java Object. |
|
125 * |
|
126 * @param obj the Java Object |
|
127 * @param mimeType the MIME type of the object |
|
128 */ |
|
129 public DataHandler(Object obj, String mimeType) { |
|
130 object = obj; |
|
131 objectMimeType = mimeType; |
|
132 oldFactory = factory; // keep track of the factory |
|
133 } |
|
134 |
|
135 /** |
|
136 * Create a <code>DataHandler</code> instance referencing a URL. |
|
137 * The DataHandler internally creates a <code>URLDataSource</code> |
|
138 * instance to represent the URL. |
|
139 * |
|
140 * @param url a URL object |
|
141 */ |
|
142 public DataHandler(URL url) { |
|
143 dataSource = new URLDataSource(url); |
|
144 oldFactory = factory; // keep track of the factory |
|
145 } |
|
146 |
|
147 /** |
|
148 * Return the CommandMap for this instance of DataHandler. |
|
149 */ |
|
150 private synchronized CommandMap getCommandMap() { |
|
151 if (currentCommandMap != null) |
|
152 return currentCommandMap; |
|
153 else |
|
154 return CommandMap.getDefaultCommandMap(); |
|
155 } |
|
156 |
|
157 /** |
|
158 * Return the DataSource associated with this instance |
|
159 * of DataHandler. |
|
160 * <p> |
|
161 * For DataHandlers that have been instantiated with a DataSource, |
|
162 * this method returns the DataSource that was used to create the |
|
163 * DataHandler object. In other cases the DataHandler |
|
164 * constructs a DataSource from the data used to construct |
|
165 * the DataHandler. DataSources created for DataHandlers <b>not</b> |
|
166 * instantiated with a DataSource are cached for performance |
|
167 * reasons. |
|
168 * |
|
169 * @return a valid DataSource object for this DataHandler |
|
170 */ |
|
171 public DataSource getDataSource() { |
|
172 if (dataSource == null) { |
|
173 // create one on the fly |
|
174 if (objDataSource == null) |
|
175 objDataSource = new DataHandlerDataSource(this); |
|
176 return objDataSource; |
|
177 } |
|
178 return dataSource; |
|
179 } |
|
180 |
|
181 /** |
|
182 * Return the name of the data object. If this DataHandler |
|
183 * was created with a DataSource, this method calls through |
|
184 * to the <code>DataSource.getName</code> method, otherwise it |
|
185 * returns <i>null</i>. |
|
186 * |
|
187 * @return the name of the object |
|
188 */ |
|
189 public String getName() { |
|
190 if (dataSource != null) |
|
191 return dataSource.getName(); |
|
192 else |
|
193 return null; |
|
194 } |
|
195 |
|
196 /** |
|
197 * Return the MIME type of this object as retrieved from |
|
198 * the source object. Note that this is the <i>full</i> |
|
199 * type with parameters. |
|
200 * |
|
201 * @return the MIME type |
|
202 */ |
|
203 public String getContentType() { |
|
204 if (dataSource != null) // data source case |
|
205 return dataSource.getContentType(); |
|
206 else |
|
207 return objectMimeType; // obj/type case |
|
208 } |
|
209 |
|
210 /** |
|
211 * Get the InputStream for this object. <p> |
|
212 * |
|
213 * For DataHandlers instantiated with a DataSource, the DataHandler |
|
214 * calls the <code>DataSource.getInputStream</code> method and |
|
215 * returns the result to the caller. |
|
216 * <p> |
|
217 * For DataHandlers instantiated with an Object, the DataHandler |
|
218 * first attempts to find a DataContentHandler for the Object. If |
|
219 * the DataHandler can not find a DataContentHandler for this MIME |
|
220 * type, it throws an UnsupportedDataTypeException. If it is |
|
221 * successful, it creates a pipe and a thread. The thread uses the |
|
222 * DataContentHandler's <code>writeTo</code> method to write the |
|
223 * stream data into one end of the pipe. The other end of the pipe |
|
224 * is returned to the caller. Because a thread is created to copy |
|
225 * the data, IOExceptions that may occur during the copy can not be |
|
226 * propagated back to the caller. The result is an empty stream.<p> |
|
227 * |
|
228 * @return the InputStream representing this data |
|
229 * @exception IOException if an I/O error occurs |
|
230 * |
|
231 * @see javax.activation.DataContentHandler#writeTo |
|
232 * @see javax.activation.UnsupportedDataTypeException |
|
233 */ |
|
234 public InputStream getInputStream() throws IOException { |
|
235 InputStream ins = null; |
|
236 |
|
237 if (dataSource != null) { |
|
238 ins = dataSource.getInputStream(); |
|
239 } else { |
|
240 DataContentHandler dch = getDataContentHandler(); |
|
241 // we won't even try if we can't get a dch |
|
242 if (dch == null) |
|
243 throw new UnsupportedDataTypeException( |
|
244 "no DCH for MIME type " + getBaseType()); |
|
245 |
|
246 if (dch instanceof ObjectDataContentHandler) { |
|
247 if (((ObjectDataContentHandler)dch).getDCH() == null) |
|
248 throw new UnsupportedDataTypeException( |
|
249 "no object DCH for MIME type " + getBaseType()); |
|
250 } |
|
251 // there is none but the default^^^^^^^^^^^^^^^^ |
|
252 final DataContentHandler fdch = dch; |
|
253 |
|
254 // from bill s. |
|
255 // ce n'est pas une pipe! |
|
256 // |
|
257 // NOTE: This block of code needs to throw exceptions, but |
|
258 // can't because it is in another thread!!! ARG! |
|
259 // |
|
260 final PipedOutputStream pos = new PipedOutputStream(); |
|
261 PipedInputStream pin = new PipedInputStream(pos); |
|
262 new Thread( |
|
263 new Runnable() { |
|
264 public void run() { |
|
265 try { |
|
266 fdch.writeTo(object, objectMimeType, pos); |
|
267 } catch (IOException e) { |
|
268 |
|
269 } finally { |
|
270 try { |
|
271 pos.close(); |
|
272 } catch (IOException ie) { } |
|
273 } |
|
274 } |
|
275 }, |
|
276 "DataHandler.getInputStream").start(); |
|
277 ins = pin; |
|
278 } |
|
279 |
|
280 return ins; |
|
281 } |
|
282 |
|
283 /** |
|
284 * Write the data to an <code>OutputStream</code>.<p> |
|
285 * |
|
286 * If the DataHandler was created with a DataSource, writeTo |
|
287 * retrieves the InputStream and copies the bytes from the |
|
288 * InputStream to the OutputStream passed in. |
|
289 * <p> |
|
290 * If the DataHandler was created with an object, writeTo |
|
291 * retrieves the DataContentHandler for the object's type. |
|
292 * If the DataContentHandler was found, it calls the |
|
293 * <code>writeTo</code> method on the <code>DataContentHandler</code>. |
|
294 * |
|
295 * @param os the OutputStream to write to |
|
296 * @exception IOException if an I/O error occurs |
|
297 */ |
|
298 public void writeTo(OutputStream os) throws IOException { |
|
299 // for the DataSource case |
|
300 if (dataSource != null) { |
|
301 InputStream is = null; |
|
302 byte data[] = new byte[8*1024]; |
|
303 int bytes_read; |
|
304 |
|
305 is = dataSource.getInputStream(); |
|
306 |
|
307 try { |
|
308 while ((bytes_read = is.read(data)) > 0) { |
|
309 os.write(data, 0, bytes_read); |
|
310 } |
|
311 } finally { |
|
312 is.close(); |
|
313 is = null; |
|
314 } |
|
315 } else { // for the Object case |
|
316 DataContentHandler dch = getDataContentHandler(); |
|
317 dch.writeTo(object, objectMimeType, os); |
|
318 } |
|
319 } |
|
320 |
|
321 /** |
|
322 * Get an OutputStream for this DataHandler to allow overwriting |
|
323 * the underlying data. |
|
324 * If the DataHandler was created with a DataSource, the |
|
325 * DataSource's <code>getOutputStream</code> method is called. |
|
326 * Otherwise, <code>null</code> is returned. |
|
327 * |
|
328 * @return the OutputStream |
|
329 * |
|
330 * @see javax.activation.DataSource#getOutputStream |
|
331 * @see javax.activation.URLDataSource |
|
332 */ |
|
333 public OutputStream getOutputStream() throws IOException { |
|
334 if (dataSource != null) |
|
335 return dataSource.getOutputStream(); |
|
336 else |
|
337 return null; |
|
338 } |
|
339 |
|
340 /** |
|
341 * Return the DataFlavors in which this data is available. <p> |
|
342 * |
|
343 * Returns an array of DataFlavor objects indicating the flavors |
|
344 * the data can be provided in. The array is usually ordered |
|
345 * according to preference for providing the data, from most |
|
346 * richly descriptive to least richly descriptive.<p> |
|
347 * |
|
348 * The DataHandler attempts to find a DataContentHandler that |
|
349 * corresponds to the MIME type of the data. If one is located, |
|
350 * the DataHandler calls the DataContentHandler's |
|
351 * <code>getTransferDataFlavors</code> method. <p> |
|
352 * |
|
353 * If a DataContentHandler can <i>not</i> be located, and if the |
|
354 * DataHandler was created with a DataSource (or URL), one |
|
355 * DataFlavor is returned that represents this object's MIME type |
|
356 * and the <code>java.io.InputStream</code> class. If the |
|
357 * DataHandler was created with an object and a MIME type, |
|
358 * getTransferDataFlavors returns one DataFlavor that represents |
|
359 * this object's MIME type and the object's class. |
|
360 * |
|
361 * @return an array of data flavors in which this data can be transferred |
|
362 * @see javax.activation.DataContentHandler#getTransferDataFlavors |
|
363 */ |
|
364 public synchronized DataFlavor[] getTransferDataFlavors() { |
|
365 if (factory != oldFactory) // if the factory has changed, clear cache |
|
366 transferFlavors = emptyFlavors; |
|
367 |
|
368 // if it's not set, set it... |
|
369 if (transferFlavors == emptyFlavors) |
|
370 transferFlavors = getDataContentHandler().getTransferDataFlavors(); |
|
371 return transferFlavors; |
|
372 } |
|
373 |
|
374 /** |
|
375 * Returns whether the specified data flavor is supported |
|
376 * for this object.<p> |
|
377 * |
|
378 * This method iterates through the DataFlavors returned from |
|
379 * <code>getTransferDataFlavors</code>, comparing each with |
|
380 * the specified flavor. |
|
381 * |
|
382 * @param flavor the requested flavor for the data |
|
383 * @return true if the data flavor is supported |
|
384 * @see javax.activation.DataHandler#getTransferDataFlavors |
|
385 */ |
|
386 public boolean isDataFlavorSupported(DataFlavor flavor) { |
|
387 DataFlavor[] lFlavors = getTransferDataFlavors(); |
|
388 |
|
389 for (int i = 0; i < lFlavors.length; i++) { |
|
390 if (lFlavors[i].equals(flavor)) |
|
391 return true; |
|
392 } |
|
393 return false; |
|
394 } |
|
395 |
|
396 /** |
|
397 * Returns an object that represents the data to be |
|
398 * transferred. The class of the object returned is defined by the |
|
399 * representation class of the data flavor.<p> |
|
400 * |
|
401 * <b>For DataHandler's created with DataSources or URLs:</b><p> |
|
402 * |
|
403 * The DataHandler attempts to locate a DataContentHandler |
|
404 * for this MIME type. If one is found, the passed in DataFlavor |
|
405 * and the type of the data are passed to its <code>getTransferData</code> |
|
406 * method. If the DataHandler fails to locate a DataContentHandler |
|
407 * and the flavor specifies this object's MIME type and the |
|
408 * <code>java.io.InputStream</code> class, this object's InputStream |
|
409 * is returned. |
|
410 * Otherwise it throws an UnsupportedFlavorException. <p> |
|
411 * |
|
412 * <b>For DataHandler's created with Objects:</b><p> |
|
413 * |
|
414 * The DataHandler attempts to locate a DataContentHandler |
|
415 * for this MIME type. If one is found, the passed in DataFlavor |
|
416 * and the type of the data are passed to its getTransferData |
|
417 * method. If the DataHandler fails to locate a DataContentHandler |
|
418 * and the flavor specifies this object's MIME type and its class, |
|
419 * this DataHandler's referenced object is returned. |
|
420 * Otherwise it throws an UnsupportedFlavorException. |
|
421 * |
|
422 * @param flavor the requested flavor for the data |
|
423 * @return the object |
|
424 * @exception UnsupportedFlavorException if the data could not be |
|
425 * converted to the requested flavor |
|
426 * @exception IOException if an I/O error occurs |
|
427 * @see javax.activation.ActivationDataFlavor |
|
428 */ |
|
429 public Object getTransferData(DataFlavor flavor) |
|
430 throws UnsupportedFlavorException, IOException { |
|
431 return getDataContentHandler().getTransferData(flavor, dataSource); |
|
432 } |
|
433 |
|
434 /** |
|
435 * Set the CommandMap for use by this DataHandler. |
|
436 * Setting it to <code>null</code> causes the CommandMap to revert |
|
437 * to the CommandMap returned by the |
|
438 * <code>CommandMap.getDefaultCommandMap</code> method. |
|
439 * Changing the CommandMap, or setting it to <code>null</code>, |
|
440 * clears out any data cached from the previous CommandMap. |
|
441 * |
|
442 * @param commandMap the CommandMap to use in this DataHandler |
|
443 * |
|
444 * @see javax.activation.CommandMap#setDefaultCommandMap |
|
445 */ |
|
446 public synchronized void setCommandMap(CommandMap commandMap) { |
|
447 if (commandMap != currentCommandMap || commandMap == null) { |
|
448 // clear cached values... |
|
449 transferFlavors = emptyFlavors; |
|
450 dataContentHandler = null; |
|
451 |
|
452 currentCommandMap = commandMap; |
|
453 } |
|
454 } |
|
455 |
|
456 /** |
|
457 * Return the <i>preferred</i> commands for this type of data. |
|
458 * This method calls the <code>getPreferredCommands</code> method |
|
459 * in the CommandMap associated with this instance of DataHandler. |
|
460 * This method returns an array that represents a subset of |
|
461 * available commands. In cases where multiple commands for the |
|
462 * MIME type represented by this DataHandler are present, the |
|
463 * installed CommandMap chooses the appropriate commands. |
|
464 * |
|
465 * @return the CommandInfo objects representing the preferred commands |
|
466 * |
|
467 * @see javax.activation.CommandMap#getPreferredCommands |
|
468 */ |
|
469 public CommandInfo[] getPreferredCommands() { |
|
470 if (dataSource != null) |
|
471 return getCommandMap().getPreferredCommands(getBaseType(), |
|
472 dataSource); |
|
473 else |
|
474 return getCommandMap().getPreferredCommands(getBaseType()); |
|
475 } |
|
476 |
|
477 /** |
|
478 * Return all the commands for this type of data. |
|
479 * This method returns an array containing all commands |
|
480 * for the type of data represented by this DataHandler. The |
|
481 * MIME type for the underlying data represented by this DataHandler |
|
482 * is used to call through to the <code>getAllCommands</code> method |
|
483 * of the CommandMap associated with this DataHandler. |
|
484 * |
|
485 * @return the CommandInfo objects representing all the commands |
|
486 * |
|
487 * @see javax.activation.CommandMap#getAllCommands |
|
488 */ |
|
489 public CommandInfo[] getAllCommands() { |
|
490 if (dataSource != null) |
|
491 return getCommandMap().getAllCommands(getBaseType(), dataSource); |
|
492 else |
|
493 return getCommandMap().getAllCommands(getBaseType()); |
|
494 } |
|
495 |
|
496 /** |
|
497 * Get the command <i>cmdName</i>. Use the search semantics as |
|
498 * defined by the CommandMap installed in this DataHandler. The |
|
499 * MIME type for the underlying data represented by this DataHandler |
|
500 * is used to call through to the <code>getCommand</code> method |
|
501 * of the CommandMap associated with this DataHandler. |
|
502 * |
|
503 * @param cmdName the command name |
|
504 * @return the CommandInfo corresponding to the command |
|
505 * |
|
506 * @see javax.activation.CommandMap#getCommand |
|
507 */ |
|
508 public CommandInfo getCommand(String cmdName) { |
|
509 if (dataSource != null) |
|
510 return getCommandMap().getCommand(getBaseType(), cmdName, |
|
511 dataSource); |
|
512 else |
|
513 return getCommandMap().getCommand(getBaseType(), cmdName); |
|
514 } |
|
515 |
|
516 /** |
|
517 * Return the data in its preferred Object form. <p> |
|
518 * |
|
519 * If the DataHandler was instantiated with an object, return |
|
520 * the object. <p> |
|
521 * |
|
522 * If the DataHandler was instantiated with a DataSource, |
|
523 * this method uses a DataContentHandler to return the content |
|
524 * object for the data represented by this DataHandler. If no |
|
525 * <code>DataContentHandler</code> can be found for the |
|
526 * the type of this data, the DataHandler returns an |
|
527 * InputStream for the data. |
|
528 * |
|
529 * @return the content. |
|
530 * @exception IOException if an IOException occurs during |
|
531 * this operation. |
|
532 */ |
|
533 public Object getContent() throws IOException { |
|
534 if (object != null) |
|
535 return object; |
|
536 else |
|
537 return getDataContentHandler().getContent(getDataSource()); |
|
538 } |
|
539 |
|
540 /** |
|
541 * A convenience method that takes a CommandInfo object |
|
542 * and instantiates the corresponding command, usually |
|
543 * a JavaBean component. |
|
544 * <p> |
|
545 * This method calls the CommandInfo's <code>getCommandObject</code> |
|
546 * method with the <code>ClassLoader</code> used to load |
|
547 * the <code>javax.activation.DataHandler</code> class itself. |
|
548 * |
|
549 * @param cmdinfo the CommandInfo corresponding to a command |
|
550 * @return the instantiated command object |
|
551 */ |
|
552 public Object getBean(CommandInfo cmdinfo) { |
|
553 Object bean = null; |
|
554 |
|
555 try { |
|
556 // make the bean |
|
557 ClassLoader cld = null; |
|
558 // First try the "application's" class loader. |
|
559 cld = SecuritySupport.getContextClassLoader(); |
|
560 if (cld == null) |
|
561 cld = this.getClass().getClassLoader(); |
|
562 bean = cmdinfo.getCommandObject(this, cld); |
|
563 } catch (IOException e) { |
|
564 } catch (ClassNotFoundException e) { } |
|
565 |
|
566 return bean; |
|
567 } |
|
568 |
|
569 /** |
|
570 * Get the DataContentHandler for this DataHandler: <p> |
|
571 * |
|
572 * If a DataContentHandlerFactory is set, use it. |
|
573 * Otherwise look for an object to serve DCH in the |
|
574 * following order: <p> |
|
575 * |
|
576 * 1) if a factory is set, use it <p> |
|
577 * 2) if a CommandMap is set, use it <p> |
|
578 * 3) use the default CommandMap <p> |
|
579 * |
|
580 * In any case, wrap the real DataContentHandler with one of our own |
|
581 * to handle any missing cases, fill in defaults, and to ensure that |
|
582 * we always have a non-null DataContentHandler. |
|
583 * |
|
584 * @return the requested DataContentHandler |
|
585 */ |
|
586 private synchronized DataContentHandler getDataContentHandler() { |
|
587 |
|
588 // make sure the factory didn't change |
|
589 if (factory != oldFactory) { |
|
590 oldFactory = factory; |
|
591 factoryDCH = null; |
|
592 dataContentHandler = null; |
|
593 transferFlavors = emptyFlavors; |
|
594 } |
|
595 |
|
596 if (dataContentHandler != null) |
|
597 return dataContentHandler; |
|
598 |
|
599 String simpleMT = getBaseType(); |
|
600 |
|
601 if (factoryDCH == null && factory != null) |
|
602 factoryDCH = factory.createDataContentHandler(simpleMT); |
|
603 |
|
604 if (factoryDCH != null) |
|
605 dataContentHandler = factoryDCH; |
|
606 |
|
607 if (dataContentHandler == null) { |
|
608 if (dataSource != null) |
|
609 dataContentHandler = getCommandMap(). |
|
610 createDataContentHandler(simpleMT, dataSource); |
|
611 else |
|
612 dataContentHandler = getCommandMap(). |
|
613 createDataContentHandler(simpleMT); |
|
614 } |
|
615 |
|
616 // getDataContentHandler always uses these 'wrapper' handlers |
|
617 // to make sure it returns SOMETHING meaningful... |
|
618 if (dataSource != null) |
|
619 dataContentHandler = new DataSourceDataContentHandler( |
|
620 dataContentHandler, |
|
621 dataSource); |
|
622 else |
|
623 dataContentHandler = new ObjectDataContentHandler( |
|
624 dataContentHandler, |
|
625 object, |
|
626 objectMimeType); |
|
627 return dataContentHandler; |
|
628 } |
|
629 |
|
630 /** |
|
631 * Use the MimeType class to extract the MIME type/subtype, |
|
632 * ignoring the parameters. The type is cached. |
|
633 */ |
|
634 private synchronized String getBaseType() { |
|
635 if (shortType == null) { |
|
636 String ct = getContentType(); |
|
637 try { |
|
638 MimeType mt = new MimeType(ct); |
|
639 shortType = mt.getBaseType(); |
|
640 } catch (MimeTypeParseException e) { |
|
641 shortType = ct; |
|
642 } |
|
643 } |
|
644 return shortType; |
|
645 } |
|
646 |
|
647 /** |
|
648 * Sets the DataContentHandlerFactory. The DataContentHandlerFactory |
|
649 * is called first to find DataContentHandlers. |
|
650 * The DataContentHandlerFactory can only be set once. |
|
651 * <p> |
|
652 * If the DataContentHandlerFactory has already been set, |
|
653 * this method throws an Error. |
|
654 * |
|
655 * @param newFactory the DataContentHandlerFactory |
|
656 * @exception Error if the factory has already been defined. |
|
657 * |
|
658 * @see javax.activation.DataContentHandlerFactory |
|
659 */ |
|
660 public static synchronized void setDataContentHandlerFactory( |
|
661 DataContentHandlerFactory newFactory) { |
|
662 if (factory != null) |
|
663 throw new Error("DataContentHandlerFactory already defined"); |
|
664 |
|
665 SecurityManager security = System.getSecurityManager(); |
|
666 if (security != null) { |
|
667 try { |
|
668 // if it's ok with the SecurityManager, it's ok with me... |
|
669 security.checkSetFactory(); |
|
670 } catch (SecurityException ex) { |
|
671 // otherwise, we also allow it if this code and the |
|
672 // factory come from the same class loader (e.g., |
|
673 // the JAF classes were loaded with the applet classes). |
|
674 if (DataHandler.class.getClassLoader() != |
|
675 newFactory.getClass().getClassLoader()) |
|
676 throw ex; |
|
677 } |
|
678 } |
|
679 factory = newFactory; |
|
680 } |
|
681 } |
|
682 |
|
683 /** |
|
684 * The DataHanderDataSource class implements the |
|
685 * DataSource interface when the DataHandler is constructed |
|
686 * with an Object and a mimeType string. |
|
687 */ |
|
688 class DataHandlerDataSource implements DataSource { |
|
689 DataHandler dataHandler = null; |
|
690 |
|
691 /** |
|
692 * The constructor. |
|
693 */ |
|
694 public DataHandlerDataSource(DataHandler dh) { |
|
695 this.dataHandler = dh; |
|
696 } |
|
697 |
|
698 /** |
|
699 * Returns an <code>InputStream</code> representing this object. |
|
700 * @return the <code>InputStream</code> |
|
701 */ |
|
702 public InputStream getInputStream() throws IOException { |
|
703 return dataHandler.getInputStream(); |
|
704 } |
|
705 |
|
706 /** |
|
707 * Returns the <code>OutputStream</code> for this object. |
|
708 * @return the <code>OutputStream</code> |
|
709 */ |
|
710 public OutputStream getOutputStream() throws IOException { |
|
711 return dataHandler.getOutputStream(); |
|
712 } |
|
713 |
|
714 /** |
|
715 * Returns the MIME type of the data represented by this object. |
|
716 * @return the MIME type |
|
717 */ |
|
718 public String getContentType() { |
|
719 return dataHandler.getContentType(); |
|
720 } |
|
721 |
|
722 /** |
|
723 * Returns the name of this object. |
|
724 * @return the name of this object |
|
725 */ |
|
726 public String getName() { |
|
727 return dataHandler.getName(); // what else would it be? |
|
728 } |
|
729 } |
|
730 |
|
731 /* |
|
732 * DataSourceDataContentHandler |
|
733 * |
|
734 * This is a <i>private</i> DataContentHandler that wraps the real |
|
735 * DataContentHandler in the case where the DataHandler was instantiated |
|
736 * with a DataSource. |
|
737 */ |
|
738 class DataSourceDataContentHandler implements DataContentHandler { |
|
739 private DataSource ds = null; |
|
740 private DataFlavor transferFlavors[] = null; |
|
741 private DataContentHandler dch = null; |
|
742 |
|
743 /** |
|
744 * The constructor. |
|
745 */ |
|
746 public DataSourceDataContentHandler(DataContentHandler dch, DataSource ds) { |
|
747 this.ds = ds; |
|
748 this.dch = dch; |
|
749 } |
|
750 |
|
751 /** |
|
752 * Return the DataFlavors for this <code>DataContentHandler</code>. |
|
753 * @return the DataFlavors |
|
754 */ |
|
755 public DataFlavor[] getTransferDataFlavors() { |
|
756 |
|
757 if (transferFlavors == null) { |
|
758 if (dch != null) { // is there a dch? |
|
759 transferFlavors = dch.getTransferDataFlavors(); |
|
760 } else { |
|
761 transferFlavors = new DataFlavor[1]; |
|
762 transferFlavors[0] = |
|
763 new ActivationDataFlavor(ds.getContentType(), |
|
764 ds.getContentType()); |
|
765 } |
|
766 } |
|
767 return transferFlavors; |
|
768 } |
|
769 |
|
770 /** |
|
771 * Return the Transfer Data of type DataFlavor from InputStream. |
|
772 * @param df the DataFlavor |
|
773 * @param ds the DataSource |
|
774 * @return the constructed Object |
|
775 */ |
|
776 public Object getTransferData(DataFlavor df, DataSource ds) throws |
|
777 UnsupportedFlavorException, IOException { |
|
778 |
|
779 if (dch != null) |
|
780 return dch.getTransferData(df, ds); |
|
781 else if (df.equals(getTransferDataFlavors()[0])) // only have one now |
|
782 return ds.getInputStream(); |
|
783 else |
|
784 throw new UnsupportedFlavorException(df); |
|
785 } |
|
786 |
|
787 public Object getContent(DataSource ds) throws IOException { |
|
788 |
|
789 if (dch != null) |
|
790 return dch.getContent(ds); |
|
791 else |
|
792 return ds.getInputStream(); |
|
793 } |
|
794 |
|
795 /** |
|
796 * Write the object to the output stream. |
|
797 */ |
|
798 public void writeTo(Object obj, String mimeType, OutputStream os) |
|
799 throws IOException { |
|
800 if (dch != null) |
|
801 dch.writeTo(obj, mimeType, os); |
|
802 else |
|
803 throw new UnsupportedDataTypeException( |
|
804 "no DCH for content type " + ds.getContentType()); |
|
805 } |
|
806 } |
|
807 |
|
808 /* |
|
809 * ObjectDataContentHandler |
|
810 * |
|
811 * This is a <i>private</i> DataContentHandler that wraps the real |
|
812 * DataContentHandler in the case where the DataHandler was instantiated |
|
813 * with an object. |
|
814 */ |
|
815 class ObjectDataContentHandler implements DataContentHandler { |
|
816 private DataFlavor transferFlavors[] = null; |
|
817 private Object obj; |
|
818 private String mimeType; |
|
819 private DataContentHandler dch = null; |
|
820 |
|
821 /** |
|
822 * The constructor. |
|
823 */ |
|
824 public ObjectDataContentHandler(DataContentHandler dch, |
|
825 Object obj, String mimeType) { |
|
826 this.obj = obj; |
|
827 this.mimeType = mimeType; |
|
828 this.dch = dch; |
|
829 } |
|
830 |
|
831 /** |
|
832 * Return the DataContentHandler for this object. |
|
833 * Used only by the DataHandler class. |
|
834 */ |
|
835 public DataContentHandler getDCH() { |
|
836 return dch; |
|
837 } |
|
838 |
|
839 /** |
|
840 * Return the DataFlavors for this <code>DataContentHandler</code>. |
|
841 * @return the DataFlavors |
|
842 */ |
|
843 public synchronized DataFlavor[] getTransferDataFlavors() { |
|
844 if (transferFlavors == null) { |
|
845 if (dch != null) { |
|
846 transferFlavors = dch.getTransferDataFlavors(); |
|
847 } else { |
|
848 transferFlavors = new DataFlavor[1]; |
|
849 transferFlavors[0] = new ActivationDataFlavor(obj.getClass(), |
|
850 mimeType, mimeType); |
|
851 } |
|
852 } |
|
853 return transferFlavors; |
|
854 } |
|
855 |
|
856 /** |
|
857 * Return the Transfer Data of type DataFlavor from InputStream. |
|
858 * @param df the DataFlavor |
|
859 * @param ds the DataSource |
|
860 * @return the constructed Object |
|
861 */ |
|
862 public Object getTransferData(DataFlavor df, DataSource ds) |
|
863 throws UnsupportedFlavorException, IOException { |
|
864 |
|
865 if (dch != null) |
|
866 return dch.getTransferData(df, ds); |
|
867 else if (df.equals(getTransferDataFlavors()[0])) // only have one now |
|
868 return obj; |
|
869 else |
|
870 throw new UnsupportedFlavorException(df); |
|
871 |
|
872 } |
|
873 |
|
874 public Object getContent(DataSource ds) { |
|
875 return obj; |
|
876 } |
|
877 |
|
878 /** |
|
879 * Write the object to the output stream. |
|
880 */ |
|
881 public void writeTo(Object obj, String mimeType, OutputStream os) |
|
882 throws IOException { |
|
883 if (dch != null) |
|
884 dch.writeTo(obj, mimeType, os); |
|
885 else if (obj instanceof byte[]) |
|
886 os.write((byte[])obj); |
|
887 else if (obj instanceof String) { |
|
888 OutputStreamWriter osw = new OutputStreamWriter(os); |
|
889 osw.write((String)obj); |
|
890 osw.flush(); |
|
891 } else throw new UnsupportedDataTypeException( |
|
892 "no object DCH for MIME type " + this.mimeType); |
|
893 } |
|
894 } |