You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ant.apache.org by bo...@apache.org on 2003/07/02 16:36:14 UTC

cvs commit: ant/src/main/org/apache/tools/zip ZipFile.java ZipEntry.java

bodewig     2003/07/02 07:36:14

  Modified:    src/main/org/apache/tools/zip ZipEntry.java
  Added:       src/main/org/apache/tools/zip ZipFile.java
  Log:
  Initial support for reading of ZIP files - uncomplete as you cannot
  get an InputStream to the ZipEntrys yet.
  
  Will be needed to fix PR 10504 as well as to preserve permissions when
  updating an archive (no PR yet).
  
  Revision  Changes    Path
  1.9       +58 -12    ant/src/main/org/apache/tools/zip/ZipEntry.java
  
  Index: ZipEntry.java
  ===================================================================
  RCS file: /home/cvs/ant/src/main/org/apache/tools/zip/ZipEntry.java,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- ZipEntry.java	7 Mar 2003 11:23:10 -0000	1.8
  +++ ZipEntry.java	2 Jul 2003 14:36:14 -0000	1.9
  @@ -66,7 +66,7 @@
    * @author Stefan Bodewig
    * @version $Revision$
    */
  -public class ZipEntry extends java.util.zip.ZipEntry {
  +public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable {
   
       private static final int PLATFORM_UNIX = 3;
       private static final int PLATFORM_FAT  = 0;
  @@ -75,6 +75,7 @@
       private int platform = PLATFORM_FAT;
       private long externalAttributes = 0;
       private Vector extraFields = new Vector();
  +    private String name = null;
   
       /**
        * Creates a new zip entry with the specified name.
  @@ -136,22 +137,49 @@
       }
   
       /**
  +     * @since 1.9
  +     */
  +    protected ZipEntry() {
  +        super("");
  +    }
  +
  +    /**
        * Overwrite clone
        *
        * @since 1.1
        */
       public Object clone() {
  -        ZipEntry e = null;
           try {
  -            e = new ZipEntry((java.util.zip.ZipEntry) super.clone());
  -        } catch (Exception ex) {
  -            // impossible as extra data is in correct format
  -            ex.printStackTrace();
  -        }
  -        e.setInternalAttributes(getInternalAttributes());
  -        e.setExternalAttributes(getExternalAttributes());
  -        e.setExtraFields(getExtraFields());
  -        return e;
  +            ZipEntry e = (ZipEntry) super.clone();
  +
  +            e.setName(getName());
  +            e.setComment(getComment());
  +            e.setMethod(getMethod());
  +            e.setTime(getTime());
  +            long size = getSize();
  +            if (size > 0) {
  +                e.setSize(size);
  +            }
  +            long cSize = getCompressedSize();
  +            if (cSize > 0) {
  +                e.setComprSize(cSize);
  +            }
  +            long crc = getCrc();
  +            if (crc > 0) {
  +                e.setCrc(crc);
  +            }
  +            
  +            e.extraFields = (Vector) extraFields.clone();
  +            e.setInternalAttributes(getInternalAttributes());
  +            e.setExternalAttributes(getExternalAttributes());
  +            e.setExtraFields(getExtraFields());
  +            return e;
  +        } catch (Throwable t) {
  +            // in JDK 1.1 ZipEntry is not Cloneable, so super.clone declares
  +            // to throw CloneNotSupported - since JDK 1.2 it is overridden to
  +            // not throw that exception
  +            return null;
  +        }
       }
   
       /**
  @@ -219,6 +247,13 @@
       }
   
       /**
  +     * @since 1.9
  +     */
  +    protected void setPlatform(int platform) {
  +        this.platform = platform;
  +    }
  +
  +    /**
        * Replaces all currently attached extra fields with the new array.
        *
        * @since 1.1
  @@ -360,6 +395,17 @@
               return compressedSize.longValue();
           }
           return super.getCompressedSize();
  +    }
  +
  +    /**
  +     * @since 1.9
  +     */
  +    public String getName() {
  +        return name == null ? super.getName() : name;
  +    }
  +
  +    protected void setName(String name) {
  +        this.name = name;
       }
   
       /**
  
  
  
  1.1                  ant/src/main/org/apache/tools/zip/ZipFile.java
  
  Index: ZipFile.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2003 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "Ant" and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.tools.zip;
  
  import java.io.File;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.RandomAccessFile;
  import java.io.UnsupportedEncodingException;
  import java.util.Calendar;
  import java.util.Date;
  import java.util.Enumeration;
  import java.util.Hashtable;
  import java.util.zip.ZipException;
  
  /**
   * Replacement for <code>java.util.ZipFile</code>.
   *
   * <p>This class adds support for file name encodings other than UTF-8
   * (which is required to work on ZIP files created by native zip tools
   * and is able to skip a preamble like the one found in self
   * extracting archives.  Furthermore it returns instances of
   * <code>org.apache.tools.zip.ZipEntry</code> instead of
   * <code>java.util.zip.ZipEntry</code>.</p>
   *
   * <p>It doesn't extend <code>java.util.zip.ZipFile</code> as it would
   * have to reimplement all methods anyway.  Like
   * <code>java.util.ZipFile</code>, it uses RandomAccessFile under the
   * covers and supports compressed and uncompressed entries.</p>
   *
   * <p>The method signatures mimic the ones of
   * <code>java.util.zip.ZipFile</code>, with a couple of exceptions:
   *
   * <ul>
   *   <li>The String-arg constructor declares to throw ZipException
   *   as well.</li>
   *   <li>There is no getName method.</li>
   *   <li>entries has been renamed to getEntries.</li>
   *   <li>getEntries and getEntry return
   *   <code>org.apache.tools.zip.ZipEntry</code> instances.</li>
   *   <li>close is allowed to throw IOException.</li>
   * </ul>
   *
   * @author Stefan Bodewig
   * @version $Revision: 1.1 $
   */
  public class ZipFile {
  
      /**
       * Maps ZipEntrys to Longs, recording the offsets of the local
       * file headers.
       */
      private Hashtable entries = new Hashtable();
  
      /**
       * Maps String to ZipEntrys, name -> actual entry.
       */
      private Hashtable nameMap = new Hashtable();
  
      /**
       * Maps ZipEntrys to Longs, recording the offsets of the actual file data.
       */
      private Hashtable dataOffsets = new Hashtable();
  
      /**
       * The encoding to use for filenames and the file comment.
       *
       * <p>For a list of possible values see <a
       * href="http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html">http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html</a>.
       * Defaults to the platform's default character encoding.</p>
       */
      private String encoding = null;
  
      /**
       * The actual data source.
       */
      private RandomAccessFile archive;
  
      /**
       * Opens the given file for reading, assuming the platform's
       * native encoding for file names.
       */
      public ZipFile(File f) throws IOException, ZipException {
          this(f, null);
      }
  
      /**
       * Opens the given file for reading, assuming the platform's
       * native encoding for file names.
       */
      public ZipFile(String name) throws IOException, ZipException {
          this(new File(name), null);
      }
  
      /**
       * Opens the given file for reading, assuming the specified
       * encoding for file names.
       */
      public ZipFile(String name, String encoding) 
          throws IOException, ZipException {
          this(new File(name), encoding);
      }
  
      /**
       * Opens the given file for reading, assuming the specified
       * encoding for file names.
       */
      public ZipFile(File f, String encoding) 
          throws IOException, ZipException {
          this.encoding = encoding;
          archive = new RandomAccessFile(f, "r");
          populateFromCentralDirectory();
          resolveLocalFileHeaderData();
      }
  
      /**
       * The encoding to use for filenames and the file comment.
       *
       * @return null if using the platform's default character encoding.
       */
      public String getEncoding() {
          return encoding;
      }
  
      /**
       * Closes the archive.
       */
      public void close() throws IOException {
          archive.close();
      }
  
      /**
       * Returns all entries as {@link org.apache.tools.ant.ZipEntry
       * ZipEntry} instances.
       */
      public Enumeration getEntries() {
          return entries.keys();
      }
  
      /**
       * Returns a named entry - or <code>null</code> if no entry by
       * that name exists.
       */
      public ZipEntry getEntry(String name) {
          return (ZipEntry) nameMap.get(name);
      }
  
      /**
       * Returns an InputStream for reading the contents of the given entry.
       */
      public InputStream getInputStream(ZipEntry ze)
          throws IOException, ZipException {
          return null;
      }
  
      private static final int CFH_LEN =
          /* version made by                 */ 2 +
          /* version needed to extract       */ 2 +
          /* general purpose bit flag        */ 2 +
          /* compression method              */ 2 +
          /* last mod file time              */ 2 +
          /* last mod file date              */ 2 +
          /* crc-32                          */ 4 +
          /* compressed size                 */ 4 +
          /* uncompressed size               */ 4 +
          /* filename length                 */ 2 +
          /* extra field length              */ 2 +
          /* file comment length             */ 2 +
          /* disk number start               */ 2 +
          /* internal file attributes        */ 2 +
          /* external file attributes        */ 4 +
          /* relative offset of local header */ 4;
  
      /**
       * Reads the central directory of the given archive and populates
       * the interal tables with ZipEntry instances.
       *
       * <p>The ZipEntrys will know all data that can be obtained from
       * the central directory alone, but not the data that requires the
       * local file header or additional data to be read.</p> 
       */
      private void populateFromCentralDirectory() 
          throws IOException, ZipException {
          positionAtCentralDirectory();
  
          byte[] cfh = new byte[CFH_LEN];
  
          byte[] signatureBytes = new byte[4];
          archive.readFully(signatureBytes);
          ZipLong sig = new ZipLong(signatureBytes);
          while (sig.equals(ZipOutputStream.CFH_SIG)) {
              archive.readFully(cfh);
              int off = 0;
              ZipEntry ze = new ZipEntry();
  
              ZipShort versionMadeBy = new ZipShort(cfh, off);
              off += 2;
              ze.setPlatform((versionMadeBy.getValue() >> 8) & 0x0F);
  
              off += 4;// skip version info and general purpose byte
  
              ze.setMethod((new ZipShort(cfh, off)).getValue());
              off += 2;
              
              ze.setTime(fromDosTime(new ZipLong(cfh, off)).getTime());
              off += 4;
              
              ze.setCrc((new ZipLong(cfh, off)).getValue());
              off += 4;
              
              ze.setCompressedSize((new ZipLong(cfh, off)).getValue());
              off += 4;
  
              ze.setSize((new ZipLong(cfh, off)).getValue());
              off += 4;
  
              int fileNameLen = (new ZipShort(cfh, off)).getValue();
              off += 2;
  
              int extraLen = (new ZipShort(cfh, off)).getValue();
              off += 2;
  
              int commentLen = (new ZipShort(cfh, off)).getValue();
              off += 2;
  
              off += 2; // disk number
  
              ze.setInternalAttributes((new ZipShort(cfh, off)).getValue());
              off += 2;
  
              ze.setExternalAttributes((new ZipLong(cfh, off)).getValue());
              off += 4;
  
              // LFH offset
              entries.put(ze, new Long((new ZipLong(cfh, off)).getValue()));
  
              byte[] fileName = new byte[fileNameLen];
              archive.readFully(fileName);
              ze.setName(getString(fileName));
  
              nameMap.put(ze.getName(), ze);
  
              archive.skipBytes(extraLen);
  
              byte[] comment = new byte[commentLen];
              archive.readFully(comment);
              ze.setComment(getString(comment));
  
              archive.readFully(signatureBytes);
              sig = new ZipLong(signatureBytes);
          }
      }
  
      /**
       * Searches for the first occurence of the central file header
       * signature.
       */
      private void positionAtCentralDirectory() 
          throws IOException, ZipException {
          archive.seek(0);
          int off = 0;
          byte[] sig = ZipOutputStream.CFH_SIG.getBytes();
          int curr = archive.read();
          boolean found = false;
          while (curr != -1) {
              if (curr == sig[0]) {
                  curr = archive.read();
                  if (curr == sig[1]) {
                      curr = archive.read();
                      if (curr == sig[2]) {
                          curr = archive.read();
                          if (curr == sig[3]) {
                              found = true;
                              break;
                          }
                      }
                  }
                  archive.seek(++off);
              } else {
                  off++;
              }
              curr = archive.read();
          }
          if (!found) {
              throw new ZipException("archive is not a ZIP archive");
          }
          archive.seek(off);
      }
  
      /**
       * Number of bytes in local file header up to the &quot;length of
       * filename&quot; entry.
       */
      private static final long LFH_OFFSET_FOR_FILENAME_LENGTH =
          /* local file header signature     */ 4 +
          /* version needed to extract       */ 2 +
          /* general purpose bit flag        */ 2 +
          /* compression method              */ 2 +
          /* last mod file time              */ 2 +
          /* last mod file date              */ 2 +
          /* crc-32                          */ 4 +
          /* compressed size                 */ 4 +
          /* uncompressed size               */ 4;
  
      /**
       * Walks through all recorded entries and adds the data available
       * from the local file header.
       *
       * <p>Also records the offsets for the data to read from the
       * entries.</p>
       */
      private void resolveLocalFileHeaderData()
          throws IOException {
          Enumeration enum = getEntries();
          while (enum.hasMoreElements()) {
              ZipEntry ze = (ZipEntry) enum.nextElement();
              long offset = ((Long) entries.get(ze)).longValue();
              archive.seek(offset + LFH_OFFSET_FOR_FILENAME_LENGTH);
              byte[] b = new byte[2];
              archive.readFully(b);
              int fileNameLen = (new ZipShort(b)).getValue();
              archive.readFully(b);
              int extraFieldLen = (new ZipShort(b)).getValue();
              archive.skipBytes(fileNameLen);
              byte[] localExtraData = new byte[extraFieldLen];
              archive.readFully(localExtraData);
              ze.setExtra(localExtraData);
              dataOffsets.put(ze, 
                              new Long(offset + LFH_OFFSET_FOR_FILENAME_LENGTH
                                       + 2 + 2 + fileNameLen + extraFieldLen));
          }
      }
  
      /**
       * Convert a DOS date/time field to a Date object.
       */
      protected static Date fromDosTime(ZipLong l) {
          long dosTime = l.getValue();
          Calendar cal = Calendar.getInstance();
          cal.set(Calendar.YEAR, (int) ((dosTime >> 25) & 0x7f) + 1980);
          cal.set(Calendar.MONTH, (int) ((dosTime >> 21) & 0x0f) - 1);
          cal.set(Calendar.DATE, (int) (dosTime >> 16) & 0x1f);
          cal.set(Calendar.HOUR, (int) (dosTime >> 11) & 0x1f);
          cal.set(Calendar.MINUTE, (int) (dosTime >> 5) & 0x3f);
          cal.set(Calendar.SECOND, (int) (dosTime << 1) & 0x3e);
          return cal.getTime();
      }
  
      /**
       * Retrieve a String from the given bytes using the encoding set
       * for this ZipFile.
       */
      protected String getString(byte[] bytes) throws ZipException {
          if (encoding == null) {
              return new String(bytes);
          } else {
              try {
                  return new String(bytes, encoding);
              } catch (UnsupportedEncodingException uee) {
                  throw new ZipException(uee.getMessage());
              }
          }
      }
  
  }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org