java.util.zip.ZipOutputStream: NaN%

Overview

Search Parameters

EvoSuite Parameters

Old Parameters

Statistics

Test suite

Source Code

  1: /*
  2:  * @(#)ZipOutputStream.java	1.36 10/03/23
  3:  *
  4:  * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
  5:  * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6:  */
  7: 
  8: package java.util.zip;
  9: 
 10: import java.io.OutputStream;
 11: import java.io.IOException;
 12: import java.util.Vector;
 13: import java.util.HashSet;
 14: 
 15: /**
 16:  * This class implements an output stream filter for writing files in the
 17:  * ZIP file format. Includes support for both compressed and uncompressed
 18:  * entries.
 19:  *
 20:  * @author	David Connelly
 21:  * @version	1.36, 03/23/10
 22:  */
 23: public
 24: class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
 25: 
 26:     private static class XEntry {
 27: 	public final ZipEntry entry;
 28: 	public final long offset;
 29: 	public final int flag;
 30: 	public XEntry(ZipEntry entry, long offset) {
 31: 	    this.entry = entry;
 32: 	    this.offset = offset;
 33: 	    this.flag = (entry.method == DEFLATED &&
 34: 			 (entry.size  == -1 ||
 35: 			  entry.csize == -1 ||
 36: 			  entry.crc   == -1))
 37: 		// store size, compressed size, and crc-32 in data descriptor
 38: 		// immediately following the compressed entry data
 39: 		? 8
 40: 		// store size, compressed size, and crc-32 in LOC header
 41: 		: 0;
 42: 	}
 43:     }
 44: 
 45:     private XEntry current;
 46:     private Vector<XEntry> xentries = new Vector<XEntry>();
 47:     private HashSet<String> names = new HashSet<String>();
 48:     private CRC32 crc = new CRC32();
 49:     private long written = 0;
 50:     private long locoff = 0;
 51:     private String comment;
 52:     private int method = DEFLATED;
 53:     private boolean finished;
 54: 
 55:     private boolean closed = false;
 56: 
 57:     private static int version(ZipEntry e) throws ZipException {
 58: 	switch (e.method) {
 59: 	case DEFLATED: return 20;
 60: 	case STORED:   return 10;
 61: 	default: throw new ZipException("unsupported compression method");
 62: 	}
 63:     }
 64: 
 65:     /**
 66:      * Checks to make sure that this stream has not been closed.
 67:      */
 68:     private void ensureOpen() throws IOException {
 69: 	if (closed) {
 70: 	    throw new IOException("Stream closed");
 71:         }
 72:     }
 73:     /**
 74:      * Compression method for uncompressed (STORED) entries.
 75:      */
 76:     public static final int STORED = ZipEntry.STORED;
 77: 
 78:     /**
 79:      * Compression method for compressed (DEFLATED) entries.
 80:      */
 81:     public static final int DEFLATED = ZipEntry.DEFLATED;
 82: 
 83:     /**
 84:      * Creates a new ZIP output stream.
 85:      * @param out the actual output stream
 86:      */
 87:     public ZipOutputStream(OutputStream out) {
 88: 	super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
 89:         usesDefaultDeflater = true;
 90:     }
 91: 
 92:     /**
 93:      * Sets the ZIP file comment.
 94:      * @param comment the comment string
 95:      * @exception IllegalArgumentException if the length of the specified
 96:      *		  ZIP file comment is greater than 0xFFFF bytes
 97:      */
 98:     public void setComment(String comment) {
 99:         if (comment != null && comment.length() > 0xffff/3
100:                                            && getUTF8Length(comment) > 0xffff) {
101: 	    throw new IllegalArgumentException("ZIP file comment too long.");
102: 	}
103: 	this.comment = comment;
104:     }
105: 
106:     /**
107:      * Sets the default compression method for subsequent entries. This
108:      * default will be used whenever the compression method is not specified
109:      * for an individual ZIP file entry, and is initially set to DEFLATED.
110:      * @param method the default compression method
111:      * @exception IllegalArgumentException if the specified compression method
112:      *		  is invalid
113:      */
114:     public void setMethod(int method) {
115: 	if (method != DEFLATED && method != STORED) {
116: 	    throw new IllegalArgumentException("invalid compression method");
117: 	}
118: 	this.method = method;
119:     }
120: 
121:     /**
122:      * Sets the compression level for subsequent entries which are DEFLATED.
123:      * The default setting is DEFAULT_COMPRESSION.
124:      * @param level the compression level (0-9)
125:      * @exception IllegalArgumentException if the compression level is invalid
126:      */
127:     public void setLevel(int level) {
128: 	def.setLevel(level);
129:     }
130: 
131:     /**
132:      * Begins writing a new ZIP file entry and positions the stream to the
133:      * start of the entry data. Closes the current entry if still active.
134:      * The default compression method will be used if no compression method
135:      * was specified for the entry, and the current time will be used if
136:      * the entry has no set modification time.
137:      * @param e the ZIP entry to be written
138:      * @exception ZipException if a ZIP format error has occurred
139:      * @exception IOException if an I/O error has occurred
140:      */
141:     public void putNextEntry(ZipEntry e) throws IOException {
142: 	ensureOpen();
143: 	if (current != null) {
144: 	    closeEntry();	// close previous entry
145: 	}
146: 	if (e.time == -1) {
147: 	    e.setTime(System.currentTimeMillis());
148: 	}
149: 	if (e.method == -1) {
150: 	    e.method = method;	// use default method
151: 	}
152: 	switch (e.method) {
153: 	case DEFLATED:
154: 	    break;
155: 	case STORED:
156: 	    // compressed size, uncompressed size, and crc-32 must all be
157: 	    // set for entries using STORED compression method
158: 	    if (e.size == -1) {
159: 		e.size = e.csize;
160: 	    } else if (e.csize == -1) {
161: 		e.csize = e.size;
162: 	    } else if (e.size != e.csize) {
163: 		throw new ZipException(
164: 		    "STORED entry where compressed != uncompressed size");
165: 	    }
166: 	    if (e.size == -1 || e.crc == -1) {
167: 		throw new ZipException(
168: 		    "STORED entry missing size, compressed size, or crc-32");
169: 	    }
170: 	    break;
171: 	default:
172: 	    throw new ZipException("unsupported compression method");
173: 	}
174: 	if (! names.add(e.name)) {
175: 	    throw new ZipException("duplicate entry: " + e.name);
176: 	}
177: 	current = new XEntry(e, written);
178: 	xentries.add(current);
179:         writeLOC(current);
180:     }
181: 
182:     /**
183:      * Closes the current ZIP entry and positions the stream for writing
184:      * the next entry.
185:      * @exception ZipException if a ZIP format error has occurred
186:      * @exception IOException if an I/O error has occurred
187:      */
188:     public void closeEntry() throws IOException {
189: 	ensureOpen();
190: 	if (current != null) {
191: 	    ZipEntry e = current.entry;
192: 	    switch (e.method) {
193: 	    case DEFLATED:
194: 		def.finish();
195: 		while (!def.finished()) {
196: 		    deflate();
197: 		}
198: 		if ((current.flag & 8) == 0) {
199: 		    // verify size, compressed size, and crc-32 settings
200: 		    if (e.size != def.getBytesRead()) {
201: 			throw new ZipException(
202: 			    "invalid entry size (expected " + e.size +
203: 			    " but got " + def.getBytesRead() + " bytes)");
204: 		    }
205: 		    if (e.csize != def.getBytesWritten()) {
206: 			throw new ZipException(
207: 			    "invalid entry compressed size (expected " +
208: 			    e.csize + " but got " + def.getBytesWritten() + " bytes)");
209: 		    }
210: 		    if (e.crc != crc.getValue()) {
211: 			throw new ZipException(
212: 			    "invalid entry CRC-32 (expected 0x" +
213: 			    Long.toHexString(e.crc) + " but got 0x" +
214: 			    Long.toHexString(crc.getValue()) + ")");
215: 		    }
216: 		} else {
217: 		    e.size  = def.getBytesRead();
218: 		    e.csize = def.getBytesWritten();
219: 		    e.crc = crc.getValue();
220: 		    writeEXT(e);
221: 		}
222: 		def.reset();
223: 		written += e.csize;
224: 		break;
225: 	    case STORED:
226: 		// we already know that both e.size and e.csize are the same
227: 		if (e.size != written - locoff) {
228: 		    throw new ZipException(
229: 			"invalid entry size (expected " + e.size +
230: 			" but got " + (written - locoff) + " bytes)");
231: 		}
232: 		if (e.crc != crc.getValue()) {
233: 		    throw new ZipException(
234: 			 "invalid entry crc-32 (expected 0x" +
235: 			 Long.toHexString(e.crc) + " but got 0x" +
236: 			 Long.toHexString(crc.getValue()) + ")");
237: 		}
238: 		break;
239: 	    default:
240: 		throw new ZipException("invalid compression method");
241: 	    }
242: 	    crc.reset();
243: 	    current = null;
244: 	}
245:     }
246: 
247:     /**
248:      * Writes an array of bytes to the current ZIP entry data. This method
249:      * will block until all the bytes are written.
250:      * @param b the data to be written
251:      * @param off the start offset in the data
252:      * @param len the number of bytes that are written
253:      * @exception ZipException if a ZIP file error has occurred
254:      * @exception IOException if an I/O error has occurred
255:      */
256:     public synchronized void write(byte[] b, int off, int len)
257: 	throws IOException
258:     {
259: 	ensureOpen();
260:         if (off < 0 || len < 0 || off > b.length - len) {
261: 	    throw new IndexOutOfBoundsException();
262: 	} else if (len == 0) {
263: 	    return;
264: 	}
265: 
266: 	if (current == null) {
267: 	    throw new ZipException("no current ZIP entry");
268: 	}
269: 	ZipEntry entry = current.entry;
270: 	switch (entry.method) {
271: 	case DEFLATED:
272: 	    super.write(b, off, len);
273: 	    break;
274: 	case STORED:
275: 	    written += len;
276: 	    if (written - locoff > entry.size) {
277: 		throw new ZipException(
278: 		    "attempt to write past end of STORED entry");
279: 	    }
280: 	    out.write(b, off, len);
281: 	    break;
282: 	default:
283: 	    throw new ZipException("invalid compression method");
284: 	}
285: 	crc.update(b, off, len);
286:     }
287: 
288:     /**
289:      * Finishes writing the contents of the ZIP output stream without closing
290:      * the underlying stream. Use this method when applying multiple filters
291:      * in succession to the same output stream.
292:      * @exception ZipException if a ZIP file error has occurred
293:      * @exception IOException if an I/O exception has occurred
294:      */
295:     public void finish() throws IOException {
296: 	ensureOpen();
297: 	if (finished) {
298: 	    return;
299: 	}
300: 	if (current != null) {
301: 	    closeEntry();
302: 	}
303: 	if (xentries.size() < 1) {
304: 	    throw new ZipException("ZIP file must have at least one entry");
305: 	}
306: 	// write central directory
307: 	long off = written;
308: 	for (XEntry xentry : xentries)
309: 	    writeCEN(xentry);
310: 	writeEND(off, written - off);
311: 	finished = true;
312:     }
313: 
314:     /**
315:      * Closes the ZIP output stream as well as the stream being filtered.
316:      * @exception ZipException if a ZIP file error has occurred
317:      * @exception IOException if an I/O error has occurred
318:      */
319:     public void close() throws IOException {
320:         if (!closed) {
321:             super.close();
322:             closed = true;
323:         }
324:     }
325: 
326:     /*
327:      * Writes local file (LOC) header for specified entry.
328:      */
329:     private void writeLOC(XEntry xentry) throws IOException {
330: 	ZipEntry e = xentry.entry;
331: 	int flag = xentry.flag;
332: 	writeInt(LOCSIG);	    // LOC header signature
333: 	writeShort(version(e));     // version needed to extract
334: 	writeShort(flag);           // general purpose bit flag
335: 	writeShort(e.method);       // compression method
336: 	writeInt(e.time);           // last modification time
337: 	if ((flag & 8) == 8) {
338: 	    // store size, uncompressed size, and crc-32 in data descriptor
339: 	    // immediately following compressed entry data
340: 	    writeInt(0);
341: 	    writeInt(0);
342: 	    writeInt(0);
343: 	} else {
344: 	    writeInt(e.crc);        // crc-32
345: 	    writeInt(e.csize);      // compressed size
346: 	    writeInt(e.size);       // uncompressed size
347: 	}
348: 	byte[] nameBytes = getUTF8Bytes(e.name);
349: 	writeShort(nameBytes.length);
350: 	writeShort(e.extra != null ? e.extra.length : 0);
351: 	writeBytes(nameBytes, 0, nameBytes.length);
352: 	if (e.extra != null) {
353: 	    writeBytes(e.extra, 0, e.extra.length);
354: 	}
355: 	locoff = written;
356:     }
357: 
358:     /*
359:      * Writes extra data descriptor (EXT) for specified entry.
360:      */
361:     private void writeEXT(ZipEntry e) throws IOException {
362: 	writeInt(EXTSIG);	    // EXT header signature
363: 	writeInt(e.crc);	    // crc-32
364: 	writeInt(e.csize);	    // compressed size
365: 	writeInt(e.size);	    // uncompressed size
366:     }
367: 
368:     /*
369:      * Write central directory (CEN) header for specified entry.
370:      * REMIND: add support for file attributes
371:      */
372:     private void writeCEN(XEntry xentry) throws IOException {
373: 	ZipEntry e  = xentry.entry;
374: 	int flag = xentry.flag;
375: 	int version = version(e);
376: 	writeInt(CENSIG);	    // CEN header signature
377: 	writeShort(version);	    // version made by
378: 	writeShort(version);	    // version needed to extract
379: 	writeShort(flag);	    // general purpose bit flag
380: 	writeShort(e.method);	    // compression method
381: 	writeInt(e.time);	    // last modification time
382: 	writeInt(e.crc);	    // crc-32
383: 	writeInt(e.csize);	    // compressed size
384: 	writeInt(e.size);	    // uncompressed size
385: 	byte[] nameBytes = getUTF8Bytes(e.name);
386: 	writeShort(nameBytes.length);
387: 	writeShort(e.extra != null ? e.extra.length : 0);
388: 	byte[] commentBytes;
389: 	if (e.comment != null) {
390: 	    commentBytes = getUTF8Bytes(e.comment);
391: 	    writeShort(commentBytes.length);
392: 	} else {
393: 	    commentBytes = null;
394: 	    writeShort(0);
395: 	}
396: 	writeShort(0);		    // starting disk number
397: 	writeShort(0);		    // internal file attributes (unused)
398: 	writeInt(0);		    // external file attributes (unused)
399: 	writeInt(xentry.offset);    // relative offset of local header
400: 	writeBytes(nameBytes, 0, nameBytes.length);
401: 	if (e.extra != null) {
402: 	    writeBytes(e.extra, 0, e.extra.length);
403: 	}
404: 	if (commentBytes != null) {
405: 	    writeBytes(commentBytes, 0, commentBytes.length);
406: 	}
407:     }
408: 
409:     /*
410:      * Writes end of central directory (END) header.
411:      */
412:     private void writeEND(long off, long len) throws IOException {
413: 	int count = xentries.size();
414: 	writeInt(ENDSIG);	    // END record signature
415: 	writeShort(0);		    // number of this disk
416: 	writeShort(0);		    // central directory start disk
417: 	writeShort(count);	    // number of directory entries on disk
418: 	writeShort(count);	    // total number of directory entries
419: 	writeInt(len);		    // length of central directory
420: 	writeInt(off);		    // offset of central directory
421: 	if (comment != null) {	    // zip file comment
422: 	    byte[] b = getUTF8Bytes(comment);
423: 	    writeShort(b.length);
424: 	    writeBytes(b, 0, b.length);
425: 	} else {
426: 	    writeShort(0);
427: 	}
428:     }
429: 
430:     /*
431:      * Writes a 16-bit short to the output stream in little-endian byte order.
432:      */
433:     private void writeShort(int v) throws IOException {
434: 	OutputStream out = this.out;
435: 	out.write((v >>> 0) & 0xff);
436: 	out.write((v >>> 8) & 0xff);
437: 	written += 2;
438:     }
439: 
440:     /*
441:      * Writes a 32-bit int to the output stream in little-endian byte order.
442:      */
443:     private void writeInt(long v) throws IOException {
444: 	OutputStream out = this.out;
445: 	out.write((int)((v >>>  0) & 0xff));
446: 	out.write((int)((v >>>  8) & 0xff));
447: 	out.write((int)((v >>> 16) & 0xff));
448: 	out.write((int)((v >>> 24) & 0xff));
449: 	written += 4;
450:     }
451: 
452:     /*
453:      * Writes an array of bytes to the output stream.
454:      */
455:     private void writeBytes(byte[] b, int off, int len) throws IOException {
456: 	super.out.write(b, off, len);
457: 	written += len;
458:     }
459: 
460:     /*
461:      * Returns the length of String's UTF8 encoding.
462:      */
463:     static int getUTF8Length(String s) {
464:         int count = 0;
465:         for (int i = 0; i < s.length(); i++) {
466:             char ch = s.charAt(i);
467:             if (ch <= 0x7f) {
468:                 count++;
469:             } else if (ch <= 0x7ff) {
470:                 count += 2;
471:             } else {
472:                 count += 3;
473:             }
474:         }
475:         return count;
476:     }
477: 
478:     /*
479:      * Returns an array of bytes representing the UTF8 encoding
480:      * of the specified String.
481:      */
482:     private static byte[] getUTF8Bytes(String s) {
483: 	char[] c = s.toCharArray();
484: 	int len = c.length;
485: 	// Count the number of encoded bytes...
486: 	int count = 0;
487: 	for (int i = 0; i < len; i++) {
488: 	    int ch = c[i];
489: 	    if (ch <= 0x7f) {
490: 		count++;
491: 	    } else if (ch <= 0x7ff) {
492: 		count += 2;
493: 	    } else {
494: 		count += 3;
495: 	    }
496: 	}
497: 	// Now return the encoded bytes...
498: 	byte[] b = new byte[count];
499: 	int off = 0;
500: 	for (int i = 0; i < len; i++) {
501: 	    int ch = c[i];
502: 	    if (ch <= 0x7f) {
503: 		b[off++] = (byte)ch;
504: 	    } else if (ch <= 0x7ff) {
505: 		b[off++] = (byte)((ch >> 6) | 0xc0);
506: 		b[off++] = (byte)((ch & 0x3f) | 0x80);
507: 	    } else {
508: 		b[off++] = (byte)((ch >> 12) | 0xe0);
509: 		b[off++] = (byte)(((ch >> 6) & 0x3f) | 0x80);
510: 		b[off++] = (byte)((ch & 0x3f) | 0x80);
511: 	    }
512: 	}
513: 	return b;
514:     }
515: }