KickJava   Java API By Example, From Geeks To Geeks.

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


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

19
20 package org.openide.loaders;
21
22
23 import java.beans.*;
24 import java.io.IOException JavaDoc;
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 /** Watches a folder and its children.
32  *
33  * <p>{@link java.beans.PropertyChangeListener}s
34  * may be registered which will be informed about changes in the ordered
35  * children list. The {@link java.beans.PropertyChangeEvent}s fired by instances
36  * of this class do neither contain information about the old value nor about
37  * the new value of the children list.</p>
38  *
39  * <p>The list of children can be retrieved by calls to
40  * the methods {@link #getChildren()} resp. {@link #getChildrenList()}. If you
41  * want to filter the children which shall be included into the folder list,
42  * call {@link #computeChildrenList(FolderListListener)}. The same is true
43  * if you want to trigger children computation asynchronously. In this case
44  * the implementation of {@link FolderListListener#finished(List)} shall be
45  * used to get informed about the result of the computation.</p>
46  *
47  * <p>To retrieve the appropriate instance of this class for a given folder
48  * call {@link #find(FileObject, boolean)}.</p>
49 *
50 * @author Jaroslav Tulach
51 */

52 final class FolderList extends Object JavaDoc
53 implements FileChangeListener, DataObject.Container {
54     
55     /* -------------------------------------------------------------------- */
56     /* -- Constants ------------------------------------------------------- */
57     /* -------------------------------------------------------------------- */
58     
59     /** serial version UID */
60     static final long serialVersionUID = -592616022226761148L;
61
62     /** priority for tasks that can be run later */
63     private static final int LATER_PRIORITY = Thread.NORM_PRIORITY;
64
65     /** request processor for recognizing of folders */
66     private static final RequestProcessor PROCESSOR = new RequestProcessor (
67                 "Folder recognizer" // NOI18N
68
);
69     
70     /** map of (FileObject, Reference (FolderList)) */
71     private static final Map<FileObject, Reference<FolderList>> map =
72             new WeakHashMap<FileObject, Reference<FolderList>> (101);
73     
74     /** refresh time in milliseconds */
75     private static int REFRESH_TIME = -1; // will be updated in getRefreshTime
76

77     /* -------------------------------------------------------------------- */
78     /* -- Instance attributes --------------------------------------------- */
79     /* -------------------------------------------------------------------- */
80
81     /** data folder to work with */
82     private FileObject folder;
83
84     /** The task that computes the content of FolderList. There is also
85     * only one computation task in the PROCESSOR for each FolderList.
86     * Whenever a new change notification arrives (thru file listener)
87     * the previous task is canceled (if not running) and new is created.
88     */

89     transient private RequestProcessor.Task refreshTask;
90     /** task that is non-null if a setOrder has been called
91      */

92     transient private volatile RequestProcessor.Task comparatorTask;
93
94     /** Primary files in this folder. Maps (FileObject, Reference (DataObject))
95     */

96     transient private Map<FileObject, Reference<DataObject>> primaryFiles = null;
97
98     /** order of primary files (FileObject) */
99     transient private List<FileObject> order;
100
101     private static final Logger err = Logger.getLogger("org.openide.loaders.FolderList"); // NOI18N
102

103     /** property change support */
104     transient private PropertyChangeSupport pcs;
105     
106     /**
107      * If true, this folder has been fully created (though it might
108      * still be refreshing etc.). Used to avoid e.g. MDO.PROP_FILES
109      * firing before the folder is ready.
110      */

111     transient private boolean folderCreated = false;
112     
113     /* -------------------------------------------------------------------- */
114     /* -- Constructor (private) ------------------------------------------- */
115     /* -------------------------------------------------------------------- */
116
117     /**
118     * @param df data folder to show
119     */

120     private FolderList (FileObject folder, boolean attach) {
121         this.folder = folder;
122         if (attach) {
123             // creates object that handles all elements in array and
124
// assignes it to the
125
folder.addFileChangeListener (org.openide.filesystems.FileUtil.weakFileChangeListener (this, folder));
126         }
127     }
128
129 /* final void reassign(DataFolder df, FileObject fo) {
130         folder = df;
131         // reassign is called from DataFolder.handleMove()
132         // in this time the folder - df - does not have
133         // setup the right primary file
134         // so the fo is the new primary file for df
135         fo.addFileChangeListener (WeakListener.fileChange (this, fo));
136     }
137  */

138     public String JavaDoc toString () {
139         return "FolderList{" + folder + "}"; // NOI18N
140
}
141     
142     /* -------------------------------------------------------------------- */
143     /* -- Factory method (static) ----------------------------------------- */
144     /* -------------------------------------------------------------------- */
145     
146     /** A public method to get the correct list for given file object.
147      *
148      * @param folder the folder to find FolderList for
149      * @param create if true than new FolderList should be created if it does not exists
150      * @return the FolderList or null if create was false
151      */

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     /**
166      * Has this FolderList finished creation of this list (at least once)?
167      * @return true if it has been created (may still be refreshing), false if still in progress
168      */

169     public boolean isCreated() {
170         return folderCreated;
171     }
172     
173     /* -------------------------------------------------------------------- */
174     /* -- Static methods -------------------------------------------------- */
175     /* -------------------------------------------------------------------- */
176     
177     /** Checks whether the calling thread is the FolderRecognizer.
178      */

179     public static boolean isFolderRecognizerThread () {
180         return PROCESSOR.isRequestProcessorThread ();
181     }
182     
183     /* -------------------------------------------------------------------- */
184     /* -- Static methods to inform FolderList for a given folder ---------- */
185     /* -------------------------------------------------------------------- */
186     
187     /** A method used to notify the FolderList system that order has changed
188      * for a given file object.
189      *
190      * @param folder the affected file object
191      */

192     public static void changedFolderOrder (FileObject folder) {
193         FolderList list = find (folder, false);
194         if (list != null) {
195             list.changeComparator ();
196         }
197     }
198     
199     /** Called when a data system changed so much that there is a need for refresh
200      * of a content of a folder.
201      *
202      * @param folder file object that can be affected
203      */

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()); // NOI18N
208
}
209         if (list != null) {
210             list.refresh ();
211         }
212     }
213     
214     /* -------------------------------------------------------------------- */
215     /* -- Folder content and content processing --------------------------- */
216     /* -------------------------------------------------------------------- */
217
218     /** Computes array of children associated
219     * with this folder.
220     */

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     /** List all children.
229     * @return array with children
230     */

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     /** Blocks if the processing of content of folder is in progress.
245     */

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     /** Starts computation of children list asynchronously.
259     */

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         // and then post your read task and wait
269
lt.task = PROCESSOR.post (lt, 0, priority);
270         return lt;
271     }
272
273     /** Setter for sort mode.
274     */

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 JavaDoc () {
282                 public void run () {
283                     synchronized (COMP) {
284                         if (previous != null) {
285                             previous.waitFinished ();
286                         }
287                         // if has been notified
288
// change mode and regenerated children
289
if (primaryFiles != null) {
290                             // the old children
291
if (LOG) err.fine("changeComparator on " + folder + ": get old");
292                             List v = getObjects (null);
293                             if (v.size () != 0) {
294                                 // the new children - also are stored to be returned next time from getChildrenList ()
295
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                             // clean the task if is my own not assigned by somebody else
304
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     /* -- Refresh --------------------------------------------------------- */
317     /* -------------------------------------------------------------------- */
318
319     /** Refreshes the list of children.
320      */

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 JavaDoc () {
328                     public void run () {
329                         RequestProcessor.Task t = comparatorTask;
330                         if (t != null) {
331                             // first of all finish setting up comparator
332
t.waitFinished ();
333                         }
334                         
335                         if (LOG) err.fine("-- refresh on " + folder + ": now=" + now);
336                         if (primaryFiles != null) {
337                             // list of children is created, recreate it for new files
338
createBoth (null, true);
339                         }
340                     }
341                 }, getRefreshTime(), LATER_PRIORITY);
342             } else {
343                 refreshTask.schedule(getRefreshTime());
344             }
345         }
346     }
347
348     /** Tries to read the value of the refresh time from a system property.
349      * If the system property is not present a default value (currently 10)
350      * is used.
351      */

352     private static int getRefreshTime() {
353         if (REFRESH_TIME >= 0) {
354             return REFRESH_TIME;
355         }
356         
357         String JavaDoc sysProp = System.getProperty("org.openide.loaders.FolderList.refresh.interval"); // NOI18N
358
if (sysProp != null) {
359             try {
360                 REFRESH_TIME = Integer.parseInt(sysProp);
361             } catch (NumberFormatException JavaDoc 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     /* -- Implementation of FileChangeListener ---------------------------- */
373     /* -------------------------------------------------------------------- */
374
375     /** Fired when a file has been changed. Refreshes the list when a
376      * has be changed which up to now was not a member of the list but
377      * becomes a member as a consequence of the change.
378      *
379      * @param fe the event describing context where action has taken place
380      */

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         /** condition fo.isValid () is hot fix for solving problem (similar to #17328)
388          * inside filesystems and should be reviewed.
389          */

390         if (fo.isData () && fo.isValid ()) {
391             // when a data on the disk has been changed, look whether we
392
// should reparse children
393
if (primaryFiles != null) {
394                 // a file has been changed and the list of files is created
395
try {
396                     DataObject obj = DataObject.find (fo);
397                     if (!primaryFiles.containsKey (obj.getPrimaryFile ())) {
398                         // BUGFIX: someone who recognized the file and who isn't registered
399
// yet =>
400
// may be still not O.K.
401

402                         // this primary file is not registered yet
403
// so recreate list of children
404
refresh();
405                     }
406                 } catch (DataObjectNotFoundException ex) {
407                     Logger.getLogger(FolderList.class.getName()).log(Level.WARNING, null, ex);
408                     // file without data object => no changes
409
}
410             }
411             
412             // Resort if sorting by last modification or size:
413
DataFolder.SortMode sortMode = getComparator().getSortMode();
414             if (sortMode == DataFolder.SortMode.LAST_MODIFIED || sortMode == DataFolder.SortMode.SIZE) {
415                 changeComparator();
416             }
417         }
418     }
419
420     /** Fired when a file has been deleted.
421     * @param fe the event describing context where action has taken place
422     */

423     public void fileDeleted (FileEvent fe) {
424         final boolean LOG = err.isLoggable(Level.FINE);
425         if (LOG) err.fine("fileDeleted: " + fe);
426         // boolean debug = fe.getFile().toString().equals("P"); // NOI18N
427
//if (debug) System.out.println ("fileDeleted: " + fe.getFile ()); // NOI18N
428
//if (debug) System.out.println ("fileList: " + fileList + " file: " + fileList.get (fe.getFile ())); // NOI18N
429
if (primaryFiles == null || primaryFiles.containsKey (fe.getFile ())) {
430             // one of main files has been deleted => reparse
431
//if (debug) System.out.println ("RecreateChildenList"); // NOI18N
432
refresh();
433             //if (debug) System.out.println ("Done"); // NOI18N
434
}
435     }
436
437     /** Fired when a new file has been created. This action can only be
438     * listened in folders containing the created file up to the root of
439     * file system.
440     *
441     * @param fe the event describing context where action has taken place
442     */

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     /** Fired when a new file has been created. This action can only be
450     * listened in folders containing the created file up to the root of
451     * file system.
452     *
453     * @param fe the event describing context where action has taken place
454     */

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     /** Fired when a new file has been renamed.
462     *
463     * @param fe the event describing context where action has taken place
464     */

465     public void fileRenamed (FileRenameEvent fe) {
466         final boolean LOG = err.isLoggable(Level.FINE);
467         if (LOG) err.fine("fileRenamed: " + fe);
468         refresh();
469         // Typically order may change as a result (#13820):
470
changeComparator();
471     }
472
473     /** Fired when a file attribute has been changed.
474     *
475     * @param fe the event describing context where action has taken place
476     */

477     public void fileAttributeChanged(FileAttributeEvent fe) {
478         final boolean LOG = err.isLoggable(Level.FINE);
479         if (LOG) err.fine("fileAttributeChanged: " + fe);
480         // update list when attrs defining order were changed
481
if (fe.getFile() == folder) {
482             /** Means one of attributes were changed*/
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     /* -- Processing methods (only called in PROCESSOR) ------------------- */
498     /* -------------------------------------------------------------------- */
499     
500     /** The comparator for this file objects.
501      * @return the comparator to use
502      */

503     private FolderOrder getComparator () {
504         return FolderOrder.findFor (folder);
505     }
506
507     /** Getter for list of children.
508     * @param f filter to be notified about additions
509     * @return List with DataObject types
510     */

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         /* createChildrenAndFiles ();/*
528         ArrayList v = (Collection)childrenList.get ();
529         //if (debug) System.out.println ("Children list xxxxxxxxxxxxxx");
530         if (v == null) {
531         //if (debug) System.out.println ("Create them xxxxxxxxxxxx");
532           v = createChildrenList (f);
533         //if (debug) System.out.println ("result: " + v);
534     }
535         return v;*/

536     }
537
538     /** Sort a list of DataObject's carefully.
539      * The supplied comparator should supply a basic ordering,
540      * and may also have an associated overriding partial ordering.
541      * If the partial ordering is given and is self-contradictory,
542      * it will be ignored and a warning issued.
543      * @param l the list to sort
544      * @param c a comparator and maybe partial comparator to use
545      * @return the sorted list (may or may not be the same)
546      */

547     private /*static*/ 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         // Not quite right: topologicalSort will not guarantee that these are left alone,
551
// even if the constraints do not mention files in the existing folder order.
552
// Adding constraints between adjacent pairs in the existing folder order is
553
// not good either, since that could produce an inconsistency relative to
554
// the explicitly specified constraints. E.g. you have:
555
// {a, b, c, d, e, x, y} Folder-Order=[a, b, c, d, e] c/x c/y x/d y/d
556
// This will currently produce the order: [a, b, c, e, x, y, d]
557
// If you had the existing folder order [a, b, d, c, e], then trying to make
558
// the sort stabler would just cause it to fail. XXX could try to add in the
559
// stabilizing constraints first, and if that fails, try again without them...
560
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."); // NOI18N
573
err.log(Level.WARNING, null, ex);
574                     err.warning("Using partial sort: " + corrected); // NOI18N
575
}
576                 return corrected;
577             }
578         }
579     }
580
581     /** Creates list of primary files from the list of data objects.
582     * @param list list of DataObject
583     * @return list of FileObject
584     */

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     /** Creates array of data objects from given order
597     * and mapping between files and data objects.
598     *
599     * @param order list of FileObjects that define the order to use
600     * @param map mapping (FileObject, Reference (DataObject)) to create data objects from
601     * @param f filter that is notified about additions - only items
602     * which are accepted by the filter will be added. Null means no filtering.
603     * @return array of data objects
604     */

