KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > loaders > DataFolder


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.openide.loaders;
21
22 import java.awt.Image JavaDoc;
23 import java.awt.datatransfer.*;
24 import java.beans.*;
25 import java.io.*;
26 import java.lang.reflect.InvocationTargetException JavaDoc;
27 import java.net.URI JavaDoc;
28 import java.util.*;
29 import java.util.logging.Level JavaDoc;
30 import java.util.logging.Logger JavaDoc;
31 import javax.swing.*;
32 import javax.swing.event.ChangeEvent JavaDoc;
33 import org.openide.*;
34 import org.openide.cookies.*;
35 import org.openide.filesystems.*;
36 import org.openide.nodes.*;
37 import org.openide.util.*;
38 import org.openide.util.datatransfer.*;
39
40 /** A folder containing data objects.
41 * Is actually itself a data object, whose primary (and only) file object
42 * is a file folder.
43 * <p>Has special support for determining the sorting of the folder,
44 * or even explicit ordering of the children.
45 *
46 * @author Jaroslav Tulach, Petr Hamernik
47 */

48 public class DataFolder extends MultiDataObject implements DataObject.Container {
49     /** generated Serialized Version UID */
50     static final long serialVersionUID = -8244904281845488751L;
51
52     /** Name of property that holds children of this node. */
53     public static final String JavaDoc PROP_CHILDREN = DataObject.Container.PROP_CHILDREN;
54
55     /** Name of property which decides sorting mode. */
56     public static final String JavaDoc PROP_SORT_MODE = "sortMode"; // NOI18N
57

58     /** name of extended attribute for order of children */
59     static final String JavaDoc EA_SORT_MODE = "OpenIDE-Folder-SortMode"; // NOI18N
60
/** name of extended attribute for order of children */
61     static final String JavaDoc EA_ORDER = "OpenIDE-Folder-Order"; // NOI18N
62

63     /** Name of property for order of children. */
64     public static final String JavaDoc PROP_ORDER = "order"; // NOI18N
65
/** Name of set with sorting options. */
66     public static final String JavaDoc SET_SORTING = "sorting"; // NOI18N
67

68     /** Icon resource string for folder node */
69     private static final String JavaDoc FOLDER_ICON_BASE =
70         "org/openide/loaders/defaultFolder.gif"; // NOI18N
71

72     /** name of a shadow file for a root */
73     private static final String JavaDoc ROOT_SHADOW_NAME = "Root"; // NOI18N
74

75     /** Drag'n'drop DataFlavor used on Linux for file dragging */
76     private static DataFlavor uriListDataFlavor;
77
78     /** listener that contains array of children
79     * Also represents the folder as the node delegate.
80     */

81     private FolderList list;
82     
83     /** Listener for changes in FolderList */
84     private PropertyChangeListener pcl;
85
86     private DataTransferSupport dataTransferSupport = new Paste ();
87     
88     /** Create a data folder from a folder file object.
89
90     * @deprecated This method should not be used in client code.
91     * If you are searching for a <code>DataFolder</code> for
92     * a FileObject use {@link DataFolder#findFolder} factory method.
93     *
94     * @param fo file folder to work on
95     * @exception DataObjectExistsException if there is one already
96     * @exception IllegalArgumentException if <code>fo</code> is not folder
97     */

98     @Deprecated JavaDoc
99     public DataFolder (FileObject fo)
100     throws DataObjectExistsException, IllegalArgumentException JavaDoc {
101         this(fo, DataLoaderPool.getFolderLoader ());
102     }
103
104     /** Create a data folder from a folder file object.
105     *
106     * @param fo file folder to work on
107     * @param loader data loader for this data object
108     * @exception DataObjectExistsException if there is one already
109     * @exception IllegalArgumentException if <code>fo</code> is not folder
110     */

111     protected DataFolder (FileObject fo, MultiFileLoader loader)
112     throws DataObjectExistsException, IllegalArgumentException JavaDoc {
113         this (fo, loader, true);
114     }
115
116     /** Create a data folder from a folder file object.
117     * @param fo file folder to work on
118     * @param loader data loader for this data object
119     * @exception DataObjectExistsException if there is one already
120     * @exception IllegalArgumentException if <code>fo</code> is not folder
121     * @deprecated Since 1.13 do not use this constructor, it is for backward compatibility only.
122     */

123     @Deprecated JavaDoc
124     protected DataFolder (FileObject fo, DataLoader loader)
125     throws DataObjectExistsException, IllegalArgumentException JavaDoc {
126         super (fo, loader);
127         init(fo, true);
128     }
129     
130     /** Create a data folder from a folder file object.
131     * @param fo file folder to work on
132     * @param loader data loader for this data object
133     * @param attach listen to changes?
134     * @exception DataObjectExistsException if there is one already
135     * @exception IllegalArgumentException if <code>fo</code> is not folder
136     */

137     private DataFolder (FileObject fo, MultiFileLoader loader, boolean attach)
138     throws DataObjectExistsException, IllegalArgumentException JavaDoc {
139         super (fo, loader);
140         init(fo, attach);
141     }
142
143     /** Perform initialization after construction.
144     * @param fo file folder to work on
145     * @param attach listen to changes?
146     */

147     private void init(FileObject fo, boolean attach) throws IllegalArgumentException JavaDoc {
148         if (!fo.isFolder ()) {
149             // not folder => throw an exception
150
throw new IllegalArgumentException JavaDoc ("Not folder: " + fo); // NOI18N
151
}
152         list = reassignList (fo, attach);
153     }
154     
155     /** Attaches a listener to the folder list, removes any previous one if registered.
156      * @param fo the new primary file we should listen on
157      * @param attach really attache listener
158      */

159     private FolderList reassignList (FileObject fo, boolean attach) {
160         // creates object that handles all elements in array and
161
// assignes it to the
162
FolderList list = FolderList.find (fo, true);
163
164         if (attach) {
165             pcl = new ListPCL ();
166             list.addPropertyChangeListener (org.openide.util.WeakListeners.propertyChange (pcl, list));
167         }
168         
169         return list;
170     }
171         
172
173     /** Helper method to find or create a folder of a given path.
174     * Tries to find such a subfolder, or creates it if it needs to.
175     *
176     * @param folder the folder to start in
177     * @param name a subfolder path (e.g. <code>com/mycom/testfolder</code>)
178     * @return a folder with the given name
179     * @exception IOException if the I/O fails
180     */

181     public static DataFolder create (DataFolder folder, String JavaDoc name) throws IOException {
182         StringTokenizer tok = new StringTokenizer (name, "/"); // NOI18N
183
while (tok.hasMoreTokens ()) {
184             String JavaDoc piece = tok.nextToken ();
185             if (! confirmName (piece)) {
186                 throw new IOException (NbBundle.getMessage (DataFolder.class, "EXC_WrongName", piece));
187             }
188         }
189         return DataFolder.findFolder (FileUtil.createFolder (folder.getPrimaryFile (), name));
190     }
191
192     /** Set the sort mode for the folder.
193     * @param mode an constant from {@link DataFolder.SortMode}
194     * @exception IOException if the mode cannot be set
195     */

196     public synchronized final void setSortMode (SortMode mode) throws IOException {
197         SortMode old = getOrder ().getSortMode ();
198         getOrder ().setSortMode (mode);
199         firePropertyChange (PROP_SORT_MODE, old, getOrder ().getSortMode ());
200     }
201
202     /** Get the sort mode of the folder.
203     * @return the sort mode
204     */

205     public final SortMode getSortMode () {
206         return getOrder ().getSortMode ();
207     }
208
209     /** Set the order of the children.
210      * The provided array defines
211     * the order of some children for the folder. Such children
212     * will be returned at the beginning of the array returned from
213     * {@link #getChildren}. If there are any other children, they
214     * will be appended to the array.
215     *
216     * @param arr array of data objects (children of this
217     * folder) to define the order; or <code>null</code> if any particular ordering should
218     * be cancelled
219     *
220     * @exception IOException if the order cannot be set
221     *
222     */

223     public synchronized final void setOrder (DataObject[] arr) throws IOException {
224         getOrder ().setOrder (arr);
225         firePropertyChange (PROP_ORDER, null, null);
226     }
227
228     /** Getter for order object.
229     * @return order of children
230     */

231     private FolderOrder getOrder () {
232         return FolderOrder.findFor (getPrimaryFile ());
233     }
234
235     /** Get the name of the data folder.
236     * <p>This implementation uses the name and extension of the primary file.
237     * @return the name
238     */

239     public String JavaDoc getName () {
240         return getPrimaryFile ().getNameExt ();
241     }
242     
243     /** Get the children of this folder.
244     * @return array of children
245     */

246     public DataObject[] getChildren () {
247         return list.getChildren ();
248     }
249
250     /** Getter for list of children.
251     * @param filter filter to notify about addition of new objects
252     */

253     final List<DataObject> getChildrenList () {
254         return list.getChildrenList ();
255     }
256
257     /** Computes list of children asynchronously
258     * @param l listener to notify about the progress
259     * @return task that will handle the computation
260     */

261     final RequestProcessor.Task computeChildrenList (FolderListListener l) {
262         return list.computeChildrenList (l);
263     }
264
265     /** Get enumeration of children of this folder.
266     * @return enumeration of {@link DataObject}s
267     */

268     public Enumeration<DataObject> children () {
269         return Collections.enumeration (getChildrenList ());
270     }
271
272     /** Enumerate all children of this folder. If the children should be enumerated
273     * recursively, first all direct children are listed; then children of direct subfolders; and so on.
274     *
275     * @param rec whether to enumerate recursively
276     * @return enumeration of type <code>DataObject</code>
277     */

278     public Enumeration<DataObject> children (final boolean rec) {
279         if (!rec) {
280             return children();
281         }
282         
283         class Processor implements org.openide.util.Enumerations.Processor<DataObject, DataObject> {
284             /** @param o processes object by adding its children to the queue */
285             public DataObject process (DataObject dataObj, Collection<DataObject> toAdd) {
286                 if (rec && dataObj instanceof DataFolder) {
287                     toAdd.addAll (Arrays.asList (((DataFolder)dataObj).getChildren()));
288                 }
289                 return dataObj;
290             }
291         }
292         Enumeration<DataObject> en = org.openide.util.Enumerations.queue (
293             org.openide.util.Enumerations.array (getChildren ()),
294             new Processor ()
295         );
296         return en;
297     }
298
299     /** Create node representative for this folder.
300     */

301     protected synchronized Node createNodeDelegate () {
302         return new FolderNode();
303     }
304
305     private final class ClonedFilter extends FilterNode {
306         private DataFilter filter;
307         private int hashCode = -1; // We need to remember the hash code in
308
// order to keep it constant fix for
309

310         public ClonedFilter (Node n, DataFilter filter) {
311             super (n, DataFolder.this.createNodeChildren (filter));
312             this.filter = filter;
313         }
314         public ClonedFilter (DataFilter filter) {
315             this (DataFolder.this.getNodeDelegate (), filter);
316         }
317         public Node cloneNode () {
318             if (isValid()) {
319                 return new ClonedFilter (filter);
320             } else {
321                 return super.cloneNode();
322             }
323         }
324         public Node.Handle getHandle () {
325             return new ClonedFilterHandle (DataFolder.this, filter);
326         }
327         public boolean equals (Object JavaDoc o) {
328             if (o == null) {
329                 return false;
330             } else if (o == this) {
331                 return true;
332             } else if (o instanceof FolderNode) {
333                 FolderNode fn = (FolderNode) o;
334                 if (fn.getCookie (DataFolder.class) != DataFolder.this) return false;
335                 org.openide.nodes.Children ch = fn.getChildren ();
336                 return (ch instanceof FolderChildren) &&
337                     ((FolderChildren) ch).getFilter ().equals (filter);
338             } else if (o instanceof ClonedFilter) {
339                 ClonedFilter cf = (ClonedFilter) o;
340                 return cf.getCookie (DataFolder.class) == DataFolder.this &&
341                     cf.filter.equals (filter);
342             } else {
343                 return false;
344             }
345         }
346         public int hashCode () {
347             if ( hashCode == -1 ) {
348                 if ( isValid() ) {
349                     hashCode = getNodeDelegate().hashCode();
350                 }
351                 else {
352                     hashCode = super.hashCode();
353                 }
354                 
355                 if ( hashCode == -1 ) {
356                     hashCode = -2;
357                 }
358                 
359             }
360             return hashCode;
361             
362         }
363     }
364     private final static class ClonedFilterHandle implements Node.Handle {
365         private final static long serialVersionUID = 24234097765186L;
366         private DataObject folder;
367         private DataFilter filter;
368         public ClonedFilterHandle (DataFolder folder, DataFilter filter) {
369             this.folder = folder;
370             this.filter = filter;
371         }
372         public Node getNode () throws IOException {
373             if (folder instanceof DataFolder) {
374                 return ((DataFolder)folder).new ClonedFilter (filter);
375             } else {
376                 throw new java.io.InvalidObjectException JavaDoc(
377                     folder == null ? "" : folder.toString() // NOI18N
378
);
379             }
380         }
381     }
382     /** This method allows DataFolder to filter its nodes.
383     *
384     * @param filter filter for subdata objects
385     * @return the node delegate (without parent) for this data object
386     */

387     Node getClonedNodeDelegate (DataFilter filter) {
388         Node n = getNodeDelegate ();
389         Children c = n.getChildren ();
390         // #7362: relying on subclassers to override createNodeChildren is ugly...
391
if (c.getClass () == FolderChildren.class) {
392             DataFilter f = ((FolderChildren) c).getFilter ();
393             if (f == DataFilter.ALL) {
394                 // Either createNodeDelegate was not overridden; or
395
// it provided some node with the same children as
396
// DataFolder would have anyway. Filter the children.
397
return new ClonedFilter (n, filter);
398             } else if (filter != DataFilter.ALL && filter != f) {
399                 // Tricky. createNodeDelegate was overridden, and it is
400
// producing FolderChildren with some special filter.
401
// Apply both the subclass's filter and this additional one.
402
return new ClonedFilter (n, filterCompose (f, filter));
403             } else {
404                 // Subclass provided FolderChildren with some special filter,
405
// and we are not trying to filter specially. Let the subclass
406
// display as usual.
407
return n.cloneNode ();
408             }
409         } else {
410             // We have some DataFolder subclass with idiosyncratic children.
411
// Play it safe and let it display what it wants.
412
return n.cloneNode ();
413         }
414     }
415     
416     /** Logically compose two filters: accept the intersection. */
417     private static DataFilter filterCompose (final DataFilter f1, final DataFilter f2) {
418         if (f1.equals (f2)) {
419             return f1;
420         } else {
421             return new DataFilter () {
422                 public boolean acceptDataObject (DataObject obj) {
423                     return f1.acceptDataObject (obj) && f2.acceptDataObject (obj);
424                 }
425             };
426         }
427     }
428
429     /** Support method to obtain a children object that
430     * can be added to any {@link Node}. The provided filter can be
431     * used to exclude some objects from the list.
432     * <p><strong>Overriding this method is deprecated!</strong>
433     * @param filter filter of data objects
434     * @return children object representing content of this folder
435     */

436     public /* XXX final */ Children createNodeChildren (DataFilter filter) {
437         return new FolderChildren (this, filter);
438     }
439
440     /* Getter for delete action.
441     * @return true if the object can be deleted
442     */

443     public boolean isDeleteAllowed () {
444         return isRenameAllowed ();
445     }
446
447     /* Getter for copy action.
448     * @return true if the object can be copied
449     */

450     public boolean isCopyAllowed () {
451         return true;
452     }
453
454     /* Getter for move action.
455     * @return true if the object can be moved
456     */

457     public boolean isMoveAllowed () {
458         return isRenameAllowed ();
459     }
460
461     /* Getter for rename action.
462     * @return true if the object can be renamed
463     */

464     public boolean isRenameAllowed () {
465         FileObject fo = getPrimaryFile ();
466         return !fo.isRoot() && fo.canWrite();
467     }
468
469     /* Help context for this object.
470     * @return help context
471     */

472     public HelpCtx getHelpCtx () {
473         return null;
474     }
475
476     /** Create a folder for a specified file object.
477     * @param fo file object
478     * @return folder for the file object
479     * @exception IllegalArgumentException if the file object is not folder
480     */

481     public static DataFolder findFolder (FileObject fo) {
482         DataObject d;
483         try {
484             d = DataObject.find(fo);
485         } catch (DataObjectNotFoundException e) {
486             throw (IllegalArgumentException JavaDoc)new IllegalArgumentException JavaDoc(e.toString()).initCause(e);
487         }
488         if (!(d instanceof DataFolder)) {
489             throw new IllegalArgumentException JavaDoc("Not a DataFolder: " + fo + " (was a " + d.getClass().getName() + ") (file is folder? " + fo.isFolder() + ")"); // NOI18N
490
}
491         return (DataFolder)d;
492     }
493
494     /** Finds a DataObject.Container representing given folder.
495     * @param fo file object (must be folder)
496     * @return the container for the file object
497     * @exception IllegalArgumentException if the file object is not folder
498     *
499     * @since 1.11
500     */

501     public static DataObject.Container findContainer (FileObject fo) {
502         if (fo.isFolder ()) {
503             return FolderList.find (fo, true);
504         } else {
505             throw new IllegalArgumentException JavaDoc ("Not a folder: " + fo); // NOI18N
506
}
507     }
508     
509     /* Copy this object to a folder.
510      * The copy of the object is required to
511     * be deletable and movable.
512     *
513     * @param f the folder to copy object to
514     * @exception IOException if something went wrong
515     * @return the new object
516     */

517     protected DataObject handleCopy (DataFolder f) throws IOException {
518         testNesting(this, f);
519         
520         Enumeration en = children ();
521
522         DataFolder newFolder = (DataFolder)super.handleCopy (f);
523
524         while (en.hasMoreElements ()) {
525             try {
526                 DataObject obj = (DataObject)en.nextElement ();
527                 if (obj.isCopyAllowed()) {
528                     obj.copy (newFolder);
529                 } else {
530                     // data object can not be copied, inform user
531
DataObject.LOG.warning(
532                         NbBundle.getMessage(DataFolder.class,
533                 "FMT_CannotCopyDo", obj.getName() )
534                     );
535                 }
536             } catch (IOException ex) {
537                 Exceptions.printStackTrace(ex);
538             }
539         }
540
541         return newFolder;
542     }
543
544     /**
545      * Ensure that given folder is not parent of targetFolder. Also
546      * ensure that they are not equal.
547      */

548     static void testNesting(DataFolder folder, DataFolder targetFolder) throws IOException {
549         if (targetFolder.equals(folder)) {
550             IOException ioe = new IOException("Error Copying File or Folder"); //NOI18N
551
Exceptions.attachLocalizedMessage(ioe, NbBundle.getMessage(DataFolder.class, "EXC_CannotCopyTheSame", folder.getName()));
552             throw ioe;
553         } else {
554             DataFolder testFolder = targetFolder.getFolder();
555             while (testFolder != null) {
556                 if (testFolder.equals(folder)) {
557                     IOException ioe = new IOException("Error copying file or folder: " +
558                         folder.getPrimaryFile() + " cannot be copied to its subfolder " +
559                         targetFolder.getPrimaryFile());
560                     Exceptions.attachLocalizedMessage(ioe, NbBundle.getMessage(DataFolder.class, "EXC_CannotCopySubfolder", folder.getName()));
561                     throw ioe;
562                 }
563                 testFolder = testFolder.getFolder();
564             }
565         }
566     }
567     
568     /* Deals with deleting of the object. Must be overriden in children.
569     * @exception IOException if an error occures
570     */

571     protected void handleDelete () throws IOException {
572         Enumeration en = children ();
573         FileLock lightWeightLock = null;//#43278
574
try {
575             lightWeightLock = createLightWeightLock(this);
576             while (en.hasMoreElements ()) {
577                 DataObject obj = (DataObject)en.nextElement ();
578                 if (obj.isValid ()) {
579                     obj.delete ();
580                 }
581             }
582         } catch (IOException iex) {
583             /** Annotates exception and throws again*/
584             FileObject fo = getPrimaryFile();
585             String JavaDoc message = NbBundle.getMessage(DataFolder.class, "EXC_CannotDelete2", FileUtil.getFileDisplayName(fo));
586             Exceptions.attachLocalizedMessage(iex, message);
587             throw iex;
588         } finally {
589             if (lightWeightLock != null) {
590                 lightWeightLock.releaseLock();
591             }
592         }
593         
594         super.handleDelete ();
595     }
596     
597     private static FileLock createLightWeightLock(DataFolder df) {//#43278
598
FileObject fo = df.getPrimaryFile();
599         assert fo != null;
600         Object JavaDoc o = fo.getAttribute("LIGHTWEIGHT_LOCK_SET");//NOI18N
601
assert o == null || (o instanceof FileLock) : fo.toString();
602         return (FileLock)o;
603     }
604
605     /* Handles renaming of the object.
606     * Must be overriden in children.
607     *
608     * @param name name to rename the object to
609     * @return new primary file of the object
610     * @exception IOException if an error occures
611     */

612     protected FileObject handleRename (final String JavaDoc name) throws IOException {
613         if (! confirmName (name)) {
614             IOException e = new IOException("bad name: " + name); // NOI18N
615
Exceptions.attachLocalizedMessage(e, NbBundle.getMessage(DataFolder.class, "EXC_WrongName", name));
616             throw e;
617         }
618         return super.handleRename (name);
619     }
620     
621     private static final ThreadLocal JavaDoc<boolean[]> KEEP_ALIVE = new ThreadLocal JavaDoc<boolean[]>();
622     
623     /* Handles move of the object. Must be overriden in children. Since 1.13 move operation
624     * behaves similar like copy, it merges folders whith existing folders in target location.
625     * @param df target data folder
626     * @return new primary file of the object
627     * @exception IOException if an error occures
628     */

629     protected FileObject handleMove (DataFolder df) throws IOException {
630         FileObject originalFolder = getPrimaryFile ();
631         FileLock lock = originalFolder.lock();
632         List<Pair> backup = saveEntries();
633         
634         boolean clearKeepAlive = false;
635         try {
636             // move entries (FolderEntry creates new folder when moved)
637

638             FileObject newFile = super.handleMove (df);
639             
640             DataFolder newFolder = null;
641             boolean dispose = false;
642             
643             boolean[] keepAlive = KEEP_ALIVE.get();
644             if (keepAlive == null) {
645                 keepAlive = new boolean[] { false };
646                 KEEP_ALIVE.set(keepAlive);
647             }
648
649             /*
650              * The following code is a partial bugfix of the issue #8705.
651              * Please note that this problem is hardly reproducible by users,
652              * but only by unit test.
653              *
654              * The root of the problem is that it is not possible to disable
655              * recognizing of DataObjects for some time. Couple of lines above
656              * the file object (destination folder) is created using
657              * super.handleMove(df) and couple of lines below DataFolder if created
658              * for this file object using createMultiObject.
659              * The problems are:
660              * 1) Temporary DataFolder created as destination folder is used only
661              * during copying the original (this) DataFolder content.
662              * Then is is marked as not valid using setValid(false). The original
663              * datafolder switches its primary file to the destination file object.
664              * The problem occurs, when some other thread takes the node representing
665              * the temporary folder.
666              * Solution: Special DataFolder that delegates nodeDelegate and
667              * clonedNodeDelegate to the original folder.
668              *
669              * 2) There is still some sort time between creating of fileobject
670              * and its datafolder. Another thread can ask for parent folder's
671              * dataobjects and it forces creation of "normal" datafolder,
672              * not the special one (with delegating nodes). Then it is necessary
673              * to dispose the normal DataFolder and try to create our one.
674              * To prevent infinite look there is a count down initialy set
675              * to 20 repeats. Acording to results of DataFolderMoveTest it should
676              * help. When this solution fails it only means that in some rare
677              * cases some DataNode might represent invalid DataFolder. It is
678              * not possible to delete such a node in explorer for instance.
679              *
680              * This is really strange hack (especially the 2nd part), and it is
681              * necessary to think about better solution for NetBeans 4.0
682              * data system architecture changes.
683              *
684              */

685             final int COUNT_DOWN_INIT = 20;
686             int countDown = COUNT_DOWN_INIT;
687             while (countDown >= 0) {
688                 countDown--;
689                 try {
690                     // resolve temporary object for moving into
691
Object JavaDoc loader = getMultiFileLoader ();
692                     assert loader instanceof DataLoaderPool.FolderLoader : "This has to be FolderLoader: " + loader + " for " + getPrimaryFile (); // NOI18N
693
DataLoaderPool.FolderLoader folderLoader = (DataLoaderPool.FolderLoader) loader;
694                     newFolder = (DataFolder) DataObjectPool.createMultiObject (folderLoader, newFile, this);
695                     dispose = false;
696                     break;
697                 } catch (DataObjectExistsException e) {
698                     // object already exists, get it and remember we should be discarded
699
newFolder = (DataFolder)e.getDataObject ();
700                     newFolder.dispose();
701                     dispose = true;
702                 }
703             }
704
705             // move all children
706
Enumeration en = children ();
707
708             while (en.hasMoreElements ()) {
709                 try {
710                     DataObject obj = (DataObject)en.nextElement ();
711                     if (obj.isMoveAllowed ()) {
712                         obj.move (newFolder);
713                     } else {
714                         keepAlive[0] = true;
715                         
716                         // data object can not be moved, inform user
717
DataObject.LOG.warning(
718                             NbBundle.getMessage (DataFolder.class,
719                     "FMT_CannotMoveDo", obj.getName ())
720                         );
721                     }
722                 } catch (IOException ex) {
723                     keepAlive[0] = true;
724                     Exceptions.printStackTrace(ex);
725                 }
726             }
727
728             if (keepAlive[0]) {
729                 // some children couldn't be moved -> folder shouldn't be moved
730
restoreEntries (backup);
731                 list.refresh ();
732                 return originalFolder;
733             }
734
735             // remove original folder
736
try {
737                 originalFolder.delete (lock);
738             } catch (IOException e) {
739                 Throwable JavaDoc t = Exceptions.attachLocalizedMessage(e,
740                                                   org.openide.loaders.DataObject.getString("EXC_folder_delete_failed")); // NOI18N
741
Exceptions.printStackTrace(t);
742             }
743
744             if (dispose) {
745                 // current object will be discarded, target already existed
746
try {
747                     setValid (false);
748                     newFile = originalFolder;
749                 } catch (PropertyVetoException e) {
750                     // ignore, just repair entries
751
restoreEntries (backup);
752                     newFile = getPrimaryEntry ().getFile ();
753                 }
754             } else {
755                 // dispose temporary folder and place itself instead of it
756
// call of changePrimaryFile and dispose must be in this order
757
// to silently change DataFolders in the DataObjectPool
758
item.changePrimaryFile (newFile);
759                 newFolder.dispose ();
760                 list = reassignList (newFile, true);
761             }
762
763             return newFile;
764         } finally {
765             if (clearKeepAlive) {
766                 KEEP_ALIVE.remove();
767             }
768             lock.releaseLock();
769         }
770     }
771
772     /* Creates new object from template.
773     * @param f folder to create object in
774     * @return new data object
775     * @exception IOException if an error occured
776     */

777     protected DataObject handleCreateFromTemplate (
778         DataFolder f, String JavaDoc name
779     ) throws IOException {
780         DataFolder newFolder = (DataFolder)super.handleCreateFromTemplate (f, name);
781         Enumeration en = children ();
782
783         while (en.hasMoreElements ()) {
784             try {
785                 DataObject obj = (DataObject)en.nextElement ();
786                 obj.createFromTemplate (newFolder);
787             } catch (IOException ex) {
788                 Exceptions.printStackTrace(ex);
789             }
790         }
791
792         return newFolder;
793     }
794
795     /** Creates shadow for this object in specified folder (overridable in subclasses).
796      * <p>The default
797     * implementation creates a reference data shadow and pastes it into
798     * the specified folder.
799     *
800     * @param f the folder to create a shortcut in
801     * @return the shadow
802     */

803     protected DataShadow handleCreateShadow (DataFolder f) throws IOException {
804         // #33871 - prevent creation of recursive folder structure
805
testNesting(this, f);
806         
807         String JavaDoc name;
808         if (getPrimaryFile ().isRoot ()) {
809             name = FileUtil.findFreeFileName (
810                        f.getPrimaryFile (), ROOT_SHADOW_NAME, DataShadow.SHADOW_EXTENSION
811                    );
812         } else {
813             name = null;
814         }
815
816         return DataShadow.create (f, name, this);
817     }
818
819     /** Merge folder on move or copy when it exists in target location.
820      * @returns <code>true</code>
821      * @since 1.13
822      */

823     boolean isMergingFolders(FileObject who, FileObject targetFolder) {
824         return !targetFolder.equals (who.getParent ());
825     }
826     
827     /** Support for index cookie for folder nodes.
828     */

829     public static class Index extends org.openide.nodes.Index.Support {
830
831         /** Asociated data folder */
832         private DataFolder df;
833         /** node to be associated with */
834         private Node node;
835         /** change listener */
836         private Listener listener;
837
838         /** Create an index cookie associated with a data folder.
839          * @param df the data folder
840          * @deprecated Please explicitly specify a node to be safe.
841         */

842         @Deprecated JavaDoc
843         public Index(final DataFolder df) {
844             this (df, df.getNodeDelegate ());
845         }
846
847         /** Create an index cookie associated with a data folder.
848         * @param df the data folder
849         * @param node node to be associated with. subnodes of this node will be returned, etc.
850         */

851         public Index(final DataFolder df, Node node) {
852             this.df = df;
853             this.node = node;
854             listener = new Listener ();
855             node.addNodeListener (org.openide.nodes.NodeOp.weakNodeListener (listener, node));
856         }
857
858         /* Returns count of the nodes.
859         */

860         public int getNodesCount () {
861             return node.getChildren().getNodes(FolderChildren.checkChildrenMutex()).length;
862         }
863
864         /* Returns array of subnodes
865         * @return array of subnodes
866         */

867         public Node[] getNodes () {
868             return node.getChildren().getNodes(FolderChildren.checkChildrenMutex());
869         }
870
871         /* Reorders all children with given permutation.
872         * @param perm permutation with the length of current nodes
873         * @exception IllegalArgumentException if the perm is not
874         * valid permutation
875         */

876         public void reorder (int[] perm) {
877             // #11809: the children of the node may not directly match the data folder
878
// children. Specifically, it is legal to reorder a set of nodes where
879
// each node has a distinct data object cookie, each object being a child of
880
// this folder, but there are some objects missing. In such a case, the
881
// specified objects are permuted according to the node permutation, while
882
// other objects in the folder are left in their original positions and order.
883
DataObject[] curObjs = df.getChildren();
884             DataObject[] newObjs = new DataObject[curObjs.length];
885             Node[] nodes = getNodes ();
886             if (nodes.length != perm.length) {
887                 throw new IllegalArgumentException JavaDoc ("permutation of incorrect length: " + perm.length + " rather than " + nodes.length); // NOI18N
888
}
889             
890             // hashtable from names of nodes to their data objects for
891
// nodes that do not express their data object as their cookie
892
HashMap<String JavaDoc, DataObject> names = new HashMap<String JavaDoc, DataObject> (2 * curObjs.length);
893             for (int i = 0; i < curObjs.length; i++) {
894                 Node del = curObjs[i].getNodeDelegate ();
895                 if (del.getCookie (DataObject.class) == null) {
896                     names.put (del.getName (), curObjs[i]);
897                 }
898             }
899             
900             DataObject[] dperm = new DataObject[perm.length];
901             for (int i = 0; i < perm.length; i++) {
902                 DataObject d = nodes[i].getCookie(DataObject.class);
903                 
904                 if (d == null) {
905                     // try to scan the names table too
906
d = names.get (nodes[i].getName ());
907                 }
908                 
909                 
910                 if (d == null) {
911                     throw new IllegalArgumentException JavaDoc ("cannot reorder node with no DataObject: " + nodes[i]); // NOI18N
912
}
913                 if (d.getFolder () != df) {
914                     throw new IllegalArgumentException JavaDoc ("wrong folder for: " + d.getPrimaryFile () + " rather than " + df.getPrimaryFile ()); // NOI18N
915
}
916                 dperm[perm[i]] = d;
917             }
918             Set<DataObject> dpermSet = new HashSet<DataObject> (Arrays.asList (dperm));
919             if (dpermSet.size () != dperm.length) {
920                 throw new IllegalArgumentException JavaDoc ("duplicate DataObject's among reordered childen"); // NOI18N
921
}
922             int dindex = 0;
923             for (int i = 0; i < curObjs.length; i++) {
924                 if (dpermSet.remove (curObjs[i])) {
925                     newObjs[i] = dperm[dindex++];
926                 } else {
927                     // Not reordered, leave where it was.
928
newObjs[i] = curObjs[i];
929                 }
930             }
931             try {
932                 df.setOrder(newObjs);
933             } catch (IOException ex) {
934                 Exceptions.attachLocalizedMessage(ex,
935                                                   org.openide.loaders.DataObject.getString("EXC_ReorderFailed")); // NOI18N
936
Exceptions.printStackTrace(ex);
937             }
938         }
939
940         /* Invokes a dialog for reordering subnodes.
941         */

942         public void reorder () {
943             Index.Support.showIndexedCustomizer(this);
944         }
945
946         /** Fires notification about reordering to all
947         * registered listeners.
948         */

949         void fireChangeEventAccess () {
950             fireChangeEvent (new ChangeEvent JavaDoc (this));
951         }
952
953         /** Listener to change of children of the folder.
954         */

955         private final class Listener extends Object JavaDoc implements NodeListener {
956             Listener() {}
957             /** Change of children?
958             */

959             public void propertyChange (PropertyChangeEvent ev) {
960             }
961             /** Fired when the node is deleted.
962             * @param ev event describing the node
963             */

964             public void nodeDestroyed(NodeEvent ev) {
965             }
966
967             /** Fired when the order of children is changed.
968             * @param ev event describing the change
969             */

970             public void childrenReordered(NodeReorderEvent ev) {
971                 fireChangeEventAccess ();
972             }
973             /** Fired when a set of children is removed.
974             * @param ev event describing the action
975             */

976             public void childrenRemoved(NodeMemberEvent ev) {
977                 fireChangeEventAccess ();
978             }
979             /** Fired when a set of new children is added.
980             * @param ev event describing the action
981             */

982             public void childrenAdded(NodeMemberEvent ev) {
983                 fireChangeEventAccess ();
984             }
985         } // end of Listener
986

987     } // end of Index inner class
988

989
990     /** Type-safe enumeration of sort modes for data folders.
991     */

992     public abstract static class SortMode extends Object JavaDoc implements Comparator {
993         /** Objects are unsorted. */
994         public static final SortMode NONE = new FolderComparator (FolderComparator.NONE);
995
996         /** Objects are sorted by their names. */
997         public static final SortMode NAMES = new FolderComparator (FolderComparator.NAMES);
998
999         /** Objects are sorted by their types and then by names. */
1000        public static final SortMode CLASS = new FolderComparator (FolderComparator.CLASS);
1001
1002        /** Folders go first (sorted by name) followed by non-folder
1003        * objects sorted by name.
1004        */

1005        public static final SortMode FOLDER_NAMES = new FolderComparator (FolderComparator.FOLDER_NAMES);
1006        
1007        /**
1008         * Folders go first (sorted by name) followed by files sorted by decreasing
1009         * last modification time.
1010         * @since org.openide.loaders 4.10
1011         */

1012        public static final SortMode LAST_MODIFIED = new FolderComparator(FolderComparator.LAST_MODIFIED);
1013
1014        /**
1015         * Folders go first (sorted by name) followed by files sorted by decreasing size.
1016         * @since org.openide.loaders 4.10
1017         */

1018        public static final SortMode SIZE = new FolderComparator(FolderComparator.SIZE);
1019
1020        /** Method to write the sort mode to a folder's attributes.
1021        * @param folder folder write this mode to
1022        */

1023        void write (FileObject f) throws IOException {
1024            // Let it throw the IOException:
1025
//if (f.getPrimaryFile ().getFileSystem ().isReadOnly ()) return; // cannot write to read-only FS
1026

1027            String JavaDoc x;
1028            if (this == FOLDER_NAMES) x = "F"; // NOI18N
1029
else if (this == NAMES) x = "N"; // NOI18N
1030
else if (this == CLASS) x = "C"; // NOI18N
1031
else if (this == LAST_MODIFIED) x = "M"; // NOI18N
1032
else if (this == SIZE) x = "S"; // NOI18N
1033
else x = "O"; // NOI18N
1034

1035            f.setAttribute (EA_SORT_MODE, x);
1036        }
1037
1038        /** Reads sort mode for given folder.
1039        */

1040        static SortMode read (FileObject f) {
1041            String JavaDoc x = (String JavaDoc)f.getAttribute (EA_SORT_MODE);
1042            if (x == null || x.length () != 1) return FOLDER_NAMES;
1043
1044            char c = x.charAt (0);
1045            switch (c) {
1046            case 'N': return NAMES;
1047            case 'C': return CLASS;
1048            case 'O': return NONE;
1049            case 'M': return LAST_MODIFIED;
1050            case 'S': return SIZE;
1051            case 'F':
1052            default:
1053                return FOLDER_NAMES;
1054            }
1055        }
1056    }
1057
1058    /** true if the new folder name is acceptable */
1059    private static boolean confirmName (String JavaDoc folderName) {
1060        return folderName.indexOf ('/') == -1 && folderName.indexOf ('\\') == -1;
1061    }
1062    
1063    /** Gets an icon from UIManager and converts it to Image
1064     */

1065    private static Image JavaDoc icon2image(String JavaDoc key) {
1066        Object JavaDoc obj = UIManager.get(key);
1067        if (obj instanceof Image JavaDoc) {
1068            return (Image JavaDoc)obj;
1069        }
1070        
1071        if (obj instanceof Icon) {
1072            Icon icon = (Icon)obj;
1073            return Utilities.icon2Image(icon);
1074        }
1075        
1076        return null;
1077    }
1078
1079    /** array to cache images in */
1080    private static Image JavaDoc[] IMGS = new Image JavaDoc[2];
1081
1082    static Image JavaDoc findIcon(int index, String JavaDoc k1, String JavaDoc k2) {
1083        if (IMGS[index] != null) {
1084            return IMGS[index];
1085        }
1086
1087        Image JavaDoc i1 = icon2image(k1);
1088        if (i1 == null) {
1089            i1 = icon2image(k2);
1090        }
1091
1092        IMGS[index] = i1;
1093        return i1;
1094    }
1095    
1096    /** Node for a folder.
1097    */

1098    public class FolderNode extends DataNode {
1099        /** Create a folder node with some children.
1100        * @param ch children to use for the node
1101        */

1102        public FolderNode (Children ch) {
1103            super (DataFolder.this, ch);
1104            setIconBaseWithExtension(FOLDER_ICON_BASE);
1105        }
1106
1107        /** Create a folder node with default folder children.
1108        */

1109        protected FolderNode () {
1110            super (DataFolder.this, new FolderChildren (DataFolder.this));
1111            setIconBaseWithExtension(FOLDER_ICON_BASE);
1112        }
1113        
1114
1115        /** Overrides folder icon to search for icon in UIManager table for
1116         * BeanInfo.ICON_COLOR_16x16 type, to allow for different icons
1117         * across Look and Feels.
1118         * Keeps possibility of icon annotations.
1119         */

1120        public Image JavaDoc getIcon (int type) {
1121            Image JavaDoc img = null;
1122            if (type == BeanInfo.ICON_COLOR_16x16) {
1123                // search for proper folder icon installed by core/windows module
1124
img = findIcon(0, "Nb.Explorer.Folder.icon", "Tree.closedIcon"); // NOI18N
1125
}
1126            if (img == null) {
1127                img = super.getIcon(type);
1128            } else {
1129                // give chance to annotate icon returned from UIManeger
1130
// copied from DataNode to keep the contract
1131
try {
1132                    DataObject obj = getDataObject();
1133                    img = obj.getPrimaryFile().getFileSystem().
1134                          getStatus().annotateIcon(img, type, obj.files());
1135                } catch (FileStateInvalidException e) {
1136                    // no fs, do nothing
1137
}
1138            }
1139            return img;
1140        }
1141        
1142        /** Overrides folder icon to search for icon in UIManager table for
1143         * BeanInfo.ICON_COLOR_16x16 type, to allow for different icons
1144         * across Look and Feels.
1145         * Keeps possibility of icon annotations.
1146         */

1147        public Image JavaDoc getOpenedIcon (int type) {
1148            Image JavaDoc img = null;
1149            if (type == BeanInfo.ICON_COLOR_16x16) {
1150                // search for proper folder icon installed by core/windows module
1151
img = findIcon(1, "Nb.Explorer.Folder.openedIcon", "Tree.openIcon"); // NOI18N
1152
}
1153            if (img == null) {
1154                img = super.getOpenedIcon(type);
1155            } else {
1156                // give chance to annotate icon returned from UIManeger
1157
// copied from DataNode to keep the contract
1158
try {
1159                    DataObject obj = getDataObject();
1160                    img = obj.getPrimaryFile().getFileSystem().
1161                    getStatus().annotateIcon(img, type, obj.files());
1162                } catch (FileStateInvalidException e) {
1163                    // no fs, do nothing
1164
}
1165            }
1166            return img;
1167        }
1168        
1169        @SuppressWarnings JavaDoc("unchecked")
1170        public Node.Cookie getCookie (Class JavaDoc clazz) {
1171            if (clazz == org.openide.nodes.Index.class || clazz == Index.class) {
1172                //#33130 - enable IndexCookie only on SystemFileSystem
1173
// (also on apisupport layers...)
1174
try {
1175                    if (DataFolder.this.getPrimaryFile().getFileSystem() ==
1176                                Repository.getDefault().getDefaultFileSystem() ||
1177                            Boolean.TRUE.equals(DataFolder.this.getPrimaryFile().getAttribute("DataFolder.Index.reorderable"))) { // NOI18N
1178
return new Index (DataFolder.this, this);
1179                    }
1180                } catch (FileStateInvalidException ex) {
1181                    Logger.getLogger(DataFolder.class.getName()).log(Level.WARNING, null, ex);
1182                }
1183            }
1184            return super.getCookie (clazz);
1185        }
1186
1187        /* Adds properties for sorting.
1188         * @return the augmented property sheet
1189        */

1190        protected Sheet createSheet () {
1191            Sheet s = super.createSheet ();
1192
1193            Sheet.Set ss = new Sheet.Set ();
1194            ss.setName (SET_SORTING);
1195            ss.setDisplayName (DataObject.getString ("PROP_sorting"));
1196            ss.setShortDescription (DataObject.getString ("HINT_sorting"));
1197
1198            Node.Property p;
1199
1200            p = new PropertySupport.ReadWrite<SortMode> (
1201                    PROP_SORT_MODE, SortMode.class,
1202                    DataObject.getString("PROP_sort"),
1203                    DataObject.getString("HINT_sort")
1204                ) {
1205                    public SortMode getValue () {
1206                        return DataFolder.this.getSortMode ();
1207                    }
1208
1209                    public void setValue (SortMode o) throws InvocationTargetException JavaDoc {
1210                        try {
1211                            DataFolder.this.setSortMode (o);
1212                        } catch (IOException ex) {
1213                            throw new InvocationTargetException JavaDoc (ex);
1214                        }
1215                    }
1216
1217                    public java.beans.PropertyEditor JavaDoc getPropertyEditor () {
1218                        return new SortModeEditor ();
1219                    }
1220                };
1221            ss.put (p);
1222
1223            s.put (ss);
1224            return s;
1225        }
1226        
1227        /* No default action on data folder node.
1228         * @return null
1229         */

1230        public Action getPreferredAction() {
1231            return null;
1232        }
1233        
1234
1235        /* New type for creating new subfolder.
1236        * @return array with one element
1237        */

1238        public NewType[] getNewTypes () {
1239            return new NewType[0];
1240/* Commented out. Folder is now created via template.
1241
1242            if (getPrimaryFile ().isReadOnly ()) {
1243                // no new types
1244                return new NewType[0];
1245            } else {
1246                return new NewType[] { new NewFolder () };
1247            }
1248 */

1249        }
1250        private synchronized FolderRenameHandler getRenameHandler() {
1251            Collection handlers = Lookup.getDefault().lookupAll(FolderRenameHandler.class);
1252            if (handlers.size()==0)
1253                return null;
1254            if (handlers.size()>1)
1255                DataObject.LOG.warning(
1256                    "Multiple instances of FolderRenameHandler found in Lookup; only using first one: " + handlers); //NOI18N
1257
return (FolderRenameHandler) handlers.iterator().next();
1258        }
1259
1260        public void setName(String JavaDoc name) {
1261            FolderRenameHandler handler = getRenameHandler();
1262            if (handler == null) {
1263                super.setName(name);
1264            } else {
1265                handler.handleRename(DataFolder.this, name);
1266            }
1267        }
1268        
1269        /* May add some paste types for objects being added to folders.
1270        * May move data objects; copy them; create links for them; instantiate
1271        * them as templates; serialize instances; or create instance data objects
1272        * from instances, according to the abilities of the transferable.
1273        *
1274        * @param t transferable to use
1275        * @param s list of {@link PasteType}s
1276        */

1277        protected void createPasteTypes (Transferable t, java.util.List JavaDoc<PasteType> s) {
1278            super.createPasteTypes (t, s);
1279            if (getPrimaryFile().canWrite()) {
1280                dataTransferSupport.createPasteTypes (t, s);
1281            }
1282
1283            List<File> files = getDraggedFilesList( t );
1284            if( null != files && !files.isEmpty() && s.isEmpty() ) {
1285                //there are some files in the Transferable so let's try to
1286
//convert them to DataObjects and create PasteTypes for them
1287
List<Transferable> transferables = new ArrayList<Transferable>( files.size() );
1288                for(File f: files) {
1289                    Transferable nodeTransferable = createNodeTransferable( f );
1290                    if( null != nodeTransferable )
1291                        transferables.add( nodeTransferable );
1292                }
1293                ExTransferable.Multi multi = new ExTransferable.Multi(
1294                        transferables.toArray(new Transferable[transferables.size()]) );
1295                super.createPasteTypes (multi, s);
1296                if (getPrimaryFile().canWrite()) {
1297                    dataTransferSupport.createPasteTypes (multi, s);
1298                }
1299            }
1300        }
1301
1302        Transferable createNodeTransferable( File f ) {
1303            Transferable result = null;
1304            FileObject fo = FileUtil.toFileObject(FileUtil.normalizeFile(f));
1305            if( null != fo ) {
1306                try {
1307                    DataObject dob = DataObject.find( fo );
1308                    if( null != dob ) {
1309                        Node delegate = dob.getNodeDelegate();
1310                        //cannot paste a node to itself
1311
if (!delegate.equals(this)) {
1312                            result = dob.getNodeDelegate().clipboardCopy();
1313                            ExClipboard exClipboard = Lookup.getDefault().lookup(ExClipboard.class);
1314                            if (exClipboard != null) {
1315                                //let refactoring and others to add their own paste wrappers
1316
result = exClipboard.convert( result );
1317                            }
1318                        }
1319                    }
1320                } catch( IOException ioE ) {
1321                    Logger.getLogger(DataFolder.class.getName()).log(Level.WARNING, null, ioE);
1322                }
1323            }
1324            return result;
1325        }
1326
1327        private List<File> getDraggedFilesList( Transferable t ) {
1328            try {
1329                if( t.isDataFlavorSupported( DataFlavor.javaFileListFlavor ) ) {
1330                    //windows & mac
1331
List fileList = (List) t.getTransferData(DataFlavor.javaFileListFlavor);
1332                    //#92812 - make sure mac os does not return null value
1333
if( null != fileList )
1334                        return NbCollections.checkedListByCopy((List) t.getTransferData(DataFlavor.javaFileListFlavor), File.class, true);
1335                } else if( t.isDataFlavorSupported( getUriListDataFlavor() ) ) {
1336                    //linux
1337
String JavaDoc uriList = (String JavaDoc)t.getTransferData( getUriListDataFlavor() );
1338                    return textURIListToFileList( uriList );
1339                }
1340            } catch( UnsupportedFlavorException ex ) {
1341                Logger.getLogger(DataFolder.class.getName()).log(Level.WARNING, null, ex);
1342            } catch( IOException ex ) {
1343                // Ignore. Can be just "Owner timed out" from sun.awt.X11.XSelection.getData.
1344
Logger.getLogger(DataFlavor.class.getName()).log(Level.FINE, null, ex);
1345            }
1346            return null;
1347        }
1348
1349        private DataFlavor getUriListDataFlavor() {
1350            if( null == uriListDataFlavor ) {
1351                try {
1352                    uriListDataFlavor = new DataFlavor("text/uri-list;class=java.lang.String");
1353                } catch( ClassNotFoundException JavaDoc cnfE ) {
1354                    //cannot happen
1355
throw new AssertionError JavaDoc(cnfE);
1356                }
1357            }
1358            return uriListDataFlavor;
1359        }
1360
1361        private List<File> textURIListToFileList( String JavaDoc data ) {
1362            List<File> list = new ArrayList<File>(1);
1363            // XXX consider using BufferedReader(StringReader) instead
1364
for( StringTokenizer st = new StringTokenizer(data, "\r\n");
1365                st.hasMoreTokens();) {
1366                String JavaDoc s = st.nextToken();
1367                if( s.startsWith("#") ) {
1368                    // the line is a comment (as per the RFC 2483)
1369
continue;
1370                }
1371                try {
1372                    URI JavaDoc uri = new URI JavaDoc(s);
1373                    File file = new File(uri);
1374                    list.add( file );
1375                } catch( java.net.URISyntaxException JavaDoc e ) {
1376                    // malformed URI
1377
} catch( IllegalArgumentException JavaDoc e ) {
1378                    // the URI is not a valid 'file:' URI
1379
}
1380            }
1381            return list;
1382        }
1383
1384    } // end of FolderNode
1385

1386    /** New type for creation of new folder.
1387    */

1388    private final class NewFolder extends NewType {
1389        NewFolder() {}
1390        
1391        /** Display name for the creation action. This should be
1392        * presented as an item in a menu.
1393        *
1394        * @return the name of the action
1395        */

1396        public String JavaDoc getName() {
1397            return DataObject.getString ("CTL_NewFolder");
1398        }
1399
1400        /** Help context for the creation action.
1401        * @return the help context
1402        */

1403        public HelpCtx getHelpCtx() {
1404            return HelpCtx.DEFAULT_HELP;
1405        }
1406
1407        /** Create the object.
1408        * @exception IOException if something fails
1409        */

1410        public void create () throws IOException {
1411            NotifyDescriptor.InputLine input = new NotifyDescriptor.InputLine (
1412                                                   DataObject.getString ("CTL_NewFolderName"), DataObject.getString ("CTL_NewFolderTitle")
1413                                               );
1414            input.setInputText (DataObject.getString ("CTL_NewFolderValue"));
1415            if (DialogDisplayer.getDefault ().notify (input) == NotifyDescriptor.OK_OPTION) {
1416                String JavaDoc folderName = input.getInputText ();
1417                if ("".equals (folderName)) return; // empty name = cancel // NOI18N
1418

1419                FileObject folder = getPrimaryFile ();
1420                int dotPos = -1;
1421
1422                while ((dotPos = folderName.indexOf (".")) != -1) { // NOI18N
1423
String JavaDoc subFolder = folderName.substring (0, dotPos);
1424                    folderName = folderName.substring (dotPos + 1);
1425
1426
1427                    FileObject existingFile = folder.getFileObject (subFolder);
1428                    if (existingFile != null) {
1429                        if (!existingFile.isFolder ()) {
1430                            DialogDisplayer.getDefault ().notify (
1431                                new NotifyDescriptor.Message (
1432                                    NbBundle.getMessage (DataObject.class,
1433                        "MSG_FMT_FileExists",
1434                         subFolder, folder.getName ()),
1435                                    NotifyDescriptor.WARNING_MESSAGE
1436                                )
1437                            );
1438                            return;
1439                        }
1440                        folder = existingFile;
1441                    } else {
1442                        if (! confirmName (subFolder)) {
1443                            throw new IOException(
1444                NbBundle.getMessage(DataObject.class,
1445                    "EXC_WrongName", subFolder)
1446                            );
1447                        }
1448                        folder = folder.createFolder (subFolder);
1449                    }
1450                }
1451                if (!"".equals (folderName)) { // NOI18N
1452
FileObject existingFile = folder.getFileObject (folderName);
1453                    if (existingFile != null) {
1454                        if (existingFile.isFolder ()) {
1455                            DialogDisplayer.getDefault ().notify (
1456                                new NotifyDescriptor.Message (
1457                                    NbBundle.getMessage (DataObject.class,
1458                        "MSG_FMT_FolderExists",
1459                        folderName, folder.getName ()),
1460                                    NotifyDescriptor.INFORMATION_MESSAGE
1461                                )
1462                            );
1463                        } else {
1464                            DialogDisplayer.getDefault ().notify (
1465                                new NotifyDescriptor.Message (
1466                                    NbBundle.getMessage (DataObject.class,
1467                        "MSG_FMT_FileExists",
1468                        folderName, folder.getName ()),
1469                                    NotifyDescriptor.WARNING_MESSAGE
1470                                )
1471                            );
1472                        }
1473                        return;
1474                    }
1475
1476                    if (! confirmName (folderName)) {
1477                        throw new IOException(
1478                            NbBundle.getMessage(DataObject.class,
1479                    "EXC_WrongName", folderName )
1480                        );
1481                    }
1482
1483                    DataObject created = DataObject.find(folder.createFolder (folderName));
1484                    if (created != null) {
1485                        DataLoaderPool.getDefault().fireOperationEvent(
1486                            new OperationEvent.Copy (created, DataFolder.this), OperationEvent.TEMPL
1487                        );
1488                    }
1489                }
1490            }
1491        }
1492    }
1493
1494    private class Paste extends DataTransferSupport {
1495        Paste() {}
1496        
1497        /** Defines array of classes implementing paste for specified clipboard operation.
1498        * @param op clopboard operation to specify paste types for
1499        * @return array of classes extending PasteTypeExt class
1500        */

1501        protected DataTransferSupport.PasteTypeExt[] definePasteTypes (int op) {
1502            switch (op) {
1503                case LoaderTransfer.CLIPBOARD_CUT:
1504                return new DataTransferSupport.PasteTypeExt [] {
1505                    new DataTransferSupport.PasteTypeExt() {
1506                        public String JavaDoc getName () {
1507                            return DataObject.getString ("PT_move"); // NOI18N
1508
}
1509                        public HelpCtx getHelpCtx () {
1510                            return new HelpCtx (Paste.class.getName () + ".move"); // NOI18N
1511
}
1512                        protected boolean handleCanPaste (DataObject obj) {
1513                            return obj.isMoveAllowed () && !isParent (getPrimaryFile (), obj.getPrimaryFile ());
1514                        }
1515                        protected void handlePaste (DataObject obj) throws IOException {
1516                            obj.move (DataFolder.this);
1517                        }
1518
1519                        /** Cleans clipboard after paste. Overrides superclass method. */
1520                        protected boolean cleanClipboard() {
1521                            return true;
1522                        }
1523                        
1524                        /** Check if one file object has another as a parent.
1525                         * @param fo the file object to check
1526                         * @param parent
1527                         * @return true if parent is fo's (indirect) parent
1528                         */

1529                        /*not private called from FolderNode*/
1530                        private boolean isParent (FileObject fo, FileObject parent) {
1531                            File parentFile = FileUtil.toFile(parent);
1532                            File foFile = FileUtil.toFile(fo);
1533                            
1534                            if (foFile != null && parentFile != null) {
1535                                return isParentFile(foFile, parentFile);
1536                            }
1537                            
1538                            try {
1539                                if (fo.getFileSystem () != parent.getFileSystem ()) {
1540                                    return false;
1541                                }
1542                            } catch (IOException ex) {
1543                            }
1544
1545                            while (fo != null) {
1546                                if (fo.equals (parent)) {
1547                                    return true;
1548                                }
1549
1550                                fo = fo.getParent ();
1551                            }
1552
1553                            return false;
1554                        }
1555                    }
1556                };
1557
1558                case LoaderTransfer.CLIPBOARD_COPY:
1559                return new DataTransferSupport.PasteTypeExt[] {
1560                    new DataTransferSupport.PasteTypeExt() {
1561                        public String JavaDoc getName () {
1562                            return DataObject.getString ("PT_copy"); // NOI18N
1563
}
1564                        public HelpCtx getHelpCtx () {
1565                            return new HelpCtx (Paste.class.getName () + ".copy"); // NOI18N
1566
}
1567                        protected boolean handleCanPaste (DataObject obj) {
1568                            return obj.isCopyAllowed ();
1569                        }
1570                        protected void handlePaste (DataObject obj) throws IOException {
1571                            saveIfModified(obj);
1572                            obj.copy (DataFolder.this);
1573                        }
1574
1575                        private void saveIfModified(DataObject obj) throws IOException {
1576                            if (obj.isModified()) {
1577                                SaveCookie sc = obj.getCookie(SaveCookie.class);
1578                                if (sc != null) {
1579                                    sc.save();
1580                                }
1581                            }
1582                        }
1583                    },
1584                    new DataTransferSupport.PasteTypeExt() {
1585                        public String JavaDoc getName () {
1586                            return DataObject.getString ("PT_instantiate"); // NOI18N
1587
}
1588                        public HelpCtx getHelpCtx () {
1589                            return new HelpCtx (Paste.class.getName () + ".instantiate"); // NOI18N
1590
}
1591                        protected boolean handleCanPaste (DataObject obj) {
1592                            return obj.isTemplate ();
1593                        }
1594                        protected void handlePaste (DataObject obj) throws IOException {
1595                            obj.createFromTemplate (DataFolder.this);
1596                        }
1597                    },
1598                    new DataTransferSupport.PasteTypeExt() {
1599                        public String JavaDoc getName () {
1600                            return DataObject.getString ("PT_shadow"); // NOI18N
1601
}
1602                        public HelpCtx getHelpCtx () {
1603                            return new HelpCtx (Paste.class.getName () + ".shadow"); // NOI18N
1604
}
1605                        protected boolean handleCanPaste (DataObject obj) {
1606                            // #42888 - disable "Create as Link" action on non-SystemFileSystem
1607
try {
1608                                if (!DataFolder.this.getPrimaryFile().getFileSystem().equals(
1609                                        Repository.getDefault().getDefaultFileSystem())) {
1610                                    return false;
1611                                }
1612                            } catch (FileStateInvalidException ex) {
1613                                // something wrong. disable.
1614
return false;
1615                            }
1616                            return obj.isShadowAllowed ();
1617                        }
1618                        protected void handlePaste (DataObject obj) throws IOException {
1619                            obj.createShadow (DataFolder.this);
1620                        }
1621                    }
1622                };
1623            }
1624            return new DataTransferSupport.PasteTypeExt[0];
1625        }
1626
1627        private boolean isParentFile(File foFile, File parentFile) {
1628            boolean retVal = false;
1629            while (foFile != null) {
1630                if (foFile.equals (parentFile)) {
1631                    retVal = true;
1632                    break;
1633                }
1634                foFile = foFile.getParentFile ();
1635            }
1636            return retVal;
1637        }
1638
1639        /** Defines array of data clipboard operations recognized by this paste support.
1640        * @return array of DataFlavors
1641        */

1642        protected int [] defineOperations () {
1643            return new int [] {
1644                LoaderTransfer.CLIPBOARD_CUT,
1645                LoaderTransfer.CLIPBOARD_COPY
1646            };
1647        }
1648        protected void handleCreatePasteTypes (Transferable t, java.util.List JavaDoc<PasteType> s) {
1649            // These should only accept single-node transfers, since they require dialogs.
1650
Node node = NodeTransfer.node (t, NodeTransfer.CLIPBOARD_COPY);
1651
1652            // lastly try special cookies
1653
if (node != null) {
1654                try {
1655                    InstanceCookie cookie = node.getCookie(InstanceCookie.class);
1656                    if (cookie != null && java.io.Serializable JavaDoc.class.isAssignableFrom (cookie.instanceClass ())) {
1657                        s.add (new DataTransferSupport.SerializePaste (DataFolder.this, cookie));
1658                        s.add (new DataTransferSupport.InstantiatePaste (DataFolder.this, cookie));
1659                    }
1660                } catch (IOException e) {
1661                } catch (ClassNotFoundException JavaDoc e) {
1662                }
1663            }
1664        }
1665    }
1666    
1667    /** Listener on changes in FolderList that delegates to our PCL.
1668     */

1669    private final class ListPCL extends Object JavaDoc implements PropertyChangeListener {
1670        ListPCL() {}
1671        public void propertyChange(java.beans.PropertyChangeEvent JavaDoc ev) {
1672            if (this == DataFolder.this.pcl) {
1673                // if I am still folder's correct listener
1674
DataFolder.this.firePropertyChange (PROP_CHILDREN, null, null);
1675            }
1676        }
1677        
1678    }
1679}
1680
Popular Tags