001package jmri.util.iharder.dnd;
002
003import java.awt.datatransfer.DataFlavor;
004
005/**
006 * At last an easy way to encapsulate your custom objects for dragging and
007 * dropping in your Java programs! When you need to create a
008 * {@link java.awt.datatransfer.Transferable} object, use this class to wrap
009 * your object. For example:
010 * <pre><code>
011 *      ...
012 *      MyCoolClass myObj = new MyCoolClass();
013 *      Transferable xfer = new TransferableObject( myObj );
014 *      ...
015 * </code></pre> Or if you need to know when the data was actually dropped, like
016 * when you're moving data out of a list, say, you can use the
017 * {@link TransferableObject.Fetcher} inner class to return your object Just in
018 * Time. For example:
019 * <pre><code>
020 *      ...
021 *      final MyCoolClass myObj = new MyCoolClass();
022 *
023 *      TransferableObject.Fetcher fetcher = new TransferableObject.Fetcher()
024 *      {   public Object getObject(){ return myObj; }
025 *      }; // end fetcher
026 *
027 *      Transferable xfer = new TransferableObject( fetcher );
028 *      ...
029 * </code></pre>
030 *
031 * The {@link java.awt.datatransfer.DataFlavor} associated with
032 * {@link TransferableObject} has the representation class
033 * {@code net.iharder.dnd.TransferableObject.class} and MIME type
034 * {@code application/x-net.iharder.dnd.TransferableObject}. This data flavor
035 * is accessible via the static {@link #DATA_FLAVOR} property.
036 *
037 * <p>
038 * <em>This code is licensed for public use under the Common Public License
039 * version 0.5.</em><br>
040 * The Common Public License, developed by IBM and modeled after their
041 * industry-friendly IBM Public License, differs from other common open source
042 * licenses in several important ways:
043 * <ul>
044 * <li>You may include this software with other software that uses a different
045 * (even non-open source) license.</li>
046 * <li>You may use this software to make for-profit software.</li>
047 * <li>Your patent rights, should you generate patents, are protected.</li>
048 * </ul>
049 * <p>
050 * <em>Copyright 2001 Robert Harder</em>
051 *
052 * @author Robert.Harder copyright 2001
053 * @version 1.1
054 */
055public class TransferableObject implements java.awt.datatransfer.Transferable {
056
057    /**
058     * The MIME type for {@link #DATA_FLAVOR} is
059     * {@code application/x-net.iharder.dnd.TransferableObject}.
060     *
061     * @since 1.1
062     */
063    public final static String MIME_TYPE = "application/x-net.iharder.dnd.TransferableObject";
064
065    /**
066     * The default {@link java.awt.datatransfer.DataFlavor} for
067     * {@link TransferableObject} has the representation class
068     * {@code net.iharder.dnd.TransferableObject.class}
069     * and the MIME type
070     * {@code application/x-net.iharder.dnd.TransferableObject}.
071     *
072     * @since 1.1
073     */
074    public final static java.awt.datatransfer.DataFlavor DATA_FLAVOR
075            = new DataFlavor(jmri.util.iharder.dnd.TransferableObject.class, MIME_TYPE);
076
077    private Fetcher fetcher;
078    private Object data;
079
080    private java.awt.datatransfer.DataFlavor customFlavor;
081
082    /**
083     * Creates a new {@link TransferableObject} that wraps <var>data</var>.
084     * Along with the {@link #DATA_FLAVOR} associated with this class, this
085     * creates a custom data flavor with a representation class determined from
086     * <code>data.getClass()</code> and the MIME type
087     * {@code application/x-net.iharder.dnd.TransferableObject}.
088     *
089     * @param data The data to transfer
090     * @since 1.1
091     */
092    public TransferableObject(Object data) {
093        this.data = data;
094        this.customFlavor = new java.awt.datatransfer.DataFlavor(data.getClass(), MIME_TYPE);
095    }   // end constructor
096
097    /**
098     * Creates a new {@link TransferableObject} that will return the object that
099     * is returned by <var>fetcher</var>. No custom data flavor is set other
100     * than the default {@link #DATA_FLAVOR}.
101     *
102     * @see Fetcher
103     * @param fetcher The {@link Fetcher} that will return the data object
104     * @since 1.1
105     */
106    public TransferableObject(Fetcher fetcher) {
107        this.fetcher = fetcher;
108    }   // end constructor
109
110    /**
111     * Creates a new {@link TransferableObject} that will return the object that
112     * is returned by <var>fetcher</var>. Along with the {@link #DATA_FLAVOR}
113     * associated with this class, this creates a custom data flavor with a
114     * representation class <var>dataClass</var>
115     * and the MIME type
116     * {@code application/x-net.iharder.dnd.TransferableObject}.
117     *
118     * @see Fetcher
119     * @param dataClass The {@link java.lang.Class} to use in the custom data
120     *                  flavor
121     * @param fetcher   The {@link Fetcher} that will return the data object
122     * @since 1.1
123     */
124    public TransferableObject(Class<?> dataClass, Fetcher fetcher) {
125        this.fetcher = fetcher;
126        this.customFlavor = new java.awt.datatransfer.DataFlavor(dataClass, MIME_TYPE);
127    }   // end constructor
128
129    /**
130     * Returns the custom {@link java.awt.datatransfer.DataFlavor} associated
131     * with the encapsulated object or {@code null} if the {@link Fetcher}
132     * constructor was used without passing a {@link java.lang.Class}.
133     *
134     * @return The custom data flavor for the encapsulated object
135     * @since 1.1
136     */
137    public java.awt.datatransfer.DataFlavor getCustomDataFlavor() {
138        return customFlavor;
139    }   // end getCustomDataFlavor
140
141    /* ********  T R A N S F E R A B L E   M E T H O D S  ******** */
142    /**
143     * Returns a two- or three-element array containing first the custom data
144     * flavor, if one was created in the constructors, second the default
145     * DATA_FLAVOR associated with the TransferableObject, and third the
146     * java.awt.datatransfer.DataFlavor.stringFlavor.
147     *
148     * @return An array of supported data flavors
149     * @since 1.1
150     */
151    @Override
152    public java.awt.datatransfer.DataFlavor[] getTransferDataFlavors() {
153        if (customFlavor != null) {
154            return new java.awt.datatransfer.DataFlavor[]{customFlavor,
155                DATA_FLAVOR,
156                java.awt.datatransfer.DataFlavor.stringFlavor
157            };  // end flavors array
158        } else {
159            return new java.awt.datatransfer.DataFlavor[]{DATA_FLAVOR,
160                java.awt.datatransfer.DataFlavor.stringFlavor
161            };  // end flavors array
162        }
163    }   // end getTransferDataFlavors
164
165    /**
166     * Returns the data encapsulated in this {@link TransferableObject}. If the
167     * {@link Fetcher} constructor was used, then this is when the
168     * {@link Fetcher#getObject getObject()} method will be called. If the
169     * requested data flavor is not supported, then the
170     * {@link Fetcher#getObject getObject()} method will not be called.
171     *
172     * @param flavor The data flavor for the data to return
173     * @return The dropped data
174     * @since 1.1
175     */
176    @Override
177    public Object getTransferData(java.awt.datatransfer.DataFlavor flavor)
178            throws java.awt.datatransfer.UnsupportedFlavorException, java.io.IOException {
179        // Native object
180        if (flavor.equals(DATA_FLAVOR)) {
181            return fetcher == null ? data : fetcher.getObject();
182        }
183
184        // String
185        if (flavor.equals(java.awt.datatransfer.DataFlavor.stringFlavor)) {
186            return fetcher == null ? data.toString() : fetcher.getObject().toString();
187        }
188
189        // We can't do anything else
190        throw new java.awt.datatransfer.UnsupportedFlavorException(flavor);
191    }   // end getTransferData
192
193    /**
194     * Returns {@code true} if <var>flavor</var> is one of the supported
195     * flavors. Flavors are supported using the <code>equals(...)</code> method.
196     *
197     * @param flavor The data flavor to check
198     * @return Whether or not the flavor is supported
199     * @since 1.1
200     */
201    @Override
202    public boolean isDataFlavorSupported(java.awt.datatransfer.DataFlavor flavor) {
203        // Native object
204        if (flavor.equals(DATA_FLAVOR)) {
205            return true;
206        }
207
208        // String
209        if (flavor.equals(java.awt.datatransfer.DataFlavor.stringFlavor)) {
210            return true;
211        }
212
213        // We can't do anything else
214        return false;
215    }   // end isDataFlavorSupported
216
217    /* ********  I N N E R   I N T E R F A C E   F E T C H E R  ******** */
218    /**
219     * Instead of passing your data directly to the {@link TransferableObject}
220     * constructor, you may want to know exactly when your data was received in
221     * case you need to remove it from its source (or do anyting else to it).
222     * When the {@link #getTransferData getTransferData(...)} method is called
223     * on the {@link TransferableObject}, the {@link Fetcher}'s
224     * {@link #getObject getObject()} method will be called.
225     *
226     * @author Robert Harder copyright 2001
227     * @version 1.1
228     * @since 1.1
229     */
230    public static interface Fetcher {
231
232        /**
233         * Return the object being encapsulated in the
234         * {@link TransferableObject}.
235         *
236         * @return The dropped object
237         * @since 1.1
238         */
239        public abstract Object getObject();
240    }   // end inner interface Fetcher
241
242}   // end class TransferableObject