KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > vfs > provider > AbstractFileObject


1 /*
2  * Copyright 2002-2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.commons.vfs.provider;
17
18 import org.apache.commons.vfs.Capability;
19 import org.apache.commons.vfs.FileContent;
20 import org.apache.commons.vfs.FileContentInfoFactory;
21 import org.apache.commons.vfs.FileName;
22 import org.apache.commons.vfs.FileObject;
23 import org.apache.commons.vfs.FileSelector;
24 import org.apache.commons.vfs.FileSystem;
25 import org.apache.commons.vfs.FileSystemException;
26 import org.apache.commons.vfs.FileType;
27 import org.apache.commons.vfs.FileUtil;
28 import org.apache.commons.vfs.NameScope;
29 import org.apache.commons.vfs.RandomAccessContent;
30 import org.apache.commons.vfs.Selectors;
31 import org.apache.commons.vfs.util.RandomAccessMode;
32
33 import java.io.IOException JavaDoc;
34 import java.io.InputStream JavaDoc;
35 import java.io.OutputStream JavaDoc;
36 import java.net.MalformedURLException JavaDoc;
37 import java.net.URL JavaDoc;
38 import java.security.AccessController JavaDoc;
39 import java.security.PrivilegedActionException JavaDoc;
40 import java.security.PrivilegedExceptionAction JavaDoc;
41 import java.security.cert.Certificate JavaDoc;
42 import java.util.ArrayList JavaDoc;
43 import java.util.Arrays JavaDoc;
44 import java.util.Collections JavaDoc;
45 import java.util.List JavaDoc;
46 import java.util.Map JavaDoc;
47
48 /**
49  * A partial file object implementation.
50  *
51  * @author <a HREF="mailto:adammurdoch@apache.org">Adam Murdoch</a>
52  * @author Gary D. Gregory
53  * @todo Chop this class up - move all the protected methods to several
54  * interfaces, so that structure and content can be separately overridden.
55  * @todo Check caps in methods like getChildren(), etc, and give better error messages
56  * (eg 'this file type does not support listing children', vs 'this is not a folder')
57  */

