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