605     private /*static*/ 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                 // try to find new data object
633
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             // add if accepted
646
if (obj != null) {
647                 if (LOG) {
648                     err.fine(" deliver: ref is " + ref + " obj is " + obj);
649                 }
650                 // JST: Cannot be avoided otherwise DataObject.files () can be unconsistent
651
// avoid to checkFiles(this)
652
// obj.recognizedByFolder();
653
if (f == null) {
654                     // accept all objects
655
res.add(obj);
656                 } else {
657                     // allow the listener f to filter
658
// objects in the array res
659
f.process(obj, res);
660                 }
661             }
662         }
663
664         if (f != null) {
665             if (LOG) {
666                 err.fine(" finished: " + res); // NOI18N
667
}
668             f.finished (res);
669         }
670         
671         if (LOG) {
672             err.fine("createObjects ends on " + folder); // NOI18N
673
}
674         return res;
675     }
676
677     /** Scans for files in the folder and creates representation for
678      * children. Fires info about changes in the nodes.
679      *
680      * @param filter listener to addition of nodes or null
681      * @param notify true if changes in the children should be fired
682      * @return vector of children
683      */

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         // map for (FileObject, DataObject)
688
final HashMap<FileObject,Reference<DataObject>> file =
689                 new HashMap<FileObject, Reference<DataObject>> ();
690
691         // list of all processed objects
692
List<DataObject> all = new ArrayList<DataObject> ();
693         // result list to return from the method
694
List<DataObject> res = new ArrayList<DataObject> ();
695
696         // map of current objects (FileObject, DataObject)
697
final Map<FileObject, Reference<DataObject>> remove = primaryFiles == null ?
698                                new HashMap<FileObject, Reference<DataObject>> () :
699                                new HashMap<FileObject,Reference<DataObject>>(primaryFiles);
700
701         // list of new objects to add
702
final List<DataObject> add = new ArrayList<DataObject> ();
703
704         DataLoaderPool pool = DataLoaderPool.getDefault();
705
706         // hashtable with FileObjects that are marked to be recognized
707
// and that is why being out of enumeration
708
final HashSet<FileObject> marked = new HashSet<FileObject> ();
709         DataLoader.RecognizedFiles recog = new DataLoader.RecognizedFiles () {
710                                                /** Adds the file object to the marked hashtable.
711                                                * @param fo file object (can be <CODE>null</CODE>)
712                                                */

713                                                public void markRecognized (FileObject fo) {
714                                                    if (fo != null) {
715                                                        marked.add (fo);
716                                                    }
717                                                }
718                                            };
719         // enumeration of all files in the folder
720
Enumeration<? extends FileObject> en = folder.getChildren (false);
721         while (en.hasMoreElements ()) {
722             FileObject fo = en.nextElement ();
723             if (!marked.contains (fo)) {
724                 // the object fo has not been yet marked as recognized
725
// => continue in computation
726
DataObject obj;
727                 try {
728                     obj = pool.findDataObject (fo, recog);
729                 } catch (DataObjectExistsException ex) {
730                     // use existing data object
731
obj = ex.getDataObject ();
732                 } catch (IOException JavaDoc ex) {
733                     // data object not recognized or not found
734
obj = null;
735                     Exceptions.printStackTrace(ex);
736                 }
737
738                 if (obj != null) {
739                     // adds object to data if it is not already there
740

741                     // avoid to checkFiles(this)
742
obj.recognizedByFolder();
743
744                     // primary file
745
FileObject primary = obj.getPrimaryFile ();
746
747                     boolean doNotRemovePrimaryFile = false;
748                     if (!file.containsKey (primary)) {
749                         // realy added object, test if it is new
750

751                         // if we have not created primaryFiles before, then it is new
752
boolean goIn = primaryFiles == null;
753                         if (!goIn) {
754                             Reference<DataObject> r = primaryFiles.get (primary);
755                             // if its primary file is not between original primary files
756
// then data object is new
757
goIn = r == null;
758                             if (!goIn) {
759                                 // if the primary file is there, but the previous data object
760
// exists and is different, then this one is new
761
DataObject obj2 = r.get ();
762                                 goIn = obj2 == null || obj2 != obj;
763                                 if (goIn) {
764                                     doNotRemovePrimaryFile = true;
765                                 }
766                             }
767                         }
768
769                         if (goIn) {
770                             // realy new
771
add.add (obj);
772                             /* JST: In my opinion it should not be here
773                             * so I moved this out of this if. Is it ok?
774
775                             if (filter != null) {
776                               // fire info about addition
777                               filter.acceptDataObject (obj);
778                         }
779                             */

780                         }
781                         // adds the object
782
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                         // this object exists it should not be removed
792
remove.remove (primary);
793                     }
794
795                     // add it to the list of primary files
796
file.put (primary, new SoftReference<DataObject> (obj));
797                 } else {
798                     // 1. nothing to add to data object list
799
// 2. remove this object if it was in list of previous ones
800
// 3. do not put the file into list of know primary files
801
// => do nothing at all
802
}
803             }
804         }
805
806         // !!! section that fires info about changes should be here !!!
807

