1 19 20 package org.openide.loaders; 21 22 23 import java.beans.*; 24 import java.io.IOException ; 25 import java.lang.ref.*; 26 import java.util.*; 27 import java.util.logging.*; 28 import org.openide.filesystems.*; 29 import org.openide.util.*; 30 31 52 final class FolderList extends Object 53 implements FileChangeListener, DataObject.Container { 54 55 56 57 58 59 60 static final long serialVersionUID = -592616022226761148L; 61 62 63 private static final int LATER_PRIORITY = Thread.NORM_PRIORITY; 64 65 66 private static final RequestProcessor PROCESSOR = new RequestProcessor ( 67 "Folder recognizer" ); 69 70 71 private static final Map<FileObject, Reference<FolderList>> map = 72 new WeakHashMap<FileObject, Reference<FolderList>> (101); 73 74 75 private static int REFRESH_TIME = -1; 77 78 79 80 81 82 private FileObject folder; 83 84 89 transient private RequestProcessor.Task refreshTask; 90 92 transient private volatile RequestProcessor.Task comparatorTask; 93 94 96 transient private Map<FileObject, Reference<DataObject>> primaryFiles = null; 97 98 99 transient private List<FileObject> order; 100 101 private static final Logger err = Logger.getLogger("org.openide.loaders.FolderList"); 103 104 transient private PropertyChangeSupport pcs; 105 106 111 transient private boolean folderCreated = false; 112 113 114 115 116 117 120 private FolderList (FileObject folder, boolean attach) { 121 this.folder = folder; 122 if (attach) { 123 folder.addFileChangeListener (org.openide.filesystems.FileUtil.weakFileChangeListener (this, folder)); 126 } 127 } 128 129 138 public String toString () { 139 return "FolderList{" + folder + "}"; } 141 142 143 144 145 146 152 public static FolderList find (FileObject folder, boolean create) { 153 FolderList list = null; 154 synchronized (FolderList.class) { 155 Reference<FolderList> ref = map.get (folder); 156 list = ref == null ? null : ref.get (); 157 if (list == null && create) { 158 list = new FolderList (folder, true); 159 map.put (folder, new SoftReference<FolderList> (list)); 160 } 161 } 162 return list; 163 } 164 165 169 public boolean isCreated() { 170 return folderCreated; 171 } 172 173 174 175 176 177 179 public static boolean isFolderRecognizerThread () { 180 return PROCESSOR.isRequestProcessorThread (); 181 } 182 183 184 185 186 187 192 public static void changedFolderOrder (FileObject folder) { 193 FolderList list = find (folder, false); 194 if (list != null) { 195 list.changeComparator (); 196 } 197 } 198 199 204 public static void changedDataSystem (FileObject folder) { 205 FolderList list = find (folder, false); 206 if (err.isLoggable(Level.FINE)) { 207 err.fine("changedDataSystem: " + folder + " on " + Thread.currentThread()); } 209 if (list != null) { 210 list.refresh (); 211 } 212 } 213 214 215 216 217 218 221 public DataObject[] getChildren () { 222 List<DataObject> res = getChildrenList (); 223 DataObject[] arr = new DataObject[res.size ()]; 224 res.toArray (arr); 225 return arr; 226 } 227 228 231 public List<DataObject> getChildrenList () { 232 ListTask lt; 233 try { 234 DataObjectPool.getPOOL().enterPrivilegedProcessor (PROCESSOR); 235 lt = getChildrenList (null); 236 lt.task.waitFinished(); 237 } finally { 238 DataObjectPool.getPOOL().exitPrivilegedProcessor (PROCESSOR); 239 } 240 assert lt.result != null; 241 return lt.result; 242 } 243 244 246 public void waitProcessingFinished () { 247 Task t = comparatorTask; 248 if (t != null) { 249 t.waitFinished (); 250 } 251 252 t = refreshTask; 253 if (t != null) { 254 t.waitFinished (); 255 } 256 } 257 258 260 public RequestProcessor.Task computeChildrenList (FolderListListener filter) { 261 return getChildrenList (filter).task; 262 } 263 264 private ListTask getChildrenList (FolderListListener filter) { 265 ListTask lt = new ListTask (filter); 266 int priority = Thread.currentThread().getPriority(); 267 268 lt.task = PROCESSOR.post (lt, 0, priority); 270 return lt; 271 } 272 273 275 private synchronized void changeComparator () { 276 final boolean LOG = err.isLoggable(Level.FINE); 277 if (LOG) err.fine("changeComparator on " + folder); 278 final RequestProcessor.Task previous = comparatorTask; 279 final RequestProcessor.Task[] COMP = new RequestProcessor.Task[1]; 280 synchronized (COMP) { 281 comparatorTask = PROCESSOR.post (new Runnable () { 282 public void run () { 283 synchronized (COMP) { 284 if (previous != null) { 285 previous.waitFinished (); 286 } 287 if (primaryFiles != null) { 290 if (LOG) err.fine("changeComparator on " + folder + ": get old"); 292 List v = getObjects (null); 293 if (v.size () != 0) { 294 order = null; 296 if (LOG) err.fine("changeComparator: get new"); 297 List r = getObjects (null); 298 if (LOG) err.fine("changeComparator: fire change"); 299 fireChildrenChange (r, v); 300 } 301 } 302 synchronized (FolderList.this) { 303 if (comparatorTask == COMP[0]) { 305 comparatorTask = null; 306 } 307 } 308 } 309 } 310 }, 0, Thread.MIN_PRIORITY); 311 COMP[0] = comparatorTask; 312 } 313 } 314 315 316 317 318 319 321 public void refresh () { 322 final long now = System.currentTimeMillis(); 323 final boolean LOG = err.isLoggable(Level.FINE); 324 if (LOG) err.fine("refresh on " + folder + " @" + now); 325 synchronized (this) { 326 if (refreshTask == null) { 327 refreshTask = PROCESSOR.post (new Runnable () { 328 public void run () { 329 RequestProcessor.Task t = comparatorTask; 330 if (t != null) { 331 t.waitFinished (); 333 } 334 335 if (LOG) err.fine("-- refresh on " + folder + ": now=" + now); 336 if (primaryFiles != null) { 337 createBoth (null, true); 339 } 340 } 341 }, getRefreshTime(), LATER_PRIORITY); 342 } else { 343 refreshTask.schedule(getRefreshTime()); 344 } 345 } 346 } 347 348 352 private static int getRefreshTime() { 353 if (REFRESH_TIME >= 0) { 354 return REFRESH_TIME; 355 } 356 357 String sysProp = System.getProperty("org.openide.loaders.FolderList.refresh.interval"); if (sysProp != null) { 359 try { 360 REFRESH_TIME = Integer.parseInt(sysProp); 361 } catch (NumberFormatException nfe) { 362 Logger.getLogger(FolderList.class.getName()).log(Level.WARNING, null, nfe); 363 } 364 } 365 if (REFRESH_TIME < 0) { 366 REFRESH_TIME = 10; 367 } 368 return REFRESH_TIME; 369 } 370 371 372 373 374 375 381 public void fileChanged (FileEvent fe) { 382 final boolean LOG = err.isLoggable(Level.FINE); 383 if (LOG) err.fine("fileChanged: " + fe); 384 385 FileObject fo = fe.getFile (); 386 387 390 if (fo.isData () && fo.isValid ()) { 391 if (primaryFiles != null) { 394 try { 396 DataObject obj = DataObject.find (fo); 397 if (!primaryFiles.containsKey (obj.getPrimaryFile ())) { 398 402 refresh(); 405 } 406 } catch (DataObjectNotFoundException ex) { 407 Logger.getLogger(FolderList.class.getName()).log(Level.WARNING, null, ex); 408 } 410 } 411 412 DataFolder.SortMode sortMode = getComparator().getSortMode(); 414 if (sortMode == DataFolder.SortMode.LAST_MODIFIED || sortMode == DataFolder.SortMode.SIZE) { 415 changeComparator(); 416 } 417 } 418 } 419 420 423 public void fileDeleted (FileEvent fe) { 424 final boolean LOG = err.isLoggable(Level.FINE); 425 if (LOG) err.fine("fileDeleted: " + fe); 426 if (primaryFiles == null || primaryFiles.containsKey (fe.getFile ())) { 430 refresh(); 433 } 435 } 436 437 443 public void fileDataCreated (FileEvent fe) { 444 final boolean LOG = err.isLoggable(Level.FINE); 445 if (LOG) err.fine("fileDataCreated: " + fe); 446 refresh(); 447 } 448 449 455 public void fileFolderCreated (FileEvent fe) { 456 final boolean LOG = err.isLoggable(Level.FINE); 457 if (LOG) err.fine("fileFolderCreated: " + fe); 458 refresh(); 459 } 460 461 465 public void fileRenamed (FileRenameEvent fe) { 466 final boolean LOG = err.isLoggable(Level.FINE); 467 if (LOG) err.fine("fileRenamed: " + fe); 468 refresh(); 469 changeComparator(); 471 } 472 473 477 public void fileAttributeChanged(FileAttributeEvent fe) { 478 final boolean LOG = err.isLoggable(Level.FINE); 479 if (LOG) err.fine("fileAttributeChanged: " + fe); 480 if (fe.getFile() == folder) { 482 483 if (fe.getName() == null) { 484 changeComparator(); 485 return; 486 } 487 488 if (DataFolder.EA_ORDER.equals(fe.getName()) || 489 DataFolder.EA_SORT_MODE.equals(fe.getName()) || 490 -1 != fe.getName().indexOf("/")) { 491 changeComparator(); 492 } 493 } 494 } 495 496 497 498 499 500 503 private FolderOrder getComparator () { 504 return FolderOrder.findFor (folder); 505 } 506 507 511 private List<DataObject> getObjects (FolderListListener f) { 512 final boolean LOG = err.isLoggable(Level.FINE); 513 if (LOG) err.fine("getObjects on " + folder); 514 List<DataObject> res; 515 if (primaryFiles == null) { 516 res = createBoth (f, false); 517 } else { 518 if (order != null) { 519 res = createObjects (order, primaryFiles, f); 520 } else { 521 res = createObjects (primaryFiles.keySet (), primaryFiles, f); 522 res = carefullySort (res, getComparator ()); 523 order = createOrder (res); 524 } 525 } 526 return res; 527 536 } 537 538 547 private List<DataObject> carefullySort (List<DataObject> l, FolderOrder c) { 548 final boolean LOG = err.isLoggable(Level.FINE); 549 if (LOG) err.fine("carefullySort on " + folder); 550 Collections.sort (l, c); 561 Map<DataObject, List<DataObject>> constraints = c.getOrderingConstraints(l); 562 if (constraints == null) { 563 return l; 564 } else { 565 if (LOG) err.fine("carefullySort: partial orders"); 566 567 try { 568 return Utilities.topologicalSort(l, constraints); 569 } catch (TopologicalSortException ex) { 570 List<DataObject> corrected = NbCollections.checkedListByCopy(ex.partialSort(), DataObject.class, true); 571 if (err.isLoggable(Level.WARNING)) { 572 err.warning("Note: folder " + folder + " cannot be consistently sorted due to ordering conflicts."); err.log(Level.WARNING, null, ex); 574 err.warning("Using partial sort: " + corrected); } 576 return corrected; 577 } 578 } 579 } 580 581 585 private static List<FileObject> createOrder (List<DataObject> list) { 586 int size = list.size (); 587 List<FileObject> res = new ArrayList<FileObject> (size); 588 589 for (int i = 0; i < size; i++) { 590 res.add (list.get (i).getPrimaryFile ()); 591 } 592 593 return res; 594 } 595 596 605 private List<DataObject> createObjects ( 606 Collection<FileObject> order, Map<FileObject, Reference<DataObject>> map, FolderListListener f 607 ) { 608 final boolean LOG = err.isLoggable(Level.FINE); 609 if (LOG) { 610 err.fine("createObjects on " + folder); 611 } 612 int size = order.size (); 613 614 Iterator it = order.iterator (); 615 616 List<DataObject> res = new ArrayList<DataObject> (size); 617 for (FileObject fo: order) { 618 619 if (LOG) { 620 err.fine(" iterating" + fo); 621 } 622 if (!fo.isValid()) { 623 if (LOG) { 624 err.fine(" not valid, continue"); 625 } 626 continue; 627 } 628 Reference<DataObject> ref = map.get(fo); 629 DataObject obj = ref != null ? ref.get(): null; 630 631 if (obj == null) { 632 if (LOG) { 634 err.fine(" reference is " + ref + " obj is " + obj); 635 } 636 try { 637 obj = DataObject.find(fo); 638 ref = new SoftReference<DataObject>(obj); 639 map.put(fo, ref); 640 } 641 catch (DataObjectNotFoundException ex) { 642 Logger.getLogger(FolderList.class.getName()).log(Level.WARNING, null, ex); 643 } 644 } 645 if (obj != null) { 647 if (LOG) { 648 err.fine(" deliver: ref is " + ref + " obj is " + obj); 649 } 650 if (f == null) { 654 res.add(obj); 656 } else { 657 f.process(obj, res); 660 } 661 } 662 } 663 664 if (f != null) { 665 if (LOG) { 666 err.fine(" finished: " + res); } 668 f.finished (res); 669 } 670 671 if (LOG) { 672 err.fine("createObjects ends on " + folder); } 674 return res; 675 } 676 677 684 private List<DataObject> createBoth (FolderListListener filter, boolean notify) { 685 final boolean LOG = err.isLoggable(Level.FINE); 686 if (LOG) err.fine("createBoth on " + folder); 687 final HashMap<FileObject,Reference<DataObject>> file = 689 new HashMap<FileObject, Reference<DataObject>> (); 690 691 List<DataObject> all = new ArrayList<DataObject> (); 693 List<DataObject> res = new ArrayList<DataObject> (); 695 696 final Map<FileObject, Reference<DataObject>> remove = primaryFiles == null ? 698 new HashMap<FileObject, Reference<DataObject>> () : 699 new HashMap<FileObject,Reference<DataObject>>(primaryFiles); 700 701 final List<DataObject> add = new ArrayList<DataObject> (); 703 704 DataLoaderPool pool = DataLoaderPool.getDefault(); 705 706 final HashSet<FileObject> marked = new HashSet<FileObject> (); 709 DataLoader.RecognizedFiles recog = new DataLoader.RecognizedFiles () { 710 713 public void markRecognized (FileObject fo) { 714 if (fo != null) { 715 marked.add (fo); 716 } 717 } 718 }; 719 Enumeration<? extends FileObject> en = folder.getChildren (false); 721 while (en.hasMoreElements ()) { 722 FileObject fo = en.nextElement (); 723 if (!marked.contains (fo)) { 724 DataObject obj; 727 try { 728 obj = pool.findDataObject (fo, recog); 729 } catch (DataObjectExistsException ex) { 730 obj = ex.getDataObject (); 732 } catch (IOException ex) { 733 obj = null; 735 Exceptions.printStackTrace(ex); 736 } 737 738 if (obj != null) { 739 741 obj.recognizedByFolder(); 743 744 FileObject primary = obj.getPrimaryFile (); 746 747 boolean doNotRemovePrimaryFile = false; 748 if (!file.containsKey (primary)) { 749 751 boolean goIn = primaryFiles == null; 753 if (!goIn) { 754 Reference<DataObject> r = primaryFiles.get (primary); 755 goIn = r == null; 758 if (!goIn) { 759 DataObject obj2 = r.get (); 762 goIn = obj2 == null || obj2 != obj; 763 if (goIn) { 764 doNotRemovePrimaryFile = true; 765 } 766 } 767 } 768 769 if (goIn) { 770 add.add (obj); 772 780 } 781 all.add (obj); 783 if (filter == null) { 784 res.add (obj); 785 } else { 786 filter.process (obj, res); 787 } 788 } 789 790 if (!doNotRemovePrimaryFile) { 791 remove.remove (primary); 793 } 794 795 file.put (primary, new SoftReference<DataObject> (obj)); 797 } else { 798 } 803 } 804 } 805 806 808 813 primaryFiles = file; 814 815 all = carefullySort (all, getComparator ()); 816 order = createOrder (all); 817 if (all.size () == res.size ()) { 818 res = all; 820 } else { 821 res = carefullySort (res, getComparator ()); 823 } 824 825 826 if (notify) { 828 fireChildrenChange (add, remove.keySet ()); 829 } 830 831 if (LOG) { 833 err.fine("Notifying filter: " + filter); } 835 if (filter != null) { 836 filter.finished (res); 837 } 838 839 return res; 840 } 841 842 843 844 845 846 850 private void fireChildrenChange (Collection add, Collection removed) { 851 if (pcs != null) { 852 if (!add.isEmpty() || !removed.isEmpty()) { 853 pcs.firePropertyChange (PROP_CHILDREN, null, null); 854 } 855 } 856 } 857 858 861 public void removePropertyChangeListener(PropertyChangeListener l) { 862 if (pcs != null) { 863 pcs.removePropertyChangeListener (l); 864 } 865 } 866 867 870 public synchronized void addPropertyChangeListener(PropertyChangeListener l) { 871 if (pcs == null) { 872 pcs = new PropertyChangeSupport(this); 873 } 874 pcs.addPropertyChangeListener(l); 875 } 876 877 878 879 880 881 884 private final class ListTask implements Runnable { 885 private FolderListListener filter; 886 887 public ListTask (FolderListListener filter) { 888 this.filter = filter; 889 } 890 891 public List<DataObject> result; 892 public RequestProcessor.Task task; 893 894 public void run () { 895 final boolean LOG = err.isLoggable(Level.FINE); 896 if (LOG) err.fine("ListTask.run 1 on " + folder); 897 if (comparatorTask != null) { 899 comparatorTask.waitFinished (); 900 } 901 if (refreshTask != null) { 902 refreshTask.waitFinished (); 903 } 904 err.fine("ListTask.run 2"); 905 906 result = getObjects (filter); 907 assert result != null; 908 err.fine("ListTask.run 3"); 909 910 folderCreated = true; 911 } 912 913 public String toString() { 914 return "ListTask@" + Integer.toHexString(System.identityHashCode(this)) + "[" + folder + "]"; } 916 } 917 918 919 } 920 | Popular Tags |