KickJava   Java API By Example, From Geeks To Geeks.

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


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.FileNotFoundException JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.InputStream JavaDoc;
25 import java.io.OutputStream JavaDoc;
26 import java.io.Serializable JavaDoc;
27 import java.lang.ref.Reference JavaDoc;
28 import java.lang.ref.WeakReference JavaDoc;
29 import java.util.Date JavaDoc;
30 import java.util.Enumeration JavaDoc;
31 import org.openide.util.Enumerations;
32
33 // XXX most usages of FileSystem.getDisplayName for localized exception messages
34
// should be using FileUtil.getFileDisplayName instead
35

36 /** Implementation of the file object for abstract file system.
37 *
38 * @author Jaroslav Tulach,
39 */

40 final class AbstractFileObject extends AbstractFolder {
41     /** generated Serialized Version UID */
42     static final long serialVersionUID = -2343651324897646809L;
43
44     /** default extension separator */
45     private static final char EXT_SEP = '.';
46
47     /** default path separator */
48     private static final char PATH_SEP = '/';
49
50     /** Reference to lock or null */
51     private Reference JavaDoc<FileLock> lock;
52
53     /** cache to remember if this object is folder or not */
54     private Boolean JavaDoc folder;
55
56     /** the time of last modification */
57     private Date JavaDoc lastModified = lastModified();
58
59     /** Constructor. Takes reference to file system this file belongs to.
60     *
61     * @param fs the file system
62     * @param parent the parent object (folder)
63     * @param name name of the object (e.g. <code>filename.ext</code>)
64     */

65     public AbstractFileObject(AbstractFileSystem fs, AbstractFileObject parent, String JavaDoc name) {
66         super(fs, parent, name);
67     }
68
69     /** Getter for the right file system */
70     private AbstractFileSystem getAbstractFileSystem() {
71         return (AbstractFileSystem) getFileSystem();
72     }
73
74     /** Getter for one of children.
75     */

76     private AbstractFileObject getAbstractChild(String JavaDoc name) {
77         return (AbstractFileObject) getChild(name);
78     }
79
80     //
81
// List
82
//
83

84     /** Method that allows subclasses to return its children.
85     *
86     * @return names (name . ext) of subfiles
87     */

88     protected final String JavaDoc[] list() {
89         return getAbstractFileSystem().list.children(getPath());
90     }
91
92     /** Method to create a file object for given subfile.
93     * @param name of the subfile
94     * @return the file object
95     */

96     protected final AbstractFolder createFile(String JavaDoc name) {
97         return getAbstractFileSystem().createFileObject(this, name);
98     }
99
100     //
101
// Info
102
//
103

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

107     public boolean isFolder() {
108         if (folder == null) {
109             if ((parent == null) || getAbstractFileSystem().info.folder(getPath())) {
110                 folder = Boolean.TRUE;
111
112                 return true;
113             } else {
114                 folder = Boolean.FALSE;
115
116                 return false;
117             }
118         } else {
119             return folder.booleanValue();
120         }
121     }
122
123     /* Test whether this object is a data object.
124     * This is exclusive with {@link #isFolder}.
125     * @return true if the file object represents data (i.e., can be read and written)
126     */

127     public final boolean isData() {
128         return !isFolder();
129     }
130
131     /*
132     * Get last modification time.
133     * @return the date
134     */

135     public Date JavaDoc lastModified() {
136         if ((lastModified == null) || !getAbstractFileSystem().isLastModifiedCacheEnabled()) {
137             String JavaDoc path = getPath();
138             AbstractFileSystem.Info i = getAbstractFileSystem().info;
139             lastModified = i.lastModified(path);
140             assert lastModified != null : "Null lastModified from " + i.getClass().getName() + " on " + path;
141         }
142
143         return lastModified;
144     }
145
146     @Deprecated JavaDoc // have to override for compat
147
public boolean isReadOnly() {
148         AbstractFileSystem fs = getAbstractFileSystem();
149
150         return fs.isReadOnly() || fs.info.readOnly(getPath());
151     }
152
153     public String JavaDoc getMIMEType() {
154         String JavaDoc retVal = getAbstractFileSystem().info.mimeType(getPath());
155
156         if (retVal == null) {
157             retVal = FileUtil.getMIMETypeOrDefault(this);
158         }
159
160         return retVal;
161     }
162
163     /* Get the size of the file.
164     * @return the size of the file in bytes or zero if the file does not contain data (does not
165     * exist or is a folder).
166     */

167     public long getSize() {
168         return getAbstractFileSystem().info.size(getPath());
169     }
170
171     /** Get input stream.
172     * @return an input stream to read the contents of this file
173     * @exception FileNotFoundException if the file does not exists, is a folder
174     * rather than a regular file or is invalid
175     */

176     public InputStream JavaDoc getInputStream() throws FileNotFoundException JavaDoc {
177         return StreamPool.createInputStream(this);
178     }
179
180     /* Get output stream.
181     * @param lock the lock that belongs to this file (obtained by a call to
182     * {@link #lock})
183     * @return output stream to overwrite the contents of this file
184     * @exception IOException if an error occures (the file is invalid, etc.)
185     */

186     public OutputStream JavaDoc getOutputStream(FileLock lock)
187     throws IOException JavaDoc {
188         return getOutputStream(lock, true);
189     }
190
191     /** fireFileChange defines if should be fired fileChanged event after close of stream*/
192     synchronized OutputStream JavaDoc getOutputStream(FileLock lock, boolean fireFileChanged)
193     throws IOException JavaDoc {
194         FileSystem fs = getAbstractFileSystem();
195
196         if (fs.isReadOnly()) {
197             FSException.io("EXC_FSisRO", fs.getDisplayName()); // NOI18N
198
}
199
200         if (isReadOnly()) {
201             FSException.io("EXC_FisRO", name, fs.getDisplayName()); // NOI18N
202
}
203
204         testLock(lock);
205
206         return StreamPool.createOutputStream(this, fireFileChanged);
207     }
208
209     /* Lock this file.
210     * @return lock that can be used to perform various modifications on the file
211     * @throws FileAlreadyLockedException if the file is already locked
212     */

213     public synchronized FileLock lock() throws IOException JavaDoc {
214         if (lock != null) {
215             FileLock f = lock.get();
216
217             if (f != null) {
218                 // System.out.println ("Already locked: " + this); // NOI18N
219
throw new FileAlreadyLockedException();
220             }
221         }
222
223         getAbstractFileSystem().info.lock(getPath());
224
225         FileLock l = new AfLock();
226         lock = new WeakReference JavaDoc<FileLock>(l);
227
228         // Thread.dumpStack ();
229
// System.out.println ("Locking file: " + this); // NOI18N
230
return l;
231     }
232
233     /** Unlocks the file. Notifies the underlaying impl.
234     */

235     void unlock(FileLock fLock) {
236         FileLock currentLock = null;
237         
238         synchronized(this) {
239             if (lock != null) {
240                 currentLock = lock.get();
241             }
242             
243             if (currentLock == fLock) {
244                 lastModified = null;
245                 // clear my lock
246
lock = null;
247             }
248         }
249         getAbstractFileSystem().info.unlock(getPath());
250         
251         if (isValid()) {
252             lastModified();
253         }
254     }
255
256     /** Tests the lock if it is valid, if not throws exception.
257     * @param l lock to test
258     */

259     private void testLock(FileLock l) throws IOException JavaDoc {
260         if (lock == null) {
261             FSException.io("EXC_InvalidLock", l, getPath(), getAbstractFileSystem().getDisplayName(), lock); // NOI18N
262
}
263
264         if (lock.get() != l) {
265             FSException.io("EXC_InvalidLock", l, getPath(), getAbstractFileSystem().getDisplayName(), lock.get()); // NOI18N
266
}
267     }
268
269     @Deprecated JavaDoc // have to override for compat
270
public void setImportant(boolean b) {
271         getAbstractFileSystem().markImportant(getPath(), b);
272     }
273
274     /* Get the file attribute with the specified name.
275     * @param attrName name of the attribute
276     * @return appropriate (serializable) value or <CODE>null</CODE> if the attribute is unset (or could not be properly restored for some reason)
277     */

278     public Object JavaDoc getAttribute(String JavaDoc attrName) {
279         return getAttribute(attrName, getPath());
280     }
281
282     /** performance hack */
283     final Object JavaDoc getAttribute(String JavaDoc attrName, String JavaDoc path) {
284         return getAbstractFileSystem().attr.readAttribute(path, attrName);
285     }
286
287     /* Set the file attribute with the specified name.
288     * @param attrName name of the attribute
289     * @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.
290     * @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}.
291     */

292     public void setAttribute(String JavaDoc attrName, Object JavaDoc value)
293     throws IOException JavaDoc {
294         setAttribute(attrName, value, true);
295     }
296
297     /* helper method for MFO.setAttribute. MFO can disable firing from underlaying
298      * layers. Should be reviewed in 3.4 or later
299      *@see MultiFileObject#setAttribute*/

300     void setAttribute(String JavaDoc attrName, Object JavaDoc value, boolean fire)
301     throws IOException JavaDoc {
302         Object JavaDoc oldValue = null;
303
304         //FileSystem fs = getAbstractFileSystem ();
305
//if (fs.isReadOnly())
306
// FSException.io("EXC_FSisRO", fs.getDisplayName ()); // NOI18N
307
if (fire) {
308             oldValue = getAttribute(attrName);
309         }
310
311         getAbstractFileSystem().attr.writeAttribute(getPath(), attrName, value);
312
313         if (fire && (oldValue != value) && hasAtLeastOneListeners()) {
314             fileAttributeChanged0(new FileAttributeEvent(this, attrName, oldValue, value));
315         }
316     }
317
318     /* Get all file attribute names for this file.
319     * @return enumeration of keys (as strings)
320     */

321     public Enumeration JavaDoc<String JavaDoc> getAttributes() {
322         return getAttributes(getPath());
323     }
324
325     final Enumeration JavaDoc<String JavaDoc> getAttributes(String JavaDoc path) {
326         return getAbstractFileSystem().attr.attributes(path);
327     }
328
329     /** Creates Reference. In FileSystem, which subclasses AbstractFileSystem, you can overload method
330     * createReference(FileObject fo) to achieve another type of Reference (weak, strong etc.)
331     * @param fo FileObject
332     * @return Reference to FileObject
333     */

334     protected final Reference JavaDoc<AbstractFolder> createReference(AbstractFolder fo) {
335         return (getAbstractFileSystem().createReference(fo));
336     }
337
338     /** Create a new folder below this one with the specified name. Fires
339     * <code>fileCreated</code> event.
340     *
341     * @param name the name of folder to create. Periods in name are allowed.
342     * @return the new folder
343     * @exception IOException if the folder cannot be created (e.g. already exists)
344     */

345     public FileObject createFolder(String JavaDoc name) throws IOException JavaDoc {
346         AbstractFileObject fo;
347
348         try {
349             getFileSystem().beginAtomicAction();
350
351             synchronized (this) {
352                 AbstractFileSystem fs = getAbstractFileSystem();
353
354                 if (fs.isReadOnly()) {
355                     FSException.io("EXC_FSisRO", fs.getDisplayName()); // NOI18N
356
}
357
358                 if (isReadOnly()) {
359                     FSException.io("EXC_FisRO", name, fs.getDisplayName()); // NOI18N
360
}
361
362                 if (!isFolder()) {
363                     FSException.io("EXC_FoNotFolder", name, getPath(), fs.getDisplayName()); // NOI18N
364
}
365
366                 getAbstractFileSystem().change.createFolder(getPath() + PATH_SEP + name);
367                 registerChild(name);
368                 fo = getAbstractChild(name);
369
370                 if (fo == null) {
371                     // system error
372
throw new FileStateInvalidException(
373                         FileSystem.getString("EXC_ApplicationCreateError", getPath(), name)
374                     );
375                 }
376
377                 if (hasListeners()) {
378                     fileCreated0(new FileEvent(this, fo), false);
379                 }
380             }
381         } finally {
382             getFileSystem().finishAtomicAction();
383         }
384
385         return fo;
386     }
387
388     /* Create new data file in this folder with the specified name. Fires
389     * <code>fileCreated</code> event.
390     *
391     * @param name the name of data object to create (can contain a period)
392     * @param ext the extension of the file (or <code>null</code> or <code>""</code>)
393     *
394     * @return the new data file object
395     * @exception IOException if the file cannot be created (e.g. already exists)
396     */

397     public FileObject createData(String JavaDoc name, String JavaDoc ext)
398     throws IOException JavaDoc {
399         if (name.indexOf(PATH_SEP) != -1) {
400             throw new IllegalArgumentException JavaDoc("Use FileUtil.createData() instead!"); // NOI18N
401
}
402
403         String JavaDoc fName = null;
404         try {
405             getFileSystem().beginAtomicAction();
406             String JavaDoc n = null;
407             synchronized (this) {
408                 AbstractFileSystem fs = getAbstractFileSystem();
409
410                 if (fs.isReadOnly()) {
411                     FSException.io("EXC_FSisRO", fs.getDisplayName()); // NOI18N
412
}
413
414                 if (isReadOnly()) {
415                     FSException.io("EXC_FisRO", name, fs.getDisplayName()); // NOI18N
416
}
417
418                 n = ((ext == null) || "".equals(ext)) ? name : (name + EXT_SEP + ext); // NOI18N
419

420                 if (!isFolder()) {
421                     FSException.io("EXC_FoNotFolder", n, getPath(), fs.getDisplayName()); // NOI18N
422
}
423                 
424                 fName = getPath() + PATH_SEP + n;
425             }
426             //called outside of sync. block because of #72695
427
getAbstractFileSystem().change.createData(fName);
428             
429             synchronized (this) {
430                 registerChild(n);
431
432                 AbstractFileObject fo = getAbstractChild(n);
433
434                 if (fo == null) {
435                     // system error
436
throw new FileStateInvalidException(
437                         FileSystem.getString("EXC_ApplicationCreateError", getPath(), n)
438                     );
439                 }
440
441                 if (hasListeners()) {
442                     fileCreated0(new FileEvent(this, fo), true);
443                 }
444
445                 return fo;
446             }
447         } finally {
448             getFileSystem().finishAtomicAction();
449         }
450     }
451
452     /* Renames this file (or folder).
453     * Both the new basename and new extension should be specified.
454     * <p>
455     * Note that using this call, it is currently only possible to rename <em>within</em>
456     * a parent folder, and not to do moves <em>across</em> folders.
457     * Conversely, implementing file systems need only implement "simple" renames.
458     * If you wish to move a file across folders, you should call {@link FileUtil#moveFile}.
459     * @param lock File must be locked before renaming.
460     * @param name new basename of file
461     * @param ext new extension of file or null if no extension requested (ignored for folders).
462     */

463     public void rename(FileLock lock, String JavaDoc name, String JavaDoc ext)
464     throws IOException JavaDoc {
465         if (parent == null) {
466             FSException.io("EXC_CannotRenameRoot", getAbstractFileSystem().getDisplayName()); // NOI18N
467
}
468
469         if (
470             (name.indexOf('/') != -1) || ((ext != null) && (ext.indexOf('/') != -1)) || (name.indexOf('\\') != -1) ||
471                 ((ext != null) && (ext.indexOf('\\') != -1))
472         ) {
473             FSException.io("EXC_CannotRename", getPath(), getAbstractFileSystem().getDisplayName(), name + "." + ext); // NOI18N
474
}
475
476         try {
477             getFileSystem().beginAtomicAction();
478
479             String JavaDoc newFullName = null;
480             String JavaDoc oldFullName = null;
481
482             synchronized (parent) {
483                 // synchronize on your folder
484
testLock(lock);
485
486                 if (isData()) {
487                     if (ext != null) {
488                         name = name + EXT_SEP + ext;
489                     }
490                 }
491
492                 newFullName = parent.isRoot() ? name : parent.getPath() + PATH_SEP + name;
493                 oldFullName = getPath();
494
495                 if (isReadOnly()) {
496                     FSException.io(
497                         "EXC_CannotRename", getPath(), getAbstractFileSystem().getDisplayName(), newFullName
498                     ); // NOI18N
499
}
500
501                 if (getFileSystem().isReadOnly()) {
502                     FSException.io("EXC_FSisRO", getAbstractFileSystem().getDisplayName()); // NOI18N
503
}
504
505                 String JavaDoc on = getName();
506                 String JavaDoc oe = getExt();
507
508                 getAbstractFileSystem().change.rename(oldFullName, newFullName);
509
510                 String JavaDoc oldName = this.name;
511                 this.name = name;
512
513                 /*
514                       System.out.println ("Resulting file is: " + getPath ());
515                       System.out.println ("Bedw file is: " + newFullName);
516                       System.out.println ("Name: " + name);
517                       System.out.println ("Old : " + oldName);
518                 */

519                 parent.refresh(name, oldName);
520
521                 if (hasAtLeastOneListeners()) {
522                     fileRenamed0(new FileRenameEvent(this, on, oe));
523                 }
524             }
525
526             getAbstractFileSystem().attr.renameAttributes(oldFullName, newFullName);
527         } finally {
528             getFileSystem().finishAtomicAction();
529         }
530     }
531
532     /* Delete this file. If the file is a folder and it is not empty then
533     * all of its contents are also recursively deleted.
534     *
535     * @param lock the lock obtained by a call to {@link #lock}
536     * @exception IOException if the file could not be deleted
537     */

538     void handleDelete(FileLock lock) throws IOException JavaDoc {
539         if (parent == null) {
540             FSException.io("EXC_CannotDeleteRoot", getAbstractFileSystem().getDisplayName()); // NOI18N
541
}
542
543         String JavaDoc fullName;
544
545         try {
546             getFileSystem().beginAtomicAction();
547
548             synchronized (parent) {
549                 testLock(lock);
550
551                 fullName = getPath();
552
553                 try {
554                     getAbstractFileSystem().change.delete(fullName);
555                 } catch (IOException JavaDoc ex) {
556                     StreamPool p = StreamPool.find(this);
557
558                     if (p != null) {
559                         p.annotate(ex);
560                     }
561
562                     throw ex;
563                 }
564
565                 String JavaDoc n = name;
566                 validFlag = false;
567
568                 parent.refresh(null, n, true);
569             }
570
571             getAbstractFileSystem().attr.deleteAttributes(fullName);
572
573             if (hasAtLeastOneListeners()) {
574                 fileDeleted0(new FileEvent(this));
575             }
576         } finally {
577             getFileSystem().finishAtomicAction();
578         }
579     }
580
581     //
582
// Transfer
583
//
584

585     /** Copies this file. This allows the filesystem to perform any additional
586     * operation associated with the copy. But the default implementation is simple
587     * copy of the file and its attributes
588     *
589     * @param target target folder to move this file to
590     * @param name new basename of file
591     * @param ext new extension of file (ignored for folders)
592     * @return the newly created file object representing the moved file
593     */

594     public FileObject copy(FileObject target, String JavaDoc name, String JavaDoc ext)
595     throws IOException JavaDoc {
596         AbstractFileSystem.Transfer from = getAbstractFileSystem().transfer;
597
598         if ((from == null) || !(target instanceof AbstractFileObject)) {
599             return super.copy(target, name, ext);
600         }
601
602         AbstractFileObject abstractTarget = (AbstractFileObject) target;
603         AbstractFileSystem abstractFS = abstractTarget.getAbstractFileSystem();
604         AbstractFileSystem.Transfer to = abstractFS.transfer;
605
606         if (to != null) {
607             try {
608                 getFileSystem().beginAtomicAction();
609
610                 synchronized (abstractTarget) {
611                     // try copying thru the transfer
612
if (abstractFS.isReadOnly()) {
613                         FSException.io("EXC_FSisRO", abstractFS.getDisplayName()); // NOI18N
614
}
615
616                     if (!target.canWrite()) {
617                         FSException.io("EXC_FisRO", target.getPath(), abstractFS.getDisplayName()); // NOI18N
618
}
619
620                     String JavaDoc n = "".equals(ext) ? name : (name + EXT_SEP + ext); // NOI18N
621

622                     if (from.copy(getPath(), to, target.getPath() + PATH_SEP + n)) {
623                         // the transfer implementation thinks that the copy succeeded
624
abstractTarget.registerChild(n);
625
626                         AbstractFileObject fo = abstractTarget.getAbstractChild(n);
627
628                         if (fo == null) {
629                             // system error
630
throw new FileStateInvalidException(
631                                 FileSystem.getString("EXC_ApplicationCreateError", abstractTarget.getPath(), n)
632                             );
633                         }
634
635                         if (abstractTarget.hasListeners()) {
636                             abstractTarget.fileCreated0(new FileEvent(abstractTarget, fo), true);
637                         }
638
639                         return fo;
640                     }
641                 }
642             } finally {
643                 getFileSystem().finishAtomicAction();
644             }
645         }
646
647         return super.copy(target, name, ext);
648     }
649
650     /** Moves this file. This allows the filesystem to perform any additional
651     * operation associated with the move. But the default implementation is encapsulated
652     * as copy and delete.
653     *
654     * @param lock File must be locked before renaming.
655     * @param target target folder to move this file to
656     * @param name new basename of file
657     * @param ext new extension of file (ignored for folders)
658     * @return the newly created file object representing the moved file
659     */

660     public FileObject move(FileLock lock, FileObject target, String JavaDoc name, String JavaDoc ext)
661     throws IOException JavaDoc {
662         AbstractFileSystem fs = getAbstractFileSystem();
663
664         if (parent == null) {
665             FSException.io("EXC_CannotDeleteRoot", fs.getDisplayName()); // NOI18N
666
}
667
668         AbstractFileSystem.Transfer from = getAbstractFileSystem().transfer;
669
670         if ((from == null) || !(target instanceof AbstractFileObject)) {
671             return super.move(lock, target, name, ext);
672         }
673
674         AbstractFileObject abstractTarget = (AbstractFileObject) target;
675         AbstractFileSystem abstractFS = abstractTarget.getAbstractFileSystem();
676         AbstractFileSystem.Transfer to = abstractFS.transfer;
677
678         if (to != null) {
679             try {
680                 getFileSystem().beginAtomicAction();
681
682                 synchronized (parent) {
683                     testLock(lock);
684
685                     if (abstractFS.isReadOnly()) {
686                         FSException.io("EXC_FSisRO", abstractFS.getDisplayName()); // NOI18N
687
}
688
689                     if (!target.canWrite()) {
690                         FSException.io("EXC_FisRO", target.getPath(), abstractFS.getDisplayName()); // NOI18N
691
}
692
693                     String JavaDoc n = "".equals(ext) ? name : (name + EXT_SEP + ext); // NOI18N
694
String JavaDoc fullName = getPath();
695
696                     if (from.move(fullName, to, target.getPath() + PATH_SEP + n)) {
697                         // the transfer implementation thinks that the move succeeded
698
String JavaDoc oldN = name;
699                         validFlag = false;
700
701                         // refresh the parent because this file has been deleted
702
parent.refresh(null, oldN);
703
704                         // deletes all attributes asssociated with the moved file
705
// JST: I am not sure if this is the right behaviour, maybe this
706
// should be the reposibility of from.move?
707
// fs.attr.deleteAttributes (fullName);
708
// refresh the target so new file appears there
709
abstractTarget.registerChild(n);
710
711                         AbstractFileObject fo = abstractTarget.getAbstractChild(n);
712
713                         if (fo == null) {
714                             // system error
715
throw new FileStateInvalidException(
716                                 FileSystem.getString("EXC_ApplicationCreateError", abstractTarget.getPath(), n)
717                             );
718                         }
719
720                         if (hasAtLeastOneListeners()) {
721                             fileDeleted0(new FileEvent(this));
722                         }
723
724                         if (abstractTarget.hasListeners()) {
725                             abstractTarget.fileCreated0(new FileEvent(abstractTarget, fo), true);
726                         }
727
728                         return fo;
729                     }
730                 }
731             } finally {
732                 getFileSystem().finishAtomicAction();
733             }
734         }
735
736         return super.move(lock, target, name, ext);
737     }
738
739     /**
740      * Tests if file really exists or is missing. Some operation on it may be restricted.
741      * @return true indicates that the file is missing.
742      * @since 1.9
743      */

744     public boolean isVirtual() {
745         return getAbstractFileSystem().checkVirtual(getPath());
746     }
747
748     /** Refresh the content of file. Ignores changes to the files provided,
749     * instead returns its file object.
750     * @param added do not notify addition of this file
751     * @param removed do not notify removing of this file
752     * @param fire true if we should fire changes
753     */

754     protected void refresh(String JavaDoc added, String JavaDoc removed, boolean fire, boolean expected) {
755         this.refresh(added, removed, fire, expected, null);
756     }
757
758     /** Refresh the content of file. Ignores changes to the files provided,
759     * instead returns its file object.
760     * @param added do not notify addition of this file
761     * @param removed do not notify removing of this file
762     * @param fire true if we should fire changes
763     * @param list a list of children
764     */

765     protected void refresh(String JavaDoc added, String JavaDoc removed, boolean fire, boolean expected, String JavaDoc[] list) {
766         FileEvent ev = null;
767         boolean refreshParent = false;
768
769         try {
770             getFileSystem().beginAtomicAction();
771
772             if (isFolder()) {
773                 super.refresh(added, removed, fire, expected, list);
774             } else {
775                 StreamPool strPool = StreamPool.find(this);
776
777                 /** Events should not be fired until stream was closed. */
778                 if ((strPool != null) && strPool.isOutputStreamOpen()) {
779                     return;
780                 }
781
782                 synchronized (this) {
783                     // check the time of a file last modification
784
Date JavaDoc l = null;
785
786                     if (lastModified == null) {
787                         lastModified = l = getAbstractFileSystem().info.lastModified(getPath());
788
789                         return;
790                     } else {
791                         l = getAbstractFileSystem().info.lastModified(getPath());
792                     }
793
794                     /*If file was already deleted then new java.io.File("...").lastModified() returns 0.
795                      This value is converted to java.util.Date (new java.io.File("...").lastModified())
796                      Such java.util.Date is then evaluated as if file was modified. But the file was
797                      actually deleted.
798                      */

799
800                     // JST: Seems like the lastModified () time can vary a bit on NT (up to 500ms)
801
// if (!l.equals (lastModified)) {
802
// Solution: if (Math.abs(lastModified.getTime() - l.getTime ()) >= 5000) {
803
// I think that above mentioned solution is not necessary. Because
804
// events should not be fired until stream was closed.
805
if (Math.abs(lastModified.getTime() - l.getTime()) != 0) {
806                         /*
807                         System.out.println("file : " + getPath ());
808                         System.out.println("prev date: " + lastModified.getTime ());
809                         System.out.println("now date: " + l.getTime());
810                         System.out.println("diff : " + (lastModified.getTime () - l.getTime()));
811                          */

812                         lastModified = l;
813
814                         if (fire && hasAtLeastOneListeners()) {
815                             ev = new FileEvent(this, this, expected);
816                         }
817
818                         if (l.getTime() == 0) {
819                             if (validFlag) {
820                                 validFlag = false;
821
822                                 if (ev != null) {
823                                     fileDeleted0(ev);
824                                 }
825
826                                 refreshParent = true;
827                             }
828                         } else if (ev != null) {
829                             fileChanged0(ev);
830                         }
831                     }
832                 }
833             }
834
835             if (refreshParent && (parent.getFileObject(getName(), getExt()) != null)) {
836                 parent.refreshFolder(null, this.getNameExt(), fire, expected, null);
837             }
838         } finally {
839             getFileSystem().finishAtomicAction();
840         }
841
842         // System.out.println ("Refresh of " + this + " ended"); // NOI18N
843
return;
844     }
845
846     /**
847      * Notification that the output stream has been closed.
848      * @param fireFileChanged defines if FileEvent should be fired to notify about
849      * change of file after close of stream
850      */

851     protected void outputStreamClosed(boolean fireFileChanged) {
852         synchronized (this) {
853             lastModified = null;
854             lastModified();
855         }
856
857         super.outputStreamClosed(fireFileChanged);
858     }
859
860     // Implements FileObject.canWrite()
861
public boolean canWrite() {
862         AbstractFileSystem fs = getAbstractFileSystem();
863
864         return fs.canWrite(getPath());
865     }
866
867     // Implements FileObject.canRead()
868
public boolean canRead() {
869         AbstractFileSystem fs = getAbstractFileSystem();
870
871         return fs.canRead(getPath());
872     }
873
874     // Helper method used in AbstractFileSystem.canWrite()
875
final boolean superCanWrite() {
876         return super.canWrite();
877     }
878
879     // Helper method used in AbstractFileSystem.canRead()
880
final boolean superCanRead() {
881         return super.canRead();
882     }
883
884     /** Implementation of lock for abstract files.
885     */

886     private class AfLock extends FileLock {
887         AfLock() {
888         }
889
890         public void releaseLock() {
891             if (this.isValid()) {
892                 super.releaseLock();
893                 unlock(this);
894             }
895         }
896     }
897
898     //
899
// Invalid object that can be created after deserialization
900
//
901
static final class Invalid extends FileObject {
902         static final long serialVersionUID = -4558997829579415276L;
903
904         /** special instance that represent root */
905         private static final Invalid ROOT = new Invalid(""); // NOI18N
906

907         /** name */
908         private String JavaDoc name;
909         private String JavaDoc fileSystemName;
910
911         /** Constructor.
912         * @param name name of the object
913         */

914         public Invalid(String JavaDoc name) {
915             int i = name.lastIndexOf('/') + 1;
916             this.name = ((i == 0) || (i == name.length())) ? name : name.substring(i);
917         }
918
919         /** Constructor. Takes reference to file system this file belongs to.
920         * @param fs file system name
921         * @param name name of the object
922         */

923         public Invalid(String JavaDoc fs, String JavaDoc name) {
924             this(name);
925             fileSystemName = fs;
926         }
927
928         /** Get the name without extension of this file or folder.
929         * Period at first position is not considered as extension-separator
930         *
931         * @return name of the file or folder(in its enclosing folder)
932         */

933         public String JavaDoc getName() {
934             int i = name.lastIndexOf('.');
935
936             return (i <= 0) ? name : name.substring(0, i);
937         }
938
939         /** Get the extension of this file or folder.
940         * Period at first position is not considered as extension-separator
941         * This is the string after the last dot of the full name, if any.
942         *
943         * @return extension of the file or folder(if any) or empty string if there is none
944         */

945         public String JavaDoc getExt() {
946             int i = name.lastIndexOf('.') + 1;
947
948             return ((i <= 1) || (i == name.length())) ? "" : name.substring(i); // NOI18N
949
}
950
951         /** @exception FileStateInvalidException always
952         */

953         public FileSystem getFileSystem() throws FileStateInvalidException {
954             throw new FileStateInvalidException(null, name + "[" + fileSystemName + "]"); // NOI18N
955
}
956
957         //
958
// Info
959
//
960

961         /** Test whether this object is the root folder.
962         * The root should always be a folder.
963         * @return true if the object is the root of a file system
964         */

965         public boolean isRoot() {
966             return this == ROOT;
967         }
968
969         /** Test whether this object is a folder.
970         * @return true if the file object is a folder (i.e., can have children)
971         */

972         public boolean isFolder() {
973             return this == ROOT;
974         }
975
976         /**
977         * Get last modification time.
978         * @return the date
979         */

980         public Date JavaDoc lastModified() {
981             return new Date JavaDoc();
982         }
983
984         /** Test whether this object is a data object.
985         * This is exclusive with {@link #isFolder}.
986         * @return true if the file object represents data (i.e., can be read and written)
987         */

988         public boolean isData() {
989             return false;
990         }
991
992         @Deprecated JavaDoc // have to override for compat
993
public boolean isReadOnly() {
994             return false;
995         }
996
997         /** Test whether the file is valid. The file can be invalid if it has been deserialized
998         * and the file no longer exists on disk; or if the file has been deleted.
999         *
1000        * @return true if the file object is valid
1001        */

1002        public boolean isValid() {
1003            return false;
1004        }
1005
1006        /** Get the MIME type of this file.
1007        * The MIME type identifies the type of the file's contents and should be used in the same way as in the <B>Java
1008        * Activation Framework</B> or in the {@link java.awt.datatransfer} package.
1009        * <P>
1010        * The default implementation calls {@link FileUtil#getMIMEType}.
1011        *
1012        * @return the MIME type textual representation, e.g. <code>"text/plain"</code>
1013        */

1014        public String JavaDoc getMIMEType() {
1015            return "content/unknown"; // NOI18N
1016
}
1017
1018        /** Get the size of the file.
1019        * @return the size of the file in bytes or zero if the file does not contain data (does not
1020        * exist or is a folder).
1021        */

1022        public long getSize() {
1023            return 0;
1024        }
1025
1026        /** Get input stream.
1027        * @return an input stream to read the contents of this file
1028        * @exception FileNotFoundException if the file does not exists or is invalid
1029        */

1030        public InputStream JavaDoc getInputStream() throws FileNotFoundException JavaDoc {
1031            throw new FileNotFoundException JavaDoc();
1032        }
1033
1034        /** Get output stream.
1035        * @param lock the lock that belongs to this file (obtained by a call to
1036        * {@link #lock})
1037        * @return output stream to overwrite the contents of this file
1038        * @exception IOException if an error occures (the file is invalid, etc.)
1039        */

1040        public synchronized OutputStream JavaDoc getOutputStream(FileLock lock)
1041        throws IOException JavaDoc {
1042            throw new IOException JavaDoc();
1043        }
1044
1045        /** Lock this file.
1046        * @return lock that can be used to perform various modifications on the file
1047        * @throws FileAlreadyLockedException if the file is already locked
1048        */

1049        public synchronized FileLock lock() throws IOException JavaDoc {
1050            throw new IOException JavaDoc();
1051        }
1052
1053        @Deprecated JavaDoc // have to override for compat
1054
public void setImportant(boolean b) {
1055        }
1056
1057        /** Get the file attribute with the specified name.
1058        * @param attrName name of the attribute
1059        * @return appropriate (serializable) value or <CODE>null</CODE> if the attribute is unset (or could not be properly restored for some reason)
1060        */

1061        public Object JavaDoc getAttribute(String JavaDoc attrName) {
1062            return null;
1063        }
1064
1065        /** Set the file attribute with the specified name.
1066        * @param attrName name of the attribute
1067        * @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.
1068        * @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}.
1069        */

1070        public void setAttribute(String JavaDoc attrName, Object JavaDoc value)
1071        throws IOException JavaDoc {
1072            throw new IOException JavaDoc();
1073        }
1074
1075        /** Get all file attribute names for this file.
1076        * @return enumeration of keys (as strings)
1077        */

1078        public Enumeration JavaDoc<String JavaDoc> getAttributes() {
1079            return Enumerations.empty();
1080        }
1081
1082        /** Create a new folder below this one with the specified name. Fires
1083        * <code>fileCreated</code> event.
1084        *
1085        * @param name the name of folder to create (without extension)
1086        * @return the new folder
1087        * @exception IOException if the folder cannot be created (e.g. already exists)
1088        */

1089        public synchronized FileObject createFolder(String JavaDoc name)
1090        throws IOException JavaDoc {
1091            throw new IOException JavaDoc();
1092        }
1093
1094        /** Create new data file in this folder with the specified name. Fires
1095        * <code>fileCreated</code> event.
1096        *
1097        * @param name the name of data object to create (should not contain a period)
1098        * @param ext the extension of the file (or <code>null</code> or <code>""</code>)
1099        *
1100        * @return the new data file object
1101        * @exception IOException if the file cannot be created (e.g. already exists)
1102        */

1103        public synchronized FileObject createData(String JavaDoc name, String JavaDoc ext)
1104        throws IOException JavaDoc {
1105            throw new IOException JavaDoc();
1106        }
1107
1108        /** Renames this file (or folder).
1109        * Both the new basename and new extension should be specified.
1110        * <p>
1111        * Note that using this call, it is currently only possible to rename <em>within</em>
1112        * a parent folder, and not to do moves <em>across</em> folders.
1113        * Conversely, implementing file systems need only implement "simple" renames.
1114        * If you wish to move a file across folders, you should call {@link FileUtil#moveFile}.
1115        * @param lock File must be locked before renaming.
1116        * @param name new basename of file
1117        * @param ext new extension of file (ignored for folders)
1118        */

1119        public void rename(FileLock lock, String JavaDoc name, String JavaDoc ext)
1120        throws IOException JavaDoc {
1121            throw new IOException JavaDoc();
1122        }
1123
1124        /** Delete this file. If the file is a folder and it is not empty then
1125        * all of its contents are also recursively deleted.
1126        *
1127        * @param lock the lock obtained by a call to {@link #lock}
1128        * @exception IOException if the file could not be deleted
1129        */

1130        public void delete(FileLock lock) throws IOException JavaDoc {
1131            throw new IOException JavaDoc();
1132        }
1133
1134        //
1135
// List
1136
//
1137

1138        /** Get parent folder.
1139        * The returned object will satisfy {@link #isFolder}.
1140        *
1141        * @return common root for all invalid objects
1142        */

1143        public FileObject getParent() {
1144            return (this == ROOT) ? null : ROOT;
1145        }
1146
1147        /** Get all children of this folder (files and subfolders). If the file does not have children
1148        * (does not exist or is not a folder) then an empty array should be returned. No particular order is assumed.
1149        *
1150        * @return array of direct children
1151        * @see #getChildren(boolean)
1152        * @see #getFolders
1153        * @see #getData
1154        */

1155        public synchronized FileObject[] getChildren() {
1156            return new FileObject[0];
1157        }
1158
1159        /** Retrieve file or folder contained in this folder by name.
1160        * <em>Note</em> that neither file nor folder is created on disk.
1161        * @param name basename of the file or folder (in this folder)
1162        * @param ext extension of the file; <CODE>null</CODE> or <code>""</code>
1163        * if the file should have no extension or if folder is requested
1164        * @return the object representing this file or <CODE>null</CODE> if the file
1165        * or folder does not exist
1166        * @exception IllegalArgumentException if <code>this</code> is not a folder
1167        */

1168        public synchronized FileObject getFileObject(String JavaDoc name, String JavaDoc ext) {
1169            return null;
1170        }
1171
1172        /** Refresh the contents of a folder. Rescans the list of children names.
1173        */

1174        public void refresh() {
1175        }
1176
1177        //
1178
// Listeners section
1179
//
1180

1181        /** Add new listener to this object.
1182        * @param fcl the listener
1183        */

1184        public void addFileChangeListener(FileChangeListener fcl) {
1185        }
1186
1187        /** Remove listener from this object.
1188        * @param fcl the listener
1189        */

1190        public void removeFileChangeListener(FileChangeListener fcl) {
1191        }
1192    }
1193     // end of Invalid
1194

1195    /** Replace that stores name of fs and file.
1196     * @deprecated In favor of AbstractFolder.Replace.
1197    */

1198    @Deprecated JavaDoc
1199    static final class Replace extends Object JavaDoc implements Serializable JavaDoc {
1200        /** generated Serialized Version UID */
1201        static final long serialVersionUID = -8543432135435542113L;
1202        private String JavaDoc fsName;
1203        private String JavaDoc fileName;
1204
1205        /** Constructor
1206        */

1207        public Replace(String JavaDoc fsName, String JavaDoc fileName) {
1208            this.fsName = fsName;
1209            this.fileName = fileName;
1210        }
1211
1212        /** Finds the right file.
1213        */

1214        public Object JavaDoc readResolve() {
1215            Repository rep = ExternalUtil.getRepository();
1216            @SuppressWarnings JavaDoc("deprecation") // FileSystem.systemName historical part of serial form
1217
FileSystem fs = rep.findFileSystem(fsName);
1218            FileObject fo = null;
1219
1220            if (fs != null) {
1221                // scan desired system
1222
fo = fs.findResource(fileName);
1223            }
1224
1225            if (fo == null) {
1226                // create invalid file instead
1227
return new Invalid(fsName, fileName);
1228            }
1229
1230            return fo;
1231        }
1232    }
1233     // end of Replace
1234
}
1235
Popular Tags