KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > filesystems > MultiFileObject


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.filesystems;
21
22 import java.io.Externalizable JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.InputStream JavaDoc;
25 import java.io.ObjectInput JavaDoc;
26 import java.io.ObjectOutput JavaDoc;
27 import java.io.OutputStream JavaDoc;
28 import java.lang.ref.Reference JavaDoc;
29 import java.lang.ref.WeakReference JavaDoc;
30 import java.util.Collections JavaDoc;
31 import java.util.Enumeration JavaDoc;
32 import java.util.HashMap JavaDoc;
33 import java.util.HashSet JavaDoc;
34 import java.util.Iterator JavaDoc;
35 import java.util.LinkedList JavaDoc;
36 import java.util.List JavaDoc;
37 import java.util.Map JavaDoc;
38 import java.util.Properties JavaDoc;
39 import java.util.Set JavaDoc;
40
41 /** Implementation of the file object for multi file system.
42 *
43 * @author Jaroslav Tulach,
44 */

45 final class MultiFileObject extends AbstractFolder implements FileObject.PriorityFileChangeListener {
46     /** generated Serialized Version UID */
47     static final long serialVersionUID = -2343651324897646809L;
48
49     /** default extension separator */
50     private static final char EXT_SEP = '.';
51
52     /** default path separator */
53     private static final char PATH_SEP = '/';
54     private static final FileSystem.AtomicAction markAtomicAction = new FileSystem.AtomicAction() {
55             public void run() {
56             }
57         };
58
59     static final ThreadLocal JavaDoc<FileObject> attrAskedFileObject = new ThreadLocal JavaDoc<FileObject>();
60
61     /** list of objects that we delegate to and that already
62     * has been created.
63     */

64     private Set JavaDoc delegates;
65
66     /** current delegate (the first object to delegate to), never null */
67     private FileObject leader;
68
69     /** Reference to lock or null */
70     private Reference JavaDoc<MfLock> lock;
71
72     /** listener */
73     private FileChangeListener weakL;
74
75     /**performance trick: holds delegetad FileObject used last time to getAttributes.
76      * It may looks simply, but all more sophisticated solutions was less efficient.
77      */

78     private FileObject lastAttrCacheFile;
79     private String JavaDoc lastAttrCacheName = ""; // NOI18N
80

81     /** Constructor. Takes reference to file system this file belongs to.
82     *
83     * @param fs the file system
84     * @param parent the parent object (folder)
85     * @param name name of the object (e.g. <code>filename.ext</code>)
86     */

87     public MultiFileObject(MultiFileSystem fs, MultiFileObject parent, String JavaDoc name) {
88         super(fs, parent, name);
89
90         weakL = org.openide.util.WeakListeners.create(
91                 FileObject.PriorityFileChangeListener.class, FileChangeListener.class, this, null
92             );
93
94         update();
95
96         if (leader == null) {
97             leader = new AbstractFileObject.Invalid(name);
98             validFlag = false;
99         }
100     }
101
102     /** Constructor for root.
103     *
104     * @param fs the file system
105     */

106     public MultiFileObject(MultiFileSystem fs) {
107         this(fs, null, ""); // NOI18N
108
}
109
110     /** File system.
111     */

112     public FileSystem getLeaderFileSystem() throws FileStateInvalidException {
113         return leader.getFileSystem();
114     }
115
116     /** Frees cached objects - added with perf.changes */
117     private void freeLastAttrCache() {
118         lastAttrCacheFile = null;
119         lastAttrCacheName = ""; // NOI18N
120
}
121
122     /** Updates list of all references.
123     */

124     private void update() {
125         MultiFileSystem mfs = getMultiFileSystem();
126         FileSystem[] arr = mfs.getDelegates();
127
128         Set JavaDoc now = (delegates == null) ? Collections.EMPTY_SET : delegates;
129         Set JavaDoc<FileObject> del = new HashSet JavaDoc<FileObject>(arr.length * 2);
130         FileObject led = null;
131
132         String JavaDoc name = getPath();
133
134         for (int i = 0; i < arr.length; i++) {
135             if (arr[i] != null) {
136                 FileObject fo = mfs.findResourceOn(arr[i], name);
137
138                 if (fo != null) {
139                     del.add(fo);
140
141                     if (!now.remove(fo)) {
142                         // now there yet
143
fo.addFileChangeListener(weakL);
144                     }
145
146                     if ((led == null) && fo.isValid()) {
147                         led = fo;
148                     }
149                 }
150             }
151         }
152
153         Iterator JavaDoc it = now.iterator();
154
155         while (it.hasNext()) {
156             FileObject fo = (FileObject) it.next();
157             fo.removeFileChangeListener(weakL);
158         }
159
160         if (led != null) {
161             // otherwise leave the leader to be last file that represented
162
// this one
163
if (!led.equals(this.leader) && (this.leader != null)) {
164                 // isValid prevents here from firing events after MFO.delete
165
if (isData() && isValid()) {
166                     fileChanged0(new FileEvent(this));
167                 }
168
169                 getMultiFileSystem().notifyMigration(this);
170             }
171
172             this.leader = led;
173         }
174
175         this.delegates = del;
176     }
177
178     /** Update all existing subobjects.
179     */

180     void updateAll() {
181         FileSystem mfs = getMultiFileSystem();
182
183         try {
184             mfs.beginAtomicAction();
185
186             // enumeration of all existing objects
187
Enumeration JavaDoc<AbstractFolder> en = existingSubFiles(true);
188
189             while (en.hasMoreElements()) {
190                 MultiFileObject mfo = (MultiFileObject) en.nextElement();
191
192                 if (mfo.isFolder() && !mfo.isInitialized()) {
193                     continue;
194                 }
195
196                 mfo.freeLastAttrCache();
197                 mfo.superRefresh(true);
198             }
199         } finally {
200             mfs.finishAtomicAction();
201         }
202     }
203
204     /** This method was added to achieve firing events that attributes
205      * were changed after setDelegates. Events are not fired reliable but this solution was
206      * choosed because of performance reasons. Attributes name is set to null - what means
207      * that one of attributes was probably changed.
208      */

209     void updateAllAfterSetDelegates(FileSystem[] oldFileSystems) {
210         try {
211             getMultiFileSystem().beginAtomicAction();
212
213             FileSystem[] fileSystems = getMultiFileSystem().getDelegates();
214             Enumeration JavaDoc<AbstractFolder> en = existingSubFiles(true);
215
216             while (en.hasMoreElements()) {
217                 MultiFileObject mfo = (MultiFileObject) en.nextElement();
218
219                 if (mfo.isFolder() && !mfo.isInitialized()) {
220                     continue;
221                 }
222
223                 if (mfo.hasListeners()) {
224                     String JavaDoc path = mfo.getPath();
225                     FileObject oldLeader = findLeader(oldFileSystems, path);
226                     FileObject newLeader = findLeader(fileSystems, path);
227
228                     if ((oldLeader != null) && (newLeader != null) && !oldLeader.equals(newLeader)) {
229                         mfo.fileAttributeChanged0(new FileAttributeEvent(mfo, null, null, null));
230                     }
231                 }
232
233                 mfo.freeLastAttrCache();
234                 mfo.refresh(true);
235             }
236         } finally {
237             getMultiFileSystem().finishAtomicAction();
238         }
239     }
240
241     private void refreshAfterEvent(FileEvent fe) {
242         FileObject fFile = fe.getFile();
243         superRefresh(false);
244
245         MultiFileObject mFile = (MultiFileObject) getFileObject(fFile.getName(), fFile.getExt());
246
247         if (mFile != null) {
248             mFile.superRefresh(false);
249         }
250     }
251
252     private void superRefresh(boolean expected) {
253         super.refresh(expected);
254     }
255
256     private FileObject findLeader(FileSystem[] fs, String JavaDoc path) {
257         MultiFileSystem mfs = getMultiFileSystem();
258
259         for (int i = 0; i < fs.length; i++) {
260             FileObject fo = mfs.findResourceOn(fs[i], path);
261
262             if (fo != null) {
263                 return fo;
264             }
265         }
266
267         return null;
268     }
269
270     /** Getter for the right file system */
271     private MultiFileSystem getMultiFileSystem() {
272         return (MultiFileSystem) getFileSystem();
273     }
274
275     /** Getter for one of children.
276     */

277     private MultiFileObject getMultiChild(String JavaDoc name) {
278         return (MultiFileObject) getChild(name);
279     }
280
281     /** Converts the file to be writable.
282     * The file has to be locked!
283     *
284     * @return file object (new leader) that is writable
285     * @exception IOException if the object cannot be writable
286     */

287     private FileObject writable() throws IOException JavaDoc {
288         MultiFileSystem fs = getMultiFileSystem();
289         FileSystem single = fs.createWritableOn(getPath());
290
291         if (single != leader.getFileSystem()) {
292             // if writing to a file that is not on writable fs =>
293
// copy it
294
if (leader.isFolder()) {
295                 leader = FileUtil.createFolder(root(single), getPath());
296             } else {
297                 FileObject folder = FileUtil.createFolder(root(single), getParent().getPath());
298                 leader = leader.copy(folder, leader.getName(), leader.getExt());
299             }
300
301             MfLock l = ((lock == null) ? null : lock.get());
302
303             if (l != null) {
304                 // update the lock
305
l.addLock(leader);
306             }
307         }
308
309         return leader;
310     }
311
312     /** All objects that are beyond this one.
313     * @return enumeration of FileObject
314     */

315     private Enumeration JavaDoc<FileObject> delegates() {
316         return getMultiFileSystem().delegates(getPath());
317     }
318
319     /** Method that goes upon list of folders and updates its locks. This is used when
320     * an object is masked which may lead to creation of folders on a disk.
321     *
322     * @param fo folder to check
323     * @exception IOException if something locks cannot be updated
324     */

325     private static void updateFoldersLock(FileObject fo)
326     throws IOException JavaDoc {
327         while (fo != null) {
328             MultiFileObject mfo = (MultiFileObject) fo;
329
330             MfLock l = (mfo.lock == null) ? null : mfo.lock.get();
331
332             if (l != null) {
333                 // the file has been locked => update the lock
334
mfo.writable();
335             }
336
337             fo = fo.getParent();
338         }
339     }
340
341     //
342
// List
343
//
344

345     /** Method that allows subclasses to return its children.
346     *
347     * @return names (name . ext) of subfiles
348     */

349     protected final String JavaDoc[] list() {
350         Properties JavaDoc exclude = new Properties JavaDoc();
351         List JavaDoc<String JavaDoc> addList = new LinkedList JavaDoc<String JavaDoc>();
352         Set JavaDoc<String JavaDoc> addSet = new HashSet JavaDoc<String JavaDoc>(101);
353
354         Enumeration JavaDoc<FileObject> it = delegates();
355
356         while (it.hasMoreElements()) { // cycle 1
357

358             FileObject folder = it.nextElement();
359
360             if ((folder == null) || !folder.isFolder()) {
361                 continue;
362             }
363
364             FileObject[] arr = folder.getChildren();
365             Properties JavaDoc local = null;
366
367             for (int i = 0; i < arr.length; i++) {
368                 String JavaDoc name = arr[i].getNameExt();
369
370                 if (name.endsWith(MultiFileSystem.MASK)) {
371                     String JavaDoc basename = name.substring(0, name.length() - MultiFileSystem.MASK.length());
372
373                     // this name should be excluded from next rounds of cycle 1
374
if (local == null) {
375                         local = new Properties JavaDoc(exclude);
376                     }
377
378                     local.setProperty(basename, basename);
379
380                     if (!getMultiFileSystem().getPropagateMasks()) {
381                         // By default, unused mask files are not displayed as file children.
382
// When propagate masks is turned on, though, they should be.
383
// This is useful e.g. when using MFSs as delegates for other MFSs.
384
continue;
385                     }
386                 }
387
388                 // lets add this name, if not added yet and is not excluded by
389
// previous level
390
if (!addSet.contains(name) && (exclude.getProperty(name) == null)) {
391                     addSet.add(name);
392                     addList.add(name);
393                 }
394             }
395
396             // change the excludes to the new ones produced at this round
397
if (local != null) {
398                 exclude = local;
399             }
400         }
401
402         if (getMultiFileSystem().getPropagateMasks()) {
403             // remove all masked files from the array, even if they were
404
// masked on the same level as they were added
405
addList.removeAll(exclude.keySet());
406         }
407
408         String JavaDoc[] res = addList.toArray(new String JavaDoc[addList.size()]);
409
410         return res;
411     }
412
413     /** When refreshing, also update the state of delegates.
414     */

415     /** [PENDING] expected rename of some refresh method */
416
417     /*protected void internalRefresh (
418         String add, String remove, boolean fire, boolean expected, String[] list
419      **/

420     protected void refresh(String JavaDoc add, String JavaDoc remove, boolean fire, boolean expected) {
421         try {
422             getFileSystem().beginAtomicAction();
423
424             synchronized (this) {
425                 update();
426
427                 /** [PENDING] expected rename of some refresh method */
428
429                 //super.internalRefresh (add, remove, fire, expected, list);
430
super.refresh(add, remove, fire, expected);
431             }
432
433             validFlag &= leader.isValid();
434         } finally {
435             getFileSystem().finishAtomicAction();
436         }
437     }
438
439     /** Method to create a file object for given subfile.
440     * @param name of the subfile
441     * @return the file object
442     */

443     protected final AbstractFolder createFile(String JavaDoc name) {
444         return new MultiFileObject(getMultiFileSystem(), this, name);
445     }
446
447     //
448
// Info
449
//
450

451     /* Test whether this object is a folder.
452     * @return true if the file object is a folder (i.e., can have children)
453     */

454     public boolean isFolder() {
455         return (parent == null) || leader.isFolder();
456     }
457
458     /*
459     * Get last modification time.
460     * @return the date
461     */

462     public java.util.Date JavaDoc lastModified() {
463         return leader.lastModified();
464     }
465
466     /* Test whether this object is a data object.
467     * This is exclusive with {@link #isFolder}.
468     * @return true if the file object represents data (i.e., can be read and written)
469     */

470     public boolean isData() {
471         return leader.isData();
472     }
473
474     /* Test whether this FileObject is Virtual.
475     * @return true if the file object represents a Virtual File
476     */

477     public boolean isVirtual() {
478         return leader.isVirtual();
479     }
480
481     @Deprecated JavaDoc // have to override for compat
482
public boolean isReadOnly() {
483         MultiFileSystem fs = getMultiFileSystem();
484
485         if (fs.isReadOnly()) {
486             return true;
487         }
488
489         if (leader.isReadOnly()) {
490             // if we can make it writable then nothing
491
try {
492                 FileSystem simple = fs.createWritableOn(getPath());
493
494                 return simple == leader.getFileSystem();
495             } catch (IOException JavaDoc e) {
496                 return true;
497             }
498         }
499
500         return false;
501     }
502
503     public boolean canWrite() {
504         MultiFileSystem fs = getMultiFileSystem();
505
506         if (fs.isReadOnly()) {
507             return false;
508         }
509
510         if (!leader.canWrite()) {
511             // if we can make it writable then nothing
512
try {
513                 FileSystem simple = fs.createWritableOn(getPath());
514
515                 return simple != leader.getFileSystem();
516             } catch (IOException JavaDoc e) {
517                 return false;
518             }
519         }
520
521         return true;
522     }
523
524     /* Get the MIME type of this file.
525     * The MIME type identifies the type of the file's contents and should be used in the same way as in the <B>Java
526     * Activation Framework</B> or in the {@link java.awt.datatransfer} package.
527     * <P>
528     * The default implementation calls {@link FileUtil#getMIMEType}.
529     *
530     * @return the MIME type textual representation, e.g. <code>"text/plain"</code>
531     */

532     public String JavaDoc getMIMEType() {
533         return leader.getMIMEType();
534     }
535
536     /* Get the size of the file.
537     * @return the size of the file in bytes or zero if the file does not contain data (does not
538     * exist or is a folder).
539     */

540     public long getSize() {
541         return leader.getSize();
542     }
543
544     /** Get input stream.
545     * @return an input stream to read the contents of this file
546     * @exception FileNotFoundException if the file does not exists, is a folder
547     * rather than a regular file or is invalid
548     */

549     public InputStream JavaDoc getInputStream() throws java.io.FileNotFoundException JavaDoc {
550         return leader.getInputStream();
551     }
552
553     /* Get output stream.
554     * @param lock the lock that belongs to this file (obtained by a call to
555     * {@link #lock})
556     * @return output stream to overwrite the contents of this file
557     * @exception IOException if an error occures (the file is invalid, etc.)
558     */

559     public OutputStream JavaDoc getOutputStream(FileLock lock)
560     throws java.io.IOException JavaDoc {
561         MfLock l;
562         FileLock lWritable;
563         FileObject fo;
564
565         try {
566             getFileSystem().beginAtomicAction(markAtomicAction);
567
568             synchronized (this) {
569                 l = testLock(lock);
570
571                 // this can also change lock in l.lock
572
fo = writable();
573                 lWritable = l.findLock(fo);
574             }
575
576             return fo.getOutputStream(lWritable);
577         } finally {
578             getFileSystem().finishAtomicAction();
579         }
580     }
581
582     /* Lock this file.
583     * @return lock that can be used to perform various modifications on the file
584     * @throws FileAlreadyLockedException if the file is already locked
585     */

586     public synchronized FileLock lock() throws IOException JavaDoc {
587         if (lock != null) {
588             FileLock f = (FileLock) lock.get();
589
590             if (f != null) {
591                 // [PENDING] construct localized message
592
throw new FileAlreadyLockedException(getPath());
593             }
594         }
595
596         java.util.Set JavaDoc set = getMultiFileSystem().createLocksOn(getPath());
597         MfLock l = new MfLock(leader, delegates(), set);
598
599         lock = new WeakReference JavaDoc<MfLock>(l);
600
601         // Thread.dumpStack ();
602
// System.out.println ("Locking file: " + this); // NOI18N
603
return l;
604     }
605
606     /** Tests the lock if it is valid, if not throws exception.
607     * @param l lock to test
608     * @return the mf lock for this file object
609     */

610     private MfLock testLock(FileLock l) throws java.io.IOException JavaDoc {
611         if (lock == null) {
612             FSException.io("EXC_InvalidLock", l, getPath(), getMultiFileSystem().getDisplayName(), lock); // NOI18N
613
}
614
615         if (lock.get() != l) {
616             FSException.io("EXC_InvalidLock", l, getPath(), getMultiFileSystem().getDisplayName(), lock.get()); // NOI18N
617
}
618
619         return (MfLock) l;
620     }
621
622     @Deprecated JavaDoc // have to override for compat
623
public void setImportant(boolean b) {
624         Enumeration JavaDoc<FileObject> en = delegates();
625
626         while (en.hasMoreElements()) {
627             FileObject fo = en.nextElement();
628             fo.setImportant(b);
629         }
630
631         if (!b) {
632             getMultiFileSystem().markUnimportant(this);
633         }
634     }
635
636     /** Add one void-wrapping to an object. */
637     private static final Object JavaDoc voidify(Object JavaDoc o) {
638         if (o == null) {
639             return new VoidValue(0);
640         } else if (o instanceof VoidValue) {
641             VoidValue vv = (VoidValue) o;
642
643             return new VoidValue(vv.level + 1);
644         } else {
645             return o;
646         }
647     }
648
649     /** Strip off one void-wrapping from an object. */
650     private static final Object JavaDoc devoidify(Object JavaDoc o) {
651         if (o instanceof VoidValue) {
652             VoidValue vv = (VoidValue) o;
653
654             if (vv.level == 0) {
655                 return null;
656             } else {
657                 return new VoidValue(vv.level - 1);
658             }
659         } else {
660             return o;
661         }
662     }
663
664     /* Get the file attribute with the specified name.
665     * @param attrName name of the attribute
666     * @return appropriate (serializable) value or <CODE>null</CODE> if the attribute is unset (or could not be properly restored for some reason)
667     */

668     public Object JavaDoc getAttribute(String JavaDoc attrName) {
669         // Performance optimization (avoid calling getPath() too many times):
670
return getAttribute(attrName, getPath());
671     }
672
673     private final Object JavaDoc getAttribute(String JavaDoc attrName, String JavaDoc path) {
674         // Look for attribute in any file system starting at the front.
675
// Additionally, look for attribute in root folder, where
676
// the relative path from the folder to the target file is
677
// prepended to the attribute name, all separated with slashes.
678
// This search scheme permits writable front systems to set file
679
// attributes on deeply buried files in back systems without
680
// actually creating copies of the files or even their parent folders.
681
// [PENDING] consider the effects of mask files
682
String JavaDoc prefixattr = ((path.length() == 0) ? null : (path.replace('/', '\\') + '\\' + attrName));
683
684         { /*There was proved that this block enhances performance*/
685
686             Object JavaDoc oPerf;
687             FileObject localFo = lastAttrCacheFile;
688             String JavaDoc cachedAttrName = lastAttrCacheName;
689
690             if ((localFo != null) && !localFo.equals(this) && cachedAttrName.equals(attrName)) {
691                 if (localFo.isRoot() && (prefixattr != null)) {
692                     try {
693                         FileSystem foFs = localFo.getFileSystem();
694
695                         if (!(foFs instanceof XMLFileSystem)) {
696                             localFo = foFs.getRoot();
697                             oPerf = getAttribute(localFo, prefixattr, ""); // NOI18N
698

699                             if (oPerf != null) {
700                                 return devoidify(oPerf);
701                             }
702                         }
703                     } catch (FileStateInvalidException fiex) {
704                         //then continue
705
}
706                 }
707
708                 /** There is no chance to cache localFo.getPath(), because every
709                  * rename up to it in hierarchy makes this cache invalid.
710                  */

711                 oPerf = getAttribute(localFo, attrName, localFo.getPath());
712
713                 if (oPerf != null) {
714                     return devoidify(oPerf);
715                 }
716             }
717         }
718
719         FileSystem[] systems = getMultiFileSystem().getDelegates();
720         FileSystem leaderfs;
721
722         try {
723             leaderfs = getLeaderFileSystem();
724         } catch (FileStateInvalidException fsie) {
725             // Whatever.
726
leaderfs = null;
727         }
728
729         // boolean isLoaderAttr = /* DataObject.EA_ASSIGNED_LOADER */ "NetBeansAttrAssignedLoader".equals (attrName); // NOI18N
730
for (int i = 0; i < systems.length; i++) {
731             if (systems[i] == null) {
732                 continue;
733             }
734
735             // Don't check for any assigned loader overrides except for leader & writable systems.
736
// Note that this prevents front layers from overriding default loader!
737
// Could be revisited but in the meantime this is a performance optimization.
738
// if (isLoaderAttr && leaderfs != null && systems[i] != leaderfs && systems[i].isReadOnly ()) {
739
// continue;
740
// }
741
// The normal check:
742
FileObject fo = getMultiFileSystem().findResourceOn(systems[i], path);
743
744             if (fo != null) {
745                 Object JavaDoc o = getAttribute(fo, attrName, fo.getPath()); // Performance tricks:
746

747                 if (o != null) {
748                     return devoidify(o);
749                 }
750             }
751
752             // Don't check for root override on XMLFileSystem's; the override
753
// could only have been made on a writable filesystem to begin with.
754
// Could skip all RO FSs but then multi-user installs would not work
755
// quite right.
756
if ((prefixattr != null) && !(systems[i] instanceof XMLFileSystem)) {
757                 fo = systems[i].getRoot();
758
759                 Object JavaDoc o = getAttribute(fo, prefixattr, ""); // NOI18N
760

761                 if (o != null) {
762                     return devoidify(o);
763                 }
764             }
765         }
766
767         return null;
768     }
769
770     private Object JavaDoc getAttribute(FileObject fo, String JavaDoc attrName, String JavaDoc path) {
771         Object JavaDoc o;
772
773         FileObject topFO = attrAskedFileObject.get();
774
775         if (topFO == null) {
776             attrAskedFileObject.set(this);
777         }
778
779         try {
780             if (fo instanceof MultiFileObject) {
781                 o = ((MultiFileObject) fo).getAttribute(attrName, path);
782             } else if (fo instanceof AbstractFileObject) {
783                 o = ((AbstractFileObject) fo).getAttribute(attrName, path);
784             } else {
785                 o = fo.getAttribute(attrName);
786             }
787         } finally {
788             if (topFO == null) {
789                 attrAskedFileObject.set(null);
790             }
791         }
792
793         if (o != null) {
794             lastAttrCacheFile = fo;
795             lastAttrCacheName = attrName;
796         }
797
798         return o;
799     }
800
801     /* Set the file attribute with the specified name.
802     * @param attrName name of the attribute
803     * @param value new value or <code>null</code> to clear the attribute. Must be serializable, although particular file systems may or may not use serialization to store attribute values.
804     * @exception IOException if the attribute cannot be set. If serialization is used to store it, this may in fact be a subclass such as {@link NotSerializableException}.
805     */

806     public void setAttribute(String JavaDoc attrName, Object JavaDoc value)
807     throws IOException JavaDoc {
808         setAttribute(attrName, value, true); //NOI18N
809
}
810
811     /* helper method for MFO.setAttribute. MFO can disable firing from underlaying
812      * layers. Should be reviewed in 3.4 or later
813       *@see MultiFileObject#setAttribute*/

814     void setAttribute(String JavaDoc attrName, Object JavaDoc value, boolean fire)
815     throws IOException JavaDoc {
816         // Similar to getAttribute. Here we use createWritableOn to decide which fs
817
// the attribute should be stored on; it is stored on the actual file if
818
// possible, if not then the lowest containing folder.
819
String JavaDoc path = getPath();
820         FileSystem fs = getMultiFileSystem().createWritableOn(path);
821         FileObject fo = getMultiFileSystem().findResourceOn(fs, path);
822         Object JavaDoc oldValue = null;
823         String JavaDoc attrToSet = attrName;
824
825         if (fire) {
826             oldValue = getAttribute(attrName);
827         }
828
829         if (fo == null) {
830             fo = fs.getRoot();
831             attrToSet = path.replace('/', '\\') + '\\' + attrName;
832         }
833
834         lastAttrCacheFile = fo;
835         lastAttrCacheName = attrToSet;
836
837         if (fo instanceof AbstractFolder) {
838             ((AbstractFolder) fo).setAttribute(attrToSet, voidify(value), false);
839         } else {
840             /** cannot disable firing from underlaying delegate if not AbstractFolder
841              * without API change. So I don`t fire events from this method with one
842              * exception - root.
843              */

844             fire = fire && fo.isRoot();
845             fo.setAttribute(attrToSet, voidify(value));
846         }
847
848         // fire changes for original attribute name even if the attr is actually
849
// stored in the root FO if FO instanceof AbstractFolder (that has supressed firing).
850

851         /* [PENDING] only this MultiFileObject should fire event and it`s delegate
852          * should not fire event (nevertheless this delegate is physically used
853          * to write given attribute - only implementation detail -
854          * nobody should rely on current implementation).
855          **/

856         if (fire && (oldValue != value) && hasAtLeastOneListeners()) {
857             fileAttributeChanged0(new FileAttributeEvent(this, attrName, oldValue, value));
858         }
859     }
860
861     /* Get all file attribute names for this file.
862     * @return enumeration of keys (as strings)
863     */

864     public Enumeration JavaDoc<String JavaDoc> getAttributes() {
865         return getAttributes(getPath());
866     }
867
868     private final Enumeration JavaDoc<String JavaDoc> getAttributes(String JavaDoc path) {
869         Set JavaDoc<String JavaDoc> s = new HashSet JavaDoc<String JavaDoc>();
870         FileSystem[] systems = getMultiFileSystem().getDelegates();
871
872         // [PENDING] will not remove from the enumeration voided-out attributes
873
// (though this is probably not actually necessary)
874
String JavaDoc prefix = (path.length() == 0) ? null : (path.replace('/', '\\') + '\\');
875
876         for (int i = 0; i < systems.length; i++) {
877             if (systems[i] == null) {
878                 continue;
879             }
880
881             FileObject fo = getMultiFileSystem().findResourceOn(systems[i], path);
882
883             if (fo != null) {
884                 Enumeration JavaDoc<String JavaDoc> e = fo.getAttributes();
885
886                 while (e.hasMoreElements()) {
887                     String JavaDoc attr = e.nextElement();
888                     s.add(attr);
889                 }
890             }
891
892             if (prefix != null) {
893                 fo = systems[i].getRoot();
894
895                 Enumeration JavaDoc<String JavaDoc> e;
896
897                 if (fo instanceof MultiFileObject) {
898                     e = ((MultiFileObject) fo).getAttributes(""); // NOI18N
899
} else if (fo instanceof AbstractFileObject) {
900                     e = ((AbstractFileObject) fo).getAttributes(""); // NOI18N
901
} else {
902                     e = fo.getAttributes();
903                 }
904
905                 while (e.hasMoreElements()) {
906                     String JavaDoc attr = e.nextElement();
907
908                     if (attr.startsWith(prefix) && (attr.substring(prefix.length()).indexOf('\\') == -1)) {
909                         s.add(attr.substring(prefix.length()));
910                     }
911                 }
912             }
913         }
914
915         return Collections.enumeration(s);
916     }
917
918     /* Create a new folder below this one with the specified name. Fires
919     * <code>fileCreated</code> event.
920     *
921     * @param name the name of folder to create (without extension)
922     * @return the new folder
923     * @exception IOException if the folder cannot be created (e.g. already exists)
924     */

925     public FileObject createFolder(String JavaDoc name) throws IOException JavaDoc {
926         MultiFileObject fo;
927
928         try {
929             getFileSystem().beginAtomicAction();
930
931             synchronized (this) {
932                 MultiFileSystem fs = getMultiFileSystem();
933
934                 if (fs.isReadOnly()) {
935                     FSException.io("EXC_FSisRO", fs.getDisplayName()); // NOI18N
936
}
937
938                 if (isReadOnly()) {
939                     FSException.io("EXC_FisRO", name, fs.getDisplayName()); // NOI18N
940
}
941
942                 String JavaDoc fullName = getPath() + PATH_SEP + name;
943
944                 if (!isFolder()) {
945                     FSException.io("EXC_FoNotFolder", name, getPath(), fs.getDisplayName()); // NOI18N
946
}
947
948                 if (this.getFileObject(name) != null) {
949                     FSException.io("EXC_FolderAlreadyExist", name, fs.getDisplayName()); // NOI18N
950
}
951
952                 FileSystem simple = fs.createWritableOn(fullName);
953
954                 // create
955
FileUtil.createFolder(root(simple), fullName);
956
957                 // try to unmask if necessary
958
getMultiFileSystem().unmaskFileOnAll(simple, fullName);
959
960                 /** [PENDING] expected rename of some refresh method */
961                 //internalRefresh (name, null, true, false,null);
962
refresh(name, null, true, false);
963
964                 fo = getMultiChild(name);
965
966                 if (fo == null) {
967                     // system error
968
throw new FileStateInvalidException(
969                         FileSystem.getString("EXC_ApplicationCreateError", getPath(), name)
970                     );
971                 }
972
973                 FileObject[] chlds = fo.getChildren();
974
975                 for (int i = 0; i < chlds.length; i++) {
976                     getMultiFileSystem().maskFile(simple, chlds[i].getPath());
977                 }
978
979                 if (hasListeners()) {
980                     fileCreated0(new FileEvent(this, fo), false);
981                 }
982             }
983         } finally {
984             getFileSystem().finishAtomicAction();
985         }
986
987         return fo;
988     }
989
990     /* Create new data file in this folder with the specified name. Fires
991     * <code>fileCreated</code> event.
992     *
993     * @param name the name of data object to create (should not contain a period)
994     * @param ext the extension of the file (or <code>null</code> or <code>""</code>)
995     *
996     * @return the new data file object
997     * @exception IOException if the file cannot be created (e.g. already exists)
998     */

999     public FileObject createData(String JavaDoc name, String JavaDoc ext)
1000    throws IOException JavaDoc {
1001        MultiFileObject fo;
1002
1003        try {
1004            getFileSystem().beginAtomicAction();
1005
1006            synchronized (this) {
1007                MultiFileSystem fs = getMultiFileSystem();
1008
1009                if (fs.isReadOnly()) {
1010                    FSException.io("EXC_FSisRO", fs.getDisplayName()); // NOI18N
1011
}
1012
1013                if (isReadOnly()) {
1014                    FSException.io("EXC_FisRO", name, fs.getDisplayName()); // NOI18N
1015
}
1016
1017                String JavaDoc n = "".equals(ext) ? name : (name + EXT_SEP + ext); // NOI18N
1018

1019                if (!isFolder()) {
1020                    FSException.io("EXC_FoNotFolder", n, getPath(), fs.getDisplayName()); // NOI18N
1021
}
1022
1023                if (this.getFileObject(name, ext) != null) {
1024                    FSException.io("EXC_DataAlreadyExist", n, fs.getDisplayName()); // NOI18N
1025
}
1026
1027                String JavaDoc fullName = getPath() + PATH_SEP + n;
1028
1029                FileSystem simple = fs.createWritableOn(fullName);
1030
1031                // create
1032
FileUtil.createData(root(simple), fullName);
1033
1034                // try to unmask if necessary
1035
getMultiFileSystem().unmaskFileOnAll(simple, fullName);
1036
1037                /** [PENDING] expected rename of some refresh method */
1038                //internalRefresh (n, null, true, false,null);
1039
refresh(n, null, true, false);
1040
1041                fo = getMultiChild(n);
1042
1043                if (fo == null) {
1044                    // system error
1045
throw new FileStateInvalidException(
1046                        FileSystem.getString("EXC_ApplicationCreateError", getPath(), n)
1047                    );
1048                }
1049
1050                if (hasListeners()) {
1051                    fileCreated0(new FileEvent(this, fo), true);
1052                }
1053            }
1054        } finally {
1055            getFileSystem().finishAtomicAction();
1056        }
1057
1058        return fo;
1059    }
1060
1061    /* Renames this file (or folder).
1062    * Both the new basename and new extension should be specified.
1063    * <p>
1064    * Note that using this call, it is currently only possible to rename <em>within</em>
1065    * a parent folder, and not to do moves <em>across</em> folders.
1066    * Conversely, implementing file systems need only implement "simple" renames.
1067    * If you wish to move a file across folders, you should call {@link FileUtil#moveFile}.
1068    * @param lock File must be locked before renaming.
1069    * @param name new basename of file
1070    * @param ext new extension of file (ignored for folders)
1071    */

1072    public void rename(FileLock lock, String JavaDoc name, String JavaDoc ext)
1073    throws IOException JavaDoc {
1074        MultiFileSystem fs = getMultiFileSystem();
1075
1076        if (parent == null) {
1077            FSException.io("EXC_CannotRenameRoot", fs.getDisplayName()); // NOI18N
1078
}
1079
1080        try {
1081            getFileSystem().beginAtomicAction();
1082
1083            synchronized (parent) {
1084                // synchronize on your folder
1085
MfLock l = testLock(lock);
1086
1087                String JavaDoc newFullName = parent.getPath() + PATH_SEP + name;
1088
1089                if (isData()) {
1090                    newFullName += (EXT_SEP + ext);
1091                }
1092
1093                String JavaDoc oldFullName = getPath();
1094
1095                if (isReadOnly()) {
1096                    FSException.io("EXC_CannotRename", getPath(), getMultiFileSystem().getDisplayName(), newFullName); // NOI18N
1097
}
1098
1099                if (getFileSystem().isReadOnly()) {
1100                    FSException.io("EXC_FSisRO", getMultiFileSystem().getDisplayName()); // NOI18N
1101
}
1102
1103                String JavaDoc on = getName();
1104                String JavaDoc oe = getExt();
1105
1106                //!!! getMultiFileSystem ().change.rename (oldFullName, newFullName);
1107
FileSystem single = fs.createWritableOnForRename(oldFullName, newFullName);
1108
1109                if (single == leader.getFileSystem()) {
1110                    // delete the file if we can on the selected
1111
// system
1112
leader.rename(l.findLock(leader), name, ext);
1113                    getMultiFileSystem().unmaskFileOnAll(single, newFullName);
1114                    copyContent(this, leader);
1115                } else {
1116                    // rename file that is on different file system
1117
// means to copy it
1118
FileObject previousLeader = leader;
1119
1120                    if (isData()) {
1121                        // data
1122
FileObject folder = FileUtil.createFolder(root(single), getParent().getPath());
1123                        leader = leader.copy(folder, name, ext);
1124                        copyAttrs(this, leader);
1125                    } else {
1126                        // folder
1127
FileObject fo = FileUtil.createFolder(root(single), newFullName);
1128                        copyContent(this, fo);
1129
1130                        leader = fo;
1131                        this.name = name; // must be done before update
1132
update();
1133                    }
1134
1135                    // releases lock for previousLeader and aquiares
1136
// new for leader
1137
l.changeLocks(previousLeader, leader);
1138                }
1139
1140                if (getMultiFileSystem().delegates(oldFullName).hasMoreElements()) {
1141                    // if there is older version of the file
1142
// then we have to mask it
1143
getMultiFileSystem().maskFile(single, oldFullName);
1144                    updateFoldersLock(getParent());
1145                }
1146
1147                if (isData()) {
1148                    name = name + EXT_SEP + ext;
1149                }
1150
1151                String JavaDoc oldName = this.name;
1152                this.name = name;
1153
1154                /*
1155                      System.out.println ("Resulting file is: " + getPath ());
1156                      System.out.println ("Bedw file is: " + newFullName);
1157                      System.out.println ("Name: " + name);
1158                      System.out.println ("Old : " + oldName);
1159                */

1160
1161                /** [PENDING] expected to delete*/
1162                parent.refresh(name, oldName);
1163
1164                //!!! getMultiFileSystem ().attr.renameAttributes (oldFullName, newFullName);
1165
if (hasAtLeastOneListeners()) {
1166                    fileRenamed0(new FileRenameEvent(this, on, oe));
1167                }
1168            }
1169        } finally {
1170            getFileSystem().finishAtomicAction();
1171        }
1172    }
1173
1174    /* Delete this file. If the file is a folder and it is not empty then
1175    * all of its contents are also recursively deleted.
1176    *
1177    * @param lock the lock obtained by a call to {@link #lock}
1178    * @exception IOException if the file could not be deleted
1179    */

1180    void handleDelete(FileLock lock) throws IOException JavaDoc {
1181        if (parent == null) {
1182            FSException.io("EXC_CannotDeleteRoot", getMultiFileSystem().getDisplayName() // NOI18N
1183
);
1184        }
1185
1186        MultiFileSystem fs = getMultiFileSystem();
1187
1188        try {
1189            getFileSystem().beginAtomicAction();
1190
1191            synchronized (parent) {
1192                String JavaDoc fullName = getPath();
1193                FileSystem single = fs.createWritableOn(fullName);
1194
1195                if (needsMask(lock, true)) {
1196                    getMultiFileSystem().maskFile(single, fullName);
1197                    updateFoldersLock(getParent());
1198                }
1199
1200                String JavaDoc n = name;
1201                validFlag = false;
1202
1203                /** [PENDING] expected rename of some refresh method */
1204                //parent.internalRefresh (null, n, true, false, null);
1205
parent.refresh(null, n, true, false);
1206
1207                if (hasAtLeastOneListeners()) {
1208                    fileDeleted0(new FileEvent(this));
1209                }
1210            }
1211        } finally {
1212            getFileSystem().finishAtomicAction();
1213        }
1214    }
1215
1216    //
1217
// Transfer
1218
//
1219

1220    /** Copies this file. This allows the filesystem to perform any additional
1221    * operation associated with the copy. But the default implementation is simple
1222    * copy of the file and its attributes
1223    *
1224    * @param target target folder to move this file to
1225    * @param name new basename of file
1226    * @param ext new extension of file (ignored for folders)
1227    * @return the newly created file object representing the moved file
1228    */

1229    public FileObject copy(FileObject target, String JavaDoc name, String JavaDoc ext)
1230    throws IOException JavaDoc {
1231        return leader.copy(target, name, ext);
1232    }
1233
1234    /** Moves this file. This allows the filesystem to perform any additional
1235    * operation associated with the move. But the default implementation is encapsulated
1236    * as copy and delete.
1237    *
1238    * @param lock File must be locked before renaming.
1239    * @param target target folder to move this file to
1240    * @param name new basename of file
1241    * @param ext new extension of file (ignored for folders)
1242    * @return the newly created file object representing the moved file
1243    */

1244    public FileObject move(FileLock lock, FileObject target, String JavaDoc name, String JavaDoc ext)
1245    throws IOException JavaDoc {
1246        MultiFileSystem fs = getMultiFileSystem();
1247
1248        try {
1249            fs.beginAtomicAction();
1250
1251            if (parent == null) {
1252                FSException.io("EXC_CannotDeleteRoot", fs.getDisplayName() // NOI18N
1253
);
1254            }
1255
1256            MfLock lck = testLock(lock);
1257            FileLock l = lck.findLock(leader);
1258
1259            FileSystem simple = fs.createWritableOn(getPath());
1260
1261            if (fs.isReadOnly()) {
1262                FSException.io("EXC_FSisRO", fs.getDisplayName()); // NOI18N
1263
}
1264
1265            if ((l == null) && (leader.getFileSystem() != simple)) {
1266                leader = writable();
1267                l = lck.findLock(leader);
1268            }
1269
1270            if (needsMask(lock, false)) {
1271                getMultiFileSystem().maskFile(simple, getPath());
1272                updateFoldersLock(getParent());
1273            }
1274
1275            return leader.move(l, target, name, ext);
1276        } finally {
1277            fs.finishAtomicAction();
1278        }
1279    }
1280
1281    /* Refresh the contents of a folder. Rescans the list of children names.
1282    */

1283    public final void refresh(boolean expected) {
1284        if (!isInitialized() && isFolder()) {
1285            return;
1286        }
1287
1288        Enumeration JavaDoc<FileObject> en = delegates();
1289
1290        while (en.hasMoreElements()) {
1291            FileObject fo = en.nextElement();
1292            fo.refresh(expected);
1293        }
1294
1295        super.refresh(expected);
1296    }
1297
1298    //
1299
// Listeners
1300
//
1301

1302    /** Fired when a new folder is created. This action can only be
1303     * listened to in folders containing the created folder up to the root of
1304     * file system.
1305     *
1306     * @param fe the event describing context where action has taken place
1307     */

1308    public void fileFolderCreated(FileEvent fe) {
1309        /*One of underlaing layers notifies that new folder was created.
1310         And this folder may have any other childern anywhere deep in hierarchy
1311         and then must be updated and refreshed deep down*/

1312        updateAll();
1313    }
1314
1315    /** Fired when a new file is created. This action can only be
1316     * listened in folders containing the created file up to the root of
1317     * file system.
1318     *
1319     * @param fe the event describing context where action has taken place
1320     */

1321    public void fileDataCreated(FileEvent fe) {
1322        refreshAfterEvent(fe);
1323    }
1324
1325    /** Fired when a file is changed.
1326     * @param fe the event describing context where action has taken place
1327     */

1328    public void fileChanged(FileEvent fe) {
1329        FileObject changedFile = this;
1330
1331        if (fe.getSource().equals(leader) && hasAtLeastOneListeners() && !fe.firedFrom(markAtomicAction)) {
1332            /**There should not dissapear information about source and changed file*/
1333            if (!fe.getFile().equals(fe.getSource())) {
1334                changedFile = getFileObject(fe.getFile().getName(), fe.getFile().getExt());
1335            }
1336
1337            /**fileChanged1 - should not fire event for this.getParent ().
1338             * I think that already bottom layer forked event.*/

1339            /** [PENDING] fix of #16926, #16895. But there should be investigated
1340             * why this MFO doesn`t know about child ?*/

1341            if (changedFile != null) {
1342                fileChanged1(new FileEvent(this, changedFile, fe.getTime()));
1343            }
1344        }
1345    }
1346
1347    /** Fired when a file is deleted.
1348     * @param fe the event describing context where action has taken place
1349     */

1350    public void fileDeleted(FileEvent fe) {
1351        if (fe.getFile().isFolder()) {
1352            updateAll();
1353        } else {
1354            refreshAfterEvent(fe);
1355        }
1356    }
1357
1358    /** Fired when a file is renamed.
1359     * @param fe the event describing context where action has taken place
1360     * and the original name and extension.
1361     */

1362    public void fileRenamed(FileRenameEvent fe) {
1363        updateAll();
1364    }
1365
1366    /** Fired when a file attribute is changed.
1367     * @param fe the event describing context where action has taken place,
1368     * the name of attribute and the old and new values.
1369     */

1370    public void fileAttributeChanged(FileAttributeEvent fe) {
1371        // [PENDING] this is not at all sufficient to notify every change in attributes.
1372
// One, parent dirs of front filesystems can now hold attributes for missing
1373
// files. Two, non-leader files can have attributes too which are merged in.
1374
// In principle all files/folders whose path is a prefix of this path on all
1375
// contained filesystems should be listened to for attribute change events.
1376
if (!hasAtLeastOneListeners() || (leader == null)) {
1377            return;
1378        }
1379
1380        /** If change is not fired from leader then leader may mask this attribute
1381         * and then event should not be fired */

1382        if (!fe.getFile().equals(leader) && (fe.getName() != null) && (leader.getAttribute(fe.getName()) != null)) {
1383            return;
1384        }
1385
1386        /** If change is not fired from leader then another delegate may mask this attribute
1387         * and then event should not be fired. */

1388        if (
1389            !fe.getFile().equals(leader) && (fe.getNewValue() != null) && (fe.getName() != null) &&
1390                !fe.getNewValue().equals(getAttribute(fe.getName()))
1391        ) {
1392            return;
1393        }
1394
1395        fileAttributeChanged0(new FileAttributeEvent(this, fe.getName(), fe.getOldValue(), fe.getNewValue()));
1396    }
1397
1398    /** Copies content of one folder into another.
1399    * @param source source folder
1400    * @param target target folder
1401    * @exception IOException if it fails
1402    */

1403    private static void copyContent(FileObject source, FileObject target)
1404    throws IOException JavaDoc {
1405        FileObject[] srcArr = source.getChildren();
1406
1407        copyAttrs(source, target); //added
1408

1409        for (int i = 0; i < srcArr.length; i++) {
1410            FileObject child = srcArr[i];
1411
1412            if (MultiFileSystem.isMaskFile(child)) {
1413                continue;
1414            }
1415
1416            if (target.getFileObject(child.getName(), child.getExt()) == null) {
1417                if (child.isData()) {
1418                    FileObject fo = FileUtil.copyFile(child, target, child.getName(), child.getExt());
1419
1420                    if (fo != null) {
1421                        copyAttrs(child, fo);
1422                    }
1423                } else {
1424                    FileObject targetChild = target.createFolder(child.getName());
1425                    copyContent(child, targetChild);
1426                }
1427            }
1428        }
1429    }
1430
1431    /** Copies attributes of one FileObject into another.
1432    * @param source source folder or file
1433    * @param target target folder or file
1434    * @exception IOException if it fails
1435    */

1436    private static void copyAttrs(FileObject source, FileObject target) {
1437        Enumeration JavaDoc<String JavaDoc> en = source.getAttributes();
1438
1439        while (en.hasMoreElements()) {
1440            String JavaDoc key = en.nextElement();
1441            Object JavaDoc value = source.getAttribute(key);
1442
1443            try {
1444                target.setAttribute(key, value);
1445            } catch (IOException JavaDoc ie) {
1446            }
1447        }
1448    }
1449
1450    /**
1451     * auxiliary method that returns true if mask is needed and deletes all delegates
1452     * on writable layers if deleteDelegates is true.
1453     * @param lock
1454     * @param deleteDelegates if true all delegates on writable layers will be deleted
1455     * @throws IOException is thrown if lock is not valid.
1456     * @return true if mask is necessary*/

1457    private boolean needsMask(FileLock lock, boolean deleteDelegates)
1458    throws IOException JavaDoc {
1459        MfLock lck = testLock(lock);
1460        Enumeration JavaDoc<FileObject> e = getMultiFileSystem().delegates(getPath());
1461        boolean needsMask = false;
1462
1463        while (e.hasMoreElements()) {
1464            FileObject fo = e.nextElement();
1465            FileLock lockForFo = lck.findLock(fo);
1466
1467            if (lockForFo == null) {
1468                // we will need to create mask
1469
needsMask = true;
1470            } else {
1471                if (deleteDelegates) {
1472                    fo.delete(lockForFo);
1473                }
1474            }
1475        }
1476
1477        return needsMask;
1478    }
1479
1480    /** Finds a root for given file system. It also counts with
1481    * redefined method findResourceOn.
1482    *
1483    * @param fs the filesystem to seach on
1484    * @return the root on the fs
1485    */

1486    private FileObject root(FileSystem fs) {
1487        return getMultiFileSystem().findResourceOn(fs, ""); // NOI18N
1488
}
1489
1490    final FileObject getLeader() {
1491        return leader;
1492    }
1493
1494    /** Special value used to indicate null masking of an attribute.
1495     * The level is zero in simple cases; incremented when one MFS asks
1496     * another to store a VoidValue.
1497     */

1498    private static final class VoidValue implements Externalizable JavaDoc {
1499        // Externalizable:
1500
private static final long serialVersionUID = -2743645909916238684L;
1501        int level;
1502
1503        VoidValue(int level) {
1504            this.level = level;
1505        }
1506
1507        public VoidValue() {
1508        }
1509
1510        public String JavaDoc toString() {
1511            return "org.openide.filesystems.MultiFileObject.VoidValue#" + level; // NOI18N
1512
}
1513
1514        public void writeExternal(ObjectOutput JavaDoc out) throws IOException JavaDoc {
1515            out.writeInt(level);
1516        }
1517
1518        public void readExternal(ObjectInput JavaDoc in) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
1519            level = in.readInt();
1520        }
1521    }
1522
1523    /** Implementation of lock for abstract files.
1524    */

