1 19 20 package org.netbeans.spi.java.project.support.ui; 21 22 import java.awt.EventQueue ; 23 import java.awt.Image ; 24 import java.awt.datatransfer.DataFlavor ; 25 import java.awt.datatransfer.Transferable ; 26 import java.awt.datatransfer.UnsupportedFlavorException ; 27 import java.awt.dnd.DnDConstants ; 28 import java.beans.PropertyChangeEvent ; 29 import java.beans.PropertyChangeListener ; 30 import java.beans.PropertyChangeSupport ; 31 import java.io.IOException ; 32 import java.lang.reflect.InvocationTargetException ; 33 import java.text.MessageFormat ; 34 import java.util.ArrayList ; 35 import java.util.Collection ; 36 import java.util.Collections ; 37 import java.util.HashSet ; 38 import java.util.Iterator ; 39 import java.util.List ; 40 import java.util.Set ; 41 import java.util.StringTokenizer ; 42 import java.util.TreeMap ; 43 import java.util.TreeSet ; 44 import javax.swing.Action ; 45 import javax.swing.SwingUtilities ; 46 import javax.swing.event.ChangeEvent ; 47 import javax.swing.event.ChangeListener ; 48 import javax.swing.event.EventListenerList ; 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 94 final class PackageViewChildren extends Children.Keys<String > implements FileChangeListener, ChangeListener , Runnable { 95 96 private static final String NODE_NOT_CREATED = "NNC"; private static final String NODE_NOT_CREATED_EMPTY = "NNC_E"; 99 private static final MessageFormat PACKAGE_FLAVOR = new MessageFormat ("application/x-java-org-netbeans-modules-java-project-packagenodednd; class=org.netbeans.spi.java.project.support.ui.PackageViewChildren$PackageNode; mask={0}"); 101 static final String PRIMARY_TYPE = "application"; static final String SUBTYPE = "x-java-org-netbeans-modules-java-project-packagenodednd"; static final String MASK = "mask"; 105 private java.util.Map <String ,Object > names2nodes; 106 private final FileObject root; 107 private final SourceGroup group; 108 private FileChangeListener wfcl; private ChangeListener wvqcl; 111 115 public PackageViewChildren(SourceGroup group) { 116 117 119 this.root = group.getRootFolder(); 120 this.group = group; 121 } 122 123 FileObject getRoot() { 124 return root; } 126 127 protected Node[] createNodes(String path) { 128 FileObject fo = root.getFileObject(path); 129 if ( fo != null && fo.isValid()) { 130 Object 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 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 name) { 167 while (true) { 168 Node n = super.findChild(name); 169 if (n != null) { 170 return n; 172 } 173 try { 178 if (task.waitFinished(5000)) { 179 return super.findChild(name); 180 } 181 refreshKeys(); 183 } catch (InterruptedException 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 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 [0]); 214 names2nodes.clear(); 215 super.removeNotify(); 216 } 217 218 220 private void refreshKeys() { 221 Set <String > keys; 222 synchronized (names2nodes) { 223 keys = new TreeSet <String >(names2nodes.keySet()); 224 } 225 setKeys(keys); 226 } 227 228 231 private void refreshKeysAsync () { 232 EventQueue.invokeLater(new Runnable () { 233 public void run () { 234 refreshKeys(); 235 } 236 }); 237 } 238 239 private void computeKeys() { 240 names2nodes = Collections.synchronizedMap(new TreeMap <String ,Object >()); 244 findNonExcludedPackages( root ); 245 } 246 247 251 private void findNonExcludedPackages( FileObject fo ) { 252 PackageView.findNonExcludedPackages(this, null, fo, group, true); 253 } 254 255 256 258 private void cleanEmptyKeys( FileObject fo ) { 259 FileObject parent = fo.getParent(); 260 261 if ( root.equals( parent ) ) { 263 PackageNode n = get( parent ); 264 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 remove( parent ); 277 } 278 parent = parent.getParent(); 279 } 280 } 281 282 void add( FileObject fo, boolean empty ) { 285 String 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 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 path = FileUtil.getRelativePath( root, fo ); 301 assert path != null : "Removing wrong folder" + fo; 302 synchronized (names2nodes) { 303 Set <String > keys = names2nodes.keySet(); 304 keys.remove(path); 305 path = path + '/'; Iterator <String > 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 path = FileUtil.getRelativePath( root, fo ); 317 assert path != null : "Asking for wrong folder" + fo; 318 Object o = names2nodes.get( path ); 319 return !isNodeCreated( o ) ? null : (PackageNode)o; 320 } 321 322 private boolean contains( FileObject fo ) { 323 String path = FileUtil.getRelativePath( root, fo ); 324 assert path != null : "Asking for wrong folder" + fo; 325 Object o = names2nodes.get( path ); 326 return o != null; 327 } 328 329 private boolean exists( FileObject fo ) { 330 String path = FileUtil.getRelativePath( root, fo ); 331 return names2nodes.get( path ) != null; 332 } 333 334 private boolean isNodeCreated( Object o ) { 335 return o instanceof Node; 336 } 337 338 private PackageNode updatePath( String oldPath, String newPath ) { 339 Object 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 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 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 if ( !VisibilityQuery.getDefault().isVisible( parent ) ) { 370 return; } 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 388 if ( FileUtil.isParentOf( root, fo ) && isVisible( root, fo ) ) { 389 390 392 if ( fo.isFolder() || get( fo ) != null ) { 393 removeSubTree( fo ); 395 FileObject parent = fo.getParent(); 397 if ( ( FileUtil.isParentOf( root, parent ) || root.equals( parent ) ) && get( parent ) == null && parent.isValid() ) { 398 if ( !toBeRemoved( parent ) ) { 400 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 boolean leaf = n.isLeaf(); 412 DataFolder df = n.getDataFolder(); 413 boolean empty = isEmpty(df); 414 415 if (leaf != empty) { 416 SwingUtilities.invokeLater(new Runnable () { 417 public void run() { 418 n.updateChildren(); 419 } 420 }); 421 } else { 422 n.updateChildren(); 423 } 424 } 425 if ( toBeRemoved( parent ) ) { 427 remove( parent ); 428 refreshKeysAsync(); 429 } 430 431 } 432 } 433 } 437 438 441 private boolean toBeRemoved( FileObject folder ) { 442 boolean ignoredOnly = true; 443 boolean foldersOnly = true; 444 for (FileObject kid : folder.getChildren()) { 445 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; } 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 rp = FileUtil.getRelativePath( root, fo.getParent() ); 468 String oldPath = rp + ( rp.length() == 0 ? "" : "/" ) + fe.getName() + fe.getExt(); 470 boolean visible = VisibilityQuery.getDefault().isVisible( fo ); 472 boolean doUpdate = false; 473 474 List <String > needsUpdate = new ArrayList <String >(); 476 synchronized (names2nodes) { 477 for (Iterator <String > it = names2nodes.keySet().iterator(); it.hasNext(); ) { 478 String 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 ( get( fo ) == null && visible ) { 493 cleanEmptyKeys( fo ); 494 findNonExcludedPackages( fo ); 495 doUpdate = true; } 497 498 int oldPathLen = oldPath.length(); 499 String newPath = FileUtil.getRelativePath( root, fo ); 500 for (String p : needsUpdate) { 501 StringBuilder np = new StringBuilder (p); 502 np.replace( 0, oldPathLen, newPath ); 503 PackageNode n = updatePath( p, np.toString() ); if ( n != null ) { 505 n.updateDisplayName(); } 507 } 508 509 if ( needsUpdate.size() > 1 || doUpdate ) { 510 refreshKeys(); 512 } 513 } 514 524 525 } 526 527 530 private boolean isVisible( FileObject parent, FileObject file ) { 531 532 do { 533 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 547 public void stateChanged( ChangeEvent e ) { 548 computeKeys(); 549 refreshKeys(); 550 } 551 552 553 561 562 private final DataFilter NO_FOLDERS_FILTER = new NoFoldersDataFilter(); 563 564 private static Action 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, 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; } 602 603 604 public String getName() { 605 String relativePath = FileUtil.getRelativePath(root, dataFolder.getPrimaryFile()); 606 return relativePath == null ? null : relativePath.replace('/', '.'); } 608 609 public Action [] getActions( boolean context ) { 610 611 if ( !context ) { 612 if ( actions == null ) { 613 Action superActions[] = super.getActions( context ); 615 List <Action > actionList = new ArrayList <Action >(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); actionList.add (FileSensitiveActions.fileCommandAction(ActionProvider.COMMAND_COMPILE_SINGLE, 629 NbBundle.getMessage( PackageViewChildren.class, "LBL_CompilePackage_Action" ), null )); 631 } 632 633 actionList.add( superActions[i] ); 634 } 635 636 actions = new Action [ 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 662 public Transferable clipboardCopy () throws IOException { 663 try { 664 return new PackageTransferable (this, DnDConstants.ACTION_COPY); 665 } catch (ClassNotFoundException e) { 666 throw new AssertionError (e); 667 } 668 } 669 670 public Transferable clipboardCut () throws IOException { 671 try { 672 return new PackageTransferable (this, DnDConstants.ACTION_MOVE); 673 } catch (ClassNotFoundException e) { 674 throw new AssertionError (e); 675 } 676 } 677 678 public Transferable drag () throws IOException { 679 try { 680 return new PackageTransferable (this, DnDConstants.ACTION_NONE); 681 } catch (ClassNotFoundException e) { 682 throw new AssertionError (e); 683 } 684 } 685 686 public PasteType[] getPasteTypes(Transferable 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 [] 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 e) { 699 ErrorManager.getDefault().notify(e); 700 return new PasteType[0]; 701 } catch (IOException e) { 702 ErrorManager.getDefault().notify(e); 703 return new PasteType[0]; 704 } 705 } 706 else { 707 DataFlavor [] 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 PasteType getDropType (Transferable 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 [] 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 e) { 730 ErrorManager.getDefault().notify(e); 731 return null; 732 } catch (IOException e) { 733 ErrorManager.getDefault().notify(e); 734 return null; 735 } 736 } 737 else { 738 DataFlavor [] 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 [] flavors) { 750 for (int i=0; i<flavors.length; i++) { 751 if (SUBTYPE.equals(flavors[i].getSubType ()) && PRIMARY_TYPE.equals(flavors[i].getPrimaryType ())) { 752 return true; 754 } 755 } 756 return false; 757 } 758 759 private synchronized PackageRenameHandler getRenameHandler() { 760 Collection <? 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); return handlers.iterator().next(); 766 } 767 768 public void setName(String 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 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('.','/')+'/'; oldName = oldName.replace('.','/')+'/'; 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); String commonPrefix = index == -1 ? null : oldName.substring(0,index); 798 String toCreate = (index+1 == name.length()) ? "" : name.substring(index+1); try { 800 FileObject commonFolder = commonPrefix == null ? this.root : this.root.getFileObject(commonPrefix); 801 FileObject destination = commonFolder; 802 StringTokenizer dtk = new StringTokenizer (toCreate,"/"); while (dtk.hasMoreTokens()) { 804 String 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 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 { 847 FileObject parent = dataFolder.getPrimaryFile().getParent(); 848 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 ) { 862 super.destroy(); 863 } 864 865 866 while( !parent.equals( root ) && parent.getChildren().length == 0 ) { 868 FileObject newParent = parent.getParent(); 869 parent.delete(); 870 parent = newParent; 871 } 872 } 873 874 879 public String getHtmlDisplayName() { 880 String name = getDisplayName(); 881 try { 882 FileObject fo = dataFolder.getPrimaryFile(); 883 Set <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 if (name.startsWith("<")) { 890 name = null; 891 } else { 892 name = status.annotateName(name, set); 893 } 894 } 895 } catch (FileStateInvalidException e) { 896 } 898 return name; 899 } 900 901 public String getDisplayName() { 902 FileObject folder = dataFolder.getPrimaryFile(); 903 String path = FileUtil.getRelativePath(root, folder); 904 if (path == null) { 905 return ""; 907 } 908 return PackageDisplayUtils.getDisplayLabel( path.replace('/', '.')); 909 } 910 911 public String getShortDescription() { 912 FileObject folder = dataFolder.getPrimaryFile(); 913 String path = FileUtil.getRelativePath(root, folder); 914 if (path == null) { 915 return ""; 917 } 918 return PackageDisplayUtils.getToolTip(folder, path.replace('/', '.')); 919 } 920 921 public Image getIcon (int type) { 922 Image img = getMyIcon (type); 923 924 try { 925 FileObject fo = dataFolder.getPrimaryFile(); 926 Set <FileObject> set = new NonRecursiveFolderSet(fo); 927 img = fo.getFileSystem ().getStatus ().annotateIcon (img, type, set); 928 } catch (FileStateInvalidException e) { 929 } 931 932 return img; 933 } 934 935 public Image getOpenedIcon (int type) { 936 Image img = getMyOpenedIcon(type); 937 938 try { 939 FileObject fo = dataFolder.getPrimaryFile(); 940 Set <FileObject> set = new NonRecursiveFolderSet(fo); 941 img = fo.getFileSystem ().getStatus ().annotateIcon (img, type, set); 942 } catch (FileStateInvalidException e) { 943 } 945 946 return img; 947 } 948 949 950 private Image getMyIcon(int type) { 951 FileObject folder = dataFolder.getPrimaryFile(); 952 String path = FileUtil.getRelativePath(root, folder); 953 if (path == null) { 954 return null; 956 } 957 return PackageDisplayUtils.getIcon(folder, path.replace('/', '.'), isLeaf() ); 958 } 959 960 private Image 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 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 properties[i] = Sheet.createPropertiesSet(); 993 ((Sheet.Set) properties[i]).put(new PropertySupport.ReadWrite<String >(DataObject.PROP_NAME, String .class, 994 NbBundle.getMessage(PackageViewChildren.class,"PROP_name"), NbBundle.getMessage(PackageViewChildren.class,"HINT_name")) { 995 @Override 996 public String getValue() { 997 return PackageViewChildren.PackageNode.this.getName(); 998 } 999 @Override 1000 public void setValue(String n) throws IllegalAccessException , IllegalArgumentException , InvocationTargetException { 1001 if (!canRename()) { 1002 throw new IllegalAccessException (); 1003 } 1004 PackageViewChildren.PackageNode.this.setName(n); 1005 } 1006 @Override 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 name) { 1021 if (name.length() == 0) { 1022 return true; 1024 } 1025 StringTokenizer tk = new StringTokenizer (name,".",true); boolean delimExpected = false; 1027 while (tk.hasMoreTokens()) { 1028 String namePart = tk.nextToken(); 1029 if (!delimExpected) { 1030 if (namePart.equals(".")) { 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(".")) { 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 , 1060 NonRecursiveFolder { 1061 private DataFolder folder; 1062 private PropertyChangeSupport prop = new PropertyChangeSupport (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 <DataObject> list = new ArrayList <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 l) { 1084 prop.addPropertyChangeListener (l); 1085 } 1086 1087 public void removePropertyChangeListener(PropertyChangeListener l) { 1088 prop.removePropertyChangeListener (l); 1089 } 1090 1091 public void propertyChange(PropertyChangeEvent 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 , ChangeableDataFilter { 1099 1100 EventListenerList ell = new EventListenerList (); 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 e) { 1112 Object [] listeners = ell.getListenerList(); 1113 ChangeEvent event = null; 1114 for (int i = listeners.length-2; i>=0; i-=2) { 1115 if (listeners[i] == ChangeListener .class) { 1116 if ( event == null) { 1117 event = new ChangeEvent ( this ); 1118 } 1119 ((ChangeListener )listeners[i+1]).stateChanged( event ); 1120 } 1121 } 1122 } 1123 1124 public void addChangeListener( ChangeListener listener ) { 1125 ell.add( ChangeListener .class, listener ); 1126 } 1127 1128 public void removeChangeListener( ChangeListener listener ) { 1129 ell.remove( ChangeListener .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 { 1139 super(new DataFlavor (PACKAGE_FLAVOR.format(new Object [] {new Integer (operation)}), null, PackageNode.class.getClassLoader())); 1140 this.node = node; 1141 } 1142 1143 protected Object getData() throws IOException , UnsupportedFlavorException { 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"; 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 paste() throws IOException { 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 pkgName = nodes[ni].getName(); 1172 StringTokenizer tk = new StringTokenizer (pkgName,"."); while (tk.hasMoreTokens()) { 1174 String 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 children[i].copy (dest); 1190 if (this.op == DnDConstants.ACTION_MOVE) { 1191 try { 1192 children[i].delete(); 1193 } catch (IOException 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 ioe) { 1218 } 1220 } 1221 } 1222 return ExTransferable.EMPTY; 1223 } 1224 1225 public String getName() { 1226 return NbBundle.getMessage(PackageViewChildren.class,"TXT_PastePackage"); 1227 } 1228 } 1229 1230 1234 private static class NonRecursiveFolderSet extends HashSet <FileObject> implements NonRecursiveFolder { 1235 1236 private final FileObject folder; 1237 1238 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 |