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:
168: private void writeLeInt(long value) throws IOException
169: {
170: writeLeInt((int) value);
171: }
172:
173:
183: public void putNextEntry(ZipEntry entry) throws IOException
184: {
185: if (entries == null)
186: throw new ZipException("ZipOutputStream was finished");
187:
188: int method = entry.getMethod();
189: int flags = 0;
190: if (method == -1)
191: method = defaultMethod;
192:
193: if (method == STORED)
194: {
195: if (entry.getCompressedSize() >= 0)
196: {
197: if (entry.getSize() < 0)
198: entry.setSize(entry.getCompressedSize());
199: else if (entry.getSize() != entry.getCompressedSize())
200: throw new ZipException
201: ("Method STORED, but compressed size != size");
202: }
203: else
204: entry.setCompressedSize(entry.getSize());
205:
206: if (entry.getSize() < 0)
207: throw new ZipException("Method STORED, but size not set");
208: if (entry.getCrc() < 0)
209: throw new ZipException("Method STORED, but crc not set");
210: }
211: else if (method == DEFLATED)
212: {
213: if (entry.getCompressedSize() < 0
214: || entry.getSize() < 0 || entry.getCrc() < 0)
215: flags |= 8;
216: }
217:
218: if (curEntry != null)
219: closeEntry();
220:
221: if (entry.getTime() < 0)
222: entry.setTime(System.currentTimeMillis());
223:
224: entry.flags = flags;
225: entry.offset = offset;
226: entry.setMethod(method);
227: curMethod = method;
228:
229: writeLeInt(LOCSIG);
230: writeLeShort(method == STORED
231: ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION);
232: writeLeShort(flags);
233: writeLeShort(method);
234: writeLeInt(entry.getDOSTime());
235: if ((flags & 8) == 0)
236: {
237: writeLeInt((int)entry.getCrc());
238: writeLeInt((int)entry.getCompressedSize());
239: writeLeInt((int)entry.getSize());
240: }
241: else
242: {
243: writeLeInt(0);
244: writeLeInt(0);
245: writeLeInt(0);
246: }
247: byte[] name;
248: try
249: {
250: name = entry.getName().getBytes("UTF-8");
251: }
252: catch (UnsupportedEncodingException uee)
253: {
254: throw new AssertionError(uee);
255: }
256: if (name.length > 0xffff)
257: throw new ZipException("Name too long.");
258: byte[] extra = entry.getExtra();
259: if (extra == null)
260: extra = new byte[0];
261: writeLeShort(name.length);
262: writeLeShort(extra.length);
263: out.write(name);
264: out.write(extra);
265:
266: offset += LOCHDR + name.length + extra.length;
267:
268:
269:
270: curEntry = entry;
271: crc.reset();
272: if (method == DEFLATED)
273: def.reset();
274: size = 0;
275: }
276:
277:
282: public void closeEntry() throws IOException
283: {
284: if (curEntry == null)
285: throw new ZipException("No open entry");
286:
287:
288: if (curMethod == DEFLATED)
289: super.finish();
290:
291: int csize = curMethod == DEFLATED ? def.getTotalOut() : size;
292:
293: if (curEntry.getSize() < 0)
294: curEntry.setSize(size);
295: else if (curEntry.getSize() != size)
296: throw new ZipException("size was "+size
297: +", but I expected "+curEntry.getSize());
298:
299: if (curEntry.getCompressedSize() < 0)
300: curEntry.setCompressedSize(csize);
301: else if (curEntry.getCompressedSize() != csize)
302: throw new ZipException("compressed size was "+csize
303: +", but I expected "+curEntry.getSize());
304:
305: if (curEntry.getCrc() < 0)
306: curEntry.setCrc(crc.getValue());
307: else if (curEntry.getCrc() != crc.getValue())
308: throw new ZipException("crc was " + Long.toHexString(crc.getValue())
309: + ", but I expected "
310: + Long.toHexString(curEntry.getCrc()));
311:
312: offset += csize;
313:
314:
315: if (curMethod == DEFLATED && (curEntry.flags & 8) != 0)
316: {
317: writeLeInt(EXTSIG);
318: writeLeInt((int)curEntry.getCrc());
319: writeLeInt((int)curEntry.getCompressedSize());
320: writeLeInt((int)curEntry.getSize());
321: offset += EXTHDR;
322: }
323:
324: entries.addElement(curEntry);
325: curEntry = null;
326: }
327:
328:
333: public void write(byte[] b, int off, int len) throws IOException
334: {
335: if (curEntry == null)
336: throw new ZipException("No open entry.");
337:
338: switch (curMethod)
339: {
340: case DEFLATED:
341: super.write(b, off, len);
342: break;
343:
344: case STORED:
345: out.write(b, off, len);
346: break;
347: }
348:
349: crc.update(b, off, len);
350: size += len;
351: }
352:
353:
358: public void finish() throws IOException
359: {
360: if (entries == null)
361: return;
362: if (curEntry != null)
363: closeEntry();
364:
365: int numEntries = 0;
366: int sizeEntries = 0;
367:
368: Enumeration e = entries.elements();
369: while (e.hasMoreElements())
370: {
371: ZipEntry entry = (ZipEntry) e.nextElement();
372:
373: int method = entry.getMethod();
374: writeLeInt(CENSIG);
375: writeLeShort(method == STORED
376: ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION);
377: writeLeShort(method == STORED
378: ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION);
379: writeLeShort(entry.flags);
380: writeLeShort(method);
381: writeLeInt(entry.getDOSTime());
382: writeLeInt((int)entry.getCrc());
383: writeLeInt((int)entry.getCompressedSize());
384: writeLeInt((int)entry.getSize());
385:
386: byte[] name;
387: try
388: {
389: name = entry.getName().getBytes("UTF-8");
390: }
391: catch (UnsupportedEncodingException uee)
392: {
393: throw new AssertionError(uee);
394: }
395: if (name.length > 0xffff)
396: throw new ZipException("Name too long.");
397: byte[] extra = entry.getExtra();
398: if (extra == null)
399: extra = new byte[0];
400: String str = entry.getComment();
401: byte[] comment;
402: try
403: {
404: comment = str != null ? str.getBytes("UTF-8") : new byte[0];
405: }
406: catch (UnsupportedEncodingException uee)
407: {
408: throw new AssertionError(uee);
409: }
410: if (comment.length > 0xffff)
411: throw new ZipException("Comment too long.");
412:
413: writeLeShort(name.length);
414: writeLeShort(extra.length);
415: writeLeShort(comment.length);
416: writeLeShort(0);
417: writeLeShort(0);
418: writeLeInt(0);
419: writeLeInt(entry.offset);
420:
421: out.write(name);
422: out.write(extra);
423: out.write(comment);
424: numEntries++;
425: sizeEntries += CENHDR + name.length + extra.length + comment.length;
426: }
427:
428: writeLeInt(ENDSIG);
429: writeLeShort(0);
430: writeLeShort(0);
431: writeLeShort(numEntries);
432: writeLeShort(numEntries);
433: writeLeInt(sizeEntries);
434: writeLeInt(offset);
435: writeLeShort(zipComment.length);
436: out.write(zipComment);
437: out.flush();
438: entries = null;
439: }
440: }