View Javadoc

1   ////////////////////////////////////////////////////////////////////////////////
2   // MillScript: an Open Spice interpreter and batch website creation tool
3   // Copyright (C) 2005 Kevin Rogers
4   //
5   // This file is part of MillScript.
6   //
7   // MillScript is free software; you can redistribute it and/or modify it under
8   // the terms of the GNU General Public License as published by the Free
9   // Software Foundation; either version 2 of the License, or (at your option)
10  // any later version.
11  //
12  // MillScript is distributed in the hope that it will be useful, but WITHOUT
13  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  // more details.
16  //
17  // You should have received a copy of the GNU General Public License along with
18  // MillScript; if not, write to the Free Software Foundation, Inc., 59 Temple
19  // Place, Suite 330, Boston, MA  02111-1307  USA
20  ////////////////////////////////////////////////////////////////////////////////
21  package org.millscript.commons.vfs;
22  
23  import org.millscript.commons.alert.Alert;
24  import org.millscript.commons.alert.AlertDecorator;
25  import org.millscript.commons.alert.alerts.Fault;
26  import org.millscript.commons.alert.alerts.IOAlert;
27  import org.millscript.commons.vfs.alerts.VEntryNotFoundAlert;
28  import org.millscript.commons.vfs.protocols.file.LocalFile;
29  
30  import java.io.File;
31  import java.io.FileNotFoundException;
32  import java.io.IOException;
33  import java.io.InputStream;
34  import java.io.InputStreamReader;
35  import java.io.OutputStream;
36  import java.io.OutputStreamWriter;
37  import java.io.Reader;
38  import java.io.Writer;
39  import java.net.URI;
40  
41  /**
42   * This class provides the skeletal implementation of a virtual filesystem
43   * file,
44   */
45  public abstract class AbstractVFile< V extends VVolume > extends AbstractVEntry< V > implements AlertDecorator, VFile {
46  
47      /**
48       * Constructs a new virtual filesystem file with the specified name,
49       * parent folder and volume.
50       *
51       * @param vol   the volume this entry is held on
52       * @param parent    the parent folder for this file
53       * @param name  the name of the file
54       */
55      public AbstractVFile( final V vol, final VFolder parent, final String name ) {
56          super( vol, parent, name );
57      }
58  
59      /**
60       * @see org.millscript.commons.vfs.VEntry#appendRelativePath(java.lang.StringBuffer)
61       */
62      public final StringBuffer appendRelativePath( final StringBuffer buff ) {
63          // If we have a parent, append it's relative path to the buffer.
64          // This will recurse all the way up the chain to give us the relative
65          // path from the root of the filesystem.
66          if ( this.getParent() != null ) {
67              this.getParent().appendRelativePath( buff );
68          }
69          return buff.append( this.getName() );
70      }
71  
72      /**
73       * @see org.millscript.commons.vfs.VEntry#appendRelativePathOnVolume(java.lang.StringBuffer)
74       */
75      public StringBuffer appendRelativePathOnVolume( final StringBuffer buff ) {
76          // If we have a parent and it's volume is the same as ours, append it's
77          // relative path to the buffer.
78          // This will recurse all the way up the chain to the root of the volume
79          // this file belongs on, to give us the relative path from the root of
80          // the volume.
81          // TODO - This should probably use equality not identity but we need
82          // implementations of hashCode() and equals()
83          if ( this.getParent() == null ) {
84              // Ah. No parent, so we must be at the top of the hierarchy
85              return buff.append( this.getName() );
86          } else if ( this.getParent().getVolume() == this.getVolume() ) {
87              // The parent volume is the same as this one, so we should append
88              // the parents path as it's on the same volume
89              this.getParent().appendRelativePathOnVolume( buff );
90          }
91          return buff.append( this.getName() );
92      }
93  
94      /**
95       * @see org.millscript.commons.vfs.VEntry#appendURI(java.lang.StringBuffer)
96       */
97      public final StringBuffer appendURI( final StringBuffer buffer ) {
98          // If we have a parent, append it's URI to the buffer.
99          // This will recurse all the way up the chain to give us the complete
100         // URI for this entry
101         if ( this.getParent() == null ) {
102             // No parent - this is the root entry
103             this.getVolume().appendBaseURI( buffer );
104         } else {
105             // We have a parent, so append it's URI
106             this.getParent().appendURI( buffer );
107         }
108         return buffer.append( this.getName() );
109     }
110 
111     /**
112      * @see org.millscript.commons.alert.AlertDecorator#decorate(org.millscript.commons.alert.Alert)
113      */
114     public Alert decorate( final Alert alert ) {
115         return alert.culprit( "vfile", this );
116     }
117 
118     /**
119      * @see org.millscript.commons.vfs.VFile#getAppendWriter()
120      */
121     public final Writer getAppendWriter() {
122         return new OutputStreamWriter(
123             this.getAppendOutputStream(),
124             this.getVolume().getParentVFS().getDefaultCharset()
125         );
126     }
127 
128     /**
129      * @see org.millscript.commons.vfs.VFile#getContentMIMEType()
130      */
131     public String getContentMIMEType() {
132         return this.getMIMEType( this.getName() );
133     }
134 
135     /**
136      * @see org.millscript.commons.vfs.VFile#getReader()
137      */
138     public final Reader getReader() {
139         return new InputStreamReader(
140             this.getInputStream(),
141             this.getVolume().getParentVFS().getDefaultCharset()
142         );
143     }
144 
145     /**
146      * @see org.millscript.commons.vfs.VFile#getWriter()
147      */
148     public final Writer getWriter() {
149         return new OutputStreamWriter(
150             this.getOutputStream(),
151             this.getVolume().getParentVFS().getDefaultCharset()
152         );
153     }
154 
155     /**
156      * @see org.millscript.commons.vfs.VFile#toLocal()
157      */
158     public LocalFile toLocal() {
159         // TODO - Must add hook to remove temporary files
160         // Get the virtual file name, to use as a template for the temporary
161         // one
162         final String originialFileName = this.getName();
163         LocalFile tempFile;
164         try {
165             final String name = originialFileName.substring( 0, originialFileName.indexOf( '.' ) );
166             final String extn = originialFileName.substring( originialFileName.indexOf( '.' ) );
167             final URI localFileURI = File.createTempFile( name, extn, null ).toURI();
168             tempFile = (LocalFile) this.getVolume().getParentVFS().resolveAsFile( localFileURI );
169         } catch ( IOException ex ) {
170             throw new IOAlert(
171                 "Could not create a temporary copy of the virtual file"
172             ).culpritURI( this.getURI() ).setParentThrowable( ex ).mishap();
173         }
174         InputStream source = null;
175         OutputStream destination = null;
176         try {
177             // We have to put this code here to ensure proper clean up if
178             // something goes wrong. e.g. the variables need to be declared
179             // outside the try-block so that we can refer to them inside the
180             // catch, to ensure that we free any resources used by the streams
181             source = this.getInputStream();
182             destination = tempFile.getOutputStream();
183             // Get a buffer to use for copying the file
184             final byte[] buffer = new byte[1024];
185             int len = 0;
186             // Transfer everything from the input to the output
187             while ( ( len = source.read( buffer ) ) > 0 ) {
188                  destination.write( buffer, 0, len );
189             }
190             source.close();
191             destination.close();
192             return tempFile;
193         } catch ( FileNotFoundException ex ) {
194             // Clean up - avoids leaking file descriptors
195             try {
196                 if ( source != null ) {
197                     source.close();
198                 }
199                 if ( destination != null ) {
200                     destination.close();
201                 }
202             } catch ( IOException ex2 ) {
203                 throw new Fault(
204                     "Failed to close input or output stream"
205                 ).mishap();
206             }
207             // WTF? We've only just made it, but I suppose it could happen...
208             throw new VEntryNotFoundAlert(
209                 "Temporary file seems to have disappeared before we could open it!"
210             ).setParentThrowable( ex ).mishap();
211         } catch ( IOException ex ) {
212             // Clean up - avoids leaking file descriptors
213             try {
214                 if ( source != null ) {
215                     source.close();
216                 }
217                 if ( destination != null ) {
218                     destination.close();
219                 }
220             } catch ( IOException ex2 ) {
221                 throw new Fault(
222                     "Failed to close input or output stream"
223                 ).mishap();
224             }
225             throw new IOAlert(
226                 "A problem occured transfering data from the virtual file to the temporary copy"
227             ).culpritURI( this.getURI() ).setParentThrowable( ex ).mishap();
228         }
229     }
230 
231     /**
232      * @see java.lang.Object#toString()
233      */
234     @Override
235     public String toString() {
236         final StringBuffer buff = new StringBuffer( "VFILE( " );
237         this.appendAbsolutePath( buff );
238         buff.append( " )" );
239         return buff.toString();
240     }
241 
242 }