Save This Page
Home » openjdk-7 » java » util » zip » [javadoc | source]
    1   /*
    2    * Copyright 1995-2006 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   package java.util.zip;
   27   
   28   import java.io.InputStream;
   29   import java.io.IOException;
   30   import java.io.EOFException;
   31   import java.io.File;
   32   import java.util.Vector;
   33   import java.util.Enumeration;
   34   import java.util.NoSuchElementException;
   35   
   36   /**
   37    * This class is used to read entries from a zip file.
   38    *
   39    * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
   40    * or method in this class will cause a {@link NullPointerException} to be
   41    * thrown.
   42    *
   43    * @author      David Connelly
   44    */
   45   public
   46   class ZipFile implements ZipConstants {
   47       private long jzfile;  // address of jzfile data
   48       private String name;  // zip file name
   49       private int total;    // total number of entries
   50       private boolean closeRequested;
   51   
   52       private static final int STORED = ZipEntry.STORED;
   53       private static final int DEFLATED = ZipEntry.DEFLATED;
   54   
   55       /**
   56        * Mode flag to open a zip file for reading.
   57        */
   58       public static final int OPEN_READ = 0x1;
   59   
   60       /**
   61        * Mode flag to open a zip file and mark it for deletion.  The file will be
   62        * deleted some time between the moment that it is opened and the moment
   63        * that it is closed, but its contents will remain accessible via the
   64        * <tt>ZipFile</tt> object until either the close method is invoked or the
   65        * virtual machine exits.
   66        */
   67       public static final int OPEN_DELETE = 0x4;
   68   
   69       static {
   70           /* Zip library is loaded from System.initializeSystemClass */
   71           initIDs();
   72       }
   73   
   74       private static native void initIDs();
   75   
   76       /**
   77        * Opens a zip file for reading.
   78        *
   79        * <p>First, if there is a security
   80        * manager, its <code>checkRead</code> method
   81        * is called with the <code>name</code> argument
   82        * as its argument to ensure the read is allowed.
   83        *
   84        * @param name the name of the zip file
   85        * @throws ZipException if a ZIP format error has occurred
   86        * @throws IOException if an I/O error has occurred
   87        * @throws SecurityException if a security manager exists and its
   88        *         <code>checkRead</code> method doesn't allow read access to the file.
   89        * @see SecurityManager#checkRead(java.lang.String)
   90        */
   91       public ZipFile(String name) throws IOException {
   92           this(new File(name), OPEN_READ);
   93       }
   94   
   95       /**
   96        * Opens a new <code>ZipFile</code> to read from the specified
   97        * <code>File</code> object in the specified mode.  The mode argument
   98        * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>.
   99        *
  100        * <p>First, if there is a security manager, its <code>checkRead</code>
  101        * method is called with the <code>name</code> argument as its argument to
  102        * ensure the read is allowed.
  103        *
  104        * @param file the ZIP file to be opened for reading
  105        * @param mode the mode in which the file is to be opened
  106        * @throws ZipException if a ZIP format error has occurred
  107        * @throws IOException if an I/O error has occurred
  108        * @throws SecurityException if a security manager exists and
  109        *         its <code>checkRead</code> method
  110        *         doesn't allow read access to the file,
  111        *         or its <code>checkDelete</code> method doesn't allow deleting
  112        *         the file when the <tt>OPEN_DELETE</tt> flag is set.
  113        * @throws IllegalArgumentException if the <tt>mode</tt> argument is invalid
  114        * @see SecurityManager#checkRead(java.lang.String)
  115        * @since 1.3
  116        */
  117       public ZipFile(File file, int mode) throws IOException {
  118           if (((mode & OPEN_READ) == 0) ||
  119               ((mode & ~(OPEN_READ | OPEN_DELETE)) != 0)) {
  120               throw new IllegalArgumentException("Illegal mode: 0x"+
  121                                                  Integer.toHexString(mode));
  122           }
  123           String name = file.getPath();
  124           SecurityManager sm = System.getSecurityManager();
  125           if (sm != null) {
  126               sm.checkRead(name);
  127               if ((mode & OPEN_DELETE) != 0) {
  128                   sm.checkDelete(name);
  129               }
  130           }
  131           jzfile = open(name, mode, file.lastModified());
  132   
  133           this.name = name;
  134           this.total = getTotal(jzfile);
  135       }
  136   
  137       private static native long open(String name, int mode, long lastModified);
  138       private static native int getTotal(long jzfile);
  139   
  140   
  141       /**
  142        * Opens a ZIP file for reading given the specified File object.
  143        * @param file the ZIP file to be opened for reading
  144        * @throws ZipException if a ZIP error has occurred
  145        * @throws IOException if an I/O error has occurred
  146        */
  147       public ZipFile(File file) throws ZipException, IOException {
  148           this(file, OPEN_READ);
  149       }
  150   
  151       /**
  152        * Returns the zip file entry for the specified name, or null
  153        * if not found.
  154        *
  155        * @param name the name of the entry
  156        * @return the zip file entry, or null if not found
  157        * @throws IllegalStateException if the zip file has been closed
  158        */
  159       public ZipEntry getEntry(String name) {
  160           if (name == null) {
  161               throw new NullPointerException("name");
  162           }
  163           long jzentry = 0;
  164           synchronized (this) {
  165               ensureOpen();
  166               jzentry = getEntry(jzfile, name, true);
  167               if (jzentry != 0) {
  168                   ZipEntry ze = new ZipEntry(name, jzentry);
  169                   freeEntry(jzfile, jzentry);
  170                   return ze;
  171               }
  172           }
  173           return null;
  174       }
  175   
  176       private static native long getEntry(long jzfile, String name,
  177                                           boolean addSlash);
  178   
  179       // freeEntry releases the C jzentry struct.
  180       private static native void freeEntry(long jzfile, long jzentry);
  181   
  182       /**
  183        * Returns an input stream for reading the contents of the specified
  184        * zip file entry.
  185        *
  186        * <p> Closing this ZIP file will, in turn, close all input
  187        * streams that have been returned by invocations of this method.
  188        *
  189        * @param entry the zip file entry
  190        * @return the input stream for reading the contents of the specified
  191        * zip file entry.
  192        * @throws ZipException if a ZIP format error has occurred
  193        * @throws IOException if an I/O error has occurred
  194        * @throws IllegalStateException if the zip file has been closed
  195        */
  196       public InputStream getInputStream(ZipEntry entry) throws IOException {
  197           return getInputStream(entry.name);
  198       }
  199   
  200       /**
  201        * Returns an input stream for reading the contents of the specified
  202        * entry, or null if the entry was not found.
  203        */
  204       private InputStream getInputStream(String name) throws IOException {
  205           if (name == null) {
  206               throw new NullPointerException("name");
  207           }
  208           long jzentry = 0;
  209           ZipFileInputStream in = null;
  210           synchronized (this) {
  211               ensureOpen();
  212               jzentry = getEntry(jzfile, name, false);
  213               if (jzentry == 0) {
  214                   return null;
  215               }
  216   
  217               in = new ZipFileInputStream(jzentry);
  218   
  219           }
  220           final ZipFileInputStream zfin = in;
  221           switch (getMethod(jzentry)) {
  222           case STORED:
  223               return zfin;
  224           case DEFLATED:
  225               // MORE: Compute good size for inflater stream:
  226               long size = getSize(jzentry) + 2; // Inflater likes a bit of slack
  227               if (size > 65536) size = 8192;
  228               if (size <= 0) size = 4096;
  229               return new InflaterInputStream(zfin, getInflater(), (int)size) {
  230                   private boolean isClosed = false;
  231   
  232                   public void close() throws IOException {
  233                       if (!isClosed) {
  234                            releaseInflater(inf);
  235                           this.in.close();
  236                           isClosed = true;
  237                       }
  238                   }
  239                   // Override fill() method to provide an extra "dummy" byte
  240                   // at the end of the input stream. This is required when
  241                   // using the "nowrap" Inflater option.
  242                   protected void fill() throws IOException {
  243                       if (eof) {
  244                           throw new EOFException(
  245                               "Unexpected end of ZLIB input stream");
  246                       }
  247                       len = this.in.read(buf, 0, buf.length);
  248                       if (len == -1) {
  249                           buf[0] = 0;
  250                           len = 1;
  251                           eof = true;
  252                       }
  253                       inf.setInput(buf, 0, len);
  254                   }
  255                   private boolean eof;
  256   
  257                   public int available() throws IOException {
  258                       if (isClosed)
  259                           return 0;
  260                       long avail = zfin.size() - inf.getBytesWritten();
  261                       return avail > (long) Integer.MAX_VALUE ?
  262                           Integer.MAX_VALUE : (int) avail;
  263                   }
  264               };
  265           default:
  266               throw new ZipException("invalid compression method");
  267           }
  268       }
  269   
  270       private static native int getMethod(long jzentry);
  271   
  272       /*
  273        * Gets an inflater from the list of available inflaters or allocates
  274        * a new one.
  275        */
  276       private Inflater getInflater() {
  277           synchronized (inflaters) {
  278               int size = inflaters.size();
  279               if (size > 0) {
  280                   Inflater inf = (Inflater)inflaters.remove(size - 1);
  281                   inf.reset();
  282                   return inf;
  283               } else {
  284                   return new Inflater(true);
  285               }
  286           }
  287       }
  288   
  289       /*
  290        * Releases the specified inflater to the list of available inflaters.
  291        */
  292       private void releaseInflater(Inflater inf) {
  293           synchronized (inflaters) {
  294               inflaters.add(inf);
  295           }
  296       }
  297   
  298       // List of available Inflater objects for decompression
  299       private Vector inflaters = new Vector();
  300   
  301       /**
  302        * Returns the path name of the ZIP file.
  303        * @return the path name of the ZIP file
  304        */
  305       public String getName() {
  306           return name;
  307       }
  308   
  309       /**
  310        * Returns an enumeration of the ZIP file entries.
  311        * @return an enumeration of the ZIP file entries
  312        * @throws IllegalStateException if the zip file has been closed
  313        */
  314       public Enumeration<? extends ZipEntry> entries() {
  315           ensureOpen();
  316           return new Enumeration<ZipEntry>() {
  317                   private int i = 0;
  318                   public boolean hasMoreElements() {
  319                       synchronized (ZipFile.this) {
  320                           ensureOpen();
  321                           return i < total;
  322                       }
  323                   }
  324                   public ZipEntry nextElement() throws NoSuchElementException {
  325                       synchronized (ZipFile.this) {
  326                           ensureOpen();
  327                           if (i >= total) {
  328                               throw new NoSuchElementException();
  329                           }
  330                           long jzentry = getNextEntry(jzfile, i++);
  331                           if (jzentry == 0) {
  332                               String message;
  333                               if (closeRequested) {
  334                                   message = "ZipFile concurrently closed";
  335                               } else {
  336                                   message = getZipMessage(ZipFile.this.jzfile);
  337                               }
  338                               throw new ZipError("jzentry == 0" +
  339                                                  ",\n jzfile = " + ZipFile.this.jzfile +
  340                                                  ",\n total = " + ZipFile.this.total +
  341                                                  ",\n name = " + ZipFile.this.name +
  342                                                  ",\n i = " + i +
  343                                                  ",\n message = " + message
  344                                   );
  345                           }
  346                           ZipEntry ze = new ZipEntry(jzentry);
  347                           freeEntry(jzfile, jzentry);
  348                           return ze;
  349                       }
  350                   }
  351               };
  352       }
  353   
  354       private static native long getNextEntry(long jzfile, int i);
  355   
  356       /**
  357        * Returns the number of entries in the ZIP file.
  358        * @return the number of entries in the ZIP file
  359        * @throws IllegalStateException if the zip file has been closed
  360        */
  361       public int size() {
  362           ensureOpen();
  363           return total;
  364       }
  365   
  366       /**
  367        * Closes the ZIP file.
  368        * <p> Closing this ZIP file will close all of the input streams
  369        * previously returned by invocations of the {@link #getInputStream
  370        * getInputStream} method.
  371        *
  372        * @throws IOException if an I/O error has occurred
  373        */
  374       public void close() throws IOException {
  375           synchronized (this) {
  376               closeRequested = true;
  377   
  378               if (jzfile != 0) {
  379                   // Close the zip file
  380                   long zf = this.jzfile;
  381                   jzfile = 0;
  382   
  383                   close(zf);
  384   
  385                   // Release inflaters
  386                   synchronized (inflaters) {
  387                       int size = inflaters.size();
  388                       for (int i = 0; i < size; i++) {
  389                           Inflater inf = (Inflater)inflaters.get(i);
  390                           inf.end();
  391                       }
  392                   }
  393               }
  394           }
  395       }
  396   
  397   
  398       /**
  399        * Ensures that the <code>close</code> method of this ZIP file is
  400        * called when there are no more references to it.
  401        *
  402        * <p>
  403        * Since the time when GC would invoke this method is undetermined,
  404        * it is strongly recommended that applications invoke the <code>close</code>
  405        * method as soon they have finished accessing this <code>ZipFile</code>.
  406        * This will prevent holding up system resources for an undetermined
  407        * length of time.
  408        *
  409        * @throws IOException if an I/O error has occurred
  410        * @see    java.util.zip.ZipFile#close()
  411        */
  412       protected void finalize() throws IOException {
  413           close();
  414       }
  415   
  416       private static native void close(long jzfile);
  417   
  418       private void ensureOpen() {
  419           if (closeRequested) {
  420               throw new IllegalStateException("zip file closed");
  421           }
  422   
  423           if (jzfile == 0) {
  424               throw new IllegalStateException("The object is not initialized.");
  425           }
  426       }
  427   
  428       private void ensureOpenOrZipException() throws IOException {
  429           if (closeRequested) {
  430               throw new ZipException("ZipFile closed");
  431           }
  432       }
  433   
  434       /*
  435        * Inner class implementing the input stream used to read a
  436        * (possibly compressed) zip file entry.
  437        */
  438      private class ZipFileInputStream extends InputStream {
  439           protected long jzentry; // address of jzentry data
  440           private   long pos;     // current position within entry data
  441           protected long rem;     // number of remaining bytes within entry
  442           protected long size;    // uncompressed size of this entry
  443   
  444           ZipFileInputStream(long jzentry) {
  445               pos = 0;
  446               rem = getCSize(jzentry);
  447               size = getSize(jzentry);
  448               this.jzentry = jzentry;
  449           }
  450   
  451           public int read(byte b[], int off, int len) throws IOException {
  452               if (rem == 0) {
  453                   return -1;
  454               }
  455               if (len <= 0) {
  456                   return 0;
  457               }
  458               if (len > rem) {
  459                   len = (int) rem;
  460               }
  461               synchronized (ZipFile.this) {
  462                   ensureOpenOrZipException();
  463   
  464                   len = ZipFile.read(ZipFile.this.jzfile, jzentry, pos, b,
  465                                      off, len);
  466               }
  467               if (len > 0) {
  468                   pos += len;
  469                   rem -= len;
  470               }
  471               if (rem == 0) {
  472                   close();
  473               }
  474               return len;
  475           }
  476   
  477           public int read() throws IOException {
  478               byte[] b = new byte[1];
  479               if (read(b, 0, 1) == 1) {
  480                   return b[0] & 0xff;
  481               } else {
  482                   return -1;
  483               }
  484           }
  485   
  486           public long skip(long n) {
  487               if (n > rem)
  488                   n = rem;
  489               pos += n;
  490               rem -= n;
  491               if (rem == 0) {
  492                   close();
  493               }
  494               return n;
  495           }
  496   
  497           public int available() {
  498               return rem > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) rem;
  499           }
  500   
  501           public long size() {
  502               return size;
  503           }
  504   
  505           public void close() {
  506               rem = 0;
  507               synchronized (ZipFile.this) {
  508                   if (jzentry != 0 && ZipFile.this.jzfile != 0) {
  509                       freeEntry(ZipFile.this.jzfile, jzentry);
  510                       jzentry = 0;
  511                   }
  512               }
  513           }
  514   
  515       }
  516   
  517       private static native int read(long jzfile, long jzentry,
  518                                      long pos, byte[] b, int off, int len);
  519   
  520       private static native long getCSize(long jzentry);
  521   
  522       private static native long getSize(long jzentry);
  523   
  524       // Temporary add on for bug troubleshooting
  525       private static native String getZipMessage(long jzfile);
  526   }

Save This Page
Home » openjdk-7 » java » util » zip » [javadoc | source]