src/java.activation/share/classes/javax/activation/DataHandler.java
changeset 47216 71c04702a3d5
parent 45678 65fdff10664d
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     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 }