OpenCores
URL https://opencores.org/ocsvn/scarts/scarts/trunk

Subversion Repositories scarts

[/] [scarts/] [trunk/] [toolchain/] [scarts-gcc/] [gcc-4.1.1/] [libjava/] [classpath/] [java/] [util/] [zip/] [ZipFile.java] - Blame information for rev 14

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 14 jlechner
/* ZipFile.java --
2
   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006
3
   Free Software Foundation, Inc.
4
 
5
This file is part of GNU Classpath.
6
 
7
GNU Classpath is free software; you can redistribute it and/or modify
8
it under the terms of the GNU General Public License as published by
9
the Free Software Foundation; either version 2, or (at your option)
10
any later version.
11
 
12
GNU Classpath is distributed in the hope that it will be useful, but
13
WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
General Public License for more details.
16
 
17
You should have received a copy of the GNU General Public License
18
along with GNU Classpath; see the file COPYING.  If not, write to the
19
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20
02110-1301 USA.
21
 
22
Linking this library statically or dynamically with other modules is
23
making a combined work based on this library.  Thus, the terms and
24
conditions of the GNU General Public License cover the whole
25
combination.
26
 
27
As a special exception, the copyright holders of this library give you
28
permission to link this library with independent modules to produce an
29
executable, regardless of the license terms of these independent
30
modules, and to copy and distribute the resulting executable under
31
terms of your choice, provided that you also meet, for each linked
32
independent module, the terms and conditions of the license of that
33
module.  An independent module is a module which is not derived from
34
or based on this library.  If you modify this library, you may extend
35
this exception to your version of the library, but you are not
36
obligated to do so.  If you do not wish to do so, delete this
37
exception statement from your version. */
38
 
39
 
40
package java.util.zip;
41
 
42
import gnu.java.util.EmptyEnumeration;
43
 
44
import java.io.BufferedInputStream;
45
import java.io.DataInput;
46
import java.io.EOFException;
47
import java.io.File;
48
import java.io.IOException;
49
import java.io.InputStream;
50
import java.io.RandomAccessFile;
51
import java.io.UnsupportedEncodingException;
52
import java.util.Enumeration;
53
import java.util.HashMap;
54
import java.util.Iterator;
55
 
56
/**
57
 * This class represents a Zip archive.  You can ask for the contained
58
 * entries, or get an input stream for a file entry.  The entry is
59
 * automatically decompressed.
60
 *
61
 * This class is thread safe:  You can open input streams for arbitrary
62
 * entries in different threads.
63
 *
64
 * @author Jochen Hoenicke
65
 * @author Artur Biesiadowski
66
 */