808         // now file contains newly inserted files
809
// data contains data objects
810
// remove contains data objects that should be removed
811
// add contains data object that were added
812

813         primaryFiles = file;
814
815         all = carefullySort (all, getComparator ());
816         order = createOrder (all);
817         if (all.size () == res.size ()) {
818             // assume no filtering has been done
819
res = all;
820         } else {
821             // sort also content of res
822
res = carefullySort (res, getComparator ());
823         }
824             
825
826         ////if (debug) System.out.println ("Notified: " + notified + " added: " + add.size () + " removed: " + remove.size ()); // NOI18N
827
if (notify) {
828             fireChildrenChange (add, remove.keySet ());
829         }
830
831         // notify the filter
832
if (LOG) {
833             err.fine("Notifying filter: " + filter); // NOI18N
834
}
835         if (filter != null) {
836             filter.finished (res);
837         }
838
839         return res;
840     }
841     
842     /* -------------------------------------------------------------------- */
843     /* -- PropertyChangeListener management ------------------------------- */
844     /* -------------------------------------------------------------------- */
845
846     /** Fires info about change of children to the folder.
847     * @param add added data objects
848     * @param removed removed data objects
849     */

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     /** Removes property change listener.
859      * @param l the listener
860      */

861     public void removePropertyChangeListener(PropertyChangeListener l) {
862         if (pcs != null) {
863             pcs.removePropertyChangeListener (l);
864         }
865     }
866     
867     /** Adds a listener.
868      * @param l the listener
869      */

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     /* -- Inner class ListTask -------------------------------------------- */
879     /* -------------------------------------------------------------------- */
880     
881     /** Task that holds result and also task. Moreover
882     * can do the computation.
883     */

884     private final class ListTask implements Runnable JavaDoc {
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             // invokes the refresh task before we do anything else
898
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 JavaDoc toString() {
914             return "ListTask@" + Integer.toHexString(System.identityHashCode(this)) + "[" + folder + "]"; // NOI18N
915
}
916     }
917     
918
919 }
920
Popular Tags