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 }