ohair@286: /* mkos@494: * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ohair@286: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ohair@286: * ohair@286: * This code is free software; you can redistribute it and/or modify it ohair@286: * under the terms of the GNU General Public License version 2 only, as ohair@286: * published by the Free Software Foundation. Oracle designates this ohair@286: * particular file as subject to the "Classpath" exception as provided ohair@286: * by Oracle in the LICENSE file that accompanied this code. ohair@286: * ohair@286: * This code is distributed in the hope that it will be useful, but WITHOUT ohair@286: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ohair@286: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ohair@286: * version 2 for more details (a copy is included in the LICENSE file that ohair@286: * accompanied this code). ohair@286: * ohair@286: * You should have received a copy of the GNU General Public License version ohair@286: * 2 along with this work; if not, write to the Free Software Foundation, ohair@286: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ohair@286: * ohair@286: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@286: * or visit www.oracle.com if you need additional information or have any ohair@286: * questions. ohair@286: */ ohair@286: ohair@286: package javax.activation; ohair@286: ohair@286: import java.io.InputStream; ohair@286: import java.io.IOException; ohair@286: import java.io.OutputStream; ohair@286: import java.io.PipedInputStream; ohair@286: import java.io.PipedOutputStream; ohair@286: import java.io.OutputStreamWriter; ohair@286: import java.net.URL; ohair@286: import java.awt.datatransfer.Transferable; ohair@286: import java.awt.datatransfer.DataFlavor; ohair@286: import java.awt.datatransfer.UnsupportedFlavorException; ohair@286: ohair@286: /** ohair@286: * The DataHandler class provides a consistent interface to data ohair@286: * available in many different sources and formats. ohair@286: * It manages simple stream to string conversions and related operations ohair@286: * using DataContentHandlers. ohair@286: * It provides access to commands that can operate on the data. ohair@286: * The commands are found using a CommandMap.

ohair@286: * ohair@286: * DataHandler and the Transferable Interface

ohair@286: * DataHandler implements the Transferable interface so that data can ohair@286: * be used in AWT data transfer operations, such as cut and paste and ohair@286: * drag and drop. The implementation of the Transferable interface ohair@286: * relies on the availability of an installed DataContentHandler ohair@286: * object corresponding to the MIME type of the data represented in ohair@286: * the specific instance of the DataHandler.

ohair@286: * ohair@286: * DataHandler and CommandMaps

ohair@286: * The DataHandler keeps track of the current CommandMap that it uses to ohair@286: * service requests for commands (getCommand, ohair@286: * getAllCommands, getPreferredCommands). ohair@286: * Each instance of a DataHandler may have a CommandMap associated with ohair@286: * it using the setCommandMap method. If a CommandMap was ohair@286: * not set, DataHandler calls the getDefaultCommandMap ohair@286: * method in CommandMap and uses the value it returns. See ohair@286: * CommandMap for more information.

ohair@286: * ohair@286: * DataHandler and URLs

ohair@286: * The current DataHandler implementation creates a private ohair@286: * instance of URLDataSource when it is constructed with a URL. ohair@286: * ohair@286: * @see javax.activation.CommandMap ohair@286: * @see javax.activation.DataContentHandler ohair@286: * @see javax.activation.DataSource ohair@286: * @see javax.activation.URLDataSource ohair@286: * ohair@286: * @since 1.6 ohair@286: */ ohair@286: ohair@286: public class DataHandler implements Transferable { ohair@286: ohair@286: // Use the datasource to indicate whether we were started via the ohair@286: // DataSource constructor or the object constructor. ohair@286: private DataSource dataSource = null; ohair@286: private DataSource objDataSource = null; ohair@286: ohair@286: // The Object and mimetype from the constructor (if passed in). ohair@286: // object remains null if it was instantiated with a ohair@286: // DataSource. ohair@286: private Object object = null; ohair@286: private String objectMimeType = null; ohair@286: ohair@286: // Keep track of the CommandMap ohair@286: private CommandMap currentCommandMap = null; ohair@286: ohair@286: // our transfer flavors ohair@286: private static final DataFlavor emptyFlavors[] = new DataFlavor[0]; ohair@286: private DataFlavor transferFlavors[] = emptyFlavors; ohair@286: ohair@286: // our DataContentHandler ohair@286: private DataContentHandler dataContentHandler = null; ohair@286: private DataContentHandler factoryDCH = null; ohair@286: ohair@286: // our DataContentHandlerFactory ohair@286: private static DataContentHandlerFactory factory = null; ohair@286: private DataContentHandlerFactory oldFactory = null; ohair@286: // the short representation of the ContentType (sans params) ohair@286: private String shortType = null; ohair@286: ohair@286: /** ohair@286: * Create a DataHandler instance referencing the ohair@286: * specified DataSource. The data exists in a byte stream form. ohair@286: * The DataSource will provide an InputStream to access the data. ohair@286: * ohair@286: * @param ds the DataSource ohair@286: */ ohair@286: public DataHandler(DataSource ds) { ohair@286: // save a reference to the incoming DS ohair@286: dataSource = ds; ohair@286: oldFactory = factory; // keep track of the factory ohair@286: } ohair@286: ohair@286: /** ohair@286: * Create a DataHandler instance representing an object ohair@286: * of this MIME type. This constructor is ohair@286: * used when the application already has an in-memory representation ohair@286: * of the data in the form of a Java Object. ohair@286: * ohair@286: * @param obj the Java Object ohair@286: * @param mimeType the MIME type of the object ohair@286: */ ohair@286: public DataHandler(Object obj, String mimeType) { ohair@286: object = obj; ohair@286: objectMimeType = mimeType; ohair@286: oldFactory = factory; // keep track of the factory ohair@286: } ohair@286: ohair@286: /** ohair@286: * Create a DataHandler instance referencing a URL. ohair@286: * The DataHandler internally creates a URLDataSource ohair@286: * instance to represent the URL. ohair@286: * ohair@286: * @param url a URL object ohair@286: */ ohair@286: public DataHandler(URL url) { ohair@286: dataSource = new URLDataSource(url); ohair@286: oldFactory = factory; // keep track of the factory ohair@286: } ohair@286: ohair@286: /** ohair@286: * Return the CommandMap for this instance of DataHandler. ohair@286: */ ohair@286: private synchronized CommandMap getCommandMap() { ohair@286: if (currentCommandMap != null) ohair@286: return currentCommandMap; ohair@286: else ohair@286: return CommandMap.getDefaultCommandMap(); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Return the DataSource associated with this instance ohair@286: * of DataHandler. ohair@286: *

ohair@286: * For DataHandlers that have been instantiated with a DataSource, ohair@286: * this method returns the DataSource that was used to create the ohair@286: * DataHandler object. In other cases the DataHandler ohair@286: * constructs a DataSource from the data used to construct ohair@286: * the DataHandler. DataSources created for DataHandlers not ohair@286: * instantiated with a DataSource are cached for performance ohair@286: * reasons. ohair@286: * ohair@286: * @return a valid DataSource object for this DataHandler ohair@286: */ ohair@286: public DataSource getDataSource() { ohair@286: if (dataSource == null) { ohair@286: // create one on the fly ohair@286: if (objDataSource == null) ohair@286: objDataSource = new DataHandlerDataSource(this); ohair@286: return objDataSource; ohair@286: } ohair@286: return dataSource; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Return the name of the data object. If this DataHandler ohair@286: * was created with a DataSource, this method calls through ohair@286: * to the DataSource.getName method, otherwise it ohair@286: * returns null. ohair@286: * ohair@286: * @return the name of the object ohair@286: */ ohair@286: public String getName() { ohair@286: if (dataSource != null) ohair@286: return dataSource.getName(); ohair@286: else ohair@286: return null; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Return the MIME type of this object as retrieved from ohair@286: * the source object. Note that this is the full ohair@286: * type with parameters. ohair@286: * ohair@286: * @return the MIME type ohair@286: */ ohair@286: public String getContentType() { ohair@286: if (dataSource != null) // data source case ohair@286: return dataSource.getContentType(); ohair@286: else ohair@286: return objectMimeType; // obj/type case ohair@286: } ohair@286: ohair@286: /** ohair@286: * Get the InputStream for this object.

ohair@286: * ohair@286: * For DataHandlers instantiated with a DataSource, the DataHandler ohair@286: * calls the DataSource.getInputStream method and ohair@286: * returns the result to the caller. ohair@286: *

ohair@286: * For DataHandlers instantiated with an Object, the DataHandler ohair@286: * first attempts to find a DataContentHandler for the Object. If ohair@286: * the DataHandler can not find a DataContentHandler for this MIME ohair@286: * type, it throws an UnsupportedDataTypeException. If it is ohair@286: * successful, it creates a pipe and a thread. The thread uses the ohair@286: * DataContentHandler's writeTo method to write the ohair@286: * stream data into one end of the pipe. The other end of the pipe ohair@286: * is returned to the caller. Because a thread is created to copy ohair@286: * the data, IOExceptions that may occur during the copy can not be ohair@286: * propagated back to the caller. The result is an empty stream.

ohair@286: * ohair@286: * @return the InputStream representing this data ohair@286: * @exception IOException if an I/O error occurs ohair@286: * ohair@286: * @see javax.activation.DataContentHandler#writeTo ohair@286: * @see javax.activation.UnsupportedDataTypeException ohair@286: */ ohair@286: public InputStream getInputStream() throws IOException { ohair@286: InputStream ins = null; ohair@286: ohair@286: if (dataSource != null) { ohair@286: ins = dataSource.getInputStream(); ohair@286: } else { ohair@286: DataContentHandler dch = getDataContentHandler(); ohair@286: // we won't even try if we can't get a dch ohair@286: if (dch == null) ohair@286: throw new UnsupportedDataTypeException( ohair@286: "no DCH for MIME type " + getBaseType()); ohair@286: ohair@286: if (dch instanceof ObjectDataContentHandler) { ohair@286: if (((ObjectDataContentHandler)dch).getDCH() == null) ohair@286: throw new UnsupportedDataTypeException( ohair@286: "no object DCH for MIME type " + getBaseType()); ohair@286: } ohair@286: // there is none but the default^^^^^^^^^^^^^^^^ ohair@286: final DataContentHandler fdch = dch; ohair@286: ohair@286: // from bill s. ohair@286: // ce n'est pas une pipe! ohair@286: // ohair@286: // NOTE: This block of code needs to throw exceptions, but ohair@286: // can't because it is in another thread!!! ARG! ohair@286: // ohair@286: final PipedOutputStream pos = new PipedOutputStream(); ohair@286: PipedInputStream pin = new PipedInputStream(pos); ohair@286: new Thread( ohair@286: new Runnable() { ohair@286: public void run() { ohair@286: try { ohair@286: fdch.writeTo(object, objectMimeType, pos); ohair@286: } catch (IOException e) { ohair@286: ohair@286: } finally { ohair@286: try { ohair@286: pos.close(); ohair@286: } catch (IOException ie) { } ohair@286: } ohair@286: } ohair@286: }, ohair@286: "DataHandler.getInputStream").start(); ohair@286: ins = pin; ohair@286: } ohair@286: ohair@286: return ins; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Write the data to an OutputStream.

ohair@286: * ohair@286: * If the DataHandler was created with a DataSource, writeTo ohair@286: * retrieves the InputStream and copies the bytes from the ohair@286: * InputStream to the OutputStream passed in. ohair@286: *

ohair@286: * If the DataHandler was created with an object, writeTo ohair@286: * retrieves the DataContentHandler for the object's type. ohair@286: * If the DataContentHandler was found, it calls the ohair@286: * writeTo method on the DataContentHandler. ohair@286: * ohair@286: * @param os the OutputStream to write to ohair@286: * @exception IOException if an I/O error occurs ohair@286: */ ohair@286: public void writeTo(OutputStream os) throws IOException { ohair@286: // for the DataSource case ohair@286: if (dataSource != null) { ohair@286: InputStream is = null; ohair@286: byte data[] = new byte[8*1024]; ohair@286: int bytes_read; ohair@286: ohair@286: is = dataSource.getInputStream(); ohair@286: ohair@286: try { ohair@286: while ((bytes_read = is.read(data)) > 0) { ohair@286: os.write(data, 0, bytes_read); ohair@286: } ohair@286: } finally { ohair@286: is.close(); ohair@286: is = null; ohair@286: } ohair@286: } else { // for the Object case ohair@286: DataContentHandler dch = getDataContentHandler(); ohair@286: dch.writeTo(object, objectMimeType, os); ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Get an OutputStream for this DataHandler to allow overwriting ohair@286: * the underlying data. ohair@286: * If the DataHandler was created with a DataSource, the ohair@286: * DataSource's getOutputStream method is called. ohair@286: * Otherwise, null is returned. ohair@286: * ohair@286: * @return the OutputStream ohair@286: * ohair@286: * @see javax.activation.DataSource#getOutputStream ohair@286: * @see javax.activation.URLDataSource ohair@286: */ ohair@286: public OutputStream getOutputStream() throws IOException { ohair@286: if (dataSource != null) ohair@286: return dataSource.getOutputStream(); ohair@286: else ohair@286: return null; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Return the DataFlavors in which this data is available.

ohair@286: * ohair@286: * Returns an array of DataFlavor objects indicating the flavors ohair@286: * the data can be provided in. The array is usually ordered ohair@286: * according to preference for providing the data, from most ohair@286: * richly descriptive to least richly descriptive.

ohair@286: * ohair@286: * The DataHandler attempts to find a DataContentHandler that ohair@286: * corresponds to the MIME type of the data. If one is located, ohair@286: * the DataHandler calls the DataContentHandler's ohair@286: * getTransferDataFlavors method.

ohair@286: * ohair@286: * If a DataContentHandler can not be located, and if the ohair@286: * DataHandler was created with a DataSource (or URL), one ohair@286: * DataFlavor is returned that represents this object's MIME type ohair@286: * and the java.io.InputStream class. If the ohair@286: * DataHandler was created with an object and a MIME type, ohair@286: * getTransferDataFlavors returns one DataFlavor that represents ohair@286: * this object's MIME type and the object's class. ohair@286: * ohair@286: * @return an array of data flavors in which this data can be transferred ohair@286: * @see javax.activation.DataContentHandler#getTransferDataFlavors ohair@286: */ ohair@286: public synchronized DataFlavor[] getTransferDataFlavors() { ohair@286: if (factory != oldFactory) // if the factory has changed, clear cache ohair@286: transferFlavors = emptyFlavors; ohair@286: ohair@286: // if it's not set, set it... ohair@286: if (transferFlavors == emptyFlavors) ohair@286: transferFlavors = getDataContentHandler().getTransferDataFlavors(); mkos@494: mkos@494: if (transferFlavors == emptyFlavors) mkos@494: return transferFlavors; mkos@494: else mkos@494: return transferFlavors.clone(); mkos@494: ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns whether the specified data flavor is supported ohair@286: * for this object.

ohair@286: * ohair@286: * This method iterates through the DataFlavors returned from ohair@286: * getTransferDataFlavors, comparing each with ohair@286: * the specified flavor. ohair@286: * ohair@286: * @param flavor the requested flavor for the data ohair@286: * @return true if the data flavor is supported ohair@286: * @see javax.activation.DataHandler#getTransferDataFlavors ohair@286: */ ohair@286: public boolean isDataFlavorSupported(DataFlavor flavor) { ohair@286: DataFlavor[] lFlavors = getTransferDataFlavors(); ohair@286: ohair@286: for (int i = 0; i < lFlavors.length; i++) { ohair@286: if (lFlavors[i].equals(flavor)) ohair@286: return true; ohair@286: } ohair@286: return false; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns an object that represents the data to be ohair@286: * transferred. The class of the object returned is defined by the ohair@286: * representation class of the data flavor.

ohair@286: * ohair@286: * For DataHandler's created with DataSources or URLs:

ohair@286: * ohair@286: * The DataHandler attempts to locate a DataContentHandler ohair@286: * for this MIME type. If one is found, the passed in DataFlavor ohair@286: * and the type of the data are passed to its getTransferData ohair@286: * method. If the DataHandler fails to locate a DataContentHandler ohair@286: * and the flavor specifies this object's MIME type and the ohair@286: * java.io.InputStream class, this object's InputStream ohair@286: * is returned. ohair@286: * Otherwise it throws an UnsupportedFlavorException.

ohair@286: * ohair@286: * For DataHandler's created with Objects:

ohair@286: * ohair@286: * The DataHandler attempts to locate a DataContentHandler ohair@286: * for this MIME type. If one is found, the passed in DataFlavor ohair@286: * and the type of the data are passed to its getTransferData ohair@286: * method. If the DataHandler fails to locate a DataContentHandler ohair@286: * and the flavor specifies this object's MIME type and its class, ohair@286: * this DataHandler's referenced object is returned. ohair@286: * Otherwise it throws an UnsupportedFlavorException. ohair@286: * ohair@286: * @param flavor the requested flavor for the data ohair@286: * @return the object ohair@286: * @exception UnsupportedFlavorException if the data could not be ohair@286: * converted to the requested flavor ohair@286: * @exception IOException if an I/O error occurs ohair@286: * @see javax.activation.ActivationDataFlavor ohair@286: */ ohair@286: public Object getTransferData(DataFlavor flavor) ohair@286: throws UnsupportedFlavorException, IOException { ohair@286: return getDataContentHandler().getTransferData(flavor, dataSource); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Set the CommandMap for use by this DataHandler. ohair@286: * Setting it to null causes the CommandMap to revert ohair@286: * to the CommandMap returned by the ohair@286: * CommandMap.getDefaultCommandMap method. ohair@286: * Changing the CommandMap, or setting it to null, ohair@286: * clears out any data cached from the previous CommandMap. ohair@286: * ohair@286: * @param commandMap the CommandMap to use in this DataHandler ohair@286: * ohair@286: * @see javax.activation.CommandMap#setDefaultCommandMap ohair@286: */ ohair@286: public synchronized void setCommandMap(CommandMap commandMap) { ohair@286: if (commandMap != currentCommandMap || commandMap == null) { ohair@286: // clear cached values... ohair@286: transferFlavors = emptyFlavors; ohair@286: dataContentHandler = null; ohair@286: ohair@286: currentCommandMap = commandMap; ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Return the preferred commands for this type of data. ohair@286: * This method calls the getPreferredCommands method ohair@286: * in the CommandMap associated with this instance of DataHandler. ohair@286: * This method returns an array that represents a subset of ohair@286: * available commands. In cases where multiple commands for the ohair@286: * MIME type represented by this DataHandler are present, the ohair@286: * installed CommandMap chooses the appropriate commands. ohair@286: * ohair@286: * @return the CommandInfo objects representing the preferred commands ohair@286: * ohair@286: * @see javax.activation.CommandMap#getPreferredCommands ohair@286: */ ohair@286: public CommandInfo[] getPreferredCommands() { ohair@286: if (dataSource != null) ohair@286: return getCommandMap().getPreferredCommands(getBaseType(), ohair@286: dataSource); ohair@286: else ohair@286: return getCommandMap().getPreferredCommands(getBaseType()); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Return all the commands for this type of data. ohair@286: * This method returns an array containing all commands ohair@286: * for the type of data represented by this DataHandler. The ohair@286: * MIME type for the underlying data represented by this DataHandler ohair@286: * is used to call through to the getAllCommands method ohair@286: * of the CommandMap associated with this DataHandler. ohair@286: * ohair@286: * @return the CommandInfo objects representing all the commands ohair@286: * ohair@286: * @see javax.activation.CommandMap#getAllCommands ohair@286: */ ohair@286: public CommandInfo[] getAllCommands() { ohair@286: if (dataSource != null) ohair@286: return getCommandMap().getAllCommands(getBaseType(), dataSource); ohair@286: else ohair@286: return getCommandMap().getAllCommands(getBaseType()); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Get the command cmdName. Use the search semantics as ohair@286: * defined by the CommandMap installed in this DataHandler. The ohair@286: * MIME type for the underlying data represented by this DataHandler ohair@286: * is used to call through to the getCommand method ohair@286: * of the CommandMap associated with this DataHandler. ohair@286: * ohair@286: * @param cmdName the command name ohair@286: * @return the CommandInfo corresponding to the command ohair@286: * ohair@286: * @see javax.activation.CommandMap#getCommand ohair@286: */ ohair@286: public CommandInfo getCommand(String cmdName) { ohair@286: if (dataSource != null) ohair@286: return getCommandMap().getCommand(getBaseType(), cmdName, ohair@286: dataSource); ohair@286: else ohair@286: return getCommandMap().getCommand(getBaseType(), cmdName); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Return the data in its preferred Object form.

ohair@286: * ohair@286: * If the DataHandler was instantiated with an object, return ohair@286: * the object.

ohair@286: * ohair@286: * If the DataHandler was instantiated with a DataSource, ohair@286: * this method uses a DataContentHandler to return the content ohair@286: * object for the data represented by this DataHandler. If no ohair@286: * DataContentHandler can be found for the ohair@286: * the type of this data, the DataHandler returns an ohair@286: * InputStream for the data. ohair@286: * ohair@286: * @return the content. ohair@286: * @exception IOException if an IOException occurs during ohair@286: * this operation. ohair@286: */ ohair@286: public Object getContent() throws IOException { ohair@286: if (object != null) ohair@286: return object; ohair@286: else ohair@286: return getDataContentHandler().getContent(getDataSource()); ohair@286: } ohair@286: ohair@286: /** ohair@286: * A convenience method that takes a CommandInfo object ohair@286: * and instantiates the corresponding command, usually ohair@286: * a JavaBean component. ohair@286: *

ohair@286: * This method calls the CommandInfo's getCommandObject ohair@286: * method with the ClassLoader used to load ohair@286: * the javax.activation.DataHandler class itself. ohair@286: * ohair@286: * @param cmdinfo the CommandInfo corresponding to a command ohair@286: * @return the instantiated command object ohair@286: */ ohair@286: public Object getBean(CommandInfo cmdinfo) { ohair@286: Object bean = null; ohair@286: ohair@286: try { ohair@286: // make the bean ohair@286: ClassLoader cld = null; ohair@286: // First try the "application's" class loader. ohair@286: cld = SecuritySupport.getContextClassLoader(); ohair@286: if (cld == null) ohair@286: cld = this.getClass().getClassLoader(); ohair@286: bean = cmdinfo.getCommandObject(this, cld); ohair@286: } catch (IOException e) { ohair@286: } catch (ClassNotFoundException e) { } ohair@286: ohair@286: return bean; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Get the DataContentHandler for this DataHandler:

ohair@286: * ohair@286: * If a DataContentHandlerFactory is set, use it. ohair@286: * Otherwise look for an object to serve DCH in the ohair@286: * following order:

ohair@286: * ohair@286: * 1) if a factory is set, use it

ohair@286: * 2) if a CommandMap is set, use it

ohair@286: * 3) use the default CommandMap

ohair@286: * ohair@286: * In any case, wrap the real DataContentHandler with one of our own ohair@286: * to handle any missing cases, fill in defaults, and to ensure that ohair@286: * we always have a non-null DataContentHandler. ohair@286: * ohair@286: * @return the requested DataContentHandler ohair@286: */ ohair@286: private synchronized DataContentHandler getDataContentHandler() { ohair@286: ohair@286: // make sure the factory didn't change ohair@286: if (factory != oldFactory) { ohair@286: oldFactory = factory; ohair@286: factoryDCH = null; ohair@286: dataContentHandler = null; ohair@286: transferFlavors = emptyFlavors; ohair@286: } ohair@286: ohair@286: if (dataContentHandler != null) ohair@286: return dataContentHandler; ohair@286: ohair@286: String simpleMT = getBaseType(); ohair@286: ohair@286: if (factoryDCH == null && factory != null) ohair@286: factoryDCH = factory.createDataContentHandler(simpleMT); ohair@286: ohair@286: if (factoryDCH != null) ohair@286: dataContentHandler = factoryDCH; ohair@286: ohair@286: if (dataContentHandler == null) { ohair@286: if (dataSource != null) ohair@286: dataContentHandler = getCommandMap(). ohair@286: createDataContentHandler(simpleMT, dataSource); ohair@286: else ohair@286: dataContentHandler = getCommandMap(). ohair@286: createDataContentHandler(simpleMT); ohair@286: } ohair@286: ohair@286: // getDataContentHandler always uses these 'wrapper' handlers ohair@286: // to make sure it returns SOMETHING meaningful... ohair@286: if (dataSource != null) ohair@286: dataContentHandler = new DataSourceDataContentHandler( ohair@286: dataContentHandler, ohair@286: dataSource); ohair@286: else ohair@286: dataContentHandler = new ObjectDataContentHandler( ohair@286: dataContentHandler, ohair@286: object, ohair@286: objectMimeType); ohair@286: return dataContentHandler; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Use the MimeType class to extract the MIME type/subtype, ohair@286: * ignoring the parameters. The type is cached. ohair@286: */ ohair@286: private synchronized String getBaseType() { ohair@286: if (shortType == null) { ohair@286: String ct = getContentType(); ohair@286: try { ohair@286: MimeType mt = new MimeType(ct); ohair@286: shortType = mt.getBaseType(); ohair@286: } catch (MimeTypeParseException e) { ohair@286: shortType = ct; ohair@286: } ohair@286: } ohair@286: return shortType; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Sets the DataContentHandlerFactory. The DataContentHandlerFactory ohair@286: * is called first to find DataContentHandlers. ohair@286: * The DataContentHandlerFactory can only be set once. ohair@286: *

ohair@286: * If the DataContentHandlerFactory has already been set, ohair@286: * this method throws an Error. ohair@286: * ohair@286: * @param newFactory the DataContentHandlerFactory ohair@286: * @exception Error if the factory has already been defined. ohair@286: * ohair@286: * @see javax.activation.DataContentHandlerFactory ohair@286: */ ohair@286: public static synchronized void setDataContentHandlerFactory( ohair@286: DataContentHandlerFactory newFactory) { ohair@286: if (factory != null) ohair@286: throw new Error("DataContentHandlerFactory already defined"); ohair@286: ohair@286: SecurityManager security = System.getSecurityManager(); ohair@286: if (security != null) { ohair@286: try { ohair@286: // if it's ok with the SecurityManager, it's ok with me... ohair@286: security.checkSetFactory(); ohair@286: } catch (SecurityException ex) { ohair@286: // otherwise, we also allow it if this code and the ohair@286: // factory come from the same class loader (e.g., ohair@286: // the JAF classes were loaded with the applet classes). ohair@286: if (DataHandler.class.getClassLoader() != ohair@286: newFactory.getClass().getClassLoader()) ohair@286: throw ex; ohair@286: } ohair@286: } ohair@286: factory = newFactory; ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * The DataHanderDataSource class implements the ohair@286: * DataSource interface when the DataHandler is constructed ohair@286: * with an Object and a mimeType string. ohair@286: */ ohair@286: class DataHandlerDataSource implements DataSource { ohair@286: DataHandler dataHandler = null; ohair@286: ohair@286: /** ohair@286: * The constructor. ohair@286: */ ohair@286: public DataHandlerDataSource(DataHandler dh) { ohair@286: this.dataHandler = dh; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns an InputStream representing this object. ohair@286: * @return the InputStream ohair@286: */ ohair@286: public InputStream getInputStream() throws IOException { ohair@286: return dataHandler.getInputStream(); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns the OutputStream for this object. ohair@286: * @return the OutputStream ohair@286: */ ohair@286: public OutputStream getOutputStream() throws IOException { ohair@286: return dataHandler.getOutputStream(); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns the MIME type of the data represented by this object. ohair@286: * @return the MIME type ohair@286: */ ohair@286: public String getContentType() { ohair@286: return dataHandler.getContentType(); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns the name of this object. ohair@286: * @return the name of this object ohair@286: */ ohair@286: public String getName() { ohair@286: return dataHandler.getName(); // what else would it be? ohair@286: } ohair@286: } ohair@286: ohair@286: /* ohair@286: * DataSourceDataContentHandler ohair@286: * ohair@286: * This is a private DataContentHandler that wraps the real ohair@286: * DataContentHandler in the case where the DataHandler was instantiated ohair@286: * with a DataSource. ohair@286: */ ohair@286: class DataSourceDataContentHandler implements DataContentHandler { ohair@286: private DataSource ds = null; ohair@286: private DataFlavor transferFlavors[] = null; ohair@286: private DataContentHandler dch = null; ohair@286: ohair@286: /** ohair@286: * The constructor. ohair@286: */ ohair@286: public DataSourceDataContentHandler(DataContentHandler dch, DataSource ds) { ohair@286: this.ds = ds; ohair@286: this.dch = dch; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Return the DataFlavors for this DataContentHandler. ohair@286: * @return the DataFlavors ohair@286: */ ohair@286: public DataFlavor[] getTransferDataFlavors() { ohair@286: ohair@286: if (transferFlavors == null) { ohair@286: if (dch != null) { // is there a dch? ohair@286: transferFlavors = dch.getTransferDataFlavors(); ohair@286: } else { ohair@286: transferFlavors = new DataFlavor[1]; ohair@286: transferFlavors[0] = ohair@286: new ActivationDataFlavor(ds.getContentType(), ohair@286: ds.getContentType()); ohair@286: } ohair@286: } ohair@286: return transferFlavors; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Return the Transfer Data of type DataFlavor from InputStream. ohair@286: * @param df the DataFlavor ohair@286: * @param ds the DataSource ohair@286: * @return the constructed Object ohair@286: */ ohair@286: public Object getTransferData(DataFlavor df, DataSource ds) throws ohair@286: UnsupportedFlavorException, IOException { ohair@286: ohair@286: if (dch != null) ohair@286: return dch.getTransferData(df, ds); ohair@286: else if (df.equals(getTransferDataFlavors()[0])) // only have one now ohair@286: return ds.getInputStream(); ohair@286: else ohair@286: throw new UnsupportedFlavorException(df); ohair@286: } ohair@286: ohair@286: public Object getContent(DataSource ds) throws IOException { ohair@286: ohair@286: if (dch != null) ohair@286: return dch.getContent(ds); ohair@286: else ohair@286: return ds.getInputStream(); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Write the object to the output stream. ohair@286: */ ohair@286: public void writeTo(Object obj, String mimeType, OutputStream os) ohair@286: throws IOException { ohair@286: if (dch != null) ohair@286: dch.writeTo(obj, mimeType, os); ohair@286: else ohair@286: throw new UnsupportedDataTypeException( ohair@286: "no DCH for content type " + ds.getContentType()); ohair@286: } ohair@286: } ohair@286: ohair@286: /* ohair@286: * ObjectDataContentHandler ohair@286: * ohair@286: * This is a private DataContentHandler that wraps the real ohair@286: * DataContentHandler in the case where the DataHandler was instantiated ohair@286: * with an object. ohair@286: */ ohair@286: class ObjectDataContentHandler implements DataContentHandler { ohair@286: private DataFlavor transferFlavors[] = null; ohair@286: private Object obj; ohair@286: private String mimeType; ohair@286: private DataContentHandler dch = null; ohair@286: ohair@286: /** ohair@286: * The constructor. ohair@286: */ ohair@286: public ObjectDataContentHandler(DataContentHandler dch, ohair@286: Object obj, String mimeType) { ohair@286: this.obj = obj; ohair@286: this.mimeType = mimeType; ohair@286: this.dch = dch; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Return the DataContentHandler for this object. ohair@286: * Used only by the DataHandler class. ohair@286: */ ohair@286: public DataContentHandler getDCH() { ohair@286: return dch; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Return the DataFlavors for this DataContentHandler. ohair@286: * @return the DataFlavors ohair@286: */ ohair@286: public synchronized DataFlavor[] getTransferDataFlavors() { ohair@286: if (transferFlavors == null) { ohair@286: if (dch != null) { ohair@286: transferFlavors = dch.getTransferDataFlavors(); ohair@286: } else { ohair@286: transferFlavors = new DataFlavor[1]; ohair@286: transferFlavors[0] = new ActivationDataFlavor(obj.getClass(), ohair@286: mimeType, mimeType); ohair@286: } ohair@286: } ohair@286: return transferFlavors; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Return the Transfer Data of type DataFlavor from InputStream. ohair@286: * @param df the DataFlavor ohair@286: * @param ds the DataSource ohair@286: * @return the constructed Object ohair@286: */ ohair@286: public Object getTransferData(DataFlavor df, DataSource ds) ohair@286: throws UnsupportedFlavorException, IOException { ohair@286: ohair@286: if (dch != null) ohair@286: return dch.getTransferData(df, ds); ohair@286: else if (df.equals(getTransferDataFlavors()[0])) // only have one now ohair@286: return obj; ohair@286: else ohair@286: throw new UnsupportedFlavorException(df); ohair@286: ohair@286: } ohair@286: ohair@286: public Object getContent(DataSource ds) { ohair@286: return obj; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Write the object to the output stream. ohair@286: */ ohair@286: public void writeTo(Object obj, String mimeType, OutputStream os) ohair@286: throws IOException { ohair@286: if (dch != null) ohair@286: dch.writeTo(obj, mimeType, os); ohair@286: else if (obj instanceof byte[]) ohair@286: os.write((byte[])obj); ohair@286: else if (obj instanceof String) { ohair@286: OutputStreamWriter osw = new OutputStreamWriter(os); ohair@286: osw.write((String)obj); ohair@286: osw.flush(); ohair@286: } else throw new UnsupportedDataTypeException( ohair@286: "no object DCH for MIME type " + this.mimeType); ohair@286: } ohair@286: }