package org.gel.mauve.gui.dnd;

/**
 * At last an easy way to encapsulate your custom objects for dragging and
 * dropping in your Java programs! When you need to create a
 * {@link java.awt.datatransfer.Transferable}object, use this class to wrap
 * your object. For example:
 * 
 * <pre><code>
 * 
 *       ...
 *       MyCoolClass myObj = new MyCoolClass();
 *       Transferable xfer = new TransferableObject( myObj );
 *       ...
 *  
 * </code></pre>
 * 
 * Or if you need to know when the data was actually dropped, like when you're
 * moving data out of a list, say, you can use the
 * {@link TransferableObject.Fetcher}inner class to return your object Just in
 * Time. For example:
 * 
 * <pre><code>
 * 
 *       ...
 *       final MyCoolClass myObj = new MyCoolClass();
 * 
 *       TransferableObject.Fetcher fetcher = new TransferableObject.Fetcher()
 *       {   public Object getObject(){ return myObj; }
 *       }; // end fetcher
 * 
 *       Transferable xfer = new TransferableObject( fetcher );
 *       ...
 *  
 * </code></pre>
 * 
 * The {@link java.awt.datatransfer.DataFlavor}associated with
 * {@link TransferableObject}has the representation class
 * <tt>net.iharder.dnd.TransferableObject.class</tt> and MIME type
 * <tt>application/x-net.iharder.dnd.TransferableObject</tt>. This data
 * flavor is accessible via the static {@link #DATA_FLAVOR}property.
 * 
 * 
 * <p>
 * <em>This code is licensed for public use under the Common Public License version 0.5.</em>
 * <br/>The Common Public License, developed by IBM and modeled after their
 * industry-friendly IBM Public License, differs from other common open source
 * licenses in several important ways:
 * <ul>
 * <li>You may include this software with other software that uses a different
 * (even non-open source) license.</li>
 * <li>You may use this software to make for-profit software.</li>
 * <li>Your patent rights, should you generate patents, are protected.</li>
 * </ul>
 * </p>
 * <p>
 * <em>Copyright (c) 2001 Robert Harder</em>
 * </p>
 * 
 * @author Robert.Harder
 * @copyright 2001
 * @version 1.1
 */
public class TransferableObject implements java.awt.datatransfer.Transferable
{
    /**
     * The MIME type for {@link #DATA_FLAVOR}is
     * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
     * 
     * @since 1.1
     */
    public final static String MIME_TYPE = "application/x-mauve.TransferableObject";
    //    public final static String MIME_TYPE =
    // "application/x-java-serialized-object";

    /**
     * The default {@link java.awt.datatransfer.DataFlavor}for
     * {@link TransferableObject}has the representation class
     * <tt>net.iharder.dnd.TransferableObject.class</tt> and the MIME type
     * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
     * 
     * @since 1.1
     */
    public final static java.awt.datatransfer.DataFlavor DATA_FLAVOR = new java.awt.datatransfer.DataFlavor(TransferableObject.class, MIME_TYPE);

    private Fetcher fetcher;
    private Object data;

    private java.awt.datatransfer.DataFlavor customFlavor;

    /**
     * Creates a new {@link TransferableObject}that wraps <var>data </var>.
     * Along with the {@link #DATA_FLAVOR}associated with this class, this
     * creates a custom data flavor with a representation class determined from
     * <code>data.getClass()</code> and the MIME type
     * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
     * 
     * @param data
     *            The data to transfer
     * @since 1.1
     */
    public TransferableObject(Object data)
    {
        this.data = data;
        this.customFlavor = new java.awt.datatransfer.DataFlavor(data.getClass(), MIME_TYPE);
    } // end constructor

    /**
     * Creates a new {@link TransferableObject}that will return the object that
     * is returned by <var>fetcher </var>. No custom data flavor is set other
     * than the default {@link #DATA_FLAVOR}.
     * 
     * @see Fetcher
     * @param fetcher
     *            The {@link Fetcher}that will return the data object
     * @since 1.1
     */
    public TransferableObject(Fetcher fetcher)
    {
        this.fetcher = fetcher;
    } // end constructor

    /**
     * Creates a new {@link TransferableObject}that will return the object that
     * is returned by <var>fetcher </var>. Along with the {@link #DATA_FLAVOR}
     * associated with this class, this creates a custom data flavor with a
     * representation class <var>dataClass </var> and the MIME type
     * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
     * 
     * @see Fetcher
     * @param dataClass
     *            The {@link java.lang.Class}to use in the custom data flavor
     * @param fetcher
     *            The {@link Fetcher}that will return the data object
     * @since 1.1
     */
    public TransferableObject(Class dataClass, Fetcher fetcher)
    {
        this.fetcher = fetcher;
        this.customFlavor = new java.awt.datatransfer.DataFlavor(dataClass, MIME_TYPE);
    } // end constructor

