001 /* Copyright 2000, 2001, Compaq Computer Corporation */
002
003 package javafe.filespace;
004
005
006 import java.io.*;
007
008 import java.util.Enumeration;
009 import java.util.zip.*;
010
011 import javafe.genericfile.*;
012
013
014 /**
015 * A ZipTree is a Tree that mirrors the contents of a zipfile;
016 * the constructor takes in a pathname and returns a tree
017 * representing the filesystem contained in that zipfile.<p>
018 *
019 * ZipTree works by scanning the zipfile at object creation time,
020 * building up a hierarchical list of all the files in the zipfile.
021 * Later modifications to the zipfile will not be reflected in the
022 * ZipTree.<p>
023 *
024 * The data field of every (sub)node in a ZipTree contains a
025 * non-null ZipGenericFile representing the file it mirrors (or would
026 * mirror if a corresponding entry existed) in the underlying zipfile.<p>
027 *
028 *
029 * Note: ZipTree interior or root nodes (and only interior or root
030 * nodes) may contain ZipGenericFiles that do not have a corresponding
031 * zip entry because there may be no zip entries for those directories.
032 * E.g., the zipfile might contain a file for X/Y but not for X<p>
033 *
034 * We use "./" as the ZipGenericFile name to represent the missing
035 * root directory (no ZipTree actually has a root directory as far as
036 * we can tell).
037 *
038 * This gives the right isDirectory behavior (since it ends with a
039 * "/"), but the wrong local name ("."). This is the best we can do,
040 * though, since the other alternative ("") gives the reverse
041 * behavior, which is worse for us since we rely on the isDirectory
042 * bit and only use local names for non-directories.
043 */
044
045 class ZipTree extends ExtTree {
046
047 /***************************************************
048 * *
049 * Creation: *
050 * *
051 **************************************************/
052
053 /** The zipfile we are a snapshot of */
054 //@ invariant zip != null;
055 protected ZipFile zip;
056
057
058 /**
059 * Initialize a node's data field to a ZipGenericFile that
060 * represents the file that it would correspond to if the tree it
061 * belongs to mirrored zip.<p>
062 */
063 //@ requires node != null && zip != null;
064 protected static void missingEntry(Tree node, ZipFile zip) {
065 String name = node.getLabel();
066 if (name==null)
067 name = "./";
068
069 for (Tree ptr = node.getParent(); ptr != null; ptr=ptr.getParent()) {
070 if (ptr.getLabel() != null)
071 name = ptr.getLabel() + "/" + name;
072 }
073
074 node.data = new ZipGenericFile(zip, new ZipEntry(name));
075 }
076
077
078 /**
079 * Create a ZipTree to mirror a zipfile's contents.<p>
080 *
081 * May throw an IOException (e.g., file doesn't exist) or a
082 * ZipException (e.g., file is not a properly formatted
083 * zipfile).
084 **/
085 public ZipTree(/*@ non_null @*/ File zipfile) throws IOException, ZipException {
086 super(null);
087
088 // Open the file as a ZipFile:
089 zip = new ZipFile(zipfile);
090
091 // Then initialize us using the ZipFile's table of contents:
092 loadZipData();
093 }
094
095 /**
096 * Create a tree of ZipEntry's from the pathnames of the ZipEntry's
097 * in zip
098 */
099 private void loadZipData() {
100 // data may be overwritten later if an entry exists for us:
101 missingEntry(this, zip);
102
103 for (Enumeration E=zip.entries(); E.hasMoreElements(); ) {
104 ZipEntry next = (ZipEntry)E.nextElement();
105 addZipEntry(next);
106 }
107 }
108
109 /** Add a ZipEntry to this tree according to its pathname: */
110 //@ requires z != null;
111 private void addZipEntry(ZipEntry z) {
112 String pathname = z.getName();
113
114 // System.out.println("entry: " + pathname + (z.isDirectory()?" D":""));
115
116 //
117 // Note: ZipEntry's always use '/' as their separator!
118 //
119
120 // Strip off any leading file separators:
121 while (pathname.length()>0 && pathname.charAt(0)=='/')
122 pathname = pathname.substring(1);
123
124 // Strip off any trailing file separators:
125 while (pathname.length()>0 &&
126 pathname.charAt(pathname.length()-1)=='/')
127 pathname = pathname.substring(0,pathname.length()-1);
128
129 // Locate/create a node corresponding to path:
130 String[] path = StringUtil.parseList(pathname, '/');
131 Tree fileNode = addPath(path);
132
133 fileNode.data = new ZipGenericFile(zip, z);
134
135 // Initialize any new interior nodes as missing entries;
136 for (; fileNode != null; fileNode = fileNode.getParent())
137 if (fileNode.data==null)
138 missingEntry(fileNode, zip);
139 }
140
141
142 /***************************************************
143 * *
144 * Debugging functions: *
145 * *
146 **************************************************/
147
148 /** A simple test driver */
149 //@ requires args != null;
150 /*@ requires (\forall int i; (0<=i && i<=args.length)
151 ==> args[i] != null); */
152 public static void main(String[] args) {
153 if (args.length != 1) {
154 System.out.println("ZipTree: usage <zipfile>");
155 return;
156 }
157
158 /*
159 * Create a new ZipTree from the file args[0] and then
160 * print it out.
161 */
162 try {
163 Tree T = new ZipTree(new File(args[0]));
164 // T.print("");
165
166 // Enumerate its subtrees:
167 for (Enumeration E = new TreeWalker(T);
168 E.hasMoreElements();) {
169 Tree subtree = (Tree)E.nextElement();
170 if (subtree.getChildrenCount() == 0) {
171 System.out.println(" " + subtree.getLabel());
172 } else
173 System.out.println(subtree.getQualifiedName(".") + ":");
174
175 /*
176 System.out.println("[local name = "
177 +((GenericFile)subtree.data).getLocalName()+"]");
178 System.out.println("[entry name = "
179 +((GenericFile)subtree.data).getHumanName()+"]");
180 */
181 }
182
183 } catch (Exception E) {
184 System.out.println("Caught " + E);
185 };
186
187 }
188 }