KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > core > projects > FileStateManager


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.netbeans.core.projects;
21
22 import java.beans.PropertyChangeEvent JavaDoc;
23 import java.beans.PropertyChangeListener JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.lang.ref.WeakReference JavaDoc;
26 import java.util.HashMap JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.LinkedList JavaDoc;
29 import java.util.Map.Entry;
30 import java.util.WeakHashMap JavaDoc;
31 import org.netbeans.core.startup.layers.SessionManager;
32 import org.openide.filesystems.FileChangeAdapter;
33 import org.openide.filesystems.FileChangeListener;
34 import org.openide.filesystems.FileEvent;
35 import org.openide.filesystems.FileLock;
36 import org.openide.filesystems.FileObject;
37 import org.openide.filesystems.FileRenameEvent;
38 import org.openide.filesystems.FileStateInvalidException;
39 import org.openide.filesystems.FileSystem;
40 import org.openide.filesystems.FileUtil;
41 import org.openide.filesystems.Repository;
42
43 /** Scans positions of FileObject-delegates for FileObjects from SystemFileSystem. Each
44  *
45  * @author Vitezslav Stejskal
46  */

47 final class FileStateManager {
48     
49     /** Identification of filesystem representing Session */
50     public static final int LAYER_SESSION = 1;
51     /** Identification of filesystem representing XML-layers from all installed modules */
52     public static final int LAYER_MODULES = 2;
53     
54     /** File State - file is defined on the layer (top-most layer containing the file) */
55     public static final int FSTATE_DEFINED = 0;
56     /** File State - file is ignored on the layer (higher layer contains file too) */
57     public static final int FSTATE_IGNORED = 1;
58     /** File State - file is inherited on the layer (file doesn't exist on the layer and exists on lower layer) */
59     public static final int FSTATE_INHERITED = 2;
60     /** File State - file is not defined on the layer (file doesn't exist on the layer and exists on higher layer) */
61     public static final int FSTATE_UNDEFINED = 3;
62     
63     /** Singleton instance of FileStateManager */
64     private static FileStateManager manager = null;
65     /** Cache of collected information */
66     private WeakHashMap JavaDoc<FileObject, FileInfo> info = new WeakHashMap JavaDoc<FileObject, FileInfo> ();
67     /** Number of layers on {@link SystemFileSystem} */
68     private static final int LAYERS_COUNT = 3;
69     /** Layers of {@link SystemFileSystem}, LAYER_* constants can be used as indexes. */
70     private FileSystem layers [] = new FileSystem [LAYERS_COUNT];
71     /** List of listeners listening on changes in file state */
72     private HashMap JavaDoc<FileStatusListener,LinkedList JavaDoc<FileObject>> listeners = new HashMap JavaDoc<FileStatusListener,LinkedList JavaDoc<FileObject>> (10);
73     /** Listener attached to SessionManager, it refreshes list of layers if some are added or removed */
74     private PropertyChangeListener JavaDoc propL = null;
75
76     public static synchronized FileStateManager getDefault () {
77         if (manager == null) {
78             manager = new FileStateManager ();
79         }
80         return manager;
81     }
82
83     /** Creates new FileStateManager */
84     private FileStateManager () {
85         // set layers
86
getLayers ();
87
88         // listen on changes of layers made through the SessionManager
89
propL = new PropL ();
90         SessionManager.getDefault ().addPropertyChangeListener (
91             org.openide.util.WeakListeners.propertyChange (propL, SessionManager.getDefault ()));
92     }
93
94     public void define (final FileObject mfo, int layer, boolean revert) throws IOException JavaDoc {
95         // ignore request when file is already defined on layer
96
if (FSTATE_DEFINED == getFileState (mfo, layer))
97             return;
98
99         FileSystem fsLayer = getLayer (layer);
100         if (fsLayer == null)
101             throw new IllegalArgumentException JavaDoc ("Invalid layer " + layer); //NOI18N
102

103         // find file on specified layer
104
FileObject fo = fsLayer.findResource (mfo.getPath());
105         
106         // remove the file if it exists and current definition should be preserved
107
if (fo != null && !revert) {
108             deleteImpl (mfo, fsLayer);
109             fo = null;
110         }
111
112         // create file on specified layer if it doesn't exist
113
if (fo == null) {
114             String JavaDoc parent = mfo.getParent ().getPath();
115             final FileObject fparent = FileUtil.createFolder (fsLayer.getRoot (), parent);
116             fparent.getFileSystem().runAtomicAction(new FileSystem.AtomicAction() {
117                 public void run () throws IOException JavaDoc {
118                     mfo.copy (fparent, mfo.getName (), mfo.getExt ());
119                 }
120             });
121         }
122
123         // remove above defined files
124
for (int i = 0; i < layer; i++) {
125             FileSystem fsl = getLayer (i);
126             if (fsl != null)
127                 deleteImpl (mfo, fsl);
128         }
129     }
130
131     public void delete (FileObject mfo, int layer) throws IOException JavaDoc {
132         FileSystem fsLayer = getLayer (layer);
133         if (fsLayer == null)
134             throw new IllegalArgumentException JavaDoc ("Invalid layer " + layer); //NOI18N
135

136         deleteImpl (mfo, fsLayer);
137     }
138     
139     public int getFileState (FileObject mfo, int layer) {
140         // check if the FileObject is from SystemFileSystem
141
FileSystem fs = null;
142         FileInfo finf = null;
143
144         try {
145             fs = mfo.getFileSystem ();
146         } catch (FileStateInvalidException e) {
147             // ignore, will be handled later
148
}
149
150         if (fs == null || !Repository.getDefault ().getDefaultFileSystem ().equals (fs))
151             throw new IllegalArgumentException JavaDoc ("FileObject has to be from DefaultFileSystem - " + mfo);
152         
153         synchronized (info) {
154             if (null == (finf = info.get(mfo))) {
155                 finf = new FileInfo(mfo);
156                 info.put(mfo, finf);
157             }
158         }
159
160         return finf.getState (layer);
161     }
162     
163     public final void addFileStatusListener (FileStatusListener l, FileObject mfo) {
164         synchronized (listeners) {
165             LinkedList JavaDoc<FileObject> lst = null;
166             if (!listeners.containsKey (l)) {
167                 lst = new LinkedList JavaDoc<FileObject> ();
168                 listeners.put (l, lst);
169             }
170             else
171                 lst = listeners.get (l);
172             
173             if (!lst.contains (mfo))
174                 lst.add (mfo);
175         }
176     }
177     
178     public final void removeFileStatusListener (FileStatusListener l, FileObject mfo) {
179         synchronized (listeners) {
180             if (mfo == null)
181                 listeners.remove (l);
182             else {
183                 LinkedList JavaDoc<FileObject> lst = listeners.get (l);
184                 if (lst != null) {
185                    lst.remove (mfo);
186                    if (lst.isEmpty ())
187                        listeners.remove (l);
188                 }
189             }
190         }
191     }
192
193     @SuppressWarnings JavaDoc("unchecked")
194     private void fireFileStatusChanged (FileObject mfo) {
195         HashMap JavaDoc<FileStatusListener,LinkedList JavaDoc<FileObject>> h = null;
196         
197         synchronized (listeners) {
198             h = (HashMap JavaDoc<FileStatusListener,LinkedList JavaDoc<FileObject>>)listeners.clone ();
199         }
200         
201         for (Entry<FileStatusListener,LinkedList JavaDoc<FileObject>> entry: h.entrySet()) {
202             FileStatusListener l = entry.getKey();
203             LinkedList JavaDoc<FileObject> lst = entry.getValue();
204             if (lst.contains (mfo))
205                 l.fileStatusChanged (mfo);
206         }
207     }
208
209     private void deleteImpl (FileObject mfo, FileSystem fsLayer) throws IOException JavaDoc {
210         FileObject fo = fsLayer.findResource (mfo.getPath());
211         if (fo != null) {
212             FileLock lock = null;
213             try {
214                 lock = fo.lock ();
215                 fo.delete (lock);
216             } finally {
217                 if (lock != null)
218                     lock.releaseLock ();
219             }
220         }
221     }
222
223     private void discard (FileObject mfo) {
224         synchronized (info) {
225             info.remove (mfo);
226         }
227     }
228
229     private void getLayers () {
230         layers [LAYER_SESSION] = SessionManager.getDefault ().getLayer (SessionManager.LAYER_SESSION);
231         layers [LAYER_MODULES] = SessionManager.getDefault ().getLayer (SessionManager.LAYER_INSTALL);
232     }
233
234     private FileSystem getLayer (int layer) {
235         return layers [layer];
236     }
237     
238     private class PropL implements PropertyChangeListener JavaDoc {
239         PropL() {}
240         public void propertyChange (PropertyChangeEvent JavaDoc evt) {
241             if (SessionManager.PROP_OPEN.equals (evt.getPropertyName ())) {
242                 FileObject mfos [] = null;
243
244                 synchronized (info) {
245                     mfos = (FileObject [])info.keySet ().toArray (new FileObject [info.size()]);
246                     
247                     // invalidate all existing FileInfos
248
for (int i = 0; i < mfos.length; i++) {
249                         FileInfo finf = info.get(mfos[i]);
250
251                         if (finf != null)
252                             finf.invalidate();
253                     }
254
255                     // clear the cache
256
info.clear ();
257
258                     // [PENDING] this should be better synchronized
259
getLayers ();
260                 }
261                 
262                 for (int i = 0; i < mfos.length; i++)
263                     fireFileStatusChanged (mfos [i]);
264             }
265         }
266     }
267
268     public static interface FileStatusListener {
269         public void fileStatusChanged (FileObject mfo);
270     }
271     
272     private class FileInfo extends FileChangeAdapter {
273         private WeakReference JavaDoc<FileObject> file = null;
274         
275         private int state [] = new int [LAYERS_COUNT];
276         private final Object JavaDoc LOCK = new Object JavaDoc ();
277
278         private FileObject notifiers [] = new FileObject [LAYERS_COUNT];
279         private FileChangeListener weakL [] = new FileChangeListener [LAYERS_COUNT];
280         
281         public FileInfo (FileObject mfo) {
282             file = new WeakReference JavaDoc<FileObject> (mfo);
283             
284             // get initial state
285
for (int i = 0; i < LAYERS_COUNT; i++) {
286                 state [i] = getStateImpl (mfo, i);
287             }
288             
289             // attach FileInfo to interesting FileObject on each layer
290
for (int i = 0; i < LAYERS_COUNT; i++) {
291                 attachNotifier (mfo, i);
292             }
293         }
294
295         public void invalidate () {
296             detachAllNotifiers ();
297             synchronized (LOCK) {
298                 for (int i = 0; i < LAYERS_COUNT; i++)
299                     state [i] = FSTATE_UNDEFINED;
300             }
301         }
302
303         public int getState (int layer) {
304             synchronized (LOCK) {
305                 return state [layer];
306             }
307         }
308
309         private void rescan (FileObject mfo) {
310             boolean changed = false;
311             
312             synchronized (LOCK) {
313                 for (int i = 0; i < LAYERS_COUNT; i++) {
314                     int ns = getStateImpl (mfo, i);
315                     if (state [i] != ns) {
316                         state [i] = ns;
317                         changed = true;
318                     }
319                 }
320             }
321             
322             if (changed)
323                 fireFileStatusChanged (mfo);
324         }
325
326         private int getStateImpl (FileObject mfo, int layer) {
327             boolean above = false;
328             boolean below = false;
329
330             // scan higher layers
331
for (int i = 0; i < layer; i++) {
332                 if (isOnLayer (mfo, i)) {
333                     above = true;
334                     break;
335                 }
336             }
337
338             // scan lower layers
339
for (int i = layer + 1; i < LAYERS_COUNT; i++) {
340                 if (isOnLayer (mfo, i)) {
341                     below = true;
342                     break;
343                 }
344             }
345
346             if (isOnLayer (mfo, layer)) {
347                 return above ? FSTATE_IGNORED : FSTATE_DEFINED;
348             }
349             else {
350                 return below && !above ? FSTATE_INHERITED : FSTATE_UNDEFINED;
351             }
352         }
353         
354         private boolean isOnLayer (FileObject mfo, int layer) {
355             FileSystem fsLayer = getLayer (layer);
356             return fsLayer == null ? false : null != fsLayer.findResource (mfo.getPath());
357         }
358         
359         /**
360          * @param mfo FileObject from default file system
361          * @param layer the layer where notifier will be searched on
362          * @return true if attached notifier is the delegate FO
363          */

364         private synchronized boolean attachNotifier (FileObject mfo, int layer) {
365             FileSystem fsLayer = getLayer (layer);
366             String JavaDoc fn = mfo.getPath();
367             FileObject fo = null;
368             boolean isDelegate = true;
369
370             if (fsLayer == null)
371                 return false;
372
373             // find new notifier - the FileObject with closest match to getFile ()
374
while (fn.length () > 0 && null == (fo = fsLayer.findResource (fn))) {
375                 int pos = fn.lastIndexOf ('/');
376                 isDelegate = false;
377
378                 if (-1 == pos)
379                     break;
380                 
381                 fn = fn.substring (0, pos);
382             }
383             
384             if (fo == null)
385                 fo = fsLayer.getRoot ();
386
387             if (fo != notifiers [layer]) {
388                 // remove listener from existing notifier if any
389
if (notifiers [layer] != null)
390                     notifiers [layer].removeFileChangeListener (weakL [layer]);
391
392                 // create new listener and attach it to new notifier
393
weakL [layer] = FileUtil.weakFileChangeListener (this, fo);
394                 fo.addFileChangeListener (weakL [layer]);
395                 notifiers [layer] = fo;
396             }
397             
398             return isDelegate;
399         }
400
401         private synchronized void detachAllNotifiers () {
402             for (int i = 0; i < LAYERS_COUNT; i++) {
403                 if (notifiers [i] != null) {
404                     notifiers [i].removeFileChangeListener (weakL [i]);
405                     notifiers [i] = null;
406                     weakL [i] = null;
407                 }
408             }
409         }
410         
411         private int layerOfFile (FileObject fo) {
412             try {
413                 FileSystem fs = fo.getFileSystem ();
414                 for (int i = 0; i < LAYERS_COUNT; i++) {
415                     if (fs.equals (getLayer (i)))
416                         return i;
417                 }
418             } catch (FileStateInvalidException e) {
419                 throw (IllegalStateException JavaDoc) new IllegalStateException JavaDoc("Invalid file - " + fo).initCause(e); // NOI18N
420
}
421             return -1;
422 // throw new IllegalStateException ("File isn't from any layer in DefaultFileSystem - " + fo); // NOI18N
423
}
424
425         // ---------------------- FileChangeListener events -----------------------------
426

427         public void fileRenamed (FileRenameEvent fe) {
428             // rename can be caused either by renaming fo or by deleting mfo,
429
// thus the safe way is to discard this FileInfo from the map and
430
// notify listeners about the change
431
FileObject mfo = file.get ();
432             if (mfo != null && mfo.isValid ()) {
433                 discard (mfo);
434                 fireFileStatusChanged (mfo);
435             }
436             else
437                 detachAllNotifiers ();
438         }
439         
440         public void fileDataCreated (FileEvent fe) {
441             FileObject mfo = file.get ();
442             if (mfo != null && mfo.isValid ()) {
443                 String JavaDoc created = fe.getFile ().getPath();
444                 String JavaDoc mfoname = mfo.getPath();
445
446                 if (created.equals (mfoname)) {
447                     int layer;
448                     if (-1 != (layer = layerOfFile (fe.getFile ())))
449                         attachNotifier (mfo, layer);
450
451                     rescan (mfo);
452                 }
453             }
454             else
455                 detachAllNotifiers ();
456         }
457         
458         public void fileFolderCreated (FileEvent fe) {
459             FileObject mfo = file.get ();
460             if (mfo != null && mfo.isValid ()) {
461                 String JavaDoc created = fe.getFile ().getPath();
462                 String JavaDoc mfoname = mfo.getPath();
463
464                 if (mfoname.startsWith (created)) {
465                     int layer;
466                     if (-1 != (layer = layerOfFile (fe.getFile ())))
467                         if (attachNotifier (mfo, layer)) {
468                             // delegate was created -> rescan
469
rescan (mfo);
470                         }
471                 }
472             }
473             else
474                 detachAllNotifiers ();
475         }
476         
477         public void fileDeleted (FileEvent fe) {
478             FileObject mfo = file.get ();
479             if (mfo != null && mfo.isValid ()) {
480                 String JavaDoc deleted = fe.getFile ().getPath();
481                 String JavaDoc mfoname = mfo.getPath();
482
483                 if (deleted.equals (mfoname)) {
484                     int layer;
485                     if (-1 != (layer = layerOfFile (fe.getFile ())))
486                         attachNotifier (mfo, layer);
487
488                     rescan (mfo);
489                 }
490             }
491             else
492                 detachAllNotifiers ();
493         }
494     }
495 }
496
Popular Tags