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 }