1:
37:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46:
47:
59: public class ZipOutputStream extends DeflaterOutputStream implements ZipConstants
60: {
61: private Vector entries = new Vector();
62: private CRC32 crc = new CRC32();
63: private ZipEntry curEntry = null;
64:
65: private int curMethod;
66: private int size;
67: private int offset = 0;
68:
69: private byte[] zipComment = new byte[0];
70: private int defaultMethod = DEFLATED;
71:
72:
75: private static final int ZIP_STORED_VERSION = 10;
76: private static final int ZIP_DEFLATED_VERSION = 20;
77:
78:
81: public static final int STORED = 0;
82:
83:
86: public static final int DEFLATED = 8;
87:
88:
92: public ZipOutputStream(OutputStream out)
93: {
94: super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
95: }
96:
97:
103: public void setComment(String comment)
104: {
105: byte[] commentBytes;
106: try
107: {
108: commentBytes = comment.getBytes("UTF-8");
109: }
110: catch (UnsupportedEncodingException uee)
111: {
112: throw new AssertionError(uee);
113: }
114: if (commentBytes.length > 0xffff)
115: throw new IllegalArgumentException("Comment too long.");
116: zipComment = commentBytes;
117: }
118:
119:
127: public void setMethod(int method)
128: {
129: if (method != STORED && method != DEFLATED)
130: throw new IllegalArgumentException("Method not supported.");
131: defaultMethod = method;
132: }
133:
134:
140: public void setLevel(int level)
141: {
142: def.setLevel(level);
143: }
144:
145:
148: private void writeLeShort(int value) throws IOException
149: {
150: out.write(value & 0xff);
151: out.write((value >> 8) & 0xff);
152: }
153:
154:
157: private void writeLeInt(int value) throws IOException
158: {
159: writeLeShort(value);
160: writeLeShort(value >> 16);
161: }
162:
163:
173: public void putNextEntry(ZipEntry entry) throws IOException
174: {
175: if (entries == null)
176: throw new ZipException("ZipOutputStream was finished");
177:
178: int method = entry.getMethod();
179: int flags = 0;
180: if (method == -1)
181: method = defaultMethod;
182:
183: if (method == STORED)
184: {
185: if (entry.getCompressedSize() >= 0)
186: {
187: if (entry.getSize() < 0)
188: entry.setSize(entry.getCompressedSize());
189: else if (entry.getSize() != entry.getCompressedSize())
190: throw new ZipException
191: ("Method STORED, but compressed size != size");
192: }
193: else
194: entry.setCompressedSize(entry.getSize());
195:
196: if (entry.getSize() < 0)
197: throw new ZipException("Method STORED, but size not set");
198: if (entry.getCrc() < 0)
199: throw new ZipException("Method STORED, but crc not set");
200: }
201: else if (method == DEFLATED)
202: {
203: if (entry.getCompressedSize() < 0
204: || entry.getSize() < 0 || entry.getCrc() < 0)
205: flags |= 8;
206: }
207:
208: if (curEntry != null)
209: closeEntry();
210:
211: if (entry.getTime() < 0)
212: entry.setTime(System.currentTimeMillis());
213:
214: entry.flags = flags;
215: entry.offset = offset;
216: entry.setMethod(method);
217: curMethod = method;
218:
219: writeLeInt(LOCSIG);
220: writeLeShort(method == STORED
221: ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION);
222: writeLeShort(flags);
223: writeLeShort(method);
224: writeLeInt(entry.getDOSTime());
225: if ((flags & 8) == 0)
226: {
227: writeLeInt((int)entry.getCrc());
228: writeLeInt((int)entry.getCompressedSize());
229: writeLeInt((int)entry.getSize());
230: }
231: else
232: {
233: writeLeInt(0);
234: writeLeInt(0);
235: writeLeInt(0);
236: }
237: byte[] name;
238: try
239: {
240: name = entry.getName().getBytes("UTF-8");
241: }
242: catch (UnsupportedEncodingException uee)
243: {
244: throw new AssertionError(uee);
245: }
246: if (name.length > 0xffff)
247: throw new ZipException("Name too long.");
248: byte[] extra = entry.getExtra();
249: if (extra == null)
250: extra = new byte[0];
251: writeLeShort(name.length);
252: writeLeShort(extra.length);
253: out.write(name);
254: out.write(extra);
255:
256: offset += LOCHDR + name.length + extra.length;
257:
258:
259:
260: curEntry = entry;
261: crc.reset();
262: if (method == DEFLATED)
263: def.reset();
264: size = 0;
265: }
266:
267:
272: public void closeEntry() throws IOException
273: {
274: if (curEntry == null)
275: throw new ZipException("No open entry");
276:
277:
278: if (curMethod == DEFLATED)
279: super.finish();
280:
281: int csize = curMethod == DEFLATED ? def.getTotalOut() : size;
282:
283: if (curEntry.getSize() < 0)
284: curEntry.setSize(size);
285: else if (curEntry.getSize() != size)
286: throw new ZipException("size was "+size
287: +", but I expected "+curEntry.getSize());
288:
289: if (curEntry.getCompressedSize() < 0)
290: curEntry.setCompressedSize(csize);
291: else if (curEntry.getCompressedSize() != csize)
292: throw new ZipException("compressed size was "+csize
293: +", but I expected "+curEntry.getSize());
294:
295: if (curEntry.getCrc() < 0)
296: curEntry.setCrc(crc.getValue());
297: else if (curEntry.getCrc() != crc.getValue())
298: throw new ZipException("crc was " + Long.toHexString(crc.getValue())
299: + ", but I expected "
300: + Long.toHexString(curEntry.getCrc()));
301:
302: offset += csize;
303:
304:
305: if (curMethod == DEFLATED && (curEntry.flags & 8) != 0)
306: {
307: writeLeInt(EXTSIG);
308: writeLeInt((int)curEntry.getCrc());
309: writeLeInt((int)curEntry.getCompressedSize());
310: writeLeInt((int)curEntry.getSize());
311: offset += EXTHDR;
312: }
313:
314: entries.addElement(curEntry);
315: curEntry = null;
316: }
317:
318:
323: public void write(byte[] b, int off, int len) throws IOException
324: {
325: if (curEntry == null)
326: throw new ZipException("No open entry.");
327:
328: switch (curMethod)
329: {
330: case DEFLATED:
331: super.write(b, off, len);
332: break;
333:
334: case STORED:
335: out.write(b, off, len);
336: break;
337: }
338:
339: crc.update(b, off, len);
340: size += len;
341: }
342:
343:
348: public void finish() throws IOException
349: {
350: if (entries == null)
351: return;
352: if (curEntry != null)
353: closeEntry();
354:
355: int numEntries = 0;
356: int sizeEntries = 0;
357:
358: Enumeration e = entries.elements();
359: while (e.hasMoreElements())
360: {
361: ZipEntry entry = (ZipEntry) e.nextElement();
362:
363: int method = entry.getMethod();
364: writeLeInt(CENSIG);
365: writeLeShort(method == STORED
366: ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION);
367: writeLeShort(method == STORED
368: ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION);
369: writeLeShort(entry.flags);
370: writeLeShort(method);
371: writeLeInt(entry.getDOSTime());
372: writeLeInt((int)entry.getCrc());
373: writeLeInt((int)entry.getCompressedSize());
374: writeLeInt((int)entry.getSize());
375:
376: byte[] name;
377: try
378: {
379: name = entry.getName().getBytes("UTF-8");
380: }
381: catch (UnsupportedEncodingException uee)
382: {
383: throw new AssertionError(uee);
384: }
385: if (name.length > 0xffff)
386: throw new ZipException("Name too long.");
387: byte[] extra = entry.getExtra();
388: if (extra == null)
389: extra = new byte[0];
390: String str = entry.getComment();
391: byte[] comment;
392: try
393: {
394: comment = str != null ? str.getBytes("UTF-8") : new byte[0];
395: }
396: catch (UnsupportedEncodingException uee)
397: {
398: throw new AssertionError(uee);
399: }
400: if (comment.length > 0xffff)
401: throw new ZipException("Comment too long.");
402:
403: writeLeShort(name.length);
404: writeLeShort(extra.length);
405: writeLeShort(comment.length);
406: writeLeShort(0);
407: writeLeShort(0);
408: writeLeInt(0);
409: writeLeInt(entry.offset);
410:
411: out.write(name);
412: out.write(extra);
413: out.write(comment);
414: numEntries++;
415: sizeEntries += CENHDR + name.length + extra.length + comment.length;
416: }
417:
418: writeLeInt(ENDSIG);
419: writeLeShort(0);
420: writeLeShort(0);
421: writeLeShort(numEntries);
422: writeLeShort(numEntries);
423: writeLeInt(sizeEntries);
424: writeLeInt(offset);
425: writeLeShort(zipComment.length);
426: out.write(zipComment);
427: out.flush();
428: entries = null;
429: }
430: }