~ubuntu-branches/ubuntu/quantal/netbeans/quantal

« back to all changes in this revision

Viewing changes to core/src/org/netbeans/core/projects/FileStateManager.java

  • Committer: Bazaar Package Importer
  • Author(s): Marek Slama
  • Date: 2008-01-29 14:11:22 UTC
  • Revision ID: james.westby@ubuntu.com-20080129141122-fnzjbo11ntghxfu7
Tags: upstream-6.0.1
ImportĀ upstreamĀ versionĀ 6.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 
5
 *
 
6
 * The contents of this file are subject to the terms of either the GNU
 
7
 * General Public License Version 2 only ("GPL") or the Common
 
8
 * Development and Distribution License("CDDL") (collectively, the
 
9
 * "License"). You may not use this file except in compliance with the
 
10
 * License. You can obtain a copy of the License at
 
11
 * http://www.netbeans.org/cddl-gplv2.html
 
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 
13
 * specific language governing permissions and limitations under the
 
14
 * License.  When distributing the software, include this License Header
 
15
 * Notice in each file and include the License file at
 
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 
17
 * particular file as subject to the "Classpath" exception as provided
 
18
 * by Sun in the GPL Version 2 section of the License file that
 
19
 * accompanied this code. If applicable, add the following below the
 
20
 * License Header, with the fields enclosed by brackets [] replaced by
 
21
 * your own identifying information:
 
22
 * "Portions Copyrighted [year] [name of copyright owner]"
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
26
 * The Original Software is NetBeans. The Initial Developer of the Original
 
27
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
 
28
 * Microsystems, Inc. All Rights Reserved.
 
29
 *
 
30
 * If you wish your version of this file to be governed by only the CDDL
 
31
 * or only the GPL Version 2, indicate your decision by adding
 
32
 * "[Contributor] elects to include this software in this distribution
 
33
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 
34
 * single choice of license, a recipient has the option to distribute
 
35
 * your version of this file under either the CDDL, the GPL Version 2 or
 
36
 * to extend the choice of license to its licensees as provided above.
 
37
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 
38
 * Version 2 license, then the option applies only if the new code is
 
39
 * made subject to such option by the copyright holder.
 
40
 */
 
41
 
 
42
package org.netbeans.core.projects;
 
43
 
 
44
import java.beans.PropertyChangeEvent;
 
45
import java.beans.PropertyChangeListener;
 
46
import java.io.IOException;
 
47
import java.lang.ref.WeakReference;
 
48
import java.util.HashMap;
 
49
import java.util.Iterator;
 
50
import java.util.LinkedList;
 
51
import java.util.Map.Entry;
 
52
import java.util.WeakHashMap;
 
53
import org.netbeans.core.startup.layers.SessionManager;
 
54
import org.openide.filesystems.FileChangeAdapter;
 
55
import org.openide.filesystems.FileChangeListener;
 
56
import org.openide.filesystems.FileEvent;
 
57
import org.openide.filesystems.FileLock;
 
58
import org.openide.filesystems.FileObject;
 
59
import org.openide.filesystems.FileRenameEvent;
 
60
import org.openide.filesystems.FileStateInvalidException;
 
61
import org.openide.filesystems.FileSystem;
 
62
import org.openide.filesystems.FileUtil;
 
63
import org.openide.filesystems.Repository;
 
64
 
 
65
/** Scans positions of FileObject-delegates for FileObjects from SystemFileSystem. Each
 
66
 *
 
67
 * @author  Vitezslav Stejskal
 
68
 */
 