58 public abstract class AbstractFileObject implements FileObject
59 {
60     // private static final FileObject[] EMPTY_FILE_ARRAY = {};
61
private static final FileName[] EMPTY_FILE_ARRAY = {};
62
63     private final AbstractFileName name;
64     private final AbstractFileSystem fs;
65
66     private DefaultFileContent content;
67
68     // Cached info
69
private boolean attached;
70     private FileType type;
71     private AbstractFileObject parent;
72
73     // Changed to hold only the name of the children and let the object
74
// go into the global files cache
75
// private FileObject[] children;
76
private FileName[] children;
77     private List JavaDoc objects;
78
79     protected AbstractFileObject(final FileName name,
80                                  final AbstractFileSystem fs)
81     {
82         this.name = (AbstractFileName) name;
83         this.fs = fs;
84     }
85
86     /**
87      * Attaches this file object to its file resource. This method is called
88      * before any of the doBlah() or onBlah() methods. Sub-classes can use
89      * this method to perform lazy initialisation.
90      * <p/>
91      * This implementation does nothing.
92      */

93     protected void doAttach() throws Exception JavaDoc
94     {
95     }
96
97     /**
98      * Detaches this file object from its file resource.
99      * <p/>
100      * <p>Called when this file is closed. Note that the file object may be
101      * reused later, so should be able to be reattached.
102      * <p/>
103      * This implementation does nothing.
104      */

105     protected void doDetach() throws Exception JavaDoc
106     {
107     }
108
109     /**
110      * Determines the type of this file. Must not return null. The return
111      * value of this method is cached, so the implementation can be expensive.
112      */

113     protected abstract FileType doGetType() throws Exception JavaDoc;
114
115     /**
116      * Determines if this file is hidden. Is only called if {@link #doGetType}
117      * does not return {@link FileType#IMAGINARY}.
118      * <p/>
119      * This implementation always returns false.
120      */

121     protected boolean doIsHidden() throws Exception JavaDoc
122     {
123         return false;
124     }
125
126     /**
127      * Determines if this file can be read. Is only called if {@link #doGetType}
128      * does not return {@link FileType#IMAGINARY}.
129      * <p/>
130      * This implementation always returns true.
131      */

132     protected boolean doIsReadable() throws Exception JavaDoc
133     {
134         return true;
135     }
136
137     /**
138      * Determines if this file can be written to. Is only called if
139      * {@link #doGetType} does not return {@link FileType#IMAGINARY}.
140      * <p/>
141      * This implementation always returns true.
142      */

143     protected boolean doIsWriteable() throws Exception JavaDoc
144     {
145         return true;
146     }
147
148     /**
149      * Lists the children of this file. Is only called if {@link #doGetType}
150      * returns {@link FileType#FOLDER}. The return value of this method
151      * is cached, so the implementation can be expensive.
152      */

153     protected abstract String JavaDoc[] doListChildren() throws Exception JavaDoc;
154
155     /**
156      * Lists the children of this file. Is only called if {@link #doGetType}
157      * returns {@link FileType#FOLDER}. The return value of this method
158      * is cached, so the implementation can be expensive.<br>
159      * Other than <code>doListChildren</code> you could return FileObject's to e.g. reinitialize the type of the file.<br>
160      * (Introduced for Webdav: "permission denied on resource" during getType())
161      */

162     protected FileObject[] doListChildrenResolved() throws Exception JavaDoc
163     {
164         return null;
165     }
166
167     /**
168      * Deletes the file. Is only called when:
169      * <ul>
170      * <li>{@link #doGetType} does not return {@link FileType#IMAGINARY}.
171      * <li>{@link #doIsWriteable} returns true.
172      * <li>This file has no children, if a folder.
173      * </ul>
174      * <p/>
175      * This implementation throws an exception.
176      */

177     protected void doDelete() throws Exception JavaDoc
178     {
179         throw new FileSystemException("vfs.provider/delete-not-supported.error");
180     }
181
182     /**
183      * Renames the file. Is only called when:
184      * <ul>
185      * <li>{@link #doIsWriteable} returns true.
186      * </ul>
187      * <p/>
188      * This implementation throws an exception.
189      */

190     protected void doRename(FileObject newfile) throws Exception JavaDoc
191     {
192         throw new FileSystemException("vfs.provider/rename-not-supported.error");
193     }
194
195     /**
196      * Creates this file as a folder. Is only called when:
197      * <ul>
198      * <li>{@link #doGetType} returns {@link FileType#IMAGINARY}.
199      * <li>The parent folder exists and is writeable, or this file is the
200      * root of the file system.
201      * </ul>
202      * <p/>
203      * This implementation throws an exception.
204      */

205     protected void doCreateFolder() throws Exception JavaDoc
206     {
207         throw new FileSystemException("vfs.provider/create-folder-not-supported.error");
208     }
209
210     /**
211      * Called when the children of this file change. Allows subclasses to
212      * refresh any cached information about the children of this file.
213      * <p/>
214      * This implementation does nothing.
215      */

216     protected void onChildrenChanged(FileName child, FileType newType) throws Exception JavaDoc
217     {
218     }
219
220     /**
221      * Called when the type or content of this file changes.
222      * <p/>
223      * This implementation does nothing.
224      */

225     protected void onChange() throws Exception JavaDoc
226     {
227     }
228
229     /**
230      * Returns the last modified time of this file. Is only called if
231      * {@link #doGetType} does not return {@link FileType#IMAGINARY}.
232      * <p/>
233      * This implementation throws an exception.
234      */

235     protected long doGetLastModifiedTime() throws Exception JavaDoc
236     {
237         throw new FileSystemException("vfs.provider/get-last-modified-not-supported.error");
238     }
239
240     /**
241      * Sets the last modified time of this file. Is only called if
242      * {@link #doGetType} does not return {@link FileType#IMAGINARY}.
243      * <p/>
244      * This implementation throws an exception.
245      */

246     protected void doSetLastModifiedTime(final long modtime)
247         throws Exception JavaDoc
248     {
249         throw new FileSystemException("vfs.provider/set-last-modified-not-supported.error");
250     }
251
252     /**
253      * Returns the attributes of this file. Is only called if {@link #doGetType}
254      * does not return {@link FileType#IMAGINARY}.
255      * <p/>
256      * This implementation always returns an empty map.
257      */

258     protected Map JavaDoc doGetAttributes()
259         throws Exception JavaDoc
260     {
261         return Collections.EMPTY_MAP;
262     }
263
264     /**
265      * Sets an attribute of this file. Is only called if {@link #doGetType}
266      * does not return {@link FileType#IMAGINARY}.
267      * <p/>
268      * This implementation throws an exception.
269      */

270     protected void doSetAttribute(final String JavaDoc atttrName, final Object JavaDoc value)
271         throws Exception JavaDoc
272     {
273         throw new FileSystemException("vfs.provider/set-attribute-not-supported.error");
274     }
275
276     /**
277      * Returns the certificates used to sign this file. Is only called if
278      * {@link #doGetType} does not return {@link FileType#IMAGINARY}.
279      * <p/>
280      * This implementation always returns null.
281      */

282     protected Certificate JavaDoc[] doGetCertificates() throws Exception JavaDoc
283     {
284         return null;
285     }
286
287     /**
288      * Returns the size of the file content (in bytes). Is only called if
289      * {@link #doGetType} returns {@link FileType#FILE}.
290      */

291     protected abstract long doGetContentSize() throws Exception JavaDoc;
292
293     /**
294      * Creates an input stream to read the file content from. Is only called
295      * if {@link #doGetType} returns {@link FileType#FILE}.
296      * <p/>
297      * <p>It is guaranteed that there are no open output streams for this file
298      * when this method is called.
299      * <p/>
300      * <p>The returned stream does not have to be buffered.
301      */

302     protected abstract InputStream JavaDoc doGetInputStream() throws Exception JavaDoc;
303
304     /**
305      * Creates access to the file for random i/o. Is only called
306      * if {@link #doGetType} returns {@link FileType#FILE}.
307      * <p/>
308      * <p>It is guaranteed that there are no open output streams for this file
309      * when this method is called.
310      * <p/>
311      */

312     protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception JavaDoc
313     {
314         throw new FileSystemException("vfs.provider/random-access-not-supported.error");
315     }
316
317     /**
318      * Creates an output stream to write the file content to. Is only
319      * called if:
320      * <ul>
321      * <li>{@link #doIsWriteable} returns true.
322      * <li>{@link #doGetType} returns {@link FileType#FILE}, or
323      * {@link #doGetType} returns {@link FileType#IMAGINARY}, and the file's
324      * parent exists and is a folder.
325      * </ul>
326      * <p/>
327      * <p>It is guaranteed that there are no open stream (input or output) for
328      * this file when this method is called.
329      * <p/>
330      * <p>The returned stream does not have to be buffered.
331      * <p/>
332      * This implementation throws an exception.
333      */

334     protected OutputStream JavaDoc doGetOutputStream(boolean bAppend) throws Exception JavaDoc
335     {
336         throw new FileSystemException("vfs.provider/write-not-supported.error");
337     }
338
339     /**
340      * Returns the URI of the file.
341      */

342     public String JavaDoc toString()
343     {
344         return name.getURI();
345     }
346
347     /**
348      * Returns the name of the file.
349      */

350     public FileName getName()
351     {
352         return name;
353     }
354
355     /**
356      * Returns the file system this file belongs to.
357      */

358     public FileSystem getFileSystem()
359     {
360         return fs;
361     }
362
363     /**
364      * Returns a URL representation of the file.
365      */

366     public URL JavaDoc getURL() throws FileSystemException
367     {
368         final StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
369         try
370         {
371             return (URL JavaDoc) AccessController.doPrivileged(new PrivilegedExceptionAction JavaDoc()
372             {
373                 public Object JavaDoc run() throws MalformedURLException JavaDoc
374                 {
375                     return new URL JavaDoc(UriParser.extractScheme(name.getURI(), buf), "", -1,
376                         buf.toString(), new DefaultURLStreamHandler(fs.getContext(), fs.getFileSystemOptions()));
377                 }
378             });
379         }
380         catch (final PrivilegedActionException JavaDoc e)
381         {
382             throw new FileSystemException("vfs.provider/get-url.error", name, e.getException());
383         }
384     }
385
386     /**
387      * Determines if the file exists.
388      */

389     public boolean exists() throws FileSystemException
390     {
391         attach();
392         return (type != FileType.IMAGINARY);
393     }
394
395     /**
396      * Returns the file's type.
397      */

398     public FileType getType() throws FileSystemException
399     {
400         attach();
401         return type;
402     }
403
404     /**
405      * Determines if this file can be read.
406      */

407     public boolean isHidden() throws FileSystemException
408     {
409         try
410         {
411             attach();
412             if (exists())
413             {
414                 return doIsHidden();
415             }
416             else
417             {
418                 return false;
419             }
420         }
421         catch (final Exception JavaDoc exc)
422         {
423             throw new FileSystemException("vfs.provider/check-is-hidden.error", name, exc);
424         }
425     }
426
427     /**
428      * Determines if this file can be read.
429      */

430     public boolean isReadable() throws FileSystemException
431     {
432         try
433         {
434             attach();
435             if (exists())
436             {
437                 return doIsReadable();
438             }
439             else
440             {
441                 return false;
442             }
443         }
444         catch (final Exception JavaDoc exc)
445         {
446             throw new FileSystemException("vfs.provider/check-is-readable.error", name, exc);
447         }
448     }
449
450     /**
451      * Determines if this file can be written to.
452      */

453     public boolean isWriteable() throws FileSystemException
454     {
455         try
456         {
457             attach();
458             if (exists())
459             {
460                 return doIsWriteable();
461             }
462             else
463             {
464                 final FileObject parent = getParent();
465                 if (parent != null)
466                 {
467                     return parent.isWriteable();
468                 }
469                 return true;
470             }
471         }
472         catch (final Exception JavaDoc exc)
473         {
474             throw new FileSystemException("vfs.provider/check-is-writeable.error", name, exc);
475         }
476     }
477
478     /**
479      * Returns the parent of the file.
480      */

481     public FileObject getParent() throws FileSystemException
482     {
483         if (this == fs.getRoot())
484         {
485             if (fs.getParentLayer() != null)
486             {
487                 // Return the parent of the parent layer
488
return fs.getParentLayer().getParent();
489             }
490             else
491             {
492                 // Root file has no parent
493
return null;
494             }
495         }
496
497         synchronized (this)
498         {
499             // Locate the parent of this file
500
if (parent == null)
501             {
502                 parent = (AbstractFileObject) fs.resolveFile(name.getParent());
503             }
504         }
505         return parent;
506     }
507
508     /**
509      * Returns the children of the file.
510      */

511     public FileObject[] getChildren() throws FileSystemException
512     {
513         synchronized (this)
514         {
515             attach();
516             if (!type.hasChildren())
517             {
518                 throw new FileSystemException("vfs.provider/list-children-not-folder.error", name);
519             }
520
521             // Use cached info, if present
522
if (children != null)
523             {
524                 return resolveFiles(children);
525             }
526
527             // allow the filesystem to return resolved children. e.g. prefill type for webdav
528
FileObject[] childrenObjects;
529             try
530             {
531                 childrenObjects = doListChildrenResolved();
532                 children = extractNames(childrenObjects);
533             }
534             catch (Exception JavaDoc exc)
535             {
536                 throw new FileSystemException("vfs.provider/list-children.error", new Object JavaDoc[]{name}, exc);
537             }
538
539             if (childrenObjects != null)
540             {
541                 return childrenObjects;
542             }
543
544             // List the children
545
final String JavaDoc[] files;
546             try
547             {
548                 files = doListChildren();
549             }
550             catch (Exception JavaDoc exc)
551             {
552                 throw new FileSystemException("vfs.provider/list-children.error", new Object JavaDoc[]{name}, exc);
553             }
554
555             if (files == null || files.length == 0)
556             {
557                 // No children
558
children = EMPTY_FILE_ARRAY;
559             }
560             else
561             {
562                 // Create file objects for the children
563
// children = new FileObject[files.length];
564
children = new FileName[files.length];
565                 for (int i = 0; i < files.length; i++)
566                 {
567                     final String JavaDoc file = files[i];
568                     // children[i] = fs.resolveFile(name.resolveName(file, NameScope.CHILD));
569
// children[i] = name.resolveName(file, NameScope.CHILD);
570
children[i] = getFileSystem().getFileSystemManager().resolveName(name, file, NameScope.CHILD);
571                 }
572             }
573
574             return resolveFiles(children);
575         }
576     }
577
578     private FileName[] extractNames(FileObject[] objects)
579     {
580         if (objects == null)
581         {
582             return null;
583         }
584
585         FileName[] names = new FileName[objects.length];
586         for (int iterObjects = 0; iterObjects < objects.length; iterObjects++)
587         {
588             names[iterObjects] = objects[iterObjects].getName();
589         }
590
591         return names;
592     }
593
594     private FileObject[] resolveFiles(FileName[] children) throws FileSystemException
595     {
596         if (children == null)
597         {
598             return null;
599         }
600
601         FileObject[] objects = new FileObject[children.length];
602         for (int iterChildren = 0; iterChildren < children.length; iterChildren++)
603         {
604             objects[iterChildren] = resolveFile(children[iterChildren]);
605         }
606
607         return objects;
608     }
609
610     private FileObject resolveFile(FileName child) throws FileSystemException
611     {
612         return fs.resolveFile(child);
613     }
614
615     /**
616      * Returns a child of this file.
617      */

618     public FileObject getChild(final String JavaDoc name) throws FileSystemException
619     {
620         // TODO - use a hashtable when there are a large number of children
621
FileObject[] children = getChildren();
622         for (int i = 0; i < children.length; i++)
623         {
624             // final FileObject child = children[i];
625
final FileName child = children[i].getName();
626             // TODO - use a comparator to compare names
627
// if (child.getName().getBaseName().equals(name))
628
if (child.getBaseName().equals(name))
629             {
630                 return resolveFile(child);
631             }
632         }
633         return null;
634     }
635
636     /**
637      * Returns a child by name.
638      */

639     public FileObject resolveFile(final String JavaDoc name, final NameScope scope)
640         throws FileSystemException
641     {
642         // return fs.resolveFile(this.name.resolveName(name, scope));
643
return fs.resolveFile(getFileSystem().getFileSystemManager().resolveName(this.name, name, scope));
644     }
645
646     /**
647      * Finds a file, relative to this file.
648      *
649      * @param path The path of the file to locate. Can either be a relative
650      * path, which is resolved relative to this file, or an
651      * absolute path, which is resolved relative to the file system
652      * that contains this file.
653      */

654     public FileObject resolveFile(final String JavaDoc path) throws FileSystemException
655     {
656         final FileName otherName = getFileSystem().getFileSystemManager().resolveName(name, path);
657         return fs.resolveFile(otherName);
658     }
659
660     /**
661      * Deletes this file, once all its children have been deleted
662      *
663      * @return true if this file has been deleted
664      */

665     private boolean deleteSelf() throws FileSystemException
666     {
667         synchronized (this)
668         {
669             /* Its possible to delete a read-only file if you have write-execute access to the directory
670             if (!isWriteable())
671             {
672                 throw new FileSystemException("vfs.provider/delete-read-only.error", name);
673             }
674             */

675
676             if (getType() == FileType.IMAGINARY)
677             {
678                 // File does not exist
679
return false;
680             }
681
682             try
683             {
684                 // Delete the file
685
doDelete();
686
687                 // Update cached info
688
handleDelete();
689             }
690             catch (final RuntimeException JavaDoc re)
691             {
692                 throw re;
693             }
694             catch (final Exception JavaDoc exc)
695             {
696                 throw new FileSystemException("vfs.provider/delete.error", new Object JavaDoc[]{name}, exc);
697             }
698
699             return true;
700         }
701     }
702
703     /**
704      * Deletes this file.
705      *
706      * @return true if this object has been deleted
707      * @todo This will not fail if this is a non-empty folder.
708      */

709     public boolean delete() throws FileSystemException
710     {
711         return delete(Selectors.SELECT_SELF) > 0;
712     }
713
714     /**
715      * Deletes this file, and all children.
716      *
717      * @return the number of deleted files
718      */

719     public int delete(final FileSelector selector) throws FileSystemException
720     {
721         int nuofDeleted = 0;
722
723         if (getType() == FileType.IMAGINARY)
724         {
725             // File does not exist
726
return nuofDeleted;
727         }
728
729         // Locate all the files to delete
730
ArrayList JavaDoc files = new ArrayList JavaDoc();
731         findFiles(selector, true, files);
732
733         // Delete 'em
734
final int count = files.size();
735         for (int i = 0; i < count; i++)
736         {
737             final AbstractFileObject file = (AbstractFileObject) files.get(i);
738             // file.attach();
739

740             // If the file is a folder, make sure all its children have been deleted
741
if (file.getType() == FileType.FOLDER && file.getChildren().length != 0)
742             {
743                 // Skip - as the selector forced us not to delete all files
744
continue;
745             }
746
747             // Delete the file
748
boolean deleted = file.deleteSelf();
749             if (deleted)
750             {
751                 nuofDeleted++;
752             }
753         }
754
755         return nuofDeleted;
756     }
757
758     /**
759      * Creates this file, if it does not exist.
760      */

761     public void createFile() throws FileSystemException
762     {
763         synchronized (this)
764         {
765             try
766             {
767                 getOutputStream().close();
768                 endOutput();
769             }
770             catch (final RuntimeException JavaDoc re)
771             {
772                 throw re;
773             }
774             catch (final Exception JavaDoc e)
775             {
776                 throw new FileSystemException("vfs.provider/create-file.error", name, e);
777             }
778         }
779     }
780
781     /**
782      * Creates this folder, if it does not exist. Also creates any ancestor
783      * files which do not exist.
784      */

785     public void createFolder() throws FileSystemException
786     {
787         synchronized (this)
788         {
789             if (getType() == FileType.FOLDER)
790             {
791                 // Already exists as correct type
792
return;
793             }
794             if (getType() != FileType.IMAGINARY)
795             {
796                 throw new FileSystemException("vfs.provider/create-folder-mismatched-type.error", name);
797             }
798             if (!isWriteable())
799             {
800                 throw new FileSystemException("vfs.provider/create-folder-read-only.error", name);
801             }
802
803             // Traverse up the heirarchy and make sure everything is a folder
804
final FileObject parent = getParent();
805             if (parent != null)
806             {
807                 parent.createFolder();
808             }
809
810             try
811             {
812                 // Create the folder
813
doCreateFolder();
814
815                 // Update cached info
816
handleCreate(FileType.FOLDER);
817             }
818             catch (final RuntimeException JavaDoc re)
819             {
820                 throw re;
821             }
822             catch (final Exception JavaDoc exc)
823             {
824                 throw new FileSystemException("vfs.provider/create-folder.error", name, exc);
825             }
826         }
827     }
828
829     /**
830      * Copies another file to this file.
831      */

832     public void copyFrom(final FileObject file, final FileSelector selector)
833         throws FileSystemException
834     {
835         if (!file.exists())
836         {
837             throw new FileSystemException("vfs.provider/copy-missing-file.error", file);
838         }
839         if (!isWriteable())
840         {
841             throw new FileSystemException("vfs.provider/copy-read-only.error", new Object JavaDoc[]{file.getType(), file.getName(), this}, null);
842         }
843
844         // Locate the files to copy across
845
final ArrayList JavaDoc files = new ArrayList JavaDoc();
846         file.findFiles(selector, false, files);
847
848         // Copy everything across
849
final int count = files.size();
850         for (int i = 0; i < count; i++)
851         {
852             final FileObject srcFile = (FileObject) files.get(i);
853
854             // Determine the destination file
855
final String JavaDoc relPath = file.getName().getRelativeName(srcFile.getName());
856             final FileObject destFile = resolveFile(relPath, NameScope.DESCENDENT_OR_SELF);
857
858             // Clean up the destination file, if necessary
859
if (destFile.exists() && destFile.getType() != srcFile.getType())
860             {
861                 // The destination file exists, and is not of the same type,
862
// so delete it
863
// TODO - add a pluggable policy for deleting and overwriting existing files
864
destFile.delete(Selectors.SELECT_ALL);
865             }
866
867             // Copy across
868
try
869             {
870                 if (srcFile.getType().hasContent())
871                 {
872                     FileUtil.copyContent(srcFile, destFile);
873                 }
874                 else if (srcFile.getType().hasChildren())
875                 {
876                     destFile.createFolder();
877                 }
878             }
879             catch (final IOException JavaDoc e)
880             {
881                 throw new FileSystemException("vfs.provider/copy-file.error", new Object JavaDoc[]{srcFile, destFile}, e);
882             }
883         }
884     }
885
886     /**
887      * Moves (rename) the file to another one
888      */

889     public void moveTo(FileObject destFile) throws FileSystemException
890     {
891         if (!isWriteable())
892         {
893             throw new FileSystemException("vfs.provider/rename-read-only.error", getName());
894         }
895         if (destFile.exists())
896         {
897             destFile.delete(Selectors.SELECT_ALL);
898             // throw new FileSystemException("vfs.provider/rename-dest-exists.error", destFile.getName());
899
}
900
901         if (canRenameTo(destFile))
902         {
903             // issue rename on same filesystem
904
try
905             {
906                 doRename(destFile);
907
908                 ((AbstractFileObject) destFile).handleCreate(getType());
909
910                 destFile.close(); // now the destFile is no longer imaginary. force reattach.
911

912                 handleDelete(); // fire delete-events. This file-object (src) is like deleted.
913
}
914             catch (final RuntimeException JavaDoc re)
915             {
916                 throw re;
917             }
918             catch (final Exception JavaDoc exc)
919             {
920                 throw new FileSystemException("vfs.provider/rename.error", new Object JavaDoc[]
921                     {
922                         getName(),
923                         destFile.getName()
924                     }, exc);
925             }
926         }
927         else
928         {
929             // different fs - do the copy/delete stuff
930

931             destFile.copyFrom(this, Selectors.SELECT_SELF);
932
933             if (((destFile.getType() == FileType.FILE && destFile.getFileSystem().hasCapability(Capability.SET_LAST_MODIFIED_FILE)) ||
934                 (destFile.getType() == FileType.FOLDER && destFile.getFileSystem().hasCapability(Capability.SET_LAST_MODIFIED_FOLDER))) &&
935                 getFileSystem().hasCapability(Capability.GET_LAST_MODIFIED))
936             {
937                 destFile.getContent().setLastModifiedTime(this.getContent().getLastModifiedTime());
938             }
939
940             deleteSelf();
941         }
942
943     }
944
945     /**
946      * Queries the object if a simple rename to the filename of <code>newfile</code>
947      * is possible.
948      *
949      * @param newfile the new filename
950      * @return true if rename is possible
951      */

952     public boolean canRenameTo(FileObject newfile)
953     {
954         if (getFileSystem() == newfile.getFileSystem())
955         {
956             return true;
957         }
958
959         return false;
960     }
961
962     /**
963      * Finds the set of matching descendents of this file, in depthwise
964      * order.
965      *
966      * @return list of files or null if the base file (this object) do not exist
967      */

968     public FileObject[] findFiles(final FileSelector selector) throws FileSystemException
969     {
970         if (!exists())
971         {
972             return null;
973         }
974
975         final ArrayList JavaDoc list = new ArrayList JavaDoc();
976         findFiles(selector, true, list);
977         return (FileObject[]) list.toArray(new FileObject[list.size()]);
978     }
979
980     /**
981      * Returns the file's content.
982      */

983     public FileContent getContent() throws FileSystemException
984     {
985         attach();
986         if (content == null)
987         {
988             content = new DefaultFileContent(this, getFileContentInfoFactory());
989         }
990         return content;
991     }
992
993     /**
994      * Closes this file, and its content.
995      */

996     public void close() throws FileSystemException
997     {
998         FileSystemException exc = null;
999
1000        // Close the content
1001
if (content != null)
1002        {
1003            try
1004            {
1005                content.close();
1006            }
1007            catch (FileSystemException e)
1008            {
1009                exc = e;
1010            }
1011        }
1012
1013        // Detach from the file
1014
try
1015        {
1016            detach();
1017        }
1018        catch (final Exception JavaDoc e)
1019        {
1020            exc = new FileSystemException("vfs.provider/close.error", name, e);
1021        }
1022
1023        if (exc != null)
1024        {
1025            throw exc;
1026        }
1027    }
1028
1029    /**
1030     * Returns an input stream to use to read the content of the file.
1031     */

1032    public InputStream JavaDoc getInputStream() throws FileSystemException
1033    {
1034        attach();
1035        if (!type.hasContent())
1036        {
1037            throw new FileSystemException("vfs.provider/read-not-file.error", name);
1038        }
1039        if (!isReadable())
1040        {
1041            throw new FileSystemException("vfs.provider/read-not-readable.error", name);
1042        }
1043
1044        // Get the raw input stream
1045
try
1046        {
1047            return doGetInputStream();
1048        }
1049        catch (final Exception JavaDoc exc)
1050        {
1051            throw new FileSystemException("vfs.provider/read.error", name, exc);
1052        }
1053    }
1054
1055    /**
1056     * Returns an input/output stream to use to read and write the content of the file in and
1057     * random manner.
1058     */

1059    public RandomAccessContent getRandomAccessContent(final RandomAccessMode mode) throws FileSystemException
1060    {
1061        attach();
1062        if (!type.hasContent())
1063        {
1064            throw new FileSystemException("vfs.provider/read-not-file.error", name);
1065        }
1066
1067        if (mode.requestRead())
1068        {
1069            if (!getFileSystem().hasCapability(Capability.RANDOM_ACCESS_READ))
1070            {
1071                throw new FileSystemException("vfs.provider/random-access-read-not-supported.error");
1072            }
1073            if (!isReadable())
1074            {
1075                throw new FileSystemException("vfs.provider/read-not-readable.error", name);
1076            }
1077        }
1078
1079        if (mode.requestWrite())
1080        {
1081            if (!getFileSystem().hasCapability(Capability.RANDOM_ACCESS_WRITE))
1082            {
1083                throw new FileSystemException("vfs.provider/random-access-write-not-supported.error");
1084            }
1085            if (!isWriteable())
1086            {
1087                throw new FileSystemException("vfs.provider/write-read-only.error", name);
1088            }
1089        }
1090
1091        // Get the raw input stream
1092
try
1093        {
1094            return doGetRandomAccessContent(mode);
1095        }
1096        catch (final Exception JavaDoc exc)
1097        {
1098            throw new FileSystemException("vfs.provider/random-access.error", name, exc);
1099        }
1100    }
1101
1102    /**
1103     * Prepares this file for writing. Makes sure it is either a file,
1104     * or its parent folder exists. Returns an output stream to use to
1105     * write the content of the file to.
1106     */

1107    public OutputStream JavaDoc getOutputStream() throws FileSystemException
1108    {
1109        return getOutputStream(false);
1110    }
1111
1112    /**
1113     * Prepares this file for writing. Makes sure it is either a file,
1114     * or its parent folder exists. Returns an output stream to use to
1115     * write the content of the file to.<br>
1116     *
1117     * @param bAppend true when append to the file.<br>
1118     * Note: If the underlaying filesystem do not support this, it wont work.
1119     */

1120    public OutputStream JavaDoc getOutputStream(boolean bAppend) throws FileSystemException
1121    {
1122        if (getType() != FileType.IMAGINARY && !getType().hasContent())
1123        {
1124            throw new FileSystemException("vfs.provider/write-not-file.error", name);
1125        }
1126        if (!isWriteable())
1127        {
1128            throw new FileSystemException("vfs.provider/write-read-only.error", name);
1129        }
1130        if (bAppend && !getFileSystem().hasCapability(Capability.APPEND_CONTENT))
1131        {
1132            throw new FileSystemException("vfs.provider/write-append-not-supported.error", name);
1133        }
1134
1135        if (getType() == FileType.IMAGINARY)
1136        {
1137// Does not exist - make sure parent does
1138
FileObject parent = getParent();
1139            if (parent != null)
1140            {
1141                parent.createFolder();
1142            }
1143        }
1144
1145// Get the raw output stream
1146
try
1147        {
1148            return doGetOutputStream(bAppend);
1149        }
1150        catch (RuntimeException JavaDoc re)
1151        {
1152            throw re;
1153        }
1154        catch (Exception JavaDoc exc)
1155        {
1156            throw new FileSystemException("vfs.provider/write.error", new Object JavaDoc[]{name}, exc);
1157        }
1158    }
1159
1160    /**
1161     * Detaches this file, invaliating all cached info. This will force
1162     * a call to {@link #doAttach} next time this file is used.
1163     */

1164    private void detach() throws Exception JavaDoc
1165    {
1166        synchronized (this)
1167        {
1168            if (attached)
1169            {
1170                try
1171                {
1172                    doDetach();
1173                }
1174                finally
1175                {
1176                    attached = false;
1177                    setFileType(null);
1178                    parent = null;
1179
1180                    fs.fileDetached(this);
1181
1182                    removeChildrenCache();
1183                    // children = null;
1184
}
1185            }
1186        }
1187    }
1188
1189    private void removeChildrenCache()
1190    {
1191        /*
1192        if (children != null)
1193        {
1194            for (int iterChildren = 0; iterChildren < children.length; iterChildren++)
1195            {
1196                fs.removeFileFromCache(children[iterChildren].getName());
1197            }
1198
1199            children = null;
1200        }
1201        */

1202        children = null;
1203    }
1204
1205    /**
1206     * Attaches to the file.
1207     */

1208    private void attach() throws FileSystemException
1209    {
1210        synchronized (this)
1211        {
1212            if (attached)
1213            {
1214                return;
1215            }
1216
1217            try
1218            {
1219                // Attach and determine the file type
1220
doAttach();
1221                attached = true;
1222                // now the type could already be injected by doAttach (e.g from parent to child)
1223
if (type == null)
1224                {
1225                    setFileType(doGetType());
1226                }
1227                if (type == null)
1228                {
1229                    setFileType(FileType.IMAGINARY);
1230                }
1231            }
1232            catch (Exception JavaDoc exc)
1233            {
1234                throw new FileSystemException("vfs.provider/get-type.error", new Object JavaDoc[]{name}, exc);
1235            }
1236
1237            fs.fileAttached(this);
1238        }
1239    }
1240
1241    /**
1242     * Called when the ouput stream for this file is closed.
1243     */

1244    protected void endOutput() throws Exception JavaDoc
1245    {
1246        if (getType() == FileType.IMAGINARY)
1247        {
1248            // File was created
1249
handleCreate(FileType.FILE);
1250        }
1251        else
1252        {
1253            // File has changed
1254
onChange();
1255        }
1256    }
1257
1258    /**
1259     * Called when this file is created. Updates cached info and notifies
1260     * the parent and file system.
1261     */

1262    protected void handleCreate(final FileType newType) throws Exception JavaDoc
1263    {
1264        synchronized (this)
1265        {
1266            if (attached)
1267            {
1268                // Fix up state
1269
injectType(newType);
1270
1271                removeChildrenCache();
1272                children = EMPTY_FILE_ARRAY;
1273
1274                // Notify subclass
1275
onChange();
1276            }
1277
1278            // Notify parent that its child list may no longer be valid
1279
notifyParent(this.getName(), newType);
1280
1281            // Notify the file system
1282
fs.fireFileCreated(this);
1283        }
1284    }
1285
1286    /**
1287     * Called when this file is deleted. Updates cached info and notifies
1288     * subclasses, parent and file system.
1289     */

1290    protected void handleDelete() throws Exception JavaDoc
1291    {
1292        synchronized (this)
1293        {
1294            if (attached)
1295            {
1296                // Fix up state
1297
injectType(FileType.IMAGINARY);
1298                removeChildrenCache();
1299                // children = null;
1300

1301                // Notify subclass
1302
onChange();
1303            }
1304
1305            // Notify parent that its child list may no longer be valid
1306
notifyParent(this.getName(), FileType.IMAGINARY);
1307
1308            // Notify the file system
1309
fs.fireFileDeleted(this);
1310        }
1311    }
1312
1313    /**
1314     * Called when this file is changed.<br />
1315     * This will only happen if you monitor the file using {@link org.apache.commons.vfs.FileMonitor}.
1316     */

1317    protected void handleChanged() throws Exception JavaDoc
1318    {
1319        // Notify the file system
1320
fs.fireFileChanged(this);
1321    }
1322
1323    /**
1324     * Notifies the file that its children have changed.
1325     *
1326     * @deprecated use {@link #childrenChanged(FileName, FileType)}
1327     */

1328    protected void childrenChanged() throws Exception JavaDoc
1329    {
1330        childrenChanged(null, null);
1331    }
1332
1333    /**
1334     * Notifies the file that its children have changed.
1335     */

1336    protected void childrenChanged(FileName childName, FileType newType) throws Exception JavaDoc
1337    {
1338        // TODO - this may be called when not attached
1339

1340        if (children != null)
1341        {
1342            if (childName != null && newType != null)
1343            {
1344                // TODO - figure out if children[] can be replaced by list
1345
ArrayList JavaDoc list = new ArrayList JavaDoc(Arrays.asList(children));
1346                if (newType.equals(FileType.IMAGINARY))
1347                {
1348                    list.remove(childName);
1349                }
1350                else
1351                {
1352                    list.add(childName);
1353                }
1354                children = new FileName[list.size()];
1355                list.toArray(children);
1356            }
1357        }
1358
1359        // removeChildrenCache();
1360
onChildrenChanged(childName, newType);
1361    }
1362
1363    /**
1364     * Notify the parent of a change to its children, when a child is created
1365     * or deleted.
1366     */

1367    private void notifyParent(FileName childName, FileType newType) throws Exception JavaDoc
1368    {
1369        if (parent == null)
1370        {
1371            FileName parentName = name.getParent();
1372            if (parentName != null)
1373            {
1374                // Locate the parent, if it is cached
1375
parent = (AbstractFileObject) fs.getFileFromCache(parentName);
1376            }
1377        }
1378
1379        if (parent != null)
1380        {
1381            parent.childrenChanged(childName, newType);
1382        }
1383    }
1384
1385    /**
1386     * Traverses the descendents of this file, and builds a list of selected
1387     * files.
1388     */

1389    public void findFiles(final FileSelector selector,
1390                          final boolean depthwise,
1391                          final List JavaDoc selected) throws FileSystemException
1392    {
1393        try
1394        {
1395            if (exists())
1396            {
1397                // Traverse starting at this file
1398
final DefaultFileSelectorInfo info = new DefaultFileSelectorInfo();
1399                info.setBaseFolder(this);
1400                info.setDepth(0);
1401                info.setFile(this);
1402                traverse(info, selector, depthwise, selected);
1403            }
1404        }
1405        catch (final Exception JavaDoc e)
1406        {
1407            throw new FileSystemException("vfs.provider/find-files.error", name, e);
1408        }
1409    }
1410
1411    /**
1412     * Traverses a file.
1413     */

1414    private static void traverse(final DefaultFileSelectorInfo fileInfo,
1415                                 final FileSelector selector,
1416                                 final boolean depthwise,
1417                                 final List JavaDoc selected)
1418        throws Exception JavaDoc
1419    {
1420        // Check the file itself
1421
final FileObject file = fileInfo.getFile();
1422        final int index = selected.size();
1423
1424        // If the file is a folder, traverse it
1425
if (file.getType().hasChildren() && selector.traverseDescendents(fileInfo))
1426        {
1427            final int curDepth = fileInfo.getDepth();
1428            fileInfo.setDepth(curDepth + 1);
1429
1430            // Traverse the children
1431
final FileObject[] children = file.getChildren();
1432            for (int i = 0; i < children.length; i++)
1433            {
1434                final FileObject child = children[i];
1435                fileInfo.setFile(child);
1436                traverse(fileInfo, selector, depthwise, selected);
1437            }
1438
1439            fileInfo.setFile(file);
1440            fileInfo.setDepth(curDepth);
1441        }
1442
1443        // Add the file if doing depthwise traversal
1444
if (selector.includeFile(fileInfo))
1445        {
1446            if (depthwise)
1447            {
1448                // Add this file after its descendents
1449
selected.add(file);
1450            }
1451            else
1452            {
1453                // Add this file before its descendents
1454
selected.add(index, file);
1455            }
1456        }
1457    }
1458
1459    /**
1460     * Check if the content stream is open
1461     *
1462     * @return true if this is the case
1463     */

1464    public boolean isContentOpen()
1465    {
1466        if (content == null)
1467        {
1468            return false;
1469        }
1470
1471        return content.isOpen();
1472    }
1473
1474    /**
1475     * Check if the internal state is "attached"
1476     *
1477     * @return true if this is the case
1478     */

1479    public boolean isAttached()
1480    {
1481        return attached;
1482    }
1483
1484    /**
1485     * create the filecontentinfo implementation
1486     */

1487    protected FileContentInfoFactory getFileContentInfoFactory()
1488    {
1489        return getFileSystem().getFileSystemManager().getFileContentInfoFactory();
1490    }
1491
1492    protected void injectType(FileType fileType)
1493    {
1494        setFileType(fileType);
1495    }
1496
1497    private void setFileType(FileType type)
1498    {
1499        if (type != null && type != FileType.IMAGINARY)
1500        {
1501            try
1502            {
1503                name.setType(type);
1504            }
1505            catch (FileSystemException e)
1506            {
1507                throw new RuntimeException JavaDoc(e.getMessage());
1508            }
1509        }
1510        this.type = type;
1511    }
1512
1513    /**
1514     * This method is meant to add a object where this object holds a strong reference then.
1515     * E.g. a archive-filesystem creates a list of all childs and they shouldnt get
1516     * garbage collected until the container is garbage collected
1517     *
1518     * @param strongRef
1519     */

1520    public void holdObject(Object JavaDoc strongRef)
1521    {
1522        if (objects == null)
1523        {
1524            objects = new ArrayList JavaDoc(5);
1525        }
1526        objects.add(strongRef);
1527    }
1528
1529    /**
1530     * will be called after this file-object closed all its streams.
1531     */

1532    protected void notifyAllStreamsClosed()
1533    {
1534    }
1535}
1536
Popular Tags