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
26package javax.activation;
27
28import java.io.InputStream;
29import java.io.IOException;
30import java.io.OutputStream;
31import java.io.PipedInputStream;
32import java.io.PipedOutputStream;
33import java.io.OutputStreamWriter;
34import java.net.URL;
35import java.awt.datatransfer.Transferable;
36import java.awt.datatransfer.DataFlavor;
37import 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
77public 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 */
694class 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 */
744class 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 */
821class 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}
901