ohair@286: /* ohair@286: * Copyright (c) 1997, 2006, 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();
ohair@286: return transferFlavors;
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: }