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.mime.zip;
22  
23  import org.millscript.commons.vfs.AbstractVFolder;
24  import org.millscript.commons.vfs.VEntry;
25  import org.millscript.commons.vfs.VFile;
26  import org.millscript.commons.vfs.VFolder;
27  import org.millscript.commons.vfs.alerts.VEntryNotFoundAlert;
28  import org.millscript.commons.vfs.alerts.VFSAlert;
29  
30  import java.util.ArrayList;
31  import java.util.Enumeration;
32  import java.util.List;
33  import java.util.zip.ZipEntry;
34  
35  /**
36   * This class implements a virtual filesystem folder for a read-only zip file
37   * folder.
38   */
39  public class ReadOnlyZipFolder extends AbstractVFolder< ReadOnlyZipVolume > {
40  
41      /**
42       * Constructs a new virtual filesystem folder with the specified name,
43       * parent folder and volume.
44       *
45       * @param vol   the volume this entry is held on
46       * @param parent    the parent folder for this folder
47       * @param name  the name of the folder
48       */
49      ReadOnlyZipFolder( final ReadOnlyZipVolume vol, final VFolder parent, final String name ) {
50          super( vol, parent, name );
51      }
52  
53      /**
54       * Constructs a new virtual filesystem folder with the specified name,
55       * parent folder. This folder will belong to the same volume as the
56       * specified parent folder.
57       *
58       * @param parent    the parent folder for this folder
59       * @param name  the name of the folder
60       */
61      ReadOnlyZipFolder( final ReadOnlyZipFolder parent, final String name ) {
62          this( parent.getVolume(), parent, name );
63      }
64  
65      /**
66       * @see org.millscript.commons.vfs.VEntry#exists()
67       */
68      public boolean exists() {
69          final String path = this.getRelativePathOnVolume();
70          if ( this.getZipEntry( path ) == null ) {
71              return false;
72          }
73          return true;
74      }
75  
76      /**
77       * @see org.millscript.commons.vfs.VFolder#checkVEntry(java.lang.String)
78       */
79      public VEntry checkVEntry( final String segment ) {
80          final StringBuffer pathToFileBuffer = this.appendRelativePathOnVolume( new StringBuffer() ).append( segment );
81          final String pathToFile = pathToFileBuffer.toString();
82          if ( this.getZipEntry( pathToFileBuffer.append( '/' ).toString() ) != null ) {
83              // There is an entry with the segment name and a / at the end so it
84              // must be a folder
85              return new ReadOnlyZipFolder( this, segment );
86          } else if ( this.getZipEntry( pathToFile ) != null ) {
87              // There is an entry with the segment name so it must be a file
88              return new ReadOnlyZipFile( this, segment );
89          } else {
90              throw VEntryNotFoundAlert.entryDoesNotExist( segment ).mishap();
91          }
92      }
93  
94      /**
95       * @see org.millscript.commons.vfs.VFolder#checkVFile(java.lang.String)
96       */
97      public ReadOnlyZipFile checkVFile( final String segment ) {
98          final StringBuffer pathToFileBuffer = this.appendRelativePathOnVolume( new StringBuffer() ).append( segment );
99          final String pathToFile = pathToFileBuffer.toString();
100         if ( this.getZipEntry( pathToFileBuffer.append( '/' ).toString() ) != null ) {
101             // There is an entry with the segment name and a / at the end so it
102             // must be a folder. This is an error as we want a file.
103             throw VEntryNotFoundAlert.entryIsNotAFile( segment ).mishap();
104         } else if ( this.getZipEntry( pathToFile ) != null ) {
105             // There is an entry with the segment name so it must be a file
106             return new ReadOnlyZipFile( this, segment );
107         } else {
108             throw VEntryNotFoundAlert.entryDoesNotExist( segment ).mishap();
109         }
110     }
111 
112     /**
113      * @see org.millscript.commons.vfs.VFolder#checkVFolder(java.lang.String)
114      */
115     public ReadOnlyZipFolder checkVFolder( final String segment ) {
116         final StringBuffer pathToFileBuffer = this.appendRelativePathOnVolume( new StringBuffer() ).append( segment );
117         final String pathToFile = pathToFileBuffer.toString();
118         if ( this.getZipEntry( pathToFileBuffer.append( '/' ).toString() ) != null ) {
119             // There is an entry with the segment name and a / at the end so it
120             // must be a folder
121             return new ReadOnlyZipFolder( this, segment );
122         } else if ( this.getZipEntry( pathToFile ) != null ) {
123             // There is an entry with the segment name so it must be a file.
124             // This is an error as we want a folder
125             throw VEntryNotFoundAlert.entryIsNotAFolder( segment ).mishap();
126         } else {
127             throw VEntryNotFoundAlert.entryDoesNotExist( segment ).mishap();
128         }
129     }
130 
131     /**
132      * Returns an enumeration over all the entries in this ZIP file.
133      *
134      * @return  an Enumeration over all the entries in this ZIP file.
135      */
136     final Enumeration getEntries() {
137         return this.getVolume().getEntries();
138     }
139 
140     /**
141      * @see org.millscript.commons.vfs.VFolder#getVFile(java.lang.String)
142      */
143     public ReadOnlyZipFile getVFile( final String segment ) {
144         return new ReadOnlyZipFile( this, segment );
145     }
146 
147     /**
148      * @see org.millscript.commons.vfs.VFolder#getVFolder(java.lang.String)
149      */
150     public ReadOnlyZipFolder getVFolder( final String segment ) {
151         return new ReadOnlyZipFolder( this, segment );
152     }
153 
154     /**
155      * Returns the ZipEntry for the specified path.
156      *
157      * @param path  the path to get a ZipEntry for
158      * @return  a ZipEntry for the specified path, or <code>null</code> if the
159      * path doesn't exist
160      */
161     final ZipEntry getZipEntry( final String path ) {
162         return this.getVolume().getZipEntry( path );
163     }
164 
165     /**
166      * @see org.millscript.commons.vfs.VFolder#listEntries()
167      */
168     public List< VEntry > listEntries() {
169         // The path for this folder
170         final String path = this.getRelativePathOnVolume();
171         // The length of the path for this folder
172         final int pathLength = path.length();
173         final Enumeration e = this.getEntries();
174         final ArrayList< VEntry > list = new ArrayList< VEntry >();
175         while ( e.hasMoreElements() ) {
176             final ZipEntry ze = (ZipEntry) e.nextElement();
177             final String entryName = ze.getName();
178             // Check the ZIP entry against this folders path. To be a sub-entry
179             // the entry name must start with this folders path and be longer.
180             if ( entryName.startsWith( path ) && entryName.length() > pathLength ) {
181                 // Get the position of the first / in the entry name starting at
182                 // the end of this folders path.
183                 final int n = entryName.indexOf( '/', pathLength );
184                 // Check the kind of entry, e.g. if there's no / in the entry name
185                 // or it's the only one at the end then the entry is in this folder
186                 // in the hierarchy
187                 if ( n == -1 ) {
188                     // No trailing slash, it must be a file
189                     list.add( new ReadOnlyZipFile( this, entryName.substring( pathLength ) ) );
190                 } else if ( n == entryName.length() - 1 ) {
191                     // a slash as the last character in the entry name, so it's
192                     // a sub folder
193                     list.add( new ReadOnlyZipFolder( this, entryName.substring( pathLength, n ) ) );
194                 }
195             }
196         }
197         return list;
198     }
199 
200     /**
201      * @see org.millscript.commons.vfs.VFolder#listFiles()
202      */
203     public List< VFile > listFiles() {
204         // The path for this folder
205         final String path = this.getRelativePathOnVolume();
206         // The length of the path for this folder
207         final int pathLength = path.length();
208         final Enumeration e = this.getEntries();
209         final ArrayList< VFile > list = new ArrayList< VFile >();
210         while ( e.hasMoreElements() ) {
211             final ZipEntry ze = (ZipEntry) e.nextElement();
212             final String entryName = ze.getName();
213             // Check the ZIP entry against this folders path. To be a sub-entry
214             // the entry name must start with this folders path and be longer.
215             if ( entryName.startsWith( path ) && entryName.length() > pathLength ) {
216                 // Get the position of the first / in the entry name starting at
217                 // the end of this folders path.
218                 final int n = entryName.indexOf( '/', pathLength );
219                 // If it's a file, there must be no / in the name
220                 if ( n < 0 ) {
221                     // ok, we've got a file
222                     list.add( new ReadOnlyZipFile( this, entryName ) );
223                 }
224             }
225         }
226         return list;
227     }
228 
229     /**
230      * @see org.millscript.commons.vfs.VFolder#listFolders()
231      */
232     public List< VFolder > listFolders() {
233         // The path for this folder
234         final String path = this.getRelativePathOnVolume();
235         // The length of the path for this folder
236         final int pathLength = path.length();
237         final Enumeration e = this.getEntries();
238         final ArrayList< VFolder > list = new ArrayList< VFolder >();
239         while ( e.hasMoreElements() ) {
240             final ZipEntry ze = (ZipEntry) e.nextElement();
241             final String entryName = ze.getName();
242             // Check the ZIP entry against this folders path. To be a sub-entry
243             // the entry name must start with this folders path and be longer.
244             if ( entryName.startsWith( path ) && entryName.length() > pathLength ) {
245                 // Get the position of the first / in the entry name starting at
246                 // the end of this folders path.
247                 final int n = entryName.indexOf( '/', pathLength );
248                 // If it's a sub folder, the index must be the end of the entry
249                 // name
250                 if ( n == entryName.length() - 1 ) {
251                     // ok, we've got a sub folder
252                     list.add( new ReadOnlyZipFolder( this, entryName.substring( 0, n ) ) );
253                 }
254             }
255         }
256         return list;
257     }
258 
259     /**
260      * @see org.millscript.commons.vfs.VFolder#make()
261      */
262     public VFolder make() {
263         throw(
264             new VFSAlert(
265                 "Can't make a folder in a read-only ZIP volume"
266             ).decorate( this ).mishap()
267         );
268     }
269 
270 }