KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > spi > java > project > support > ui > PackageViewChildren


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.netbeans.spi.java.project.support.ui;
21
22 import java.awt.EventQueue JavaDoc;
23 import java.awt.Image JavaDoc;
24 import java.awt.datatransfer.DataFlavor JavaDoc;
25 import java.awt.datatransfer.Transferable JavaDoc;
26 import java.awt.datatransfer.UnsupportedFlavorException JavaDoc;
27 import java.awt.dnd.DnDConstants JavaDoc;
28 import java.beans.PropertyChangeEvent JavaDoc;
29 import java.beans.PropertyChangeListener JavaDoc;
30 import java.beans.PropertyChangeSupport JavaDoc;
31 import java.io.IOException JavaDoc;
32 import java.lang.reflect.InvocationTargetException JavaDoc;
33 import java.text.MessageFormat JavaDoc;
34 import java.util.ArrayList JavaDoc;
35 import java.util.Collection JavaDoc;
36 import java.util.Collections JavaDoc;
37 import java.util.HashSet JavaDoc;
38 import java.util.Iterator JavaDoc;
39 import java.util.List JavaDoc;
40 import java.util.Set JavaDoc;
41 import java.util.StringTokenizer JavaDoc;
42 import java.util.TreeMap JavaDoc;
43 import java.util.TreeSet JavaDoc;
44 import javax.swing.Action JavaDoc;
45 import javax.swing.SwingUtilities JavaDoc;
46 import javax.swing.event.ChangeEvent JavaDoc;
47 import javax.swing.event.ChangeListener JavaDoc;
48 import javax.swing.event.EventListenerList JavaDoc;
49 import org.netbeans.api.fileinfo.NonRecursiveFolder;
50 import org.netbeans.api.project.SourceGroup;
51 import org.netbeans.api.queries.VisibilityQuery;
52 import org.netbeans.modules.java.project.PackageDisplayUtils;
53 import org.netbeans.spi.project.ActionProvider;
54 import org.netbeans.spi.project.ui.support.FileSensitiveActions;
55 import org.openide.DialogDisplayer;
56 import org.openide.ErrorManager;
57 import org.openide.NotifyDescriptor;
58 import org.openide.actions.FileSystemAction;
59 import org.openide.actions.PropertiesAction;
60 import org.openide.filesystems.FileAttributeEvent;
61 import org.openide.filesystems.FileChangeListener;
62 import org.openide.filesystems.FileEvent;
63 import org.openide.filesystems.FileObject;
64 import org.openide.filesystems.FileRenameEvent;
65 import org.openide.filesystems.FileStateInvalidException;
66 import org.openide.filesystems.FileSystem;
67 import org.openide.filesystems.FileUtil;
68 import org.openide.loaders.ChangeableDataFilter;
69 import org.openide.loaders.DataFilter;
70 import org.openide.loaders.DataFolder;
71 import org.openide.loaders.DataObject;
72 import org.openide.nodes.Children;
73 import org.openide.nodes.FilterNode;
74 import org.openide.nodes.Node;
75 import org.openide.nodes.PropertySupport;
76 import org.openide.nodes.Sheet;
77 import org.openide.util.Exceptions;
78 import org.openide.util.Lookup;
79 import org.openide.util.NbBundle;
80 import org.openide.util.RequestProcessor;
81 import org.openide.util.WeakListeners;
82 import org.openide.util.datatransfer.ExTransferable;
83 import org.openide.util.datatransfer.MultiTransferObject;
84 import org.openide.util.datatransfer.PasteType;
85 import org.openide.util.lookup.Lookups;
86 import org.openide.util.lookup.ProxyLookup;
87 import org.openidex.search.FileObjectFilter;
88 import org.openidex.search.SearchInfoFactory;
89
90 /**
91  * Display of Java sources in a package structure rather than folder structure.
92  * @author Adam Sotona, Jesse Glick, Petr Hrebejk, Tomas Zezula
93  */

