001 /* 002 * LAPIS lightweight structured text processing system 003 * 004 * Copyright (C) 1998-2002 Carnegie Mellon University, 005 * Copyright (C) 2003 Massachusetts Institute of Technology. 006 * All rights reserved. 007 * 008 * This library is free software; you can redistribute it 009 * and/or modify it under the terms of the GNU General 010 * Public License as published by the Free Software 011 * Foundation, version 2. 012 * 013 * LAPIS homepage: http://graphics.lcs.mit.edu/lapis/ 014 */ 015 016 package lapisx.io; 017 018 import java.io.*; 019 import java.util.*; 020 import java.util.zip.*; 021 022 public class ZipArchive implements Serializable { 023 024 private static final int BUFFER_SIZE = 512; 025 026 byte[] zipdata; 027 // ZIP archive 028 029 Vector names = new Vector (); 030 // files contained in the ZIP archive 031 032 // Used only for serialization 033 static final long serialVersionUID = -9185404800237564786L; 034 035 public ZipArchive (String[] filenames) throws IOException { 036 byte[] buffer = new byte[BUFFER_SIZE]; 037 int n; 038 039 ByteArrayOutputStream bout = new ByteArrayOutputStream (); 040 ZipOutputStream out = new ZipOutputStream (bout); 041 out.setMethod (ZipOutputStream.STORED); 042 043 try { 044 for (int i = 0; i < filenames.length; ++i) { 045 String filename = filenames[i]; 046 if (filename.endsWith (File.separator) 047 && !filename.equals (File.separator)) 048 filename = filename.substring (0, filename.length()-1); 049 050 addFile (out, new File (filename), "", buffer); 051 } 052 out.finish (); 053 } finally { 054 out.close (); 055 zipdata = bout.toByteArray (); 056 } 057 } 058 059 private void addFile (ZipOutputStream out, File f, String prefix, byte[] buffer) throws IOException { 060 String name = prefix + f.getName (); 061 062 if (f.isDirectory ()) { 063 // Mark it as a directory for the ZipFile 064 name += "/"; 065 066 // Create an entry for the directory 067 ZipEntry entry = new ZipEntry (name); 068 entry.setSize (0); 069 entry.setCrc (0); 070 071 // Add the entry to the ZIP archive 072 out.putNextEntry (entry); 073 names.addElement (name); 074 075 // Add each file in directory recursively 076 String[] subfiles = f.list (); 077 for (int i = 0; i < subfiles.length; ++i) 078 addFile (out, new File (f, subfiles[i]), name, buffer); 079 } 080 else { 081 // Create an entry for this file 082 ZipEntry entry = new ZipEntry (name); 083 entry.setSize (f.length ()); 084 085 // compute the file's CRC 086 Checksum crc = new CRC32 (); 087 CheckedInputStream checkedInput = new CheckedInputStream (new FileInputStream (f), crc); 088 while (checkedInput.read (buffer) != -1) 089 ; 090 checkedInput.close (); 091 entry.setCrc (crc.getValue ()); 092 093 // Add the entry to the ZIP archive 094 out.putNextEntry (entry); 095 names.addElement (name); 096 097 // Copy the file's data into the ZIP archive 098 InputStream in = new FileInputStream (f); 099 int n; 100 while ((n = in.read (buffer)) != -1) 101 out.write (buffer, 0, n); 102 in.close (); 103 } 104 } 105 106 public ZipArchive (InputStream in) throws IOException { 107 // Save the input stream to memory 108 ByteArrayOutputStream out = new ByteArrayOutputStream (); 109 byte[] buffer = new byte[BUFFER_SIZE]; 110 int n; 111 while ((n = in.read (buffer)) != -1) 112 out.write (buffer, 0, n); 113 out.close (); 114 zipdata = out.toByteArray (); 115 116 afterCreatingFromStream (); 117 } 118 119 public ZipArchive (InputStream in, int length) throws IOException { 120 // Save the input stream to memory 121 ByteArrayOutputStream out = new ByteArrayOutputStream (); 122 byte[] buffer = new byte[BUFFER_SIZE]; 123 int total = 0; 124 int n; 125 while (total < length && (n = in.read (buffer)) != -1) { 126 out.write (buffer, 0, n); 127 total += n; 128 } 129 out.close (); 130 zipdata = out.toByteArray (); 131 132 afterCreatingFromStream (); 133 } 134 135 private void afterCreatingFromStream () throws IOException { 136 // Get the entry names out 137 ZipInputStream zin = new ZipInputStream (getInputStream ()); 138 try { 139 ZipEntry entry; 140 while ((entry = zin.getNextEntry ()) != null) { 141 names.addElement (entry.getName ()); 142 //System.err.println (entry.getName ()); 143 zin.closeEntry (); 144 } 145 } finally { 146 zin.close (); 147 } 148 } 149 150 public String[] getNames () { 151 String[] fn = new String[names.size ()]; 152 names.copyInto (fn); 153 return fn; 154 } 155 156 public String filenameAt (int i) { 157 return names.elementAt (i).toString (); 158 } 159 160 public int getFilenameCount () { 161 return names.size (); 162 } 163 164 public int length () { 165 return zipdata.length; 166 } 167 168 public InputStream getInputStream () { 169 return new ByteArrayInputStream (zipdata); 170 } 171 172 /** 173 * Extract ZIP archive entries to the filesystem. 174 * Each destination filename is computed by prefixing 175 * the ZIP entry name with directory. 176 * @param directory Directory where files should be placed. 177 * Null or empty string means current directory. If directory 178 * is non-empty and doesn't end with a file separator like / or \, 179 * then a file separator is added automatically. 180 */ 181 public void extract (String directory) throws IOException { 182 extract (directory, null, null); 183 } 184 185 public void extract (String directory, PrintStream nameStream) throws IOException { 186 extract (directory, null, nameStream); 187 } 188 189 public void extract (String directory, String[] names) throws IOException { 190 extract (directory, names, null); 191 } 192 193 public void extract (String directory, String[] names, PrintStream nameStream) throws IOException { 194 byte[] buffer = new byte[BUFFER_SIZE]; 195 196 // Make sure dir has a terminating separator 197 if (directory == null) 198 directory = ""; 199 else if (directory.length () > 0 200 && !(directory.endsWith ("/") || 201 directory.endsWith (File.separator))) 202 directory += File.separator; 203 204 // Make a Hashtable out of the names 205 Hashtable nameHash = null; 206 if (names != null) { 207 nameHash = new Hashtable (names.length); 208 for (int i=0; i < names.length; ++i) 209 nameHash.put (names[i], names[i]); 210 } 211 212 ZipInputStream zin = new ZipInputStream (getInputStream ()); 213 try { 214 ZipEntry entry; 215 while ((entry = zin.getNextEntry ()) != null) { 216 if (names == null || nameHash.contains (entry.getName ())) { 217 File f = new File (directory + entry.getName ()); 218 219 if (nameStream != null) 220 nameStream.println (f.toString ()); 221 222 if (entry.isDirectory ()) 223 f.mkdir (); 224 else { 225 // Save the input stream to disk 226 OutputStream out = new FileOutputStream (f); 227 int length = (int)entry.getSize (); 228 int n; 229 while (length > buffer.length && (n = zin.read (buffer)) != -1) { 230 out.write (buffer, 0, n); 231 length -= n; 232 } 233 if (length > 0 && (n = zin.read (buffer, 0, length)) != -1) { 234 out.write (buffer, 0, n); 235 length -= n; 236 } 237 238 out.close (); 239 } 240 } 241 242 zin.closeEntry (); 243 } 244 245 } finally { 246 zin.close (); 247 } 248 } 249 250 public String[] extractToTempDir () throws IOException { 251 return extractToTempDir (null); 252 } 253 254 public String[] extractToTempDir (String[] names) throws IOException { 255 // Unpack to temporary directory 256 String dir = TempFile.getTempDirectory ().toString (); 257 extract (dir, names); 258 259 if (names == null) 260 names = getNames (); 261 262 // Register all the temp files we just created (in reverse order 263 // so that directories are freed when they're empty) 264 for (int i = names.length - 1; i >= 0; --i) 265 TempFile.registerTempFile (new File (dir + names[i])); 266 267 // Return an array of just the root (top-level) names 268 Vector v = new Vector (); 269 for (int i = 0; i < names.length; ++i) { 270 int slash = names[i].indexOf ("/"); 271 if (slash == -1 || slash == names[i].length()-1) 272 v.addElement (dir + names[i]); 273 } 274 275 String[] extractedFilenames = new String[v.size ()]; 276 v.copyInto (extractedFilenames); 277 return extractedFilenames; 278 } 279 }