|
1 /* |
|
2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. 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. Oracle designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 * or visit www.oracle.com if you need additional information or have any |
|
23 * 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 |
|
372 if (transferFlavors == emptyFlavors) |
|
373 return transferFlavors; |
|
374 else |
|
375 return transferFlavors.clone(); |
|
376 |
|
377 } |
|
378 |
|
379 /** |
|
380 * Returns whether the specified data flavor is supported |
|
381 * for this object.<p> |
|
382 * |
|
383 * This method iterates through the DataFlavors returned from |
|
384 * <code>getTransferDataFlavors</code>, comparing each with |
|
385 * the specified flavor. |
|
386 * |
|
387 * @param flavor the requested flavor for the data |
|
388 * @return true if the data flavor is supported |
|
389 * @see javax.activation.DataHandler#getTransferDataFlavors |
|
390 */ |
|
391 public boolean isDataFlavorSupported(DataFlavor flavor) { |
|
392 DataFlavor[] lFlavors = getTransferDataFlavors(); |
|
393 |
|
394 for (int i = 0; i < lFlavors.length; i++) { |
|
395 if (lFlavors[i].equals(flavor)) |
|
396 return true; |
|
397 } |
|
398 return false; |
|
399 } |
|
400 |
|
401 /** |
|
402 * Returns an object that represents the data to be |
|
403 * transferred. The class of the object returned is defined by the |
|
404 * representation class of the data flavor.<p> |
|
405 * |
|
406 * <b>For DataHandler's created with DataSources or URLs:</b><p> |
|
407 * |
|
408 * The DataHandler attempts to locate a DataContentHandler |
|
409 * for this MIME type. If one is found, the passed in DataFlavor |
|
410 * and the type of the data are passed to its <code>getTransferData</code> |
|
411 * method. If the DataHandler fails to locate a DataContentHandler |
|
412 * and the flavor specifies this object's MIME type and the |
|
413 * <code>java.io.InputStream</code> class, this object's InputStream |
|
414 * is returned. |
|
415 * Otherwise it throws an UnsupportedFlavorException. <p> |
|
416 * |
|
417 * <b>For DataHandler's created with Objects:</b><p> |
|
418 * |
|
419 * The DataHandler attempts to locate a DataContentHandler |
|
420 * for this MIME type. If one is found, the passed in DataFlavor |
|
421 * and the type of the data are passed to its getTransferData |
|
422 * method. If the DataHandler fails to locate a DataContentHandler |
|
423 * and the flavor specifies this object's MIME type and its class, |
|
424 * this DataHandler's referenced object is returned. |
|
425 * Otherwise it throws an UnsupportedFlavorException. |
|
426 * |
|
427 * @param flavor the requested flavor for the data |
|
428 * @return the object |
|
429 * @exception UnsupportedFlavorException if the data could not be |
|
430 * converted to the requested flavor |
|
431 * @exception IOException if an I/O error occurs |
|
432 * @see javax.activation.ActivationDataFlavor |
|
433 */ |
|
434 public Object getTransferData(DataFlavor flavor) |
|
435 throws UnsupportedFlavorException, IOException { |
|
436 return getDataContentHandler().getTransferData(flavor, dataSource); |
|
437 } |
|
438 |
|
439 /** |
|
440 * Set the CommandMap for use by this DataHandler. |
|
441 * Setting it to <code>null</code> causes the CommandMap to revert |
|
442 * to the CommandMap returned by the |
|
443 * <code>CommandMap.getDefaultCommandMap</code> method. |
|
444 * Changing the CommandMap, or setting it to <code>null</code>, |
|
445 * clears out any data cached from the previous CommandMap. |
|
446 * |
|
447 * @param commandMap the CommandMap to use in this DataHandler |
|
448 * |
|
449 * @see javax.activation.CommandMap#setDefaultCommandMap |
|
450 */ |
|
451 public synchronized void setCommandMap(CommandMap commandMap) { |
|
452 if (commandMap != currentCommandMap || commandMap == null) { |
|
453 // clear cached values... |
|
454 transferFlavors = emptyFlavors; |
|
455 dataContentHandler = null; |
|
456 |
|
457 currentCommandMap = commandMap; |
|
458 } |
|
459 } |
|
460 |
|
461 /** |
|
462 * Return the <i>preferred</i> commands for this type of data. |
|
463 * This method calls the <code>getPreferredCommands</code> method |
|
464 * in the CommandMap associated with this instance of DataHandler. |
|
465 * This method returns an array that represents a subset of |
|
466 * available commands. In cases where multiple commands for the |
|
467 * MIME type represented by this DataHandler are present, the |
|
468 * installed CommandMap chooses the appropriate commands. |
|
469 * |
|
470 * @return the CommandInfo objects representing the preferred commands |
|
471 * |
|
472 * @see javax.activation.CommandMap#getPreferredCommands |
|
473 */ |
|
474 public CommandInfo[] getPreferredCommands() { |
|
475 if (dataSource != null) |
|
476 return getCommandMap().getPreferredCommands(getBaseType(), |
|
477 dataSource); |
|
478 else |
|
479 return getCommandMap().getPreferredCommands(getBaseType()); |
|
480 } |
|
481 |
|
482 /** |
|
483 * Return all the commands for this type of data. |
|
484 * This method returns an array containing all commands |
|
485 * for the type of data represented by this DataHandler. The |
|
486 * MIME type for the underlying data represented by this DataHandler |
|
487 * is used to call through to the <code>getAllCommands</code> method |
|
488 * of the CommandMap associated with this DataHandler. |
|
489 * |
|
490 * @return the CommandInfo objects representing all the commands |
|
491 * |
|
492 * @see javax.activation.CommandMap#getAllCommands |
|
493 */ |
|
494 public CommandInfo[] getAllCommands() { |
|
495 if (dataSource != null) |
|
496 return getCommandMap().getAllCommands(getBaseType(), dataSource); |
|
497 else |
|
498 return getCommandMap().getAllCommands(getBaseType()); |
|
499 } |
|
500 |
|
501 /** |
|
502 * Get the command <i>cmdName</i>. Use the search semantics as |
|
503 * defined by the CommandMap installed in this DataHandler. The |
|
504 * MIME type for the underlying data represented by this DataHandler |
|
505 * is used to call through to the <code>getCommand</code> method |
|
506 * of the CommandMap associated with this DataHandler. |
|
507 * |
|
508 * @param cmdName the command name |
|
509 * @return the CommandInfo corresponding to the command |
|
510 * |
|
511 * @see javax.activation.CommandMap#getCommand |
|
512 */ |
|
513 public CommandInfo getCommand(String cmdName) { |
|
514 if (dataSource != null) |
|
515 return getCommandMap().getCommand(getBaseType(), cmdName, |
|
516 dataSource); |
|
517 else |
|
518 return getCommandMap().getCommand(getBaseType(), cmdName); |
|
519 } |
|
520 |
|
521 /** |
|
522 * Return the data in its preferred Object form. <p> |
|
523 * |
|
524 * If the DataHandler was instantiated with an object, return |
|
525 * the object. <p> |
|
526 * |
|
527 * If the DataHandler was instantiated with a DataSource, |
|
528 * this method uses a DataContentHandler to return the content |
|
529 * object for the data represented by this DataHandler. If no |
|
530 * <code>DataContentHandler</code> can be found for the |
|
531 * the type of this data, the DataHandler returns an |
|
532 * InputStream for the data. |
|
533 * |
|
534 * @return the content. |
|
535 * @exception IOException if an IOException occurs during |
|
536 * this operation. |
|
537 */ |
|
538 public Object getContent() throws IOException { |
|
539 if (object != null) |
|
540 return object; |
|
541 else |
|
542 return getDataContentHandler().getContent(getDataSource()); |
|
543 } |
|
544 |
|
545 /** |
|
546 * A convenience method that takes a CommandInfo object |
|
547 * and instantiates the corresponding command, usually |
|
548 * a JavaBean component. |
|
549 * <p> |
|
550 * This method calls the CommandInfo's <code>getCommandObject</code> |
|
551 * method with the <code>ClassLoader</code> used to load |
|
552 * the <code>javax.activation.DataHandler</code> class itself. |
|
553 * |
|
554 * @param cmdinfo the CommandInfo corresponding to a command |
|
555 * @return the instantiated command object |
|
556 */ |
|
557 public Object getBean(CommandInfo cmdinfo) { |
|
558 Object bean = null; |
|
559 |
|
560 try { |
|
561 // make the bean |
|
562 ClassLoader cld = null; |
|
563 // First try the "application's" class loader. |
|
564 cld = SecuritySupport.getContextClassLoader(); |
|
565 if (cld == null) |
|
566 cld = this.getClass().getClassLoader(); |
|
567 bean = cmdinfo.getCommandObject(this, cld); |
|
568 } catch (IOException e) { |
|
569 } catch (ClassNotFoundException e) { } |
|
570 |
|
571 return bean; |
|
572 } |
|
573 |
|
574 /** |
|
575 * Get the DataContentHandler for this DataHandler: <p> |
|
576 * |
|
577 * If a DataContentHandlerFactory is set, use it. |
|
578 * Otherwise look for an object to serve DCH in the |
|
579 * following order: <p> |
|
580 * |
|
581 * 1) if a factory is set, use it <p> |
|
582 * 2) if a CommandMap is set, use it <p> |
|
583 * 3) use the default CommandMap <p> |
|
584 * |
|
585 * In any case, wrap the real DataContentHandler with one of our own |
|
586 * to handle any missing cases, fill in defaults, and to ensure that |
|
587 * we always have a non-null DataContentHandler. |
|
588 * |
|
589 * @return the requested DataContentHandler |
|
590 */ |
|
591 private synchronized DataContentHandler getDataContentHandler() { |
|
592 |
|
593 // make sure the factory didn't change |
|
594 if (factory != oldFactory) { |
|
595 oldFactory = factory; |
|
596 factoryDCH = null; |
|
597 dataContentHandler = null; |
|
598 transferFlavors = emptyFlavors; |
|
599 } |
|
600 |
|
601 if (dataContentHandler != null) |
|
602 return dataContentHandler; |
|
603 |
|
604 String simpleMT = getBaseType(); |
|
605 |
|
606 if (factoryDCH == null && factory != null) |
|
607 factoryDCH = factory.createDataContentHandler(simpleMT); |
|
608 |
|
609 if (factoryDCH != null) |
|
610 dataContentHandler = factoryDCH; |
|
611 |
|
612 if (dataContentHandler == null) { |
|
613 if (dataSource != null) |
|
614 dataContentHandler = getCommandMap(). |
|
615 createDataContentHandler(simpleMT, dataSource); |
|
616 else |
|
617 dataContentHandler = getCommandMap(). |
|
618 createDataContentHandler(simpleMT); |
|
619 } |
|
620 |
|
621 // getDataContentHandler always uses these 'wrapper' handlers |
|
622 // to make sure it returns SOMETHING meaningful... |
|
623 if (dataSource != null) |
|
624 dataContentHandler = new DataSourceDataContentHandler( |
|
625 dataContentHandler, |
|
626 dataSource); |
|
627 else |
|
628 dataContentHandler = new ObjectDataContentHandler( |
|
629 dataContentHandler, |
|
630 object, |
|
631 objectMimeType); |
|
632 return dataContentHandler; |
|
633 } |
|
634 |
|
635 /** |
|
636 * Use the MimeType class to extract the MIME type/subtype, |
|
637 * ignoring the parameters. The type is cached. |
|
638 */ |
|
639 private synchronized String getBaseType() { |
|
640 if (shortType == null) { |
|
641 String ct = getContentType(); |
|
642 try { |
|
643 MimeType mt = new MimeType(ct); |
|
644 shortType = mt.getBaseType(); |
|
645 } catch (MimeTypeParseException e) { |
|
646 shortType = ct; |
|
647 } |
|
648 } |
|
649 return shortType; |
|
650 } |
|
651 |
|
652 /** |
|
653 * Sets the DataContentHandlerFactory. The DataContentHandlerFactory |
|
654 * is called first to find DataContentHandlers. |
|
655 * The DataContentHandlerFactory can only be set once. |
|
656 * <p> |
|
657 * If the DataContentHandlerFactory has already been set, |
|
658 * this method throws an Error. |
|
659 * |
|
660 * @param newFactory the DataContentHandlerFactory |
|
661 * @exception Error if the factory has already been defined. |
|
662 * |
|
663 * @see javax.activation.DataContentHandlerFactory |
|
664 */ |
|
665 public static synchronized void setDataContentHandlerFactory( |
|
666 DataContentHandlerFactory newFactory) { |
|
667 if (factory != null) |
|
668 throw new Error("DataContentHandlerFactory already defined"); |
|
669 |
|
670 SecurityManager security = System.getSecurityManager(); |
|
671 if (security != null) { |
|
672 try { |
|
673 // if it's ok with the SecurityManager, it's ok with me... |
|
674 security.checkSetFactory(); |
|
675 } catch (SecurityException ex) { |
|
676 // otherwise, we also allow it if this code and the |
|
677 // factory come from the same class loader (e.g., |
|
678 // the JAF classes were loaded with the applet classes). |
|
679 if (DataHandler.class.getClassLoader() != |
|
680 newFactory.getClass().getClassLoader()) |
|
681 throw ex; |
|
682 } |
|
683 } |
|
684 factory = newFactory; |
|
685 } |
|
686 } |
|
687 |
|
688 /** |
|
689 * The DataHanderDataSource class implements the |
|
690 * DataSource interface when the DataHandler is constructed |
|
691 * with an Object and a mimeType string. |
|
692 */ |
|
693 class DataHandlerDataSource implements DataSource { |
|
694 DataHandler dataHandler = null; |
|
695 |
|
696 /** |
|
697 * The constructor. |
|
698 */ |
|
699 public DataHandlerDataSource(DataHandler dh) { |
|
700 this.dataHandler = dh; |
|
701 } |
|
702 |
|
703 /** |
|
704 * Returns an <code>InputStream</code> representing this object. |
|
705 * @return the <code>InputStream</code> |
|
706 */ |
|
707 public InputStream getInputStream() throws IOException { |
|
708 return dataHandler.getInputStream(); |
|
709 } |
|
710 |
|
711 /** |
|
712 * Returns the <code>OutputStream</code> for this object. |
|
713 * @return the <code>OutputStream</code> |
|
714 */ |
|
715 public OutputStream getOutputStream() throws IOException { |
|
716 return dataHandler.getOutputStream(); |
|
717 } |
|
718 |
|
719 /** |
|
720 * Returns the MIME type of the data represented by this object. |
|
721 * @return the MIME type |
|
722 */ |
|
723 public String getContentType() { |
|
724 return dataHandler.getContentType(); |
|
725 } |
|
726 |
|
727 /** |
|
728 * Returns the name of this object. |
|
729 * @return the name of this object |
|
730 */ |
|
731 public String getName() { |
|
732 return dataHandler.getName(); // what else would it be? |
|
733 } |
|
734 } |
|
735 |
|
736 /* |
|
737 * DataSourceDataContentHandler |
|
738 * |
|
739 * This is a <i>private</i> DataContentHandler that wraps the real |
|
740 * DataContentHandler in the case where the DataHandler was instantiated |
|
741 * with a DataSource. |
|
742 */ |
|
743 class DataSourceDataContentHandler implements DataContentHandler { |
|
744 private DataSource ds = null; |
|
745 private DataFlavor transferFlavors[] = null; |
|
746 private DataContentHandler dch = null; |
|
747 |
|
748 /** |
|
749 * The constructor. |
|
750 */ |
|
751 public DataSourceDataContentHandler(DataContentHandler dch, DataSource ds) { |
|
752 this.ds = ds; |
|
753 this.dch = dch; |
|
754 } |
|
755 |
|
756 /** |
|
757 * Return the DataFlavors for this <code>DataContentHandler</code>. |
|
758 * @return the DataFlavors |
|
759 */ |
|
760 public DataFlavor[] getTransferDataFlavors() { |
|
761 |
|
762 if (transferFlavors == null) { |
|
763 if (dch != null) { // is there a dch? |
|
764 transferFlavors = dch.getTransferDataFlavors(); |
|
765 } else { |
|
766 transferFlavors = new DataFlavor[1]; |
|
767 transferFlavors[0] = |
|
768 new ActivationDataFlavor(ds.getContentType(), |
|
769 ds.getContentType()); |
|
770 } |
|
771 } |
|
772 return transferFlavors; |
|
773 } |
|
774 |
|
775 /** |
|
776 * Return the Transfer Data of type DataFlavor from InputStream. |
|
777 * @param df the DataFlavor |
|
778 * @param ds the DataSource |
|
779 * @return the constructed Object |
|
780 */ |
|
781 public Object getTransferData(DataFlavor df, DataSource ds) throws |
|
782 UnsupportedFlavorException, IOException { |
|
783 |
|
784 if (dch != null) |
|
785 return dch.getTransferData(df, ds); |
|
786 else if (df.equals(getTransferDataFlavors()[0])) // only have one now |
|
787 return ds.getInputStream(); |
|
788 else |
|
789 throw new UnsupportedFlavorException(df); |
|
790 } |
|
791 |
|
792 public Object getContent(DataSource ds) throws IOException { |
|
793 |
|
794 if (dch != null) |
|
795 return dch.getContent(ds); |
|
796 else |
|
797 return ds.getInputStream(); |
|
798 } |
|
799 |
|
800 /** |
|
801 * Write the object to the output stream. |
|
802 */ |
|
803 public void writeTo(Object obj, String mimeType, OutputStream os) |
|
804 throws IOException { |
|
805 if (dch != null) |
|
806 dch.writeTo(obj, mimeType, os); |
|
807 else |
|
808 throw new UnsupportedDataTypeException( |
|
809 "no DCH for content type " + ds.getContentType()); |
|
810 } |
|
811 } |
|
812 |
|
813 /* |
|
814 * ObjectDataContentHandler |
|
815 * |
|
816 * This is a <i>private</i> DataContentHandler that wraps the real |
|
817 * DataContentHandler in the case where the DataHandler was instantiated |
|
818 * with an object. |
|
819 */ |
|
820 class ObjectDataContentHandler implements DataContentHandler { |
|
821 private DataFlavor transferFlavors[] = null; |
|
822 private Object obj; |
|
823 private String mimeType; |
|
824 private DataContentHandler dch = null; |
|
825 |
|
826 /** |
|
827 * The constructor. |
|
828 */ |
|
829 public ObjectDataContentHandler(DataContentHandler dch, |
|
830 Object obj, String mimeType) { |
|
831 this.obj = obj; |
|
832 this.mimeType = mimeType; |
|
833 this.dch = dch; |
|
834 } |
|
835 |
|
836 /** |
|
837 * Return the DataContentHandler for this object. |
|
838 * Used only by the DataHandler class. |
|
839 */ |
|
840 public DataContentHandler getDCH() { |
|
841 return dch; |
|
842 } |
|
843 |
|
844 /** |
|
845 * Return the DataFlavors for this <code>DataContentHandler</code>. |
|
846 * @return the DataFlavors |
|
847 */ |
|
848 public synchronized DataFlavor[] getTransferDataFlavors() { |
|
849 if (transferFlavors == null) { |
|
850 if (dch != null) { |
|
851 transferFlavors = dch.getTransferDataFlavors(); |
|
852 } else { |
|
853 transferFlavors = new DataFlavor[1]; |
|
854 transferFlavors[0] = new ActivationDataFlavor(obj.getClass(), |
|
855 mimeType, mimeType); |
|
856 } |
|
857 } |
|
858 return transferFlavors; |
|
859 } |
|
860 |
|
861 /** |
|
862 * Return the Transfer Data of type DataFlavor from InputStream. |
|
863 * @param df the DataFlavor |
|
864 * @param ds the DataSource |
|
865 * @return the constructed Object |
|
866 */ |
|
867 public Object getTransferData(DataFlavor df, DataSource ds) |
|
868 throws UnsupportedFlavorException, IOException { |
|
869 |
|
870 if (dch != null) |
|
871 return dch.getTransferData(df, ds); |
|
872 else if (df.equals(getTransferDataFlavors()[0])) // only have one now |
|
873 return obj; |
|
874 else |
|
875 throw new UnsupportedFlavorException(df); |
|
876 |
|
877 } |
|
878 |
|
879 public Object getContent(DataSource ds) { |
|
880 return obj; |
|
881 } |
|
882 |
|
883 /** |
|
884 * Write the object to the output stream. |
|
885 */ |
|
886 public void writeTo(Object obj, String mimeType, OutputStream os) |
|
887 throws IOException { |
|
888 if (dch != null) |
|
889 dch.writeTo(obj, mimeType, os); |
|
890 else if (obj instanceof byte[]) |
|
891 os.write((byte[])obj); |
|
892 else if (obj instanceof String) { |
|
893 OutputStreamWriter osw = new OutputStreamWriter(os); |
|
894 osw.write((String)obj); |
|
895 osw.flush(); |
|
896 } else throw new UnsupportedDataTypeException( |
|
897 "no object DCH for MIME type " + this.mimeType); |
|
898 } |
|
899 } |