KickJava   Java API By Example, From Geeks To Geeks.

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


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.util.*;
25 import java.util.logging.Level JavaDoc;
26 import java.util.logging.Logger JavaDoc;
27 import javax.swing.event.*;
28 import org.openide.filesystems.FileObject;
29 import org.openide.nodes.*;
30 import org.openide.util.RequestProcessor;
31
32 /** Watches over a folder and represents its
33 * child data objects by nodes.
34 *
35 * @author Jaroslav Tulach
36 */

37 final class FolderChildren extends Children.Keys<FolderChildren.Pair>
38 implements PropertyChangeListener, ChangeListener {
39     /** the folder */
40     private DataFolder folder;
41     /** filter of objects */
42     private final DataFilter filter;
43     /** listener on changes in nodes */
44     private PropertyChangeListener listener;
45     /** logging, if needed */
46     private Logger JavaDoc err;
47     /** true if the refrersh is done after DataFilter change */
48     private boolean refresh;
49     /** we wait for this task finished in getNodes(true) */
50     private RequestProcessor.Task refreshTask;
51     /** Runnable scheduled to refRP */
52     private ChildrenRefreshRunnable refreshRunnable;
53     
54     /** Private req processor for the refresh tasks */
55     private static RequestProcessor refRP =
56         new RequestProcessor("FolderChildren_Refresh"); // NOI18N
57

58     /**
59     * @param f folder to display content of
60     * @param map map to use for holding of children
61     */

62     public FolderChildren (DataFolder f) {
63         this (f, DataFilter.ALL);
64     }
65
66     /**
67     * @param f folder to display content of
68     * @param filter filter of objects
69     */

70     public FolderChildren(DataFolder f, DataFilter filter) {
71         this.folder = f;
72         this.filter = filter;
73         this.refreshRunnable = new ChildrenRefreshRunnable();
74         this.refreshTask = refRP.create(refreshRunnable);
75         this.listener = org.openide.util.WeakListeners.propertyChange(this, folder);
76         err = Logger.getLogger("org.openide.loaders.FolderChildren." + f.getPrimaryFile().getPath().replace('/','.')); // NOI18N
77
}
78     
79     /** used from DataFolder */
80     DataFilter getFilter () {
81         return filter;
82     }
83
84     /** If the folder changed its children we change our nodes.
85     */

86     public void propertyChange (final PropertyChangeEvent ev) {
87         if (DataFolder.PROP_CHILDREN.equals (ev.getPropertyName ())) {
88             err.fine("Got PROP_CHILDREN");
89             refreshChildren().schedule (0);
90             postClearTask();
91             return;
92         }
93         if (
94             DataFolder.PROP_SORT_MODE.equals (ev.getPropertyName ()) ||
95             DataFolder.PROP_ORDER.equals (ev.getPropertyName ())
96         ) {
97             err.fine("Got PROP_SORT_MODE or PROP_ORDER");
98             refreshChildren().schedule (0);
99             postClearTask();
100             return;
101         }
102     }
103     
104     public void stateChanged( ChangeEvent e ) {
105         // Filtering changed need to recompute children
106
refresh = true;
107         refreshChildren().schedule(0);
108         postClearTask();
109         return;
110     }
111
112     /**
113      * refreshRunnable holds references to the data object
114      * to prevent GC. This method post a task to the same request processor
115      * (refRP) to clear this references after they are no longer needed.
116      */

117     private void postClearTask() {
118         refRP.post(new Runnable JavaDoc() {
119             public void run() {
120                 refreshRunnable.clear();
121             }
122         });
123     }
124     
125     /** Refreshes the children.
126     */

127     RequestProcessor.Task refreshChildren() {
128         return refreshTask;
129     }
130
131     /** Create a node for one data object.
132     * @param key DataObject
133     */

134     protected Node[] createNodes(Pair key) {
135         err.fine("createNodes: " + key);
136         FileObject fo = key.primaryFile;
137         DataObject obj;
138         try {
139             obj = DataObject.find (fo);
140             if (filter == null || filter.acceptDataObject (obj)) {
141                 return new Node[] { obj.getClonedNodeDelegate (filter) };
142             } else {
143                 return new Node[0];
144             }
145         } catch (DataObjectNotFoundException e) {
146             Logger.getLogger(FolderChildren.class.getName()).log(Level.FINE, null, e);
147             return new Node[0];
148         }
149     }
150   
151     public Node[] getNodes(boolean optimalResult) {
152         Node[] res;
153         Object JavaDoc hold;
154
155         if (optimalResult) {
156             if (checkChildrenMutex()) {
157                 err.fine("getNodes(true)"); // NOI18N
158
FolderList.find(folder.getPrimaryFile(), true).waitProcessingFinished();
159                 err.fine("getNodes(true): waitProcessingFinished"); // NOI18N
160
RequestProcessor.Task task = refreshChildren();
161                 res = getNodes();
162                 err.fine("getNodes(true): getNodes: " + res.length); // NOI18N
163
task.schedule(0);
164                 task.waitFinished();
165                 err.fine("getNodes(true): waitFinished"); // NOI18N
166
} else {
167                 Logger.getLogger(FolderChildren.class.getName()).log(Level.WARNING, null,
168                                   new java.lang.IllegalStateException JavaDoc("getNodes(true) called while holding the Children.MUTEX"));
169             }
170         }
171         res = getNodes();
172         err.fine("getNodes(boolean): post clear task"); // NOI18N
173
postClearTask(); // we can clean the references to data objects now
174
// they are no longer needed
175
return res;
176     }
177     
178     public Node findChild(String JavaDoc name) {
179         if (checkChildrenMutex()) {
180             getNodes(true);
181         }
182         return super.findChild(name);
183     }
184
185
186
187     /**
188      * @return true if it is safe to wait (our thread is
189      * not in Children.MUTEX.readAccess
190      */

191     static boolean checkChildrenMutex() {
192         return !Children.MUTEX.isReadAccess() && !Children.MUTEX.isWriteAccess ();
193     }
194     
195     /** Initializes the children.
196     */

197     protected void addNotify () {
198         err.fine("addNotify begin");
199         // add as a listener for changes on nodes
200
folder.addPropertyChangeListener (listener);
201         // add listener to the filter
202
if ( filter instanceof ChangeableDataFilter ) {
203             ((ChangeableDataFilter)filter).addChangeListener( this );
204         }
205         // start the refresh task to compute the children
206
refreshChildren().schedule(0);
207         err.fine("addNotify end");
208     }
209
210     /** Deinitializes the children.
211     */

212     protected void removeNotify () {
213         err.fine("removeNotify begin");
214         // removes the listener
215
folder.removePropertyChangeListener (listener);
216         // remove listener from filter
217
if ( filter instanceof ChangeableDataFilter ) {
218             ((ChangeableDataFilter)filter).removeChangeListener( this );
219         }
220         
221         // we need to clear the children now
222
setKeys(Collections.<Pair>emptySet());
223         err.fine("removeNotify end");
224     }
225
226     /** Display name */
227     public String JavaDoc toString () {
228         return (folder != null) ? folder.getPrimaryFile ().toString () : super.toString();
229     }
230     
231     /**
232      * Instances of this class are posted to the request processor refRP
233      * (FolderChildren_refresher). We do this because we do not want
234      * to call setKeys synchronously.
235      */

236     private final class ChildrenRefreshRunnable implements Runnable JavaDoc {
237         /** store the referneces to the data objects to
238          * prevent GC.
239          */

240         private DataObject[] ch;
241                 
242         /** calls setKeys with the folder children
243          * or with empty collection if active is false
244          */

245         public void run() {
246             // this can be run only on the refRP thread
247
assert refRP.isRequestProcessorThread();
248
249             FolderList.find(folder.getPrimaryFile(), true).waitProcessingFinished();
250             
251             ch = folder.getChildren();
252             err.fine("Children computed");
253             Pair[] keys = new Pair[ch.length];
254             for (int i = 0; i < keys.length; i++) {
255                 keys[i] = new Pair(ch[i].getPrimaryFile());
256             }
257             setKeys(Arrays.asList(keys));
258             
259             if ( refresh ) {
260                 refresh = false;
261                 for (Pair key : keys) {
262                     refreshKey(key);
263                 }
264             }
265             
266             if (!isInitialized()) {
267                 clear();
268             }
269         }
270         
271         /** stop holding the references to the data objects. After
272          * calling this they can be GCed again.
273          */

274         public void clear() {
275             // this can be run only on the refRP thread
276
assert refRP.isRequestProcessorThread();
277             err.fine("Clearing the reference to children"); // NOI18N
278
ch = null;
279         }
280     }
281     
282     /** Pair of dataobject invalidation sequence # and primary file.
283      * It serves as a key for the given data object.
284      * It is here to create something different then data object,
285      * because the data object should be finalized when not needed and
286      * that is why it should not be used as a key.
287      */

288     static final class Pair extends Object JavaDoc {
289         public FileObject primaryFile;
290         public int seq;
291
292         public Pair (FileObject primaryFile) {
293             this.primaryFile = primaryFile;
294             this.seq = DataObjectPool.getPOOL().registrationCount(primaryFile);
295         }
296
297         public int hashCode () {
298             return primaryFile.hashCode () ^ seq;
299         }
300
301         public boolean equals (Object JavaDoc o) {
302             if (o instanceof Pair) {
303                 Pair p = (Pair)o;
304                 return primaryFile.equals (p.primaryFile) && seq == p.seq;
305             }
306             return false;
307         }
308         
309         public String JavaDoc toString() {
310             return "FolderChildren.Pair[" + primaryFile + "," + seq + "]"; // NOI18N
311
}
312     }
313 }
314
Popular Tags