67
public class ZipFile implements ZipConstants
68
{
69
 
70
  /**
71
   * Mode flag to open a zip file for reading.
72
   */
73
  public static final int OPEN_READ = 0x1;
74
 
75
  /**
76
   * Mode flag to delete a zip file after reading.
77
   */
78
  public static final int OPEN_DELETE = 0x4;
79
 
80
  // Name of this zip file.
81
  private final String name;
82
 
83
  // File from which zip entries are read.
84
  private final RandomAccessFile raf;
85
 
86
  // The entries of this zip file when initialized and not yet closed.
87
  private HashMap entries;
88
 
89
  private boolean closed = false;
90
 
91
  /**
92
   * Opens a Zip file with the given name for reading.
93
   * @exception IOException if a i/o error occured.
94
   * @exception ZipException if the file doesn't contain a valid zip
95
   * archive.
96
   */
97
  public ZipFile(String name) throws ZipException, IOException
98
  {
99
    this.raf = new RandomAccessFile(name, "r");
100
    this.name = name;
101
    checkZipFile();
102
  }
103
 
104
  /**
105
   * Opens a Zip file reading the given File.
106
   * @exception IOException if a i/o error occured.
107
   * @exception ZipException if the file doesn't contain a valid zip
108
   * archive.
109
   */
110
  public ZipFile(File file) throws ZipException, IOException
111
  {
112
    this.raf = new RandomAccessFile(file, "r");
113
    this.name = file.getPath();
114
    checkZipFile();
115
  }
116
 
117
  /**
118
   * Opens a Zip file reading the given File in the given mode.
119
   *
120
   * If the OPEN_DELETE mode is specified, the zip file will be deleted at
121
   * some time moment after it is opened. It will be deleted before the zip
122
   * file is closed or the Virtual Machine exits.
123
   *
124
   * The contents of the zip file will be accessible until it is closed.
125
   *
126
   * @since JDK1.3
127
   * @param mode Must be one of OPEN_READ or OPEN_READ | OPEN_DELETE
128
   *
129
   * @exception IOException if a i/o error occured.
130
   * @exception ZipException if the file doesn't contain a valid zip
131
   * archive.
132
   */
133
  public ZipFile(File file, int mode) throws ZipException, IOException
134
  {
135
    if (mode != OPEN_READ && mode != (OPEN_READ | OPEN_DELETE))
136
      throw new IllegalArgumentException("invalid mode");
137
    if ((mode & OPEN_DELETE) != 0)
138
      file.deleteOnExit();
139
    this.raf = new RandomAccessFile(file, "r");
140
    this.name = file.getPath();
141
    checkZipFile();
142
  }
143
 
144
  private void checkZipFile() throws IOException, ZipException
145
  {
146
    byte[] magicBuf = new byte[4];
147
    boolean validRead = true;
148
 
149
    try
150
      {
151
        raf.readFully(magicBuf);
152
      }
153
    catch (EOFException eof)
154
      {
155
        validRead = false;
156
      }
157
 
158
    if (validRead == false || readLeInt(magicBuf, 0) != LOCSIG)
159
      {
160
        raf.close();
161
        throw new ZipException("Not a valid zip file");
162
      }
163
  }
164
 
165
  /**
166
   * Checks if file is closed and throws an exception.
167
   */
168
  private void checkClosed()
169
  {
170
    if (closed)
171
      throw new IllegalStateException("ZipFile has closed: " + name);
172
  }
173
 
174
  /**
175
   * Read an unsigned short in little endian byte order from the given
176
   * DataInput stream using the given byte buffer.
177
   *
178
   * @param di DataInput stream to read from.
179
   * @param b the byte buffer to read in (must be at least 2 bytes long).
180
   * @return The value read.
181
   *
182
   * @exception IOException if a i/o error occured.
183
   * @exception EOFException if the file ends prematurely
184
   */
185
  private int readLeShort(DataInput di, byte[] b) throws IOException
186
  {
187
    di.readFully(b, 0, 2);
188
    return (b[0] & 0xff) | (b[1] & 0xff) << 8;
189
  }
190
 
191
  /**
192
   * Read an int in little endian byte order from the given
193
   * DataInput stream using the given byte buffer.
194
   *
195
   * @param di DataInput stream to read from.
196
   * @param b the byte buffer to read in (must be at least 4 bytes long).
197
   * @return The value read.
198
   *
199
   * @exception IOException if a i/o error occured.
200
   * @exception EOFException if the file ends prematurely
201
   */
202
  private int readLeInt(DataInput di, byte[] b) throws IOException
203
  {
204
    di.readFully(b, 0, 4);
205
    return ((b[0] & 0xff) | (b[1] & 0xff) << 8)
206
            | ((b[2] & 0xff) | (b[3] & 0xff) << 8) << 16;
207
  }
208
 
209
  /**
210
   * Read an unsigned short in little endian byte order from the given
211
   * byte buffer at the given offset.
212
   *
213
   * @param b the byte array to read from.
214
   * @param off the offset to read from.
215
   * @return The value read.
216
   */
217
  private int readLeShort(byte[] b, int off)
218
  {
219
    return (b[off] & 0xff) | (b[off+1] & 0xff) << 8;
220
  }
221
 
222
  /**
223
   * Read an int in little endian byte order from the given
224
   * byte buffer at the given offset.
225
   *
226
   * @param b the byte array to read from.
227
   * @param off the offset to read from.
228
   * @return The value read.
229
   */
230
  private int readLeInt(byte[] b, int off)
231
  {
232
    return ((b[off] & 0xff) | (b[off+1] & 0xff) << 8)
233
            | ((b[off+2] & 0xff) | (b[off+3] & 0xff) << 8) << 16;
234
  }
235
 
236
 
237
  /**
238
   * Read the central directory of a zip file and fill the entries
239
   * array.  This is called exactly once when first needed. It is called
240
   * while holding the lock on <code>raf</code>.
241
   *
242
   * @exception IOException if a i/o error occured.
243
   * @exception ZipException if the central directory is malformed
244
   */
245
  private void readEntries() throws ZipException, IOException
246
  {
247
    /* Search for the End Of Central Directory.  When a zip comment is
248
     * present the directory may start earlier.
249
     * FIXME: This searches the whole file in a very slow manner if the
250
     * file isn't a zip file.
251
     */
252
    long pos = raf.length() - ENDHDR;
253
    byte[] ebs  = new byte[CENHDR];
254
 
255
    do
256
      {
257
        if (pos < 0)
258
          throw new ZipException
259
            ("central directory not found, probably not a zip file: " + name);
260
        raf.seek(pos--);
261
      }
262
    while (readLeInt(raf, ebs) != ENDSIG);
263
 
264
    if (raf.skipBytes(ENDTOT - ENDNRD) != ENDTOT - ENDNRD)
265
      throw new EOFException(name);
266
    int count = readLeShort(raf, ebs);
267
    if (raf.skipBytes(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ)
268
      throw new EOFException(name);
269
    int centralOffset = readLeInt(raf, ebs);
270
 
271
    entries = new HashMap(count+count/2);
272
    raf.seek(centralOffset);
273
 
274
    byte[] buffer = new byte[16];
275
    for (int i = 0; i < count; i++)
276
      {
277
        raf.readFully(ebs);
278
        if (readLeInt(ebs, 0) != CENSIG)
279
          throw new ZipException("Wrong Central Directory signature: " + name);
280
 
281
        int method = readLeShort(ebs, CENHOW);
282
        int dostime = readLeInt(ebs, CENTIM);
283
        int crc = readLeInt(ebs, CENCRC);
284
        int csize = readLeInt(ebs, CENSIZ);
285
        int size = readLeInt(ebs, CENLEN);
286
        int nameLen = readLeShort(ebs, CENNAM);
287
        int extraLen = readLeShort(ebs, CENEXT);
288
        int commentLen = readLeShort(ebs, CENCOM);
289
 
290
        int offset = readLeInt(ebs, CENOFF);
291
 
292
        int needBuffer = Math.max(nameLen, commentLen);
293
        if (buffer.length < needBuffer)
294
          buffer = new byte[needBuffer];
295
 
296
        raf.readFully(buffer, 0, nameLen);
297
        String name;
298
        try
299
          {
300
            name = new String(buffer, 0, nameLen, "UTF-8");
301
          }
302
        catch (UnsupportedEncodingException uee)
303
          {
304
            throw new AssertionError(uee);
305
          }
306
 
307
        ZipEntry entry = new ZipEntry(name);
308
        entry.setMethod(method);
309
        entry.setCrc(crc & 0xffffffffL);
310
        entry.setSize(size & 0xffffffffL);
311
        entry.setCompressedSize(csize & 0xffffffffL);
312
        entry.setDOSTime(dostime);
313
        if (extraLen > 0)
314
          {
315
            byte[] extra = new byte[extraLen];
316
            raf.readFully(extra);
317
            entry.setExtra(extra);
318
          }
319
        if (commentLen > 0)
320
          {
321
            raf.readFully(buffer, 0, commentLen);
322
            try
323
              {
324
                entry.setComment(new String(buffer, 0, commentLen, "UTF-8"));
325
              }
326
            catch (UnsupportedEncodingException uee)
327
              {
328
                throw new AssertionError(uee);
329
              }
330
          }
331
        entry.offset = offset;
332
        entries.put(name, entry);
333
      }
334
  }
335
 
336
  /**
337
   * Closes the ZipFile.  This also closes all input streams given by
338
   * this class.  After this is called, no further method should be
339
   * called.
340
   *
341
   * @exception IOException if a i/o error occured.
342
   */
343
  public void close() throws IOException
344
  {
345
    RandomAccessFile raf = this.raf;
346
    if (raf == null)
347
      return;
348
 
349
    synchronized (raf)
350
      {
351
        closed = true;
352
        entries = null;
353
        raf.close();
354
      }
355
  }
356
 
357
  /**
358
   * Calls the <code>close()</code> method when this ZipFile has not yet
359
   * been explicitly closed.
360
   */
361
  protected void finalize() throws IOException
362
  {
363
    if (!closed && raf != null) close();
364
  }
365
 
366
  /**
367
   * Returns an enumeration of all Zip entries in this Zip file.
368
   *
369
   * @exception IllegalStateException when the ZipFile has already been closed
370
   */
371
  public Enumeration entries()
372
  {
373
    checkClosed();
374
 
375
    try
376
      {
377
        return new ZipEntryEnumeration(getEntries().values().iterator());
378
      }
379
    catch (IOException ioe)
380
      {
381
        return EmptyEnumeration.getInstance();
382
      }
383
  }
384
 
385
  /**
386
   * Checks that the ZipFile is still open and reads entries when necessary.
387
   *
388
   * @exception IllegalStateException when the ZipFile has already been closed.
389
   * @exception IOException when the entries could not be read.
390
   */
391
  private HashMap getEntries() throws IOException
392
  {
393
    synchronized(raf)
394
      {
395
        checkClosed();
396
 
397
        if (entries == null)
398
          readEntries();
399
 
400
        return entries;
401
      }
402
  }
403
 
404
  /**
405
   * Searches for a zip entry in this archive with the given name.
406
   *
407
   * @param name the name. May contain directory components separated by
408
   * slashes ('/').
409
   * @return the zip entry, or null if no entry with that name exists.
410
   *
411
   * @exception IllegalStateException when the ZipFile has already been closed
412
   */
413
  public ZipEntry getEntry(String name)
414
  {
415
    checkClosed();
416
 
417
    try
418
      {
419
        HashMap entries = getEntries();
420
        ZipEntry entry = (ZipEntry) entries.get(name);
421
        // If we didn't find it, maybe it's a directory.
422
        if (entry == null && !name.endsWith("/"))
423
            entry = (ZipEntry) entries.get(name + '/');
424
        return entry != null ? new ZipEntry(entry, name) : null;
425
      }
426
    catch (IOException ioe)
427
      {
428
        return null;
429
      }
430
  }
431
 
432
 
433
  //access should be protected by synchronized(raf)
434
  private byte[] locBuf = new byte[LOCHDR];
435
 
436
  /**
437
   * Checks, if the local header of the entry at index i matches the
438
   * central directory, and returns the offset to the data.
439
   *
440
   * @param entry to check.
441
   * @return the start offset of the (compressed) data.
442
   *
443
   * @exception IOException if a i/o error occured.
444
   * @exception ZipException if the local header doesn't match the
445
   * central directory header
446
   */
447
  private long checkLocalHeader(ZipEntry entry) throws IOException
448
  {
449
    synchronized (raf)
450
      {
451
        raf.seek(entry.offset);
452
        raf.readFully(locBuf);
453
 
454
        if (readLeInt(locBuf, 0) != LOCSIG)
455
          throw new ZipException("Wrong Local header signature: " + name);
456
 
457
        if (entry.getMethod() != readLeShort(locBuf, LOCHOW))
458
          throw new ZipException("Compression method mismatch: " + name);
459
 
460
        int nameLen = readLeShort(locBuf, LOCNAM);
461
        int extraLen = nameLen + readLeShort(locBuf, LOCEXT);
462
        return entry.offset + LOCHDR + extraLen;
463
      }
464
  }
465
 
466
  /**
467
   * Creates an input stream reading the given zip entry as
468
   * uncompressed data.  Normally zip entry should be an entry
469
   * returned by getEntry() or entries().
470
   *
471
   * This implementation returns null if the requested entry does not
472
   * exist.  This decision is not obviously correct, however, it does
473
   * appear to mirror Sun's implementation, and it is consistant with
474
   * their javadoc.  On the other hand, the old JCL book, 2nd Edition,
475
   * claims that this should return a "non-null ZIP entry".  We have
476
   * chosen for now ignore the old book, as modern versions of Ant (an
477
   * important application) depend on this behaviour.  See discussion
478
   * in this thread:
479
   * http://gcc.gnu.org/ml/java-patches/2004-q2/msg00602.html
480
   *
481
   * @param entry the entry to create an InputStream for.
482
   * @return the input stream, or null if the requested entry does not exist.
483
   *
484
   * @exception IllegalStateException when the ZipFile has already been closed
485
   * @exception IOException if a i/o error occured.
486
   * @exception ZipException if the Zip archive is malformed.
487
   */
488
  public InputStream getInputStream(ZipEntry entry) throws IOException
489
  {
490
    checkClosed();
491
 
492
    HashMap entries = getEntries();
493
    String name = entry.getName();
494
    ZipEntry zipEntry = (ZipEntry) entries.get(name);
495
    if (zipEntry == null)
496
      return null;
497
 
498
    long start = checkLocalHeader(zipEntry);
499
    int method = zipEntry.getMethod();
500
    InputStream is = new BufferedInputStream(new PartialInputStream
501
      (raf, start, zipEntry.getCompressedSize()));
502
    switch (method)
503
      {
504
      case ZipOutputStream.STORED:
505
        return is;
506
      case ZipOutputStream.DEFLATED:
507
        return new InflaterInputStream(is, new Inflater(true));
508
      default:
509
        throw new ZipException("Unknown compression method " + method);
510
      }
511
  }
512
 
513
  /**
514
   * Returns the (path) name of this zip file.
515
   */
516
  public String getName()
517
  {
518
    return name;
519
  }
520
 
521
  /**
522
   * Returns the number of entries in this zip file.
523
   *
524
   * @exception IllegalStateException when the ZipFile has already been closed
525
   */
526
  public int size()
527
  {
528
    checkClosed();
529
 
530
    try
531
      {
532
        return getEntries().size();
533
      }
534
    catch (IOException ioe)
535
      {
536
        return 0;
537
      }
538
  }
539
 
540
  private static class ZipEntryEnumeration implements Enumeration
541
  {
542
    private final Iterator elements;
543
 
544
    public ZipEntryEnumeration(Iterator elements)
545
    {
546
      this.elements = elements;
547
    }
548
 
549
    public boolean hasMoreElements()
550
    {
551
      return elements.hasNext();
552
    }
553
 
554
    public Object nextElement()
555
    {
556
      /* We return a clone, just to be safe that the user doesn't
557
       * change the entry.
558
       */
559
      return ((ZipEntry)elements.next()).clone();
560
    }
561
  }
562
 
563
  private static class PartialInputStream extends InputStream
564
  {
565
    private final RandomAccessFile raf;
566
    long filepos, end;
567
 
568
    public PartialInputStream(RandomAccessFile raf, long start, long len)
569
    {
570
      this.raf = raf;
571
      filepos = start;
572
      end = start + len;
573
    }
574
 
575
    public int available()
576
    {
577
      long amount = end - filepos;
578
      if (amount > Integer.MAX_VALUE)
579
        return Integer.MAX_VALUE;
580
      return (int) amount;
581
    }
582
 
583
    public int read() throws IOException
584
    {
585
      if (filepos == end)
586
        return -1;
587
      synchronized (raf)
588
        {
589
          raf.seek(filepos++);
590
          return raf.read();
591
        }
592
    }
593
 
594
    public int read(byte[] b, int off, int len) throws IOException
595
    {
596
      if (len > end - filepos)
597
        {
598
          len = (int) (end - filepos);
599
          if (len == 0)
600
            return -1;
601
        }
602
      synchronized (raf)
603
        {
604
          raf.seek(filepos);
605
          int count = raf.read(b, off, len);
606
          if (count > 0)
607
            filepos += len;
608
          return count;
609
        }
610
    }
611
 
612
    public long skip(long amount)
613
    {
614
      if (amount < 0)
615
        throw new IllegalArgumentException();
616
      if (amount > end - filepos)
617
        amount = end - filepos;
618
      filepos += amount;
619
      return amount;
620
    }
621
  }
622
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.