94 final class PackageViewChildren extends Children.Keys<String JavaDoc> implements FileChangeListener, ChangeListener JavaDoc, Runnable JavaDoc {
95     
96     private static final String JavaDoc NODE_NOT_CREATED = "NNC"; // NOI18N
97
private static final String JavaDoc NODE_NOT_CREATED_EMPTY = "NNC_E"; //NOI18N
98

99     private static final MessageFormat JavaDoc PACKAGE_FLAVOR = new MessageFormat JavaDoc("application/x-java-org-netbeans-modules-java-project-packagenodednd; class=org.netbeans.spi.java.project.support.ui.PackageViewChildren$PackageNode; mask={0}"); //NOI18N
100

101     static final String JavaDoc PRIMARY_TYPE = "application"; //NOI18N
102
static final String JavaDoc SUBTYPE = "x-java-org-netbeans-modules-java-project-packagenodednd"; //NOI18N
103
static final String JavaDoc MASK = "mask"; //NOI18N
104

105     private java.util.Map JavaDoc<String JavaDoc,Object JavaDoc/*NODE_NOT_CREATED|NODE_NOT_CREATED_EMPTY|PackageNode*/> names2nodes;
106     private final FileObject root;
107     private final SourceGroup group;
108     private FileChangeListener wfcl; // Weak listener on the system filesystem
109
private ChangeListener JavaDoc wvqcl; // Weak listener on the VisibilityQuery
110

111     /**
112      * Creates children based on a single source root.
113      * @param root the folder where sources start (must be a package root)
114      */

115     public PackageViewChildren(SourceGroup group) {
116         
117         // Sem mas dat cache a bude to uplne nejrychlejsi na svete
118

119         this.root = group.getRootFolder();
120         this.group = group;
121     }
122
123     FileObject getRoot() {
124         return root; // Used from PackageRootNode
125
}
126     
127     protected Node[] createNodes(String JavaDoc path) {
128         FileObject fo = root.getFileObject(path);
129         if ( fo != null && fo.isValid()) {
130             Object JavaDoc o = names2nodes.get(path);
131             PackageNode n;
132             if ( o == NODE_NOT_CREATED ) {
133                 n = new PackageNode( root, DataFolder.findFolder( fo ), false );
134             }
135             else if ( o == NODE_NOT_CREATED_EMPTY ) {
136                 n = new PackageNode( root, DataFolder.findFolder( fo ), true );
137             }
138             else {
139                 n = new PackageNode( root, DataFolder.findFolder( fo ) );
140             }
141             names2nodes.put(path, n);
142             return new Node[] {n};
143         }
144         else {
145             return new Node[0];
146         }
147         
148     }
149     
150     RequestProcessor.Task task = RequestProcessor.getDefault().create( this );
151         
152     protected void addNotify() {
153         // System.out.println("ADD NOTIFY" + root + " : " + this );
154
super.addNotify();
155         task.schedule( 0 );
156     }
157     
158     public Node[] getNodes( boolean optimal ) {
159         if ( optimal ) {
160             Node[] garbage = super.getNodes( false );
161             task.waitFinished();
162         }
163         return super.getNodes( false );
164     }
165     
166     public Node findChild (String JavaDoc name) {
167         while (true) {
168             Node n = super.findChild(name);
169             if (n != null) {
170                 // If already there, get it quickly.
171
return n;
172             }
173             // In case a project is made on a large existing source root,
174
// which happens to have a file in the root dir (so package node
175
// should exist), try to select the root package node soon; no need
176
// to wait for whole tree.
177
try {
178                 if (task.waitFinished(5000)) {
179                     return super.findChild(name);
180                 }
181                 // refreshKeysAsync won't run since we are blocking EQ!
182
refreshKeys();
183             } catch (InterruptedException JavaDoc x) {
184                 Exceptions.printStackTrace(x);
185             }
186         }
187     }
188     
189     public void run() {
190         computeKeys();
191         refreshKeys();
192         try {
193             FileSystem fs = root.getFileSystem();
194             wfcl = FileUtil.weakFileChangeListener(this, fs);
195             fs.addFileChangeListener( wfcl );
196         }
197         catch ( FileStateInvalidException e ) {
198             ErrorManager.getDefault().notify( ErrorManager.INFORMATIONAL, e );
199         }
200         wvqcl = WeakListeners.change( this, VisibilityQuery.getDefault() );
201         VisibilityQuery.getDefault().addChangeListener( wvqcl );
202     }
203
204     protected void removeNotify() {
205         // System.out.println("REMOVE NOTIFY" + root + " : " + this );
206
VisibilityQuery.getDefault().removeChangeListener( wvqcl );
207         try {
208             root.getFileSystem().removeFileChangeListener( wfcl );
209         }
210         catch ( FileStateInvalidException e ) {
211             ErrorManager.getDefault().notify( ErrorManager.INFORMATIONAL, e );
212         }
213         setKeys(new String JavaDoc[0]);
214         names2nodes.clear();
215         super.removeNotify();
216     }
217     
218     // Private methods ---------------------------------------------------------
219

220     private void refreshKeys() {
221         Set JavaDoc<String JavaDoc> keys;
222         synchronized (names2nodes) {
223             keys = new TreeSet JavaDoc<String JavaDoc>(names2nodes.keySet());
224         }
225         setKeys(keys);
226     }
227     
228     /* #70097: workaround of a javacore deadlock
229      * See related issue: #61027
230      */

231     private void refreshKeysAsync () {
232         EventQueue.invokeLater(new Runnable JavaDoc() {
233             public void run () {
234                 refreshKeys();
235             }
236          });
237     }
238     
239     private void computeKeys() {
240         // XXX this is not going to perform too well for a huge source root...
241
// However we have to go through the whole hierarchy in order to find
242
// all packages (Hrebejk)
243
names2nodes = Collections.synchronizedMap(new TreeMap JavaDoc<String JavaDoc,Object JavaDoc>());
244         findNonExcludedPackages( root );
245     }
246     
247     /**
248      * Collect all recursive subfolders, except those which have subfolders
249      * but no files.
250      */

251     private void findNonExcludedPackages( FileObject fo ) {
252         PackageView.findNonExcludedPackages(this, null, fo, group, true);
253     }
254     
255     
256     /** Finds all empty parents of given package and deletes them
257      */

258     private void cleanEmptyKeys( FileObject fo ) {
259         FileObject parent = fo.getParent();
260         
261         // Special case for default package
262
if ( root.equals( parent ) ) {
263             PackageNode n = get( parent );
264             // the default package is considered empty if it only contains folders,
265
// regardless of the contents of these folders (empty or not)
266
if ( n != null && PackageDisplayUtils.isEmpty( root, false ) ) {
267                 remove( root );
268             }
269             return;
270         }
271         
272         while ( FileUtil.isParentOf( root, parent ) ) {
273             PackageNode n = get( parent );
274             if ( n != null && n.isLeaf() ) {
275                 // System.out.println("Cleaning " + parent);
276
remove( parent );
277             }
278             parent = parent.getParent();
279         }
280     }
281     
282     // Non private only to be able to have the findNonExcludedPackages impl
283
// in on place (PackageView)
284
void add( FileObject fo, boolean empty ) {
285         String JavaDoc path = FileUtil.getRelativePath( root, fo );
286         assert path != null : "Adding wrong folder " + fo +"(valid="+fo.isValid()+")"+ "under root" + this.root + "(valid="+this.root.isValid()+")";
287         if ( get( fo ) == null ) {
288             names2nodes.put( path, empty ? NODE_NOT_CREATED_EMPTY : NODE_NOT_CREATED );
289             refreshKeysAsync();
290         }
291     }
292
293     private void remove( FileObject fo ) {
294         String JavaDoc path = FileUtil.getRelativePath( root, fo );
295         assert path != null : "Removing wrong folder" + fo;
296         names2nodes.remove( path );
297     }
298
299     private void removeSubTree (FileObject fo) {
300         String JavaDoc path = FileUtil.getRelativePath( root, fo );
301         assert path != null : "Removing wrong folder" + fo;
302         synchronized (names2nodes) {
303             Set JavaDoc<String JavaDoc> keys = names2nodes.keySet();
304             keys.remove(path);
305             path = path + '/'; //NOI18N
306
Iterator JavaDoc<String JavaDoc> it = keys.iterator();
307             while (it.hasNext()) {
308                 if (it.next().startsWith(path)) {
309                     it.remove();
310                 }
311             }
312         }
313     }
314
315     private PackageNode get( FileObject fo ) {
316         String JavaDoc path = FileUtil.getRelativePath( root, fo );
317         assert path != null : "Asking for wrong folder" + fo;
318         Object JavaDoc o = names2nodes.get( path );
319         return !isNodeCreated( o ) ? null : (PackageNode)o;
320     }
321     
322     private boolean contains( FileObject fo ) {
323         String JavaDoc path = FileUtil.getRelativePath( root, fo );
324         assert path != null : "Asking for wrong folder" + fo;
325         Object JavaDoc o = names2nodes.get( path );
326         return o != null;
327     }
328     
329     private boolean exists( FileObject fo ) {
330         String JavaDoc path = FileUtil.getRelativePath( root, fo );
331         return names2nodes.get( path ) != null;
332     }
333     
334     private boolean isNodeCreated( Object JavaDoc o ) {
335         return o instanceof Node;
336     }
337     
338     private PackageNode updatePath( String JavaDoc oldPath, String JavaDoc newPath ) {
339         Object JavaDoc o = names2nodes.get( oldPath );
340         if ( o == null ) {
341             return null;
342         }
343         names2nodes.remove( oldPath );
344         names2nodes.put( newPath, o );
345         return !isNodeCreated( o ) ? null : (PackageNode)o;
346     }
347     
348     // Implementation of FileChangeListener ------------------------------------
349

350     public void fileAttributeChanged( FileAttributeEvent fe ) {}
351
352     public void fileChanged( FileEvent fe ) {}
353
354     public void fileFolderCreated( FileEvent fe ) {
355         FileObject fo = fe.getFile();
356         if ( FileUtil.isParentOf( root, fo ) && isVisible( root, fo ) ) {
357             cleanEmptyKeys( fo );
358 // add( fo, false);
359
findNonExcludedPackages( fo );
360             refreshKeys();
361         }
362     }
363     
364     public void fileDataCreated( FileEvent fe ) {
365         FileObject fo = fe.getFile();
366         if ( FileUtil.isParentOf( root, fo ) && isVisible( root, fo ) ) {
367             FileObject parent = fo.getParent();
368             // XXX consider using group.contains() here
369
if ( !VisibilityQuery.getDefault().isVisible( parent ) ) {
370                 return; // Adding file into ignored directory
371
}
372             PackageNode n = get( parent );
373             if ( n == null && !contains( parent ) ) {
374                 add( parent, false );
375                 refreshKeys();
376             }
377             else if ( n != null ) {
378                 n.updateChildren();
379             }
380         }
381     }
382
383     public void fileDeleted( FileEvent fe ) {
384         FileObject fo = fe.getFile();
385         
386         // System.out.println("FILE DELETED " + FileUtil.getRelativePath( root, fo ) );
387

388         if ( FileUtil.isParentOf( root, fo ) && isVisible( root, fo ) ) {
389             
390             // System.out.println("IS FOLDER? " + fo + " : " + fo.isFolder() );
391
/* Hack for MasterFS see #42464 */
392             if ( fo.isFolder() || get( fo ) != null ) {
393                 // System.out.println("REMOVING FODER " + fo );
394
removeSubTree( fo );
395                 // Now add the parent if necessary
396
FileObject parent = fo.getParent();
397                 if ( ( FileUtil.isParentOf( root, parent ) || root.equals( parent ) ) && get( parent ) == null && parent.isValid() ) {
398                     // Candidate for adding
399
if ( !toBeRemoved( parent ) ) {
400                         // System.out.println("ADDING PARENT " + parent );
401
add( parent, true );
402                     }
403                 }
404                 refreshKeysAsync();
405             }
406             else {
407                 FileObject parent = fo.getParent();
408                 final PackageNode n = get( parent );
409                 if ( n != null ) {
410                     //#61027: workaround to a deadlock when the package is being changed from non-leaf to leaf:
411
boolean leaf = n.isLeaf();
412                     DataFolder df = n.getDataFolder();
413                     boolean empty = isEmpty(df);
414                     
415                     if (leaf != empty) {
416                         SwingUtilities.invokeLater(new Runnable JavaDoc() {
417                             public void run() {
418                                 n.updateChildren();
419                             }
420                         });
421                     } else {
422                         n.updateChildren();
423                     }
424                 }
425                 // If the parent folder only contains folders remove it
426
if ( toBeRemoved( parent ) ) {
427                     remove( parent );
428                     refreshKeysAsync();
429                 }
430                  
431             }
432         }
433         // else {
434
// System.out.println("NOT A PARENT " + fo );
435
// }
436
}
437     
438     /** Returns true if the folder should be removed from the view
439      * i.e. it has some unignored children and the children are folders only
440      */

441     private boolean toBeRemoved( FileObject folder ) {
442         boolean ignoredOnly = true;
443         boolean foldersOnly = true;
444         for (FileObject kid : folder.getChildren()) {
445             // XXX consider using group.contains() here
446
if (VisibilityQuery.getDefault().isVisible(kid)) {
447                 ignoredOnly = false;
448                 if (!kid.isFolder()) {
449                     foldersOnly = false;
450                     break;
451                 }
452             }
453         }
454         if ( ignoredOnly ) {
455             return false; // It is either empty or it only contains ignored files
456
// thus is leaf and it means package
457
}
458         else {
459             return foldersOnly;
460         }
461     }
462     
463     
464     public void fileRenamed( FileRenameEvent fe ) {
465         FileObject fo = fe.getFile();
466         if ( FileUtil.isParentOf( root, fo ) && fo.isFolder() ) {
467             String JavaDoc rp = FileUtil.getRelativePath( root, fo.getParent() );
468             String JavaDoc oldPath = rp + ( rp.length() == 0 ? "" : "/" ) + fe.getName() + fe.getExt(); // NOI18N
469

470             // XXX consider using group.contains() here
471
boolean visible = VisibilityQuery.getDefault().isVisible( fo );
472             boolean doUpdate = false;
473             
474             // Find all entries which have to be updated
475
List JavaDoc<String JavaDoc> needsUpdate = new ArrayList JavaDoc<String JavaDoc>();
476             synchronized (names2nodes) {
477                 for (Iterator JavaDoc<String JavaDoc> it = names2nodes.keySet().iterator(); it.hasNext(); ) {
478                     String JavaDoc p = it.next();
479                     if ( p.startsWith( oldPath ) ) {
480                         if ( visible ) {
481                             needsUpdate.add( p );
482                         } else {
483                             it.remove();
484                             doUpdate = true;
485                         }
486                     }
487                 }
488             }
489                         
490             // If the node does not exists then there might have been update
491
// from ignored to non ignored
492
if ( get( fo ) == null && visible ) {
493                 cleanEmptyKeys( fo );
494                 findNonExcludedPackages( fo );
495                 doUpdate = true; // force refresh
496
}
497             
498             int oldPathLen = oldPath.length();
499             String JavaDoc newPath = FileUtil.getRelativePath( root, fo );
500             for (String JavaDoc p : needsUpdate) {
501                 StringBuilder JavaDoc np = new StringBuilder JavaDoc(p);
502                 np.replace( 0, oldPathLen, newPath );
503                 PackageNode n = updatePath( p, np.toString() ); // Replace entries in cache
504
if ( n != null ) {
505                     n.updateDisplayName(); // Update nodes
506
}
507             }
508             
509             if ( needsUpdate.size() > 1 || doUpdate ) {
510                 // Sorting might change
511
refreshKeys();
512             }
513         }
514         /*
515         else if ( FileUtil.isParentOf( root, fo ) && fo.isFolder() ) {
516             FileObject parent = fo.getParent();
517             PackageNode n = get( parent );
518             if ( n != null && VisibilityQuery.getDefault().isVisible( parent ) ) {
519                 n.updateChildren();
520             }
521             
522         }
523         */

524         
525     }
526     
527     /** Test whether file and all it's parent up to parent paremeter
528      * are visible
529      */

530     private boolean isVisible( FileObject parent, FileObject file ) {
531         
532         do {
533             // XXX consider using group.contains() here
534
if ( !VisibilityQuery.getDefault().isVisible( file ) ) {
535                 return false;
536             }
537             file = file.getParent();
538         }
539         while ( file != null && file != parent );
540                 
541         return true;
542     }
543     
544
545     // Implementation of ChangeListener ------------------------------------
546

547     public void stateChanged( ChangeEvent JavaDoc e ) {
548         computeKeys();
549         refreshKeys();
550     }
551     
552
553     /*
554     private void debugKeySet() {
555         for( Iterator it = names2nodes.keySet().iterator(); it.hasNext(); ) {
556             String k = (String)it.next();
557             System.out.println( " " + k + " -> " + names2nodes.get( k ) );
558         }
559     }
560      */

561     
562     private final DataFilter NO_FOLDERS_FILTER = new NoFoldersDataFilter();
563
564     private static Action JavaDoc actions[];
565         
566     private static boolean isEmpty(DataFolder dataFolder) {
567         if ( dataFolder == null ) {
568             return true;
569         }
570         return PackageDisplayUtils.isEmpty( dataFolder.getPrimaryFile() );
571     }
572     
573     final class PackageNode extends FilterNode {
574         
575         private final FileObject root;
576         private DataFolder dataFolder;
577         private boolean isDefaultPackage;
578         
579         public PackageNode( FileObject root, DataFolder dataFolder ) {
580             this( root, dataFolder, isEmpty( dataFolder ) );
581         }
582         
583         public PackageNode( FileObject root, DataFolder dataFolder, boolean empty ) {
584             super( dataFolder.getNodeDelegate(),
585                    empty ? Children.LEAF : dataFolder.createNodeChildren( NO_FOLDERS_FILTER ),
586                    new ProxyLookup(
587                         Lookups.singleton(new NoFoldersContainer (dataFolder)),
588                         dataFolder.getNodeDelegate().getLookup(),
589                         Lookups.singleton(PackageRootNode.alwaysSearchableSearchInfo(SearchInfoFactory.createSearchInfo(
590                                                   dataFolder.getPrimaryFile(),
591                                                   false, //not recursive
592
new FileObjectFilter[] {
593                                                           SearchInfoFactory.VISIBILITY_FILTER})))));
594             this.root = root;
595             this.dataFolder = dataFolder;
596             this.isDefaultPackage = root.equals( dataFolder.getPrimaryFile() );
597         }
598     
599         FileObject getRoot() {
600             return root; // Used from PackageRootNode
601
}
602     
603         
604         public String JavaDoc getName() {
605             String JavaDoc relativePath = FileUtil.getRelativePath(root, dataFolder.getPrimaryFile());
606             return relativePath == null ? null : relativePath.replace('/', '.'); // NOI18N
607
}
608         
609         public Action JavaDoc[] getActions( boolean context ) {
610             
611             if ( !context ) {
612                 if ( actions == null ) {
613                     // Copy actions and leave out the PropertiesAction and FileSystemAction.
614
Action JavaDoc superActions[] = super.getActions( context );
615                     List JavaDoc<Action JavaDoc> actionList = new ArrayList JavaDoc<Action JavaDoc>(superActions.length);
616                     
617                     for( int i = 0; i < superActions.length; i++ ) {
618
619                         if ( superActions[ i ] == null && superActions[i + 1] instanceof PropertiesAction ) {
620                             i ++;
621                             continue;
622                         }
623                         else if ( superActions[i] instanceof PropertiesAction ) {
624                             continue;
625                         }
626                         else if ( superActions[i] instanceof FileSystemAction ) {
627                             actionList.add (null); // insert separator and new action
628
actionList.add (FileSensitiveActions.fileCommandAction(ActionProvider.COMMAND_COMPILE_SINGLE,
629                                 NbBundle.getMessage( PackageViewChildren.class, "LBL_CompilePackage_Action" ), // NOI18N
630
null ));
631                         }
632                         
633                         actionList.add( superActions[i] );
634                     }
635
636                     actions = new Action JavaDoc[ actionList.size() ];
637                     actionList.toArray( actions );
638                 }
639                 return actions;
640             }
641             else {
642                 return super.getActions( context );
643             }
644         }
645         
646         public boolean canRename() {
647             if ( isDefaultPackage ) {
648                 return false;
649             }
650             else {
651                 return true;
652             }
653         }
654
655         public boolean canCut () {
656             return !isDefaultPackage;
657         }
658
659         /**
660          * Copy handling
661          */

662         public Transferable JavaDoc clipboardCopy () throws IOException JavaDoc {
663             try {
664                 return new PackageTransferable (this, DnDConstants.ACTION_COPY);
665             } catch (ClassNotFoundException JavaDoc e) {
666                 throw new AssertionError JavaDoc(e);
667             }
668         }
669         
670         public Transferable JavaDoc clipboardCut () throws IOException JavaDoc {
671             try {
672                 return new PackageTransferable (this, DnDConstants.ACTION_MOVE);
673             } catch (ClassNotFoundException JavaDoc e) {
674                 throw new AssertionError JavaDoc(e);
675             }
676         }
677         
678         public /*@Override*/ Transferable JavaDoc drag () throws IOException JavaDoc {
679             try {
680                 return new PackageTransferable (this, DnDConstants.ACTION_NONE);
681             } catch (ClassNotFoundException JavaDoc e) {
682                 throw new AssertionError JavaDoc(e);
683             }
684         }
685
686         public PasteType[] getPasteTypes(Transferable JavaDoc t) {
687             if (t.isDataFlavorSupported(ExTransferable.multiFlavor)) {
688                 try {
689                     MultiTransferObject mto = (MultiTransferObject) t.getTransferData (ExTransferable.multiFlavor);
690                     boolean hasPackageFlavor = false;
691                     for (int i=0; i < mto.getCount(); i++) {
692                         DataFlavor JavaDoc[] flavors = mto.getTransferDataFlavors(i);
693                         if (isPackageFlavor(flavors)) {
694                             hasPackageFlavor = true;
695                         }
696                     }
697                     return hasPackageFlavor ? new PasteType[0] : super.getPasteTypes (t);
698                 } catch (UnsupportedFlavorException JavaDoc e) {
699                     ErrorManager.getDefault().notify(e);
700                     return new PasteType[0];
701                 } catch (IOException JavaDoc e) {
702                     ErrorManager.getDefault().notify(e);
703                     return new PasteType[0];
704                 }
705             }
706             else {
707                 DataFlavor JavaDoc[] flavors = t.getTransferDataFlavors();
708                 if (isPackageFlavor(flavors)) {
709                     return new PasteType[0];
710                 }
711                 else {
712                     return super.getPasteTypes(t);
713                 }
714             }
715         }
716         
717         public /*@Override*/ PasteType getDropType (Transferable JavaDoc t, int action, int index) {
718             if (t.isDataFlavorSupported(ExTransferable.multiFlavor)) {
719                 try {
720                     MultiTransferObject mto = (MultiTransferObject) t.getTransferData (ExTransferable.multiFlavor);
721                     boolean hasPackageFlavor = false;
722                     for (int i=0; i < mto.getCount(); i++) {
723                         DataFlavor JavaDoc[] flavors = mto.getTransferDataFlavors(i);
724                         if (isPackageFlavor(flavors)) {
725                             hasPackageFlavor = true;
726                         }
727                     }
728                     return hasPackageFlavor ? null : super.getDropType (t, action, index);
729                 } catch (UnsupportedFlavorException JavaDoc e) {
730                     ErrorManager.getDefault().notify(e);
731                     return null;
732                 } catch (IOException JavaDoc e) {
733                     ErrorManager.getDefault().notify(e);
734                     return null;
735                 }
736             }
737             else {
738                 DataFlavor JavaDoc[] flavors = t.getTransferDataFlavors();
739                 if (isPackageFlavor(flavors)) {
740                     return null;
741                 }
742                 else {
743                     return super.getDropType (t, action, index);
744                 }
745             }
746         }
747
748
749         private boolean isPackageFlavor (DataFlavor JavaDoc[] flavors) {
750             for (int i=0; i<flavors.length; i++) {
751                 if (SUBTYPE.equals(flavors[i].getSubType ()) && PRIMARY_TYPE.equals(flavors[i].getPrimaryType ())) {
752                     //Disable pasting into package, only paste into root is allowed
753
return true;
754                 }
755             }
756             return false;
757         }
758
759         private synchronized PackageRenameHandler getRenameHandler() {
760             Collection JavaDoc<? extends PackageRenameHandler> handlers = Lookup.getDefault().lookupAll(PackageRenameHandler.class);
761             if (handlers.size()==0)
762                 return null;
763             if (handlers.size()>1)
764                 ErrorManager.getDefault().log(ErrorManager.WARNING, "Multiple instances of PackageRenameHandler found in Lookup; only using first one: " + handlers); //NOI18N
765
return handlers.iterator().next();
766         }
767         
768         public void setName(String JavaDoc name) {
769             PackageRenameHandler handler = getRenameHandler();
770             if (handler!=null) {
771                 handler.handleRename(this, name);
772                 return;
773             }
774             
775             if (isDefaultPackage) {
776                 return;
777             }
778             String JavaDoc oldName = getName();
779             if (oldName.equals(name)) {
780                 return;
781             }
782             if (!isValidPackageName (name)) {
783                 DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message (
784                         NbBundle.getMessage(PackageViewChildren.class,"MSG_InvalidPackageName"), NotifyDescriptor.INFORMATION_MESSAGE));
785                 return;
786             }
787             name = name.replace('.','/')+'/'; //NOI18N
788
oldName = oldName.replace('.','/')+'/'; //NOI18N
789
int i;
790             for (i=0; i<oldName.length() && i< name.length(); i++) {
791                 if (oldName.charAt(i) != name.charAt(i)) {
792                     break;
793                 }
794             }
795             i--;
796             int index = oldName.lastIndexOf('/',i); //NOI18N
797
String JavaDoc commonPrefix = index == -1 ? null : oldName.substring(0,index);
798             String JavaDoc toCreate = (index+1 == name.length()) ? "" : name.substring(index+1); //NOI18N
799
try {
800                 FileObject commonFolder = commonPrefix == null ? this.root : this.root.getFileObject(commonPrefix);
801                 FileObject destination = commonFolder;
802                 StringTokenizer JavaDoc dtk = new StringTokenizer JavaDoc(toCreate,"/"); //NOI18N
803
while (dtk.hasMoreTokens()) {
804                     String JavaDoc pathElement = dtk.nextToken();
805                     FileObject tmp = destination.getFileObject(pathElement);
806                     if (tmp == null) {
807                         tmp = destination.createFolder (pathElement);
808                     }
809                     destination = tmp;
810                 }
811                 FileObject source = this.dataFolder.getPrimaryFile();
812                 DataFolder sourceFolder = DataFolder.findFolder (source);
813                 DataFolder destinationFolder = DataFolder.findFolder (destination);
814                 DataObject[] children = sourceFolder.getChildren();
815                 for (int j=0; j<children.length; j++) {
816                     if (children[j].getPrimaryFile().isData()) {
817                         children[j].move(destinationFolder);
818                     }
819                 }
820                 while (!commonFolder.equals(source)) {
821                     if (source.getChildren().length==0) {
822                         FileObject tmp = source;
823                         source = source.getParent();
824                         tmp.delete();
825                     }
826                     else {
827                         break;
828                     }
829                 }
830             } catch (IOException JavaDoc ioe) {
831                 ErrorManager.getDefault().notify (ioe);
832             }
833         }
834         
835         
836         
837         public boolean canDestroy() {
838             if ( isDefaultPackage ) {
839                 return false;
840             }
841             else {
842                 return true;
843             }
844         }
845         
846         public void destroy() throws IOException JavaDoc {
847             FileObject parent = dataFolder.getPrimaryFile().getParent();
848             // First; delete all files except packages
849
DataObject ch[] = dataFolder.getChildren();
850             boolean empty = true;
851             for( int i = 0; ch != null && i < ch.length; i++ ) {
852                 if ( !ch[i].getPrimaryFile().isFolder() ) {
853                     ch[i].delete();
854                 }
855                 else {
856                     empty = false;
857                 }
858             }
859             
860             // If empty delete itself
861
if ( empty ) {
862                 super.destroy();
863             }
864             
865             
866             // Second; delete empty super packages
867
while( !parent.equals( root ) && parent.getChildren().length == 0 ) {
868                 FileObject newParent = parent.getParent();
869                 parent.delete();
870                 parent = newParent;
871             }
872         }
873         
874         /**
875          * Initially overridden to support CVS status labels in package nodes.
876          *
877          * @return annotated display name
878          */

879         public String JavaDoc getHtmlDisplayName() {
880             String JavaDoc name = getDisplayName();
881             try {
882                 FileObject fo = dataFolder.getPrimaryFile();
883                 Set JavaDoc<FileObject> set = new NonRecursiveFolderSet(fo);
884                 FileSystem.Status status = fo.getFileSystem().getStatus();
885                 if (status instanceof FileSystem.HtmlStatus) {
886                     name = ((FileSystem.HtmlStatus) status).annotateNameHtml(name, set);
887                 } else {
888                     // #89138: return null if the name starts with '<' and status is not HtmlStatus
889
if (name.startsWith("<")) {
890                         name = null;
891                     } else {
892                         name = status.annotateName(name, set);
893                     }
894                 }
895             } catch (FileStateInvalidException e) {
896                 // no fs, do nothing
897
}
898             return name;
899         }
900         
901         public String JavaDoc getDisplayName() {
902             FileObject folder = dataFolder.getPrimaryFile();
903             String JavaDoc path = FileUtil.getRelativePath(root, folder);
904             if (path == null) {
905                 // ???
906
return "";
907             }
908             return PackageDisplayUtils.getDisplayLabel( path.replace('/', '.'));
909         }
910         
911         public String JavaDoc getShortDescription() {
912             FileObject folder = dataFolder.getPrimaryFile();
913             String JavaDoc path = FileUtil.getRelativePath(root, folder);
914             if (path == null) {
915                 // ???
916
return "";
917             }
918             return PackageDisplayUtils.getToolTip(folder, path.replace('/', '.'));
919         }
920
921         public Image JavaDoc getIcon (int type) {
922             Image JavaDoc img = getMyIcon (type);
923
924             try {
925                 FileObject fo = dataFolder.getPrimaryFile();
926                 Set JavaDoc<FileObject> set = new NonRecursiveFolderSet(fo);
927                 img = fo.getFileSystem ().getStatus ().annotateIcon (img, type, set);
928             } catch (FileStateInvalidException e) {
929                 // no fs, do nothing
930
}
931
932             return img;
933         }
934
935         public Image JavaDoc getOpenedIcon (int type) {
936             Image JavaDoc img = getMyOpenedIcon(type);
937
938             try {
939                 FileObject fo = dataFolder.getPrimaryFile();
940                 Set JavaDoc<FileObject> set = new NonRecursiveFolderSet(fo);
941                 img = fo.getFileSystem ().getStatus ().annotateIcon (img, type, set);
942             } catch (FileStateInvalidException e) {
943                 // no fs, do nothing
944
}
945
946             return img;
947         }
948         
949         
950         private Image JavaDoc getMyIcon(int type) {
951             FileObject folder = dataFolder.getPrimaryFile();
952             String JavaDoc path = FileUtil.getRelativePath(root, folder);
953             if (path == null) {
954                 // ???
955
return null;
956             }
957             return PackageDisplayUtils.getIcon(folder, path.replace('/', '.'), isLeaf() );
958         }
959         
960         private Image JavaDoc getMyOpenedIcon(int type) {
961             return getIcon(type);
962         }
963         
964         public void update() {
965             fireIconChange();
966             fireOpenedIconChange();
967         }
968         
969         public void updateDisplayName() {
970             fireNameChange(null, null);
971             fireDisplayNameChange(null, null);
972             fireShortDescriptionChange(null, null);
973         }
974         
975         public void updateChildren() {
976             boolean leaf = isLeaf();
977             DataFolder df = getDataFolder();
978             boolean empty = isEmpty( df );
979             if ( leaf != empty ) {
980                 setChildren( empty ? Children.LEAF: df.createNodeChildren( NO_FOLDERS_FILTER ) );
981                 update();
982             }
983         }
984         
985         @Override JavaDoc
986         public Node.PropertySet[] getPropertySets () {
987             Node.PropertySet[] properties = super.getPropertySets ();
988             for (int i=0; i< properties.length; i++) {
989                 if (Sheet.PROPERTIES.equals(properties[i].getName())) {
990                     //Replace the Sheet.PROPERTIES by the new one
991
//having only the name property which does refactoring
992
properties[i] = Sheet.createPropertiesSet();
993                     ((Sheet.Set) properties[i]).put(new PropertySupport.ReadWrite<String JavaDoc>(DataObject.PROP_NAME, String JavaDoc.class,
994                             NbBundle.getMessage(PackageViewChildren.class,"PROP_name"), NbBundle.getMessage(PackageViewChildren.class,"HINT_name")) {
995                         @Override JavaDoc
996                         public String JavaDoc getValue() {
997                             return PackageViewChildren.PackageNode.this.getName();
998                         }
999                         @Override JavaDoc
1000                        public void setValue(String JavaDoc n) throws IllegalAccessException JavaDoc, IllegalArgumentException JavaDoc, InvocationTargetException JavaDoc {
1001                            if (!canRename()) {
1002                                throw new IllegalAccessException JavaDoc();
1003                            }
1004                            PackageViewChildren.PackageNode.this.setName(n);
1005                        }
1006                        @Override JavaDoc
1007                        public boolean canWrite() {
1008                            return PackageViewChildren.PackageNode.this.canRename();
1009                        }
1010                    });
1011                }
1012            }
1013            return properties;
1014        }
1015        
1016        private DataFolder getDataFolder() {
1017            return getCookie(DataFolder.class);
1018        }
1019        
1020        private boolean isValidPackageName(String JavaDoc name) {
1021            if (name.length() == 0) {
1022                //Fast check of default pkg
1023
return true;
1024            }
1025            StringTokenizer JavaDoc tk = new StringTokenizer JavaDoc(name,".",true); //NOI18N
1026
boolean delimExpected = false;
1027            while (tk.hasMoreTokens()) {
1028                String JavaDoc namePart = tk.nextToken();
1029                if (!delimExpected) {
1030                    if (namePart.equals(".")) { //NOI18N
1031
return false;
1032                    }
1033                    for (int i=0; i< namePart.length(); i++) {
1034                        char c = namePart.charAt(i);
1035                        if (i == 0) {
1036                            if (!Character.isJavaIdentifierStart (c)) {
1037                                return false;
1038                            }
1039                        }
1040                        else {
1041                            if (!Character.isJavaIdentifierPart(c)) {
1042                                return false;
1043                            }
1044                        }
1045                    }
1046                }
1047                else {
1048                    if (!namePart.equals(".")) { //NOI18N
1049
return false;
1050                    }
1051                }
1052                delimExpected = !delimExpected;
1053            }
1054            return delimExpected;
1055        }
1056    }
1057    
1058    private static final class NoFoldersContainer
1059    implements DataObject.Container, PropertyChangeListener JavaDoc,
1060               NonRecursiveFolder {
1061        private DataFolder folder;
1062        private PropertyChangeSupport JavaDoc prop = new PropertyChangeSupport JavaDoc (this);
1063        
1064        public NoFoldersContainer (DataFolder folder) {
1065            this.folder = folder;
1066        }
1067        
1068        public FileObject getFolder() {
1069            return folder.getPrimaryFile();
1070        }
1071        
1072        public DataObject[] getChildren () {
1073            DataObject[] arr = folder.getChildren ();
1074            List JavaDoc<DataObject> list = new ArrayList JavaDoc<DataObject>(arr.length);
1075            for (int i = 0; i < arr.length; i++) {
1076                if (arr[i] instanceof DataFolder) continue;
1077                
1078                list.add (arr[i]);
1079            }
1080            return list.size() == arr.length ? arr : list.toArray(new DataObject[0]);
1081        }
1082
1083        public void addPropertyChangeListener(PropertyChangeListener JavaDoc l) {
1084            prop.addPropertyChangeListener (l);
1085        }
1086
1087        public void removePropertyChangeListener(PropertyChangeListener JavaDoc l) {
1088            prop.removePropertyChangeListener (l);
1089        }
1090
1091        public void propertyChange(PropertyChangeEvent JavaDoc evt) {
1092            if (DataObject.Container.PROP_CHILDREN.equals (evt.getPropertyName ())) {
1093                prop.firePropertyChange (PROP_CHILDREN, null, null);
1094            }
1095        }
1096    }
1097    
1098    final class NoFoldersDataFilter implements ChangeListener JavaDoc, ChangeableDataFilter {
1099        
1100        EventListenerList JavaDoc ell = new EventListenerList JavaDoc();
1101        
1102        public NoFoldersDataFilter() {
1103            VisibilityQuery.getDefault().addChangeListener( this );
1104        }
1105                
1106        public boolean acceptDataObject(DataObject obj) {
1107            FileObject fo = obj.getPrimaryFile();
1108            return VisibilityQuery.getDefault().isVisible(fo) && !(obj instanceof DataFolder) && group.contains(fo);
1109        }
1110        
1111        public void stateChanged( ChangeEvent JavaDoc e) {
1112            Object JavaDoc[] listeners = ell.getListenerList();
1113            ChangeEvent JavaDoc event = null;
1114            for (int i = listeners.length-2; i>=0; i-=2) {
1115                if (listeners[i] == ChangeListener JavaDoc.class) {
1116                    if ( event == null) {
1117                        event = new ChangeEvent JavaDoc( this );
1118                    }
1119                    ((ChangeListener JavaDoc)listeners[i+1]).stateChanged( event );
1120                }
1121            }
1122        }
1123    
1124        public void addChangeListener( ChangeListener JavaDoc listener ) {
1125            ell.add( ChangeListener JavaDoc.class, listener );
1126        }
1127                        
1128        public void removeChangeListener( ChangeListener JavaDoc listener ) {
1129            ell.remove( ChangeListener JavaDoc.class, listener );
1130        }
1131        
1132    }
1133
1134    static class PackageTransferable extends ExTransferable.Single {
1135
1136        private PackageNode node;
1137
1138        public PackageTransferable (PackageNode node, int operation) throws ClassNotFoundException JavaDoc {
1139            super(new DataFlavor JavaDoc(PACKAGE_FLAVOR.format(new Object JavaDoc[] {new Integer JavaDoc(operation)}), null, PackageNode.class.getClassLoader()));
1140            this.node = node;
1141        }
1142
1143        protected Object JavaDoc getData() throws IOException JavaDoc, UnsupportedFlavorException JavaDoc {
1144            return this.node;
1145        }
1146    }
1147
1148
1149    static class PackagePasteType extends PasteType {
1150        
1151        private int op;
1152        private PackageNode[] nodes;
1153        private FileObject srcRoot;
1154
1155        public PackagePasteType (FileObject srcRoot, PackageNode[] node, int op) {
1156            assert op == DnDConstants.ACTION_COPY || op == DnDConstants.ACTION_MOVE || op == DnDConstants.ACTION_NONE : "Invalid DnD operation"; //NOI18N
1157
this.nodes = node;
1158            this.op = op;
1159            this.srcRoot = srcRoot;
1160        }
1161        
1162        public void setOperation (int op) {
1163            this.op = op;
1164        }
1165
1166        public Transferable JavaDoc paste() throws IOException JavaDoc {
1167            assert this.op != DnDConstants.ACTION_NONE;
1168            for (int ni=0; ni< nodes.length; ni++) {
1169                FileObject fo = srcRoot;
1170                if (!nodes[ni].isDefaultPackage) {
1171                    String JavaDoc pkgName = nodes[ni].getName();
1172                    StringTokenizer JavaDoc tk = new StringTokenizer JavaDoc(pkgName,"."); //NOI18N
1173
while (tk.hasMoreTokens()) {
1174                        String JavaDoc name = tk.nextToken();
1175                        FileObject tmp = fo.getFileObject(name,null);
1176                        if (tmp == null) {
1177                            tmp = fo.createFolder(name);
1178                        }
1179                        fo = tmp;
1180                    }
1181                }
1182                DataFolder dest = DataFolder.findFolder(fo);
1183                DataObject[] children = nodes[ni].dataFolder.getChildren();
1184                boolean cantDelete = false;
1185                for (int i=0; i< children.length; i++) {
1186                    if (children[i].getPrimaryFile().isData()
1187                    && VisibilityQuery.getDefault().isVisible (children[i].getPrimaryFile())) {
1188                        //Copy only the package level
1189
children[i].copy (dest);
1190                        if (this.op == DnDConstants.ACTION_MOVE) {
1191                            try {
1192                                children[i].delete();
1193                            } catch (IOException JavaDoc ioe) {
1194                                cantDelete = true;
1195                            }
1196                        }
1197                    }
1198                    else {
1199                        cantDelete = true;
1200                    }
1201                }
1202                if (this.op == DnDConstants.ACTION_MOVE && !cantDelete) {
1203                    try {
1204                        FileObject tmpFo = nodes[ni].dataFolder.getPrimaryFile();
1205                        FileObject originalRoot = nodes[ni].root;
1206                        assert tmpFo != null && originalRoot != null;
1207                        while (!tmpFo.equals(originalRoot)) {
1208                            if (tmpFo.getChildren().length == 0) {
1209                                FileObject tmpFoParent = tmpFo.getParent();
1210                                tmpFo.delete ();
1211                                tmpFo = tmpFoParent;
1212                            }
1213                            else {
1214                                break;
1215                            }
1216                        }
1217                    } catch (IOException JavaDoc ioe) {
1218                        //Not important
1219
}
1220                }
1221            }
1222            return ExTransferable.EMPTY;
1223        }
1224
1225        public String JavaDoc getName() {
1226            return NbBundle.getMessage(PackageViewChildren.class,"TXT_PastePackage");
1227        }
1228    }
1229
1230    /**
1231     * FileObject set that represents package. It means
1232     * that it's content must not be processed recursively.
1233     */

1234    private static class NonRecursiveFolderSet extends HashSet JavaDoc<FileObject> implements NonRecursiveFolder {
1235        
1236        private final FileObject folder;
1237        
1238        /**
1239         * Creates set with one element, the folder.
1240         */

1241        public NonRecursiveFolderSet(FileObject folder) {
1242            this.folder = folder;
1243            add(folder);
1244        }
1245        
1246        public FileObject getFolder() {
1247            return folder;
1248        }
1249    }
1250}
1251
Popular Tags