KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > util > zip > ZipInputStream


1 /*
2  * @(#)ZipInputStream.java 1.38 06/11/30
3  *
4  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package java.util.zip;
9
10 import java.io.InputStream JavaDoc;
11 import java.io.IOException JavaDoc;
12 import java.io.EOFException JavaDoc;
13 import java.io.PushbackInputStream JavaDoc;
14
15 /**
16  * This class implements an input stream filter for reading files in the
17  * ZIP file format. Includes support for both compressed and uncompressed
18  * entries.
19  *
20  * @author David Connelly
21  * @version 1.38, 11/30/06
22  */

23 public
24 class ZipInputStream extends InflaterInputStream JavaDoc implements ZipConstants JavaDoc {
25     private ZipEntry JavaDoc entry;
26     private CRC32 JavaDoc crc = new CRC32 JavaDoc();
27     private long remaining;
28     private byte[] tmpbuf = new byte[512];
29
30     private static final int STORED = ZipEntry.STORED;
31     private static final int DEFLATED = ZipEntry.DEFLATED;
32     
33     private boolean closed = false;
34     // this flag is set to true after EOF has reached for
35
// one entry
36
private boolean entryEOF = false;
37     
38     /**
39      * Check to make sure that this stream has not been closed
40      */

41     private void ensureOpen() throws IOException JavaDoc {
42     if (closed) {
43         throw new IOException JavaDoc("Stream closed");
44         }
45     }
46
47     /**
48      * Creates a new ZIP input stream.
49      * @param in the actual input stream
50      */

51     public ZipInputStream(InputStream JavaDoc in) {
52     super(new PushbackInputStream JavaDoc(in, 512), new Inflater JavaDoc(true), 512);
53         usesDefaultInflater = true;
54         if(in == null) {
55             throw new NullPointerException JavaDoc("in is null");
56         }
57     }
58
59     /**
60      * Reads the next ZIP file entry and positions the stream at the
61      * beginning of the entry data.
62      * @return the next ZIP file entry, or null if there are no more entries
63      * @exception ZipException if a ZIP file error has occurred
64      * @exception IOException if an I/O error has occurred
65      */

66     public ZipEntry JavaDoc getNextEntry() throws IOException JavaDoc {
67         ensureOpen();
68     if (entry != null) {
69         closeEntry();
70     }
71     crc.reset();
72     inf.reset();
73     if ((entry = readLOC()) == null) {
74         return null;
75     }
76     if (entry.method == STORED) {
77         remaining = entry.size;
78     }
79         entryEOF = false;
80     return entry;
81     }
82
83     /**
84      * Closes the current ZIP entry and positions the stream for reading the
85      * next entry.
86      * @exception ZipException if a ZIP file error has occurred
87      * @exception IOException if an I/O error has occurred
88      */

89     public void closeEntry() throws IOException JavaDoc {
90         ensureOpen();
91     while (read(tmpbuf, 0, tmpbuf.length) != -1) ;
92         entryEOF = true;
93     }
94
95     /**
96      * Returns 0 after EOF has reached for the current entry data,
97      * otherwise always return 1.
98      * <p>
99      * Programs should not count on this method to return the actual number
100      * of bytes that could be read without blocking.
101      *
102      * @return 1 before EOF and 0 after EOF has reached for current entry.
103      * @exception IOException if an I/O error occurs.
104      *
105      */

106     public int available() throws IOException JavaDoc {
107         ensureOpen();
108         if (entryEOF) {
109             return 0;
110         } else {
111             return 1;
112         }
113     }
114
115     /**
116      * Reads from the current ZIP entry into an array of bytes. Blocks until
117      * some input is available.
118      * @param b the buffer into which the data is read
119      * @param off the start offset of the data
120      * @param len the maximum number of bytes read
121      * @return the actual number of bytes read, or -1 if the end of the
122      * entry is reached
123      * @exception ZipException if a ZIP file error has occurred
124      * @exception IOException if an I/O error has occurred
125      */

126     public int read(byte[] b, int off, int len) throws IOException JavaDoc {
127         ensureOpen();
128         if (off < 0 || len < 0 || off > b.length - len) {
129         throw new IndexOutOfBoundsException JavaDoc();
130     } else if (len == 0) {
131         return 0;
132     }
133
134     if (entry == null) {
135         return -1;
136     }
137     switch (entry.method) {
138     case DEFLATED:
139         len = super.read(b, off, len);
140         if (len == -1) {
141         readEnd(entry);
142                 entryEOF = true;
143         entry = null;
144         } else {
145         crc.update(b, off, len);
146         }
147         return len;
148     case STORED:
149         if (remaining <= 0) {
150                 entryEOF = true;
151         entry = null;
152         return -1;
153         }
154         if (len > remaining) {
155         len = (int)remaining;
156         }
157         len = in.read(b, off, len);
158         if (len == -1) {
159         throw new ZipException JavaDoc("unexpected EOF");
160         }
161         crc.update(b, off, len);
162         remaining -= len;
163         if (remaining == 0 && entry.crc != crc.getValue()) {
164         throw new ZipException JavaDoc(
165             "invalid entry CRC (expected 0x" + Long.toHexString(entry.crc) +
166             " but got 0x" + Long.toHexString(crc.getValue()) + ")");
167         }
168         return len;
169     default:
170         throw new InternalError JavaDoc("invalid compression method");
171     }
172     }
173
174     /**
175      * Skips specified number of bytes in the current ZIP entry.
176      * @param n the number of bytes to skip
177      * @return the actual number of bytes skipped
178      * @exception ZipException if a ZIP file error has occurred
179      * @exception IOException if an I/O error has occurred
180      * @exception IllegalArgumentException if n < 0
181      */

182     public long skip(long n) throws IOException JavaDoc {
183         if (n < 0) {
184             throw new IllegalArgumentException JavaDoc("negative skip length");
185         }
186         ensureOpen();
187     int max = (int)Math.min(n, Integer.MAX_VALUE);
188     int total = 0;
189     while (total < max) {
190         int len = max - total;
191         if (len > tmpbuf.length) {
192         len = tmpbuf.length;
193         }
194         len = read(tmpbuf, 0, len);
195         if (len == -1) {
196                 entryEOF = true;
197         break;
198         }
199         total += len;
200     }
201     return total;
202     }
203
204     /**
205      * Closes this input stream and releases any system resources associated
206      * with the stream.
207      * @exception IOException if an I/O error has occurred
208      */

209     public void close() throws IOException JavaDoc {
210         if (!closed) {
211         super.close();
212             closed = true;
213         }
214     }
215
216     private byte[] b = new byte[256];
217
218     /*
219      * Reads local file (LOC) header for next entry.
220      */

221     private ZipEntry JavaDoc readLOC() throws IOException JavaDoc {
222     try {
223         readFully(tmpbuf, 0, LOCHDR);
224     } catch (EOFException JavaDoc e) {
225         return null;
226     }
227     if (get32(tmpbuf, 0) != LOCSIG) {
228         return null;
229     }
230     // get the entry name and create the ZipEntry first
231
int len = get16(tmpbuf, LOCNAM);
232     if (len == 0) {
233         throw new ZipException JavaDoc("missing entry name");
234     }
235         int blen = b.length;
236         if (len > blen) {
237             do
238                 blen = blen * 2;
239             while (len > blen);
240             b = new byte[blen];
241         }
242     readFully(b, 0, len);
243     ZipEntry JavaDoc e = createZipEntry(getUTF8String(b, 0, len));
244     // now get the remaining fields for the entry
245
e.version = get16(tmpbuf, LOCVER);
246     e.flag = get16(tmpbuf, LOCFLG);
247     if ((e.flag & 1) == 1) {
248         throw new ZipException JavaDoc("encrypted ZIP entry not supported");
249     }
250     e.method = get16(tmpbuf, LOCHOW);
251     e.time = get32(tmpbuf, LOCTIM);
252     if ((e.flag & 8) == 8) {
253         /* EXT descriptor present */
254         if (e.method != DEFLATED) {
255         throw new ZipException JavaDoc(
256             "only DEFLATED entries can have EXT descriptor");
257         }
258     } else {
259         e.crc = get32(tmpbuf, LOCCRC);
260         e.csize = get32(tmpbuf, LOCSIZ);
261         e.size = get32(tmpbuf, LOCLEN);
262     }
263     len = get16(tmpbuf, LOCEXT);
264     if (len > 0) {
265         byte[] bb = new byte[len];
266         readFully(bb, 0, len);
267         e.extra = bb;
268     }
269     return e;
270     }
271
272     /*
273      * Fetches a UTF8-encoded String from the specified byte array.
274      */

275     private static String JavaDoc getUTF8String(byte[] b, int off, int len) {
276     // First, count the number of characters in the sequence
277
int count = 0;
278     int max = off + len;
279     int i = off;
280     while (i < max) {
281         int c = b[i++] & 0xff;
282         switch (c >> 4) {
283         case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
284         // 0xxxxxxx
285
count++;
286         break;
287         case 12: case 13:
288         // 110xxxxx 10xxxxxx
289
if ((int)(b[i++] & 0xc0) != 0x80) {
290             throw new IllegalArgumentException JavaDoc();
291         }
292         count++;
293         break;
294         case 14:
295         // 1110xxxx 10xxxxxx 10xxxxxx
296
if (((int)(b[i++] & 0xc0) != 0x80) ||
297             ((int)(b[i++] & 0xc0) != 0x80)) {
298             throw new IllegalArgumentException JavaDoc();
299         }
300         count++;
301         break;
302         default:
303         // 10xxxxxx, 1111xxxx
304
throw new IllegalArgumentException JavaDoc();
305         }
306     }
307     if (i != max) {
308         throw new IllegalArgumentException JavaDoc();
309     }
310     // Now decode the characters...
311
char[] cs = new char[count];
312     i = 0;
313     while (off < max) {
314         int c = b[off++] & 0xff;
315         switch (c >> 4) {
316         case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
317         // 0xxxxxxx
318
cs[i++] = (char)c;
319         break;
320         case 12: case 13:
321         // 110xxxxx 10xxxxxx
322
cs[i++] = (char)(((c & 0x1f) << 6) | (b[off++] & 0x3f));
323         break;
324         case 14:
325         // 1110xxxx 10xxxxxx 10xxxxxx
326
int t = (b[off++] & 0x3f) << 6;
327         cs[i++] = (char)(((c & 0x0f) << 12) | t | (b[off++] & 0x3f));
328         break;
329         default:
330         // 10xxxxxx, 1111xxxx
331
throw new IllegalArgumentException JavaDoc();
332         }
333     }
334     return new String JavaDoc(cs, 0, count);
335     }
336
337     /**
338      * Creates a new <code>ZipEntry</code> object for the specified
339      * entry name.
340      *
341      * @param name the ZIP file entry name
342      * @return the ZipEntry just created
343      */

344     protected ZipEntry JavaDoc createZipEntry(String JavaDoc name) {
345     return new ZipEntry JavaDoc(name);
346     }
347
348     /*
349      * Reads end of deflated entry as well as EXT descriptor if present.
350      */

351     private void readEnd(ZipEntry JavaDoc e) throws IOException JavaDoc {
352     int n = inf.getRemaining();
353     if (n > 0) {
354         ((PushbackInputStream JavaDoc)in).unread(buf, len - n, n);
355     }
356     if ((e.flag & 8) == 8) {
357         /* EXT descriptor present */
358         readFully(tmpbuf, 0, EXTHDR);
359         long sig = get32(tmpbuf, 0);
360             if (sig != EXTSIG) { // no EXTSIG present
361
e.crc = sig;
362                 e.csize = get32(tmpbuf, EXTSIZ - EXTCRC);
363                 e.size = get32(tmpbuf, EXTLEN - EXTCRC);
364                 ((PushbackInputStream JavaDoc)in).unread(
365                                            tmpbuf, EXTHDR - EXTCRC - 1, EXTCRC);
366             } else {
367                 e.crc = get32(tmpbuf, EXTCRC);
368                 e.csize = get32(tmpbuf, EXTSIZ);
369                 e.size = get32(tmpbuf, EXTLEN);
370             }
371     }
372     if (e.size != inf.getBytesWritten()) {
373         throw new ZipException JavaDoc(
374         "invalid entry size (expected " + e.size +
375         " but got " + inf.getBytesWritten() + " bytes)");
376     }
377     if (e.csize != inf.getBytesRead()) {
378         throw new ZipException JavaDoc(
379         "invalid entry compressed size (expected " + e.csize +
380         " but got " + inf.getBytesRead() + " bytes)");
381     }
382     if (e.crc != crc.getValue()) {
383         throw new ZipException JavaDoc(
384         "invalid entry CRC (expected 0x" + Long.toHexString(e.crc) +
385         " but got 0x" + Long.toHexString(crc.getValue()) + ")");
386     }
387     }
388
389     /*
390      * Reads bytes, blocking until all bytes are read.
391      */

392     private void readFully(byte[] b, int off, int len) throws IOException JavaDoc {
393     while (len > 0) {
394         int n = in.read(b, off, len);
395         if (n == -1) {
396         throw new EOFException JavaDoc();
397         }
398         off += n;
399         len -= n;
400     }
401     }
402
403     /*
404      * Fetches unsigned 16-bit value from byte array at specified offset.
405      * The bytes are assumed to be in Intel (little-endian) byte order.
406      */

407     private static final int get16(byte b[], int off) {
408     return (b[off] & 0xff) | ((b[off+1] & 0xff) << 8);
409     }
410
411     /*
412      * Fetches unsigned 32-bit value from byte array at specified offset.
413      * The bytes are assumed to be in Intel (little-endian) byte order.
414      */

415     private static final long get32(byte b[], int off) {
416     return get16(b, off) | ((long)get16(b, off+2) << 16);
417     }
418 }
419
Popular Tags