1525    private class MfLock extends FileLock {
1526        /** lock for all files (map from FileObject to FileLock) */
1527        private Map JavaDoc<FileObject, FileLock> map = new HashMap JavaDoc<FileObject, FileLock>(11);
1528
1529        /**
1530        * @param leader leader file object
1531        * @param delegates all delegates for this file object
1532        * @param systems a set of filesystems we should create lock on
1533        * @exception IOException if the lock cannot be obtained
1534        */

1535        public MfLock(FileObject leader, Enumeration JavaDoc<FileObject> delegates, Set JavaDoc systems)
1536        throws IOException JavaDoc {
1537            while (delegates.hasMoreElements()) {
1538                FileObject fo = delegates.nextElement();
1539
1540                if (systems.contains(fo.getFileSystem())) {
1541                    FileLock l = fo.lock();
1542                    map.put(fo, l);
1543                }
1544            }
1545
1546            /* JST: Commented out because cause problems when locking a file that
1547                    is only of filesystems that are readonly (for example when
1548                    file is only on shared installation and locks are allowed only
1549                    on local => then there is nothing to lock.
1550
1551                      if (map.isEmpty()) {
1552                          // trouble, the filesystem returned wrong set of systems to lock
1553                          // on => warn about possibly wrong implementation
1554                          // to correct the problem override appropriatelly createLocksOn
1555                          // method of MultiFileSystem
1556                          throw new IOException ("Writable file is not on filesystem from createLocksOn"); // NOI18N
1557                          }
1558            */

1559        }
1560
1561        /** Finds lock for given file object.
1562        * @param fo one of delegates
1563        * @return the lock or null
1564        */

1565        public FileLock findLock(FileObject fo) {
1566            return map.get(fo);
1567        }
1568
1569        /** Adds another lock into this lock.
1570        * @param fo file object to keep the lock for
1571        * @exception IOException if the lock cannot be obtained
1572        */

1573        public void addLock(FileObject fo) throws IOException JavaDoc {
1574            map.put(fo, fo.lock());
1575        }
1576
1577        /** Releases lock for old file object and
1578        * takes new one from newFo
1579        */

1580        public void changeLocks(FileObject old, FileObject n)
1581        throws IOException JavaDoc {
1582            FileLock l = map.remove(old);
1583
1584            if (l != null) {
1585                l.releaseLock();
1586            }
1587
1588            addLock(n);
1589        }
1590
1591        public void releaseLock() {
1592            if (this.isValid()) {
1593                super.releaseLock();
1594                releaseLockForDelegates();
1595
1596                if (getCurrentMfLock() == this) {
1597                    // clears the reference to this lock from the file object
1598
MultiFileObject.this.lock = null;
1599                }
1600            }
1601        }
1602
1603        private FileLock getCurrentMfLock() {
1604            FileLock currentLock = null;
1605            ;
1606
1607            if (lock != null) {
1608                currentLock = (FileLock) lock.get();
1609            }
1610
1611            return currentLock;
1612        }
1613
1614        private void releaseLockForDelegates() {
1615            Iterator JavaDoc it = map.values().iterator();
1616
1617            while (it.hasNext()) {
1618                FileLock l = (FileLock) it.next();
1619                l.releaseLock();
1620            }
1621
1622            map.clear();
1623        }
1624
1625        // for better debugging
1626
public String JavaDoc toString() {
1627            return super.toString() + " for " + MultiFileObject.this + " valid=" + isValid(); // NOI18N
1628
}
1629    }
1630    // MfLock
1631
}
1632
Popular Tags