001 /* Copyright 2000, 2001, Compaq Computer Corporation */
002
003 package javafe.filespace;
004
005
006 import java.io.*;
007 import java.util.zip.*;
008 import java.util.Enumeration;
009
010 import javafe.genericfile.*;
011
012
013 /**
014 * This module encapsulates how to convert from a Java path-component
015 * name to the hierarchical filespace it denotes.<p>
016 *
017 * Filespaces are represented by Trees whose node's data fields contain
018 * (non-null) GenericFiles that represent the corresponding files.
019 * E.g., the node at X.Y.Z represents a file with pathname ./X/Y/Z in
020 * the hierarchy of the path component.<p>
021 *
022 * Changes made to the underlying file system may or may not be
023 * reflected in the returned filespaces.<p>
024 */
025
026 public class PathComponent {
027
028 /***************************************************
029 * *
030 * Creation of filespaces: *
031 * *
032 **************************************************/
033
034 /**
035 * Create an empty filespace, containing only a root directory
036 */
037 //@ ensures \result != null;
038 public static Tree empty() {
039 return new LeafTree(
040 new UnopenableFile("<root of the empty filesystem>",
041 true));
042 }
043
044
045 /**
046 * Convert from a path-component name to the filespace it denotes.<p>
047 *
048 * Throws an IOException if any errors occur while initially
049 * scanning the component. Component must be non-null. The result
050 * is always a non-null filespace.<p>
051 *
052 *
053 * If complain is set, open will throw IOExceptions if the
054 * path component does not exist, or if it is not a directory or a
055 * zipfile. If complain is not set, then an empty filespace will
056 * be returned in these situations. Either way, IOExceptions will
057 * still be thrown if an error occurs reading an actual file or
058 * directory.<p>
059 *
060 *
061 * Note: changes to the filesystem named by component may or may
062 * not be reflected in the returned Tree.<p>
063 */
064 //@ requires component != null;
065 //@ ensures \result != null;
066 public static Tree open(String component, boolean complain)
067 throws IOException {
068 // Make sure component refers to an existing file:
069 File root = new File(component);
070 if (!root.exists()) {
071 if (complain)
072 throw new IOException("no such file: " + component);
073 else
074 return empty();
075 }
076
077 // Attempt to get the filespace contained in root
078 Tree filespace = null;
079 try {
080 if (isZipFilename(component))
081 filespace = new ZipTree(root);
082 else if (root.isDirectory())
083 filespace = new FileTree(root);
084 } catch (ZipException E) {
085 throw new IOException(component + ": unable to process zipfile: "
086 + E.getMessage());
087 } catch (IOException E) {
088 throw new IOException("unable to open/read file: "
089 + E.getMessage());
090 }
091
092 // Complain if component is not a directory or a zipfile:
093 if (filespace == null) {
094 if (complain)
095 throw new IOException("invalid classpath component: "
096 + component);
097 else
098 return empty();
099 }
100
101 return filespace;
102 }
103
104 /**
105 * Does a filename indicate that it is in zip format? <p>
106 *
107 */
108 //@ requires name != null;
109 protected static boolean isZipFilename(String name) {
110 return name.endsWith(".zip") || name.endsWith(".jar");
111 }
112
113
114 /***************************************************
115 * *
116 * Debugging functions: *
117 * *
118 **************************************************/
119
120 /** A simple test driver */
121 //@ requires \nonnullelements(args);
122 public static void main(String[] args) throws IOException {
123 // Check usage:
124 if (args.length<1 || args.length>2) {
125 System.out.println("PathComponent: usage <path component> "
126 + "[<pathname>]");
127 return;
128 }
129
130 /*
131 * Create a new filespace from the path component args[0]:
132 */
133 Tree T;
134 try {
135 T = open(args[0], false);
136 } catch (IOException E) {
137 System.out.println("Caught " + E);
138 return;
139 };
140
141 // If pathname given, try to print out that file then exit:
142 if (args.length==2) {
143 String pathname = args[1];
144
145 // Strip off any leading '/'s:
146 while (pathname.length()>0 && pathname.charAt(0)=='/')
147 pathname = pathname.substring(1,pathname.length());
148
149 // Strip off any trailing '/'s:
150 while (pathname.length()>0 &&
151 pathname.charAt(pathname.length()-1)=='/')
152 pathname = pathname.substring(0,pathname.length()-1);
153
154 Tree F = T.getQualifiedChild(pathname, '/');
155 if (F==null) {
156 System.out.println("No such file: " + args[1]);
157 return;
158 }
159
160 InputStream I = ((GenericFile)F.data).getInputStream(); //@ nowarn Cast,Null;
161 for (;;) {
162 int next = I.read();
163 if (next<0)
164 return;
165
166 System.out.write(next);
167 }
168 }
169
170 // T.print("");
171
172 // Otherwise, list all the files in the filespace by their
173 // distinctive names, indicating which are directories and
174 // giving their modification times
175 Enumeration E = new TreeWalker(T);
176 while (E.hasMoreElements()) {
177 Tree node = (Tree)E.nextElement();
178 GenericFile file = (GenericFile)node.data; //@ nowarn Cast;
179 //@ assume file != null;
180
181 System.out.print(file.getHumanName() + " ");
182 if (file.lastModified()==0L)
183 System.out.print("(unknown) ");
184 else
185 System.out.print("(" + file.lastModified() + ") ");
186 if (file.isDirectory())
187 System.out.print("[D] ");
188
189 System.out.println();
190 }
191 }
192 }