69
final class FileStateManager {
 
70
    
 
71
    /** Identification of filesystem representing Session */
 
72
    public static final int LAYER_SESSION = 1;
 
73
    /** Identification of filesystem representing XML-layers from all installed modules */
 
74
    public static final int LAYER_MODULES = 2;
 
75
    
 
76
    /** File State - file is defined on the layer (top-most layer containing the file) */
 
77
    public static final int FSTATE_DEFINED = 0;
 
78
    /** File State - file is ignored on the layer (higher layer contains file too) */
 
79
    public static final int FSTATE_IGNORED = 1;
 
80
    /** File State - file is inherited on the layer (file doesn't exist on the layer and exists on lower layer) */
 
81
    public static final int FSTATE_INHERITED = 2;
 
82
    /** File State - file is not defined on the layer (file doesn't exist on the layer and exists on higher layer) */
 
83
    public static final int FSTATE_UNDEFINED = 3;
 
84
    
 
85
    /** Singleton instance of FileStateManager */
 
86
    private static FileStateManager manager = null;
 
87
    /** Cache of collected information */
 
88
    private WeakHashMap<FileObject, FileInfo> info = new WeakHashMap<FileObject, FileInfo> ();
 
89
    /** Number of layers on {@link SystemFileSystem} */
 
90
    private static final int LAYERS_COUNT = 3;
 
91
    /** Layers of {@link SystemFileSystem}, LAYER_* constants can be used as indexes. */
 
92
    private FileSystem layers [] = new FileSystem [LAYERS_COUNT];
 
93
    /** List of listeners listening on changes in file state */
 
94
    private HashMap<FileStatusListener,LinkedList<FileObject>> listeners = new HashMap<FileStatusListener,LinkedList<FileObject>> (10);
 
95
    /** Listener attached to SessionManager, it refreshes list of layers if some are added or removed */
 
96
    private PropertyChangeListener propL = null;
 
97
 
 
98
    public static synchronized FileStateManager getDefault () {
 
99
        if (manager == null) {
 
100
            manager = new FileStateManager ();
 
101
        }
 
102
        return manager;
 
103
    }
 
104
 
 
105
    /** Creates new FileStateManager */
 
106
    private FileStateManager () {
 
107
        // set layers
 
108
        getLayers ();
 
109
 
 
110
        // listen on changes of layers made through the SessionManager
 
111
        propL = new PropL ();
 
112
        SessionManager.getDefault ().addPropertyChangeListener (
 
113
            org.openide.util.WeakListeners.propertyChange (propL, SessionManager.getDefault ()));
 
114
    }
 
115
 
 
116
    public void define (final FileObject mfo, int layer, boolean revert) throws IOException {
 
117
        // ignore request when file is already defined on layer
 
118
        if (FSTATE_DEFINED == getFileState (mfo, layer))
 
119
            return;
 
120
 
 
121
        FileSystem fsLayer = getLayer (layer);
 
122
        if (fsLayer == null)
 
123
            throw new IllegalArgumentException ("Invalid layer " + layer); //NOI18N
 
124
 
 
125
        // find file on specified layer
 
126
        FileObject fo = fsLayer.findResource (mfo.getPath());
 
127
        
 
128
        // remove the file if it exists and current definition should be preserved
 
129
        if (fo != null && !revert) {
 
130
            deleteImpl (mfo, fsLayer);
 
131
            fo = null;
 
132
        }
 
133
 
 
134
        // create file on specified layer if it doesn't exist
 
135
        if (fo == null) {
 
136
            String parent = mfo.getParent ().getPath();
 
137
            final FileObject fparent = FileUtil.createFolder (fsLayer.getRoot (), parent);
 
138
            fparent.getFileSystem().runAtomicAction(new FileSystem.AtomicAction() {
 
139
                public void run () throws IOException {
 
140
                    mfo.copy (fparent, mfo.getName (), mfo.getExt ());
 
141
                }
 
142
            });
 
143
        }
 
144
 
 
145
        // remove above defined files
 
146
        for (int i = 0; i < layer; i++) {
 
147
            FileSystem fsl = getLayer (i);
 
148
            if (fsl != null)
 
149
                deleteImpl (mfo, fsl);
 
150
        }
 
151
    }
 
152
 
 
153
    public void delete (FileObject mfo, int layer) throws IOException {
 
154
        FileSystem fsLayer = getLayer (layer);
 
155
        if (fsLayer == null)
 
156
            throw new IllegalArgumentException ("Invalid layer " + layer); //NOI18N
 
157
        
 
158
        deleteImpl (mfo, fsLayer);
 
159
    }
 
160
    
 
161
    public int getFileState (FileObject mfo, int layer) {
 
162
        // check if the FileObject is from SystemFileSystem
 
163
        FileSystem fs = null;
 
164
        FileInfo finf = null;
 
165
 
 
166
        try {
 
167
            fs = mfo.getFileSystem ();
 
168
        } catch (FileStateInvalidException e) {
 
169
            // ignore, will be handled later
 
170
        }
 
171
 
 
172
        if (fs == null || !Repository.getDefault ().getDefaultFileSystem ().equals (fs))
 
173
            throw new IllegalArgumentException ("FileObject has to be from DefaultFileSystem - " + mfo);
 
174
        
 
175
        synchronized (info) {
 
176
            if (null == (finf = info.get(mfo))) {
 
177
                finf = new FileInfo(mfo);
 
178
                info.put(mfo, finf);
 
179
            }
 
180
        }
 
181
 
 
182
        return finf.getState (layer);
 
183
    }
 
184
    
 
185
    public final void addFileStatusListener (FileStatusListener l, FileObject mfo) {
 
186
        synchronized (listeners) {
 
187
            LinkedList<FileObject> lst = null;
 
188
            if (!listeners.containsKey (l)) {
 
189
                lst = new LinkedList<FileObject> ();
 
190
                listeners.put (l, lst);
 
191
            }
 
192
            else
 
193
                lst = listeners.get (l);
 
194
            
 
195
            if (!lst.contains (mfo))
 
196
                lst.add (mfo);
 
197
        }
 
198
    }
 
199
    
 
200
    public final void removeFileStatusListener (FileStatusListener l, FileObject mfo) {
 
201
        synchronized (listeners) {
 
202
            if (mfo == null)
 
203
                listeners.remove (l);
 
204
            else {
 
205
                LinkedList<FileObject> lst = listeners.get (l);
 
206
                if (lst != null) {
 
207
                   lst.remove (mfo);
 
208
                   if (lst.isEmpty ())
 
209
                       listeners.remove (l);
 
210
                }
 
211
            }
 
212
        }
 
213
    }
 
214
 
 
215
    @SuppressWarnings("unchecked") 
 
216
    private void fireFileStatusChanged (FileObject mfo) {
 
217
        HashMap<FileStatusListener,LinkedList<FileObject>> h = null;
 
218
        
 
219
        synchronized (listeners) {
 
220
            h = (HashMap<FileStatusListener,LinkedList<FileObject>>)listeners.clone ();
 
221
        }
 
222
        
 
223
        for (Entry<FileStatusListener,LinkedList<FileObject>> entry: h.entrySet()) {
 
224
            FileStatusListener l = entry.getKey();
 
225
            LinkedList<FileObject> lst = entry.getValue();
 
226
            if (lst.contains (mfo))
 
227
                l.fileStatusChanged (mfo);
 
228
        }
 
229
    }
 
230
 
 
231
    private void deleteImpl (FileObject mfo, FileSystem fsLayer) throws IOException {
 
232
        FileObject fo = fsLayer.findResource (mfo.getPath());
 
233
        if (fo != null) {
 
234
            FileLock lock = null;
 
235
            try {
 
236
                lock = fo.lock ();
 
237
                fo.delete (lock);
 
238
            } finally {
 
239
                if (lock != null)
 
240
                    lock.releaseLock ();
 
241
            }
 
242
        }
 
243
    }
 
244
 
 
245
    private void discard (FileObject mfo) {
 
246
        synchronized (info) {
 
247
            info.remove (mfo);
 
248
        }
 
249
    }
 
250
 
 
251
    private void getLayers () {
 
252
        layers [LAYER_SESSION] = SessionManager.getDefault ().getLayer (SessionManager.LAYER_SESSION);
 
253
        layers [LAYER_MODULES] = SessionManager.getDefault ().getLayer (SessionManager.LAYER_INSTALL);
 
254
    }
 
255
 
 
256
    private FileSystem getLayer (int layer) {
 
257
        return layers [layer];
 
258
    }
 
259
    
 
260
    private class PropL implements PropertyChangeListener {
 
261
        PropL() {}
 
262
        public void propertyChange (PropertyChangeEvent evt) {
 
263
            if (SessionManager.PROP_OPEN.equals (evt.getPropertyName ())) {
 
264
                FileObject mfos [] = null;
 
265
 
 
266
                synchronized (info) {
 
267
                    mfos = (FileObject [])info.keySet ().toArray (new FileObject [info.size()]);
 
268
                    
 
269
                    // invalidate all existing FileInfos
 
270
                    for (int i = 0; i < mfos.length; i++) {
 
271
                        FileInfo finf = info.get(mfos[i]);
 
272
 
 
273
                        if (finf != null)
 
274
                            finf.invalidate();
 
275
                    }
 
276
 
 
277
                    // clear the cache
 
278
                    info.clear ();
 
279
 
 
280
                    // [PENDING] this should be better synchronized
 
281
                    getLayers ();
 
282
                }
 
283
                
 
284
                for (int i = 0; i < mfos.length; i++)
 
285
                    fireFileStatusChanged (mfos [i]);
 
286
            }
 
287
        }
 
288
    }
 
289
 
 
290
    public static interface FileStatusListener {
 
291
        public void fileStatusChanged (FileObject mfo);
 
292
    }
 
293
    
 
294
    private class FileInfo extends FileChangeAdapter {
 
295
        private WeakReference<FileObject> file = null;
 
296
        
 
297
        private int state [] = new int [LAYERS_COUNT];
 
298
        private final Object LOCK = new Object ();
 
299
 
 
300
        private FileObject notifiers [] = new FileObject [LAYERS_COUNT];
 
301
        private FileChangeListener weakL [] = new FileChangeListener [LAYERS_COUNT];
 
302
        
 
303
        public FileInfo (FileObject mfo) {
 
304
            file = new WeakReference<FileObject> (mfo);
 
305
            
 
306
            // get initial state
 
307
            for (int i = 0; i < LAYERS_COUNT; i++) {
 
308
                state [i] = getStateImpl (mfo, i);
 
309
            }
 
310
            
 
311
            // attach FileInfo to interesting FileObject on each layer
 
312
            for (int i = 0; i < LAYERS_COUNT; i++) {
 
313
                attachNotifier (mfo, i);
 
314
            }
 
315
        }
 
316
 
 
317
        public void invalidate () {
 
318
            detachAllNotifiers ();
 
319
            synchronized (LOCK) {
 
320
                for (int i = 0; i < LAYERS_COUNT; i++)
 
321
                    state [i] = FSTATE_UNDEFINED;
 
322
            }
 
323
        }
 
324
 
 
325
        public int getState (int layer) {
 
326
            synchronized (LOCK) {
 
327
                return state [layer];
 
328
            }
 
329
        }
 
330
 
 
331
        private void rescan (FileObject mfo) {
 
332
            boolean changed = false;
 
333
            
 
334
            synchronized (LOCK) {
 
335
                for (int i = 0; i < LAYERS_COUNT; i++) {
 
336
                    int ns = getStateImpl (mfo, i);
 
337
                    if (state [i] != ns) {
 
338
                        state [i] = ns;
 
339
                        changed = true;
 
340
                    }
 
341
                }
 
342
            }
 
343
            
 
344
            if (changed)
 
345
                fireFileStatusChanged (mfo);
 
346
        }
 
347
 
 
348
        private int getStateImpl (FileObject mfo, int layer) {
 
349
            boolean above = false;
 
350
            boolean below = false;
 
351
 
 
352
            // scan higher layers
 
353
            for (int i = 0; i < layer; i++) {
 
354
                if (isOnLayer (mfo, i)) {
 
355
                    above = true;
 
356
                    break;
 
357
                }
 
358
            }
 
359
 
 
360
            // scan lower layers
 
361
            for (int i = layer + 1; i < LAYERS_COUNT; i++) {
 
362
                if (isOnLayer (mfo, i)) {
 
363
                    below = true;
 
364
                    break;
 
365
                }
 
366
            }
 
367
 
 
368
            if (isOnLayer (mfo, layer)) {
 
369
                return above ? FSTATE_IGNORED : FSTATE_DEFINED;
 
370
            }
 
371
            else {
 
372
                return below && !above ? FSTATE_INHERITED : FSTATE_UNDEFINED;
 
373
            }
 
374
        }
 
375
        
 
376
        private boolean isOnLayer (FileObject mfo, int layer) {
 
377
            FileSystem fsLayer = getLayer (layer);
 
378
            return fsLayer == null ? false : null != fsLayer.findResource (mfo.getPath());
 
379
        }
 
380
        
 
381
        /**
 
382
         * @param mfo FileObject from default file system
 
383
         * @param layer the layer where notifier will be searched on
 
384
         * @return true if attached notifier is the delegate FO
 
385
         */
 
386
        private synchronized boolean attachNotifier (FileObject mfo, int layer) {
 
387
            FileSystem fsLayer = getLayer (layer);
 
388
            String fn = mfo.getPath();
 
389
            FileObject fo = null;
 
390
            boolean isDelegate = true;
 
391
 
 
392
            if (fsLayer == null)
 
393
                return false;
 
394
 
 
395
            // find new notifier - the FileObject with closest match to getFile ()
 
396
            while (fn.length () > 0 && null == (fo = fsLayer.findResource (fn))) {
 
397
                int pos = fn.lastIndexOf ('/');
 
398
                isDelegate = false;
 
399
 
 
400
                if (-1 == pos)
 
401
                    break;
 
402
                
 
403
                fn = fn.substring (0, pos);
 
404
            }
 
405
            
 
406
            if (fo == null)
 
407
                fo = fsLayer.getRoot ();
 
408
 
 
409
            if (fo != notifiers [layer]) {
 
410
                // remove listener from existing notifier if any
 
411
                if (notifiers [layer] != null)
 
412
                    notifiers [layer].removeFileChangeListener (weakL [layer]);
 
413
 
 
414
                // create new listener and attach it to new notifier
 
415
                weakL [layer] = FileUtil.weakFileChangeListener (this, fo);
 
416
                fo.addFileChangeListener (weakL [layer]);
 
417
                notifiers [layer] = fo;
 
418
            }
 
419
            
 
420
            return isDelegate;
 
421
        }
 
422
 
 
423
        private synchronized void detachAllNotifiers () {
 
424
            for (int i = 0; i < LAYERS_COUNT; i++) {
 
425
                if (notifiers [i] != null) {
 
426
                    notifiers [i].removeFileChangeListener (weakL [i]);
 
427
                    notifiers [i] = null;
 
428
                    weakL [i] = null;
 
429
                }
 
430
            }
 
431
        }
 
432
        
 
433
        private int layerOfFile (FileObject fo) {
 
434
            try {
 
435
                FileSystem fs = fo.getFileSystem ();
 
436
                for (int i = 0; i < LAYERS_COUNT; i++) {
 
437
                    if (fs.equals (getLayer (i)))
 
438
                        return i;
 
439
                }
 
440
            } catch (FileStateInvalidException e) {
 
441
                throw (IllegalStateException) new IllegalStateException("Invalid file - " + fo).initCause(e); // NOI18N
 
442
            }
 
443
            return -1;
 
444
//            throw new IllegalStateException ("File isn't from any layer in DefaultFileSystem - " + fo); // NOI18N
 
445
        }
 
446
 
 
447
        // ---------------------- FileChangeListener events -----------------------------
 
448
 
 
449
        public void fileRenamed (FileRenameEvent fe) {
 
450
            // rename can be caused either by renaming fo or by deleting mfo,
 
451
            // thus the safe way is to discard this FileInfo from the map and
 
452
            // notify listeners about the change 
 
453
            FileObject mfo = file.get ();
 
454
            if (mfo != null && mfo.isValid ()) {
 
455
                discard (mfo);
 
456
                fireFileStatusChanged (mfo);
 
457
            }
 
458
            else
 
459
                detachAllNotifiers ();
 
460
        }
 
461
        
 
462
        public void fileDataCreated (FileEvent fe) {
 
463
            FileObject mfo = file.get ();
 
464
            if (mfo != null && mfo.isValid ()) {
 
465
                String created = fe.getFile ().getPath();
 
466
                String mfoname = mfo.getPath();
 
467
 
 
468
                if (created.equals (mfoname)) {
 
469
                    int layer;
 
470
                    if (-1 != (layer = layerOfFile (fe.getFile ())))
 
471
                        attachNotifier (mfo, layer);
 
472
 
 
473
                    rescan (mfo);
 
474
                }
 
475
            }
 
476
            else
 
477
                detachAllNotifiers ();
 
478
        }
 
479
        
 
480
        public void fileFolderCreated (FileEvent fe) {
 
481
            FileObject mfo = file.get ();
 
482
            if (mfo != null && mfo.isValid ()) {
 
483
                String created = fe.getFile ().getPath();
 
484
                String mfoname = mfo.getPath();
 
485
 
 
486
                if (mfoname.startsWith (created)) {
 
487
                    int layer;
 
488
                    if (-1 != (layer = layerOfFile (fe.getFile ())))
 
489
                        if (attachNotifier (mfo, layer)) {
 
490
                            // delegate was created -> rescan
 
491
                            rescan (mfo);
 
492
                        }
 
493
                }
 
494
            }
 
495
            else
 
496
                detachAllNotifiers ();
 
497
        }
 
498
        
 
499
        public void fileDeleted (FileEvent fe) {
 
500
            FileObject mfo = file.get ();
 
501
            if (mfo != null && mfo.isValid ()) {
 
502
                String deleted = fe.getFile ().getPath();
 
503
                String mfoname = mfo.getPath();
 
504
 
 
505
                if (deleted.equals (mfoname)) {
 
506
                    int layer;
 
507
                    if (-1 != (layer = layerOfFile (fe.getFile ())))
 
508
                        attachNotifier (mfo, layer);
 
509
 
 
510
                    rescan (mfo);
 
511
                }
 
512
            }
 
513
            else
 
514
                detachAllNotifiers ();
 
515
        }
 
516
    }
 
517
}