    /**
     * Returns the custom {@link java.awt.datatransfer.DataFlavor}associated
     * with the encapsulated object or <tt>null</tt> if the {@link Fetcher}
     * constructor was used without passing a {@link java.lang.Class}.
     * 
     * @return The custom data flavor for the encapsulated object
     * @since 1.1
     */
    public java.awt.datatransfer.DataFlavor getCustomDataFlavor()
    {
        return customFlavor;
    } // end getCustomDataFlavor

    /* ******** T R A N S F E R A B L E M E T H O D S ******** */

    /**
     * Returns a two- or three-element array containing first the custom data
     * flavor, if one was created in the constructors, second the default
     * {@link #DATA_FLAVOR}associated with {@link TransferableObject}, and
     * third the {@link java.awt.datatransfer.DataFlavor.stringFlavor}.
     * 
     * @return An array of supported data flavors
     * @since 1.1
     */
    public java.awt.datatransfer.DataFlavor[] getTransferDataFlavors()
    {
        if (customFlavor != null)
            return new java.awt.datatransfer.DataFlavor[] { customFlavor, DATA_FLAVOR, java.awt.datatransfer.DataFlavor.stringFlavor }; // end
                                                                                                                                        // flavors
                                                                                                                                        // array
        else
            return new java.awt.datatransfer.DataFlavor[] { DATA_FLAVOR, java.awt.datatransfer.DataFlavor.stringFlavor }; // end
                                                                                                                          // flavors
                                                                                                                          // array
    } // end getTransferDataFlavors

    /**
     * Returns the data encapsulated in this {@link TransferableObject}. If the
     * {@link Fetcher}constructor was used, then this is when the
     * {@link Fetcher#getObject getObject()}method will be called. If the
     * requested data flavor is not supported, then the
     * {@link Fetcher#getObject getObject()}method will not be called.
     * 
     * @param flavor
     *            The data flavor for the data to return
     * @return The dropped data
     * @since 1.1
     */
    public Object getTransferData(java.awt.datatransfer.DataFlavor flavor) throws java.awt.datatransfer.UnsupportedFlavorException, java.io.IOException
    {
        // Native object
        if (flavor.equals(DATA_FLAVOR))
            return fetcher == null ? data : fetcher.getObject();

        // String
        if (flavor.equals(java.awt.datatransfer.DataFlavor.stringFlavor))
            return fetcher == null ? data.toString() : fetcher.getObject().toString();

        if (flavor.equals(customFlavor))
            return fetcher == null ? data : fetcher.getObject();

        // We can't do anything else
        throw new java.awt.datatransfer.UnsupportedFlavorException(flavor);
    } // end getTransferData

    /**
     * Returns <tt>true</tt> if <var>flavor </var> is one of the supported
     * flavors. Flavors are supported using the <code>equals(...)</code>
     * method.
     * 
     * @param flavor
     *            The data flavor to check
     * @return Whether or not the flavor is supported
     * @since 1.1
     */
    public boolean isDataFlavorSupported(java.awt.datatransfer.DataFlavor flavor)
    {
        // Native object
        if (flavor.equals(DATA_FLAVOR))
            return true;

        // String
        if (flavor.equals(java.awt.datatransfer.DataFlavor.stringFlavor))
            return true;

        if (flavor.equals(customFlavor))
            return true;

        // We can't do anything else
        return false;
    } // end isDataFlavorSupported

    /* ******** I N N E R I N T E R F A C E F E T C H E R ******** */

    /**
     * Instead of passing your data directly to the {@link TransferableObject}
     * constructor, you may want to know exactly when your data was received in
     * case you need to remove it from its source (or do anyting else to it).
     * When the {@link #getTransferData getTransferData(...)}method is called
     * on the {@link TransferableObject}, the {@link Fetcher}'s
     * {@link #getObject getObject()}method will be called.
     * 
     * @author Robert Harder
     * @copyright 2001
     * @version 1.1
     * @since 1.1
     */
    public static interface Fetcher
    {
        /**
         * Return the object being encapsulated in the
         * {@link TransferableObject}.
         * 
         * @return The dropped object
         * @since 1.1
         */
        public abstract Object getObject();
    } // end inner interface Fetcher

} // end class TransferableObject
