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