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.loaders;
043:
044: import java.awt.datatransfer.*;
045: import java.awt.dnd.DnDConstants;
046: import java.io.IOException;
047: import java.text.MessageFormat;
048: import java.util.logging.Level;
049: import java.util.logging.Logger;
050: import org.openide.util.datatransfer.*;
051:
052: /** Class that contains specific datatransfer flavors and methods to work with
053: * transfered DataObjects. There are flavors to allow a DataObject
054: * to be copied or cut into clipboard, and to retrieve them from clipboard
055: * when implementing paste operation.
056: * <p>This is a dummy utility class--no instances are possible.
057: *
058: * @author Vita Stejskal
059: * @since 1.21
060: */
061: public abstract class LoaderTransfer {
062:
063: /** Creates new LoaderTransfer */
064: private LoaderTransfer() {
065: }
066:
067: /** Constants for drag-n-drop operations.
068: * Are exactly the same as constants
069: * in {@link DnDConstants}.
070: */
071: public static final int DND_NONE = DnDConstants.ACTION_NONE;
072: public static final int DND_COPY = DnDConstants.ACTION_COPY;
073: public static final int DND_MOVE = DnDConstants.ACTION_MOVE;
074: public static final int DND_COPY_OR_MOVE = DnDConstants.ACTION_COPY
075: | DnDConstants.ACTION_MOVE;
076: public static final int DND_LINK = DnDConstants.ACTION_LINK;
077: public static final int DND_REFERENCE = DnDConstants.ACTION_LINK;
078:
079: /** Constant indicating copying to the clipboard.
080: * Equal to {@link #DND_COPY}, because
081: * copy to clipboard and d'n'd copy should be the same.
082: */
083: public static final int CLIPBOARD_COPY = DND_COPY;
084:
085: /** Constant indicating cutting to the clipboard.
086: */
087: public static final int CLIPBOARD_CUT = 0x04;
088:
089: /** Generic mask for copying DataObjects (do not destroy the original).
090: * Equal to {@link #CLIPBOARD_COPY} or {@link #DND_COPY}.
091: */
092: public static final int COPY = CLIPBOARD_COPY | DND_COPY;
093:
094: /** Generic mask for moving DataObjects (destroy the original).
095: * Equal to {@link #CLIPBOARD_CUT} or {@link #DND_MOVE}.
096: */
097: public static final int MOVE = CLIPBOARD_CUT | DND_MOVE;
098:
099: /** message format to create and parse the mimetype
100: */
101: private static MessageFormat dndMimeType = new MessageFormat(
102: "application/x-java-openide-dataobjectdnd;class=org.openide.loaders.DataObject;mask={0}" // NOI18N
103: );
104:
105: /** Creates transferable that represents an operation, such as cut-to-clipboard.
106: * The transferable will be recognizable by {@link #getDataObject} and {@link #getDataObjects}.
107: *
108: * @param d the DataObject to create a transferable for
109: * @param actions the action performed on the DataObject's node
110: * @return the transferable
111: */
112: public static ExTransferable.Single transferable(
113: final DataObject d, int actions) {
114: return new ExTransferable.Single(createDndFlavor(actions)) {
115: public Object getData() {
116: return d;
117: }
118: };
119: }
120:
121: /** Obtain a DataObject from a transferable.
122: * Probes the transferable in case it includes a flavor corresponding
123: * to a DataObject's node operation (which you must specify a mask for).
124: *
125: * @param t transferable
126: * @param action one of the <code>DND_*</code> or <code>CLIPBOARD_*</code> constants
127: * @return the DataObject or <code>null</code>
128: */
129: public static DataObject getDataObject(Transferable t, int action) {
130: DataFlavor[] flavors = t.getTransferDataFlavors();
131: if (flavors == null) {
132: return null;
133: }
134: int len = flavors.length;
135:
136: String subtype = "x-java-openide-dataobjectdnd"; // NOI18N
137: String primary = "application"; // NOI18N
138: String mask = "mask"; // NOI18N
139:
140: for (int i = 0; i < len; i++) {
141: DataFlavor df = flavors[i];
142:
143: if (df.getSubType().equals(subtype)
144: && df.getPrimaryType().equals(primary)) {
145: try {
146: int m = Integer.valueOf(df.getParameter(mask))
147: .intValue();
148: if ((m & action) != 0) {
149: // found the node
150: DataObject o = (DataObject) t
151: .getTransferData(df);
152: if (o.isValid()) {
153: return o;
154: } else {
155: // #14344
156: return null;
157: }
158: }
159: } catch (NumberFormatException nfe) {
160: maybeReportException(nfe);
161: } catch (ClassCastException cce) {
162: maybeReportException(cce);
163: } catch (IOException ioe) {
164: // #32206 - this exception is thrown when underlying fileobject
165: // does not exist and DataObject cannot be found for it. It happens when
166: // user copy a DataObject into clipboard, close NB, delete the file,
167: // restart the NB. During the startup the clipboard content is checked and
168: // this exception is thrown. It would be better to catch just FileStateInvalidException,
169: // but it gets wrapped into IOException in sun.awt.datatransfer.DataTransferer.
170: // Logging this exception as informative is too confusing.
171: // There is usually several exceptions logged for one clipboard object
172: // and users file it repeatedly as bug. Just log some explanation message instead.
173: DataObject.LOG
174: .fine("Object in clipboard refers to a non existing file. "
175: + ioe.toString()); //NOI18N
176: } catch (UnsupportedFlavorException ufe) {
177: maybeReportException(ufe);
178: }
179: }
180: }
181:
182: return null;
183: }
184:
185: /** Obtain a list of DataObjects from a transferable.
186: * If there is only a single DataObject in the transferable, this will just return a singleton
187: * array like {@link #getDataObject}.
188: * If there is a {@link ExTransferable#multiFlavor multiple transfer} (of at least one element),
189: * each element of which
190: * contains a DataObject, then an array of these will be returned.
191: * If neither of these things is true, <code>null</code> will be returned.
192: * <p>This is a convenience method intended for those who wish to specially support pastes
193: * of multiple DataObjects at once.
194: * @param t the transferable to probe
195: * @param action a DnD or clipboard constant
196: * @return a non-empty array of nodes, or <code>null</code>
197: */
198: public static DataObject[] getDataObjects(Transferable t, int action) {
199: try {
200: if (t.isDataFlavorSupported(ExTransferable.multiFlavor)) {
201: MultiTransferObject mto = (MultiTransferObject) t
202: .getTransferData(ExTransferable.multiFlavor);
203: int count = mto.getCount();
204: DataObject[] datas = new DataObject[count];
205: boolean ok = true;
206: for (int i = 0; i < count; i++) {
207: DataObject d = getDataObject(mto
208: .getTransferableAt(i), action);
209: if (d == null) {
210: ok = false;
211: break;
212: } else {
213: datas[i] = d;
214: }
215: }
216: if (ok && count > 0)
217: return datas;
218: } else {
219: DataObject d = getDataObject(t, action);
220: if (d != null)
221: return new DataObject[] { d };
222: }
223: } catch (ClassCastException cce) {
224: maybeReportException(cce);
225: } catch (IOException ioe) {
226: maybeReportException(ioe);
227: } catch (UnsupportedFlavorException ufe) {
228: maybeReportException(ufe);
229: }
230: return null;
231: }
232:
233: /** Creates data flavor for given mask of dnd actions.
234: * @param actions any mask of dnd constants DND_* and CLIPBOARD_*
235: */
236: private static DataFlavor createDndFlavor(int actions) {
237: try {
238: return new DataFlavor(dndMimeType
239: .format(new Object[] { new Integer(actions) }),
240: null, DataObject.class.getClassLoader());
241: } catch (ClassNotFoundException ex) {
242: throw new AssertionError(ex);
243: }
244: }
245:
246: /** Print a stack trace if debugging is on.
247: * Used for exceptions that could occur when probing transferables,
248: * which should not interrupt the probing with an error, but
249: * indicate a bug elsewhere and should be reported somehow.
250: * @param e the exception
251: */
252: private static void maybeReportException(Exception e) {
253: Logger.getLogger(LoaderTransfer.class.getName()).log(
254: Level.WARNING, null, e);
255: }
256: }
|