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 }