1 19 20 package org.openide.filesystems; 21 22 import java.io.FileNotFoundException ; 23 import java.io.IOException ; 24 import java.io.ObjectInputStream ; 25 import java.io.ObjectOutputStream ; 26 import java.io.Serializable ; 27 import java.lang.ref.Reference ; 28 import java.lang.ref.WeakReference ; 29 import java.net.URL ; 30 import java.util.ArrayList ; 31 import java.util.Arrays ; 32 import java.util.Collection ; 33 import java.util.Collections ; 34 import java.util.Enumeration ; 35 import java.util.EventListener ; 36 import java.util.HashMap ; 37 import java.util.HashSet ; 38 import java.util.Iterator ; 39 import java.util.Map ; 40 import java.util.Set ; 41 import org.openide.util.Enumerations; 42 import org.openide.util.NbBundle; 43 import org.openide.util.Utilities; 44 45 50 abstract class AbstractFolder extends FileObject { 51 52 private static final AbstractFolder[] EMPTY_ARRAY = new AbstractFolder[0]; 53 54 55 private static final char EXT_SEP = '.'; 56 57 58 private FileSystem system; 59 60 61 protected String name; 62 63 64 protected final AbstractFolder parent; 65 66 69 boolean validFlag; 70 71 73 private final AbstractFolder validRoot; 74 75 76 private String [] children; 77 78 79 private Map <String ,Reference <AbstractFolder>> map; 80 81 82 private ListenerList<FileChangeListener> listeners; 83 84 90 public AbstractFolder(FileSystem fs, AbstractFolder parent, String name) { 91 this.system = fs; 92 this.parent = parent; 93 this.name = name; 94 validFlag = true; 95 validRoot = (parent != null) ? (AbstractFolder) fs.getRoot() : null; 96 } 97 98 102 public final String getName() { 103 int i = name.lastIndexOf('.'); 104 105 106 return (i <= 0) ? name : name.substring(0, i); 107 } 108 109 115 public final String getExt() { 116 int i = name.lastIndexOf('.') + 1; 117 118 119 return ((i <= 1) || (i == name.length())) ? "" : name.substring(i); } 121 122 125 public final String getNameExt() { 126 return name; 127 } 128 129 130 final boolean isHasExtOverride() { 131 return true; 132 } 133 134 135 boolean hasExtOverride(String ext) { 136 if (ext == null) { 137 return false; 138 } 139 140 141 if ((name.length() - ext.length()) <= 1) { 142 return false; 143 } 144 145 boolean ret = name.endsWith(ext); 146 147 if (!ret) { 148 return false; 149 } 150 151 if (name.charAt(name.length() - ext.length() - 1) != '.') { 152 return false; 153 } 154 155 return true; 156 } 157 158 159 public final FileSystem getFileSystem() { 160 return system; 161 } 162 163 167 171 public final boolean isRoot() { 172 return parent == null; 173 } 174 175 180 public final boolean isValid() { 181 if (parent == null) { 183 return validFlag; 184 } 185 186 boolean isValidRoot = getFileSystem().getRoot() == validRoot; 187 188 return validFlag && isValidRoot; 189 } 190 191 195 200 public final FileObject getParent() { 201 return parent; 202 } 203 204 212 public final synchronized FileObject[] getChildren() { 213 check(); 214 215 if (children == null) { 216 return new FileObject[0]; 217 } 218 219 int size = children.length; 220 ArrayList <FileObject> aList = new ArrayList <FileObject>(); 221 222 for (int i = 0; i < size; i++) { 223 FileObject f = getChild(children[i]); 224 225 if (f != null) { 226 aList.add(f); 227 } 228 } 229 230 return aList.toArray(new FileObject[0]); 231 } 232 233 237 final FileObject find(Enumeration <String > en) { 238 AbstractFolder fo = this; 239 240 while ((fo != null) && en.hasMoreElements()) { 241 synchronized (fo) { 244 fo.check(); 248 249 fo = fo.getChild(en.nextElement()); 250 } 251 } 252 253 return fo; 255 } 256 257 261 final FileObject findIfExists(Enumeration <String > en) { 262 Reference <AbstractFolder> r = findRefIfExists(en); 263 264 return (r == null) ? null : r.get(); 265 } 266 267 271 final Reference <AbstractFolder> findRefIfExists(Enumeration <String > en) { 272 AbstractFolder fo = this; 273 274 while ((fo != null) && en.hasMoreElements()) { 275 if (fo.map == null) { 276 return null; 278 } 279 280 synchronized (fo) { 283 String name = en.nextElement(); 284 285 if (en.hasMoreElements()) { 286 fo = fo.getChild(name); 287 } else { 288 return fo.map.get(name); 289 } 290 } 291 } 292 293 return null; 295 } 296 297 301 protected final AbstractFolder getChild(String name) { 302 return getChild(name, true); 303 } 304 305 private final AbstractFolder getChild(String name, boolean onlyValid) { 306 Reference <AbstractFolder> r = map.get(name); 307 308 if (r == null) { 309 if (Utilities.getOperatingSystem() == Utilities.OS_VMS) { 313 if (Character.isLowerCase(name.charAt(0))) { 314 r = map.get(name.toUpperCase()); 315 } else { 316 r = map.get(name.toLowerCase()); 317 } 318 319 if (r == null) { 320 return null; 321 } 322 } else { 323 return null; 324 } 325 } 326 327 AbstractFolder fo = r.get(); 328 329 if (fo == null) { 330 fo = createFile(name); 332 333 if ((fo != null) && fo.isValid()) { 334 map.put(name, (fo != null) ? createReference(fo) : null); 335 } else { 336 if (onlyValid) { 337 fo = null; 338 } 339 } 340 } 341 342 return fo; 343 } 344 345 348 final String [] getChildrenArray() { 349 return children; 350 } 351 352 357 protected Reference <AbstractFolder> createReference(AbstractFolder fo) { 358 return (new WeakReference <AbstractFolder>(fo)); 359 } 360 361 363 final synchronized AbstractFolder[] subfiles() { 364 if (map == null) { 365 return EMPTY_ARRAY; 366 } 367 368 Iterator it = map.values().iterator(); 369 ArrayList <FileObject> ll = new ArrayList <FileObject>(map.size() + 2); 370 371 while (it.hasNext()) { 372 Reference r = (Reference ) it.next(); 373 374 if (r == null) { 375 continue; 376 } 377 378 AbstractFolder fo = (AbstractFolder) r.get(); 379 380 if (fo != null ) { 381 ll.add(fo); 386 } 387 } 388 389 return ll.toArray(EMPTY_ARRAY); 390 } 391 392 final boolean isInitialized() { 393 return this.map != null; 394 } 395 396 402 final Enumeration <AbstractFolder> existingSubFiles(boolean rec) { 403 if (!rec) { 404 return Enumerations.array(subfiles()); 405 } else { 406 class P implements org.openide.util.Enumerations.Processor<AbstractFolder, AbstractFolder> { 407 public AbstractFolder process(AbstractFolder af, Collection <AbstractFolder> toAdd) { 408 toAdd.addAll(Arrays.asList(af.subfiles())); 409 410 return af; 411 } 412 } 413 414 return Enumerations.queue(Enumerations.singleton(this), new P()); 415 } 416 } 417 418 421 abstract void setAttribute(String attrName, Object value, boolean fire) 422 throws IOException ; 423 424 433 public final synchronized FileObject getFileObject(String name, String ext) { 434 check(); 435 436 if ((ext == null) || ext.equals("")) { 438 return getChild(name); 439 } else { 440 StringBuffer sb = new StringBuffer (name.length() + 1 + ((ext == null) ? 0 : ext.length())); 441 sb.append(name).append(EXT_SEP).append(ext); 442 443 return getChild(sb.toString()); 444 } 445 } 446 447 449 public void refresh(boolean expected) { 450 if (!isInitialized() && isFolder()) { 451 return; 452 } 453 454 refresh(null, null, true, expected); 455 } 456 457 461 464 public final void addFileChangeListener(FileChangeListener fcl) { 465 synchronized (EMPTY_ARRAY) { 466 if (listeners == null) { 467 listeners = new ListenerList<FileChangeListener>(); 468 } 469 } 470 471 listeners.add(fcl); 472 } 473 474 477 public final void removeFileChangeListener(FileChangeListener fcl) { 478 if (listeners != null) { 479 listeners.remove(fcl); 480 } 481 } 482 483 484 protected final void fileDeleted0(FileEvent fileevent) { 485 super.fireFileDeletedEvent(listeners(), fileevent); 486 487 if (fileevent.getFile().equals(this) && (parent != null)) { 488 FileEvent ev = new FileEvent(parent, fileevent.getFile(), fileevent.isExpected()); 489 parent.fileDeleted0(ev); 490 } 491 } 492 493 494 protected final void fileCreated0(FileEvent fileevent, boolean isData) { 495 501 dispatchEvent(listeners(), fileevent); 502 503 if (fileevent.getFile().equals(this) && (parent != null)) { 504 FileEvent ev = new FileEvent(parent, fileevent.getFile(), fileevent.isExpected()); 505 parent.fileCreated0(ev, isData); 506 } 507 } 508 509 510 protected final void fileCreated0(FileObject src, FileObject file, boolean expected) { 511 fileCreated0(new FileEvent(src, file, expected), false); 512 } 513 514 515 protected final void fileChanged0(FileEvent fileevent) { 516 super.fireFileChangedEvent(listeners(), fileevent); 517 518 if (fileevent.getFile().equals(this) && (parent != null)) { 519 FileEvent ev = new FileEvent(parent, fileevent.getFile(), fileevent.isExpected()); 520 parent.fileChanged0(ev); 521 } 522 } 523 524 525 final void fileChanged1(FileEvent fileevent) { 526 super.fireFileChangedEvent(listeners(), fileevent); 527 } 528 529 530 protected final void fileRenamed0(FileRenameEvent filerenameevent) { 531 super.fireFileRenamedEvent(listeners(), filerenameevent); 532 533 if (filerenameevent.getFile().equals(this) && (parent != null)) { 534 FileRenameEvent ev = new FileRenameEvent( 535 parent, filerenameevent.getFile(), filerenameevent.getName(), filerenameevent.getExt(), 536 filerenameevent.isExpected() 537 ); 538 parent.fileRenamed0(ev); 539 } 540 } 541 542 543 protected final void fileAttributeChanged0(FileAttributeEvent fileattributeevent) { 544 super.fireFileAttributeChangedEvent(listeners(), fileattributeevent); 545 546 if (fileattributeevent.getFile().equals(this) && (parent != null)) { 547 FileAttributeEvent ev = new FileAttributeEvent( 548 parent, fileattributeevent.getFile(), fileattributeevent.getName(), fileattributeevent.getOldValue(), 549 fileattributeevent.getNewValue(), fileattributeevent.isExpected() 550 ); 551 parent.fileAttributeChanged0(ev); 552 } 553 } 554 555 557 protected final boolean hasListeners() { 558 boolean fsHas = getFileSystem().getFCLSupport().hasListeners(); 559 boolean repHas = false; 560 Repository rep = getFileSystem().getRepository(); 561 562 if (rep != null) { 563 repHas = rep.getFCLSupport().hasListeners(); 564 } 565 566 return (listeners != null && listeners.hasListeners()) || repHas || fsHas; 567 } 568 569 571 protected final boolean hasAtLeastOneListeners() { 572 return hasListeners() || ((parent != null) && parent.hasListeners()); 573 } 574 575 577 private final Enumeration <FileChangeListener> listeners() { 578 if (listeners == null) { 579 return Enumerations.empty(); 580 } else { 581 return Collections.enumeration(listeners.getAllListeners()); 582 } 583 } 584 585 589 592 private final void check() { 593 if (map == null) { 594 refresh(null, null, false, false); 595 596 if (map == null) { 597 map = Collections.emptyMap(); 599 600 if (children == null) { 601 children = new String [] { }; 602 } 603 } 604 } 605 } 606 607 612 protected final void refresh(String added, String removed) { 613 this.refresh(added, removed, false); 614 } 615 616 622 protected final void refresh(String added, String removed, boolean reuseChildren) { 623 if (reuseChildren && (removed != null)) { 624 String [] nc = new String [children.length]; 625 System.arraycopy(children, 0, nc, 0, children.length); 626 627 for (int i = nc.length; --i >= 0;) { 628 if (removed.equals(nc[i])) { 629 nc[i] = null; 630 631 break; 632 } 633 } 634 635 refresh(added, removed, true, false, nc); 636 } else { 637 refresh(added, removed, true, false, null); 638 } 639 } 640 641 645 protected abstract String [] list(); 646 647 651 protected abstract AbstractFolder createFile(String name); 652 653 660 protected void refresh(String added, String removed, boolean fire, boolean expected) { 661 this.refresh(added, removed, fire, expected, null); 662 } 663 664 void registerChild(String name) { 665 synchronized (this) { 666 if (map == null) { 667 check(); 668 } 669 670 Reference <AbstractFolder> o = map.put(name, new WeakReference <AbstractFolder>(null)); 671 672 if (o != null) { 673 map.put(name, o); 674 } else { 675 String [] newChildren = new String [children.length + 1]; 676 System.arraycopy(children, 0, newChildren, 0, children.length); 677 newChildren[children.length] = name; 678 children = newChildren; 679 } 680 } 681 } 682 683 691 protected final void refreshFolder( 692 String added, String removed, boolean fire, boolean expected, final String [] list 693 ) { 694 try { 695 getFileSystem().beginAtomicAction(); 696 697 final String [] newChildren = getNewChildren(list); 699 final Set <String > addedNames; 700 final Map <String , AbstractFolder> removedPairs; 701 702 synchronized (this) { 703 if ((children == null) && (newChildren == null)) { 704 return; 705 } 706 707 final int initialCapacity = (newChildren != null) ? (((newChildren.length * 4) / 3) + 1) : 0; 708 final HashMap <String , Reference <AbstractFolder>> newMap = new HashMap <String , Reference <AbstractFolder>>(initialCapacity); 709 710 711 addedNames = new HashSet <String >(initialCapacity); 712 713 if (newChildren != null) { 714 final Reference <AbstractFolder> removedRef = ((map != null) ? map.get(removed) : null); 715 716 for (int i = 0; i < newChildren.length; i++) { 717 final String child = newChildren[i]; 718 Reference <AbstractFolder> foRef = null; 719 720 if (map != null) { 721 foRef = map.remove(child); 722 723 if (((foRef != null) && (added != null) && (removed != null) && child.equals(removed))) { 724 foRef = null; 726 } 727 728 if ((added != null) && (removed != null) && child.equals(added)) { 729 foRef = removedRef; 730 } 731 732 if (((foRef != null) && (added == null) && (removed != null) && child.equals(removed))) { 733 foRef = null; 735 } 736 } 737 738 if (foRef == null) { 739 if (!child.equals(added)) { 740 addedNames.add(child); 741 } 742 743 foRef = new WeakReference <AbstractFolder>(null); 745 } 746 747 newMap.put(child, foRef); 748 } 749 } 750 751 removedPairs = (map != null) ? dereferenceValues(map) : null; 752 map = newMap; 753 children = newChildren; 754 } 755 756 if (fire && (addedNames != null) && hasAtLeastOneListeners()) { 757 filesCreated(addedNames, expected); 759 } 760 761 if (fire && (removedPairs != null)) { 762 if (removed != null) { 763 removedPairs.remove(removed); 764 } 765 766 filesDeleted(removedPairs, expected); 767 } 768 769 if ( 770 fire && (added == null) && (removed == null) && !getFileSystem().isReadOnly() && 771 !(this instanceof MultiFileObject) 772 ) { 773 Set <String > nameFilter = nameFilter = new HashSet <String >(); 774 775 if (addedNames != null) { 776 nameFilter.addAll(addedNames); 777 } 778 779 if (removedPairs != null) { 780 nameFilter.addAll(removedPairs.keySet()); 781 } 782 783 refreshChildren(existingSubFiles(false), nameFilter, expected); 784 } 785 } finally { 786 getFileSystem().finishAtomicAction(); 787 } 788 } 789 790 private void refreshChildren(final Enumeration <? extends AbstractFolder> subfiles, final Collection nameFilter, boolean expected) { 791 while (subfiles.hasMoreElements()) { 792 AbstractFolder child = subfiles.nextElement(); 793 794 if (child.isData()) { 795 if (!nameFilter.contains(child.getNameExt())) { 796 child.refresh(expected); 797 } 798 } 799 } 800 } 801 802 private void filesDeleted(final Map <String ,AbstractFolder> removedToFire, final boolean expected) { 803 for (AbstractFolder fo : removedToFire.values()) { 804 fo.validFlag = false; 805 806 if (hasAtLeastOneListeners() || fo.hasAtLeastOneListeners()) { 807 FileEvent ev = new FileEvent(fo, fo, expected); 808 fo.fileDeleted0(ev); 809 } 810 } 811 } 812 813 private void filesCreated(final Set <String > addedToFire, final boolean expected) { 814 for (String s : addedToFire) { 815 AbstractFolder fo = getChild(s); 816 817 if (fo != null) { 818 fileCreated0(this, fo, expected); 819 } 820 } 821 } 822 823 private Map <String , AbstractFolder> dereferenceValues(final Map <String , Reference <AbstractFolder>> map) { 824 Map <String , AbstractFolder> retVal = new HashMap <String , AbstractFolder>(map.size()); 825 for (String name : map.keySet()) { 826 AbstractFolder child = getChild(name, false); 827 828 if (child != null) { 829 retVal.put(name, child); 830 } 831 } 832 833 return retVal; 834 } 835 836 private String [] getNewChildren(final String [] list) { 837 String [] newChildren = (list != null) ? list : list(); 838 839 if (isRoot() && (newChildren == null)) { 840 newChildren = new String [0]; 841 } 842 843 if (newChildren != null) { 844 newChildren = stripNulls(newChildren); 845 } 846 847 return newChildren; 848 } 849 850 private static String [] stripNulls(final String [] children) { 851 String [] newChildren = children; 852 Collection <String > childrenList = new ArrayList <String >(Arrays.asList(newChildren)); 853 854 for (Iterator <String > iterator = childrenList.iterator(); iterator.hasNext();) { 855 String child = iterator.next(); 856 857 if (child == null) { 858 iterator.remove(); 859 } 860 } 861 862 if (childrenList.size() != newChildren.length) { 863 newChildren = childrenList.toArray(new String [childrenList.size()]); 864 } 865 866 return newChildren; 867 } 868 869 877 protected void refresh(String added, String removed, boolean fire, boolean expected, String [] list) { 878 if (isFolder()) { 879 refreshFolder(added, removed, fire, expected, list); 880 } 881 } 882 883 887 protected void outputStreamClosed(boolean fireFileChanged) { 888 if (fireFileChanged) { 889 fileChanged0(new FileEvent(AbstractFolder.this)); 890 } 891 } 892 893 public final Object writeReplace() { 897 return new Replace(this); 898 } 899 900 906 final public void delete(FileLock lock) throws IOException { 907 if (isFolder()) { 908 FileObject[] fos = this.getChildren(); 909 910 for (int i = 0; i < fos.length; i++) { 911 FileObject fo = fos[i]; 912 FileLock foLock = fo.lock(); 913 914 try { 915 fo.delete(foLock); 916 } catch (IOException iex) { 917 String message = NbBundle.getMessage( 918 getClass(), "EXC_CannotDelete", 920 getPath(), fo.getFileSystem().getDisplayName() 922 ); 923 ExternalUtil.annotate(iex, message); throw iex; 925 } finally { 926 foLock.releaseLock(); 927 } 928 } 929 } 930 931 handleDelete(lock); 932 } 933 934 abstract void handleDelete(FileLock lock) throws IOException ; 935 936 @SuppressWarnings ("deprecation") public boolean canWrite() { 938 return !isReadOnly(); 939 } 940 941 private static final class Replace implements Serializable { 942 private static final long serialVersionUID = 1L; 943 private transient FileObject f; 944 private String fsname; 945 private String path; 946 private URL url; 947 948 public Replace(FileObject f) { 949 this.f = f; 950 } 951 952 @SuppressWarnings ("deprecation") private void writeObject(ObjectOutputStream oos) 954 throws IOException { 955 fsname = f.getFileSystem().getSystemName(); 956 path = f.getPath(); 957 url = f.getURL(); 958 assert url != null : "No URL for " + path; 959 oos.defaultWriteObject(); 960 } 961 962 private void readObject(ObjectInputStream ois) 963 throws IOException , ClassNotFoundException { 964 ois.defaultReadObject(); 965 assert fsname != null : "Should always have a non-null fsname here"; 966 967 @SuppressWarnings ("deprecation") FileSystem fs = Repository.getDefault().findFileSystem(fsname); 969 970 if (fs != null) { 971 assert path != null : "Should always have a non-null path here"; 972 f = fs.findResource(path); 973 } 974 975 if (f == null) { 976 assert url != null : "Should always have a non-null URL here"; 978 979 f = URLMapper.findFileObject(url); 980 981 if (f == null) { 982 throw new FileNotFoundException ("Could not restore: " + url); } 984 } 985 } 986 987 public Object readResolve() { 988 assert f != null : "Did not read " + url; 989 990 return f; 991 } 992 } 993 } 994 | Popular Tags |