001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.openide.nodes;
043:
044: import java.awt.datatransfer.DataFlavor;
045: import java.awt.datatransfer.Transferable;
046: import java.awt.datatransfer.UnsupportedFlavorException;
047: import java.awt.dnd.DnDConstants;
048: import java.io.IOException;
049: import java.text.MessageFormat;
050: import java.util.logging.Level;
051: import java.util.logging.Logger;
052: import org.openide.util.datatransfer.ExTransferable;
053: import org.openide.util.datatransfer.MultiTransferObject;
054: import org.openide.util.datatransfer.PasteType;
055:
056: /** Class that contains specific datatransfer flavors and methods to work with
057: * nodes. There are flavors to allow a node
058: * to be copied or cut, and to decide its paste types.
059: * <p>This is a dummy utility class--no instances are possible.
060: *
061: * @author Jaroslav Tulach
062: */
063: public abstract class NodeTransfer extends Object {
064: /** Constants for drag-n-drop operations.
065: * Are exactly the same as constants
066: * in {@link DnDConstants}.
067: */
068: public static final int DND_NONE = DnDConstants.ACTION_NONE;
069: public static final int DND_COPY = DnDConstants.ACTION_COPY;
070: public static final int DND_MOVE = DnDConstants.ACTION_MOVE;
071: public static final int DND_COPY_OR_MOVE = DnDConstants.ACTION_COPY
072: | DnDConstants.ACTION_MOVE;
073: public static final int DND_LINK = DnDConstants.ACTION_LINK;
074: public static final int DND_REFERENCE = DnDConstants.ACTION_LINK;
075:
076: /** Constant indicating copying to the clipboard.
077: * Equal to {@link #DND_COPY}, because
078: * copy to clipboard and d'n'd copy should be the same.
079: */
080: public static final int CLIPBOARD_COPY = DND_COPY;
081:
082: /** Constant indicating cutting to the clipboard.
083: */
084: public static final int CLIPBOARD_CUT = 0x04;
085:
086: /** Generic mask for copying nodes (do not destroy the original).
087: * Equal to {@link #CLIPBOARD_COPY} or {@link #DND_COPY}.
088: */
089: public static final int COPY = CLIPBOARD_COPY | DND_COPY;
090:
091: /** Generic mask for moving nodes (destroy the original).
092: * Equal to {@link #CLIPBOARD_CUT} or {@link #DND_MOVE}.
093: */
094: public static final int MOVE = CLIPBOARD_CUT | DND_MOVE;
095:
096: /** Flavor for representation class {@link NodeTransfer.Paste}.
097: * Provides methods for obtaining a set of {@link PasteType}s when
098: * the target node is known.
099: */
100: private static final DataFlavor nodePasteFlavor;
101: static {
102: try {
103: nodePasteFlavor = new DataFlavor(
104: "application/x-java-openide-nodepaste;class=org.openide.nodes.Node", // NOI18N
105: Node.getString("LBL_nodePasteFlavor"), Node.class
106: .getClassLoader());
107: } catch (ClassNotFoundException e) {
108: throw new AssertionError(e);
109: }
110: }
111:
112: /** message format to create and parse the mimetype
113: */
114: private static MessageFormat dndMimeType = new MessageFormat(
115: "application/x-java-openide-nodednd;class=org.openide.nodes.Node;mask={0}" // NOI18N
116: );
117:
118: private NodeTransfer() {
119: }
120:
121: /** Creates data flavor for given mask of dnd actions.
122: * @param actions any mask of dnd constants DND_* and CLIPBOARD_*
123: */
124: private static DataFlavor createDndFlavor(int actions) {
125: try {
126: return new DataFlavor(dndMimeType
127: .format(new Object[] { new Integer(actions) }),
128: null, Node.class.getClassLoader());
129: } catch (ClassNotFoundException cnfE) {
130: throw (IllegalStateException) new IllegalStateException()
131: .initCause(cnfE);
132: }
133: }
134:
135: /** Creates transferable that represents a node operation, such as cut-to-clipboard.
136: * The transferable will be recognizable by {@link #node}, {@link #nodes}, and {@link #cookie}.
137: *
138: * @param n the node to create a transferable for
139: * @param actions the action performed on the node
140: * @return the transferable
141: */
142: public static ExTransferable.Single transferable(final Node n,
143: int actions) {
144: return new ExTransferable.Single(createDndFlavor(actions)) {
145: public Object getData() {
146: return n;
147: }
148: };
149: }
150:
151: /** Obtain a node from a transferable.
152: * Probes the transferable in case it includes a flavor corresponding
153: * to a node operation (which you must specify a mask for).
154: *
155: * @param t transferable
156: * @param action one of the <code>DND_*</code> or <code>CLIPBOARD_*</code> constants
157: * @return the node or <code>null</code>
158: */
159: public static Node node(Transferable t, int action) {
160: DataFlavor[] flavors = t.getTransferDataFlavors();
161:
162: if (flavors == null) {
163: return null;
164: }
165:
166: int len = flavors.length;
167:
168: String subtype = "x-java-openide-nodednd"; // NOI18N
169: String primary = "application"; // NOI18N
170: String mask = "mask"; // NOI18N
171:
172: for (int i = 0; i < len; i++) {
173: DataFlavor df = flavors[i];
174:
175: if (df.getSubType().equals(subtype)
176: && df.getPrimaryType().equals(primary)) {
177: try {
178: int m = Integer.valueOf(df.getParameter(mask))
179: .intValue();
180:
181: if ((m & action) != 0) {
182: // found the node
183: return (Node) t.getTransferData(df);
184: }
185: } catch (NumberFormatException nfe) {
186: maybeReportException(nfe);
187: } catch (ClassCastException cce) {
188: maybeReportException(cce);
189: } catch (IOException ioe) {
190: maybeReportException(ioe);
191: } catch (UnsupportedFlavorException ufe) {
192: maybeReportException(ufe);
193: }
194: }
195: }
196:
197: return null;
198: }
199:
200: /** Obtain a list of nodes from a transferable.
201: * If there is only a single node in the transferable, this will just return a singleton
202: * array like {@link #node}.
203: * If there is a {@link ExTransferable#multiFlavor multiple transfer} (of at least one element),
204: * each element of which
205: * contains a node, then an array of these will be returned.
206: * If neither of these things is true, <code>null</code> will be returned.
207: * <p>This is a convenience method intended for those who wish to specially support pastes
208: * of multiple nodes at once. (By default, an explorer will
209: * fall back to presenting each component of a multiple-item transferable separately when checking for paste
210: * types on a target node, so if you have only one paste type and it makes no difference whether all of the nodes
211: * are pasted together or separately, you can just use {@link #node}.)
212: * <p>If you wish to test for cookies, you should do so manually
213: * according to your specific logic.
214: * @param t the transferable to probe
215: * @param action a DnD or clipboard constant
216: * @return a non-empty array of nodes, or <code>null</code>
217: */
218: public static Node[] nodes(Transferable t, int action) {
219: try {
220: if (t.isDataFlavorSupported(ExTransferable.multiFlavor)) {
221: MultiTransferObject mto = (MultiTransferObject) t
222: .getTransferData(ExTransferable.multiFlavor);
223: int count = mto.getCount();
224: Node[] ns = new Node[count];
225: boolean ok = true;
226:
227: for (int i = 0; i < count; i++) {
228: Node n = node(mto.getTransferableAt(i), action);
229:
230: if (n == null) {
231: ok = false;
232:
233: break;
234: } else {
235: ns[i] = n;
236: }
237: }
238:
239: if (ok && (count > 0)) {
240: return ns;
241: }
242: } else {
243: Node n = node(t, action);
244:
245: if (n != null) {
246: return new Node[] { n };
247: }
248: }
249: } catch (ClassCastException cce) {
250: maybeReportException(cce);
251: } catch (IOException ioe) {
252: maybeReportException(ioe);
253: } catch (UnsupportedFlavorException ufe) {
254: maybeReportException(ufe);
255: }
256:
257: return null;
258: }
259:
260: /** Obtain a cookie instance from the copied node in a transferable.
261: * <P>
262: * First of all it checks whether the given transferable contains
263: * a node and then asks for the cookie.
264: * <p>If you wish to specially support multiple-node transfers, please use {@link #nodes}
265: * and manually check for the desired combination of cookies.
266: *
267: * @param t transferable to check in
268: * @param cookie cookie representation class to look for
269: * @param action the action which was used to store the node
270: *
271: * @return cookie or <code>null</code> if it does not exist
272: */
273: public static <T extends Node.Cookie> T cookie(Transferable t,
274: int action, Class<T> cookie) {
275: Node n = node(t, action);
276:
277: return (n == null) ? null : n.getCookie(cookie);
278: }
279:
280: /** Creates transfer object that is used to carry an intelligent
281: * paste source through transferable or clipboard.
282: * {@link #findPaste} can retrieve it.
283: * @param paste the intelligent source of paste types
284: * @return the transferable
285: */
286: public static ExTransferable.Single createPaste(final Paste paste) {
287: return new ExTransferable.Single(nodePasteFlavor) {
288: public Object getData() {
289: return paste;
290: }
291: };
292: }
293:
294: /** Find an intelligent source of paste types in a transferable.
295: * Note that {@link AbstractNode#createPasteTypes} looks for this
296: * by default, so cut/copied nodes may specify how they may be pasted
297: * to some external node target.
298: * @param t the transferable to test
299: * @return the intelligent source or <code>null</code> if none is in the transferable
300: */
301: public static Paste findPaste(Transferable t) {
302: try {
303: if (t.isDataFlavorSupported(nodePasteFlavor)) {
304: return (Paste) t.getTransferData(nodePasteFlavor);
305: }
306: } catch (ClassCastException cce) {
307: maybeReportException(cce);
308: } catch (IOException ioe) {
309: maybeReportException(ioe);
310: } catch (UnsupportedFlavorException ufe) {
311: maybeReportException(ufe);
312: }
313:
314: return null;
315: }
316:
317: /** Print a stack trace if debugging is on.
318: * Used for exceptions that could occur when probing transferables,
319: * which should not interrupt the probing with an error, but
320: * indicate a bug elsewhere and should be reported somehow.
321: * @param e the exception
322: */
323: private static void maybeReportException(Exception e) {
324: Logger.getLogger(NodeTransfer.class.getName()).log(
325: Level.WARNING, null, e);
326:
327: // else do nothing
328: }
329:
330: /** An intelligent source of paste types (ways how to paste)
331: * for a target node.
332: * <P>
333: * Each node should check for this
334: * type in a paste operation to allow anyone to insert something
335: * into it.
336: * <P>
337: * Sample example of implementation of {@link Node#getPasteTypes}:
338: * <p><code><PRE>
339: * public PasteType[] getPasteTypes (Transferable t) {
340: * NodeTransfer.Paste p = (NodeTransfer.Paste)t.getTransferData (
341: * NodeTransfer.nodePasteFlavor
342: * );
343: * return p.types (this);
344: * }
345: * </PRE></code>
346: */
347: public interface Paste {
348: /** Method that checks the type of target node and can
349: * decide which paste types it supports.
350: *
351: * @param target the target node
352: * @return array of paste types that are valid for such a target node
353: */
354: public PasteType[] types(Node target);
355: }
356: }
|