Author: bodewig
Date: Tue Feb 16 11:37:44 2010
New Revision: 910483

URL: http://svn.apache.org/viewvc?rev=910483&view=rev
Log:
allow different strategies when parsing extra fields.  COMPRESS-73

Added:
    
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnparseableExtraFieldData.java
   (with props)
Modified:
    
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java
    
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnrecognizedExtraField.java
    
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipUtil.java
    
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtilsTest.java

Modified: 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java?rev=910483&r1=910482&r2=910483&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java
 (original)
+++ 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java
 Tue Feb 16 11:37:44 2010
@@ -86,18 +86,19 @@
 
     /**
      * Split the array into ExtraFields and populate them with the
-     * given data as local file data.
+     * given data as local file data, throwing an exception if the
+     * data cannot be parsed.
      * @param data an array of bytes as it appears in local file data
      * @return an array of ExtraFields
      * @throws ZipException on error
      */
     public static ZipExtraField[] parse(byte[] data) throws ZipException {
-        return parse(data, true);
+        return parse(data, true, UnparseableExtraField.THROW);
     }
 
     /**
      * Split the array into ExtraFields and populate them with the
-     * given data.
+     * given data, throwing an exception if the data cannot be parsed.
      * @param data an array of bytes
      * @param local whether data originates from the local file data
      * or the central directory
@@ -106,18 +107,59 @@
      */
     public static ZipExtraField[] parse(byte[] data, boolean local)
         throws ZipException {
+        return parse(data, local, UnparseableExtraField.THROW);
+    }
+
+    /**
+     * Split the array into ExtraFields and populate them with the
+     * given data.
+     * @param data an array of bytes
+     * @param local whether data originates from the local file data
+     * or the central directory
+     * @param onUnparseableData what to do if the extra field data
+     * cannot be parsed.
+     * @return an array of ExtraFields
+     * @throws ZipException on error
+     */
+    public static ZipExtraField[] parse(byte[] data, boolean local,
+                                        UnparseableExtraField 
onUnparseableData)
+        throws ZipException {
         List v = new ArrayList();
         int start = 0;
+        LOOP:
         while (start <= data.length - WORD) {
             ZipShort headerId = new ZipShort(data, start);
             int length = (new ZipShort(data, start + 2)).getValue();
             if (start + WORD + length > data.length) {
-                throw new ZipException("bad extra field starting at "
-                                       + start + ".  Block length of "
-                                       + length + " bytes exceeds remaining"
-                                       + " data of "
-                                       + (data.length - start - WORD)
-                                       + " bytes.");
+                switch(onUnparseableData.getKey()) {
+                case UnparseableExtraField.THROW_KEY:
+                    throw new ZipException("bad extra field starting at "
+                                           + start + ".  Block length of "
+                                           + length + " bytes exceeds 
remaining"
+                                           + " data of "
+                                           + (data.length - start - WORD)
+                                           + " bytes.");
+                case UnparseableExtraField.READ_KEY:
+                    UnparseableExtraFieldData field =
+                        new UnparseableExtraFieldData();
+                    if (local) {
+                        field.parseFromLocalFileData(data, start,
+                                                     data.length - start);
+                    } else {
+                        field.parseFromCentralDirectoryData(data, start,
+                                                            data.length - 
start);
+                    }
+                    v.add(field);
+                    /*FALLTHROUGH*/
+                case UnparseableExtraField.SKIP_KEY:
+                    // since we cannot parse the data we must assume
+                    // the extra field consumes the whole rest of the
+                    // available data
+                    break LOOP;
+                default:
+                    throw new ZipException("unknown UnparseableExtraField key: 
"
+                                           + onUnparseableData.getKey());
+                }
             }
             try {
                 ZipExtraField ze = createExtraField(headerId);
@@ -146,13 +188,19 @@
      * @return an array of bytes
      */
     public static byte[] mergeLocalFileDataData(ZipExtraField[] data) {
-        int sum = WORD * data.length;
+        final boolean lastIsUnparseableHolder = data.length > 0
+            && data[data.length - 1] instanceof UnparseableExtraFieldData;
+        int regularExtraFieldCount =
+            lastIsUnparseableHolder ? data.length - 1 : data.length;
+
+        int sum = WORD * regularExtraFieldCount;
         for (int i = 0; i < data.length; i++) {
             sum += data[i].getLocalFileDataLength().getValue();
         }
+
         byte[] result = new byte[sum];
         int start = 0;
-        for (int i = 0; i < data.length; i++) {
+        for (int i = 0; i < regularExtraFieldCount; i++) {
             System.arraycopy(data[i].getHeaderId().getBytes(),
                              0, result, start, 2);
             System.arraycopy(data[i].getLocalFileDataLength().getBytes(),
@@ -161,6 +209,10 @@
             System.arraycopy(local, 0, result, start + WORD, local.length);
             start += (local.length + WORD);
         }
+        if (lastIsUnparseableHolder) {
+            byte[] local = data[data.length - 1].getLocalFileDataData();
+            System.arraycopy(local, 0, result, start, local.length);
+        }
         return result;
     }
 
@@ -170,13 +222,18 @@
      * @return an array of bytes
      */
     public static byte[] mergeCentralDirectoryData(ZipExtraField[] data) {
-        int sum = WORD * data.length;
+        final boolean lastIsUnparseableHolder = data.length > 0
+            && data[data.length - 1] instanceof UnparseableExtraFieldData;
+        int regularExtraFieldCount =
+            lastIsUnparseableHolder ? data.length - 1 : data.length;
+
+        int sum = WORD * regularExtraFieldCount;
         for (int i = 0; i < data.length; i++) {
             sum += data[i].getCentralDirectoryLength().getValue();
         }
         byte[] result = new byte[sum];
         int start = 0;
-        for (int i = 0; i < data.length; i++) {
+        for (int i = 0; i < regularExtraFieldCount; i++) {
             System.arraycopy(data[i].getHeaderId().getBytes(),
                              0, result, start, 2);
             System.arraycopy(data[i].getCentralDirectoryLength().getBytes(),
@@ -185,6 +242,60 @@
             System.arraycopy(local, 0, result, start + WORD, local.length);
             start += (local.length + WORD);
         }
+        if (lastIsUnparseableHolder) {
+            byte[] local = data[data.length - 1].getCentralDirectoryData();
+            System.arraycopy(local, 0, result, start, local.length);
+        }
         return result;
     }
+
+    /**
+     * "enum" for the possible actions to take if the extra field
+     * cannot be parsed.
+     */
+    public static final class UnparseableExtraField {
+        /**
+         * Key for "throw an exception" action.
+         */
+        public static final int THROW_KEY = 0;
+        /**
+         * Key for "skip" action.
+         */
+        public static final int SKIP_KEY = 1;
+        /**
+         * Key for "read" action.
+         */
+        public static final int READ_KEY = 2;
+
+        /**
+         * Throw an exception if field cannot be parsed.
+         */
+        public static final UnparseableExtraField THROW
+            = new UnparseableExtraField(THROW_KEY);
+
+        /**
+         * Skip the extra field entirely and don't make its data
+         * available - effectively removing the extra field data.
+         */
+        public static final UnparseableExtraField SKIP
+            = new UnparseableExtraField(SKIP_KEY);
+
+        /**
+         * Read the extra field data into an instance of {...@link
+         * UnparseableExtraFieldData UnparseableExtraFieldData}.
+         */
+        public static final UnparseableExtraField READ
+            = new UnparseableExtraField(READ_KEY);
+
+        private final int key;
+
+        private UnparseableExtraField(int k) {
+            key = k;
+        }
+
+        /**
+         * Key of the action to take.
+         */
+        public int getKey() { return key; }
+    }
 }

Added: 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnparseableExtraFieldData.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnparseableExtraFieldData.java?rev=910483&view=auto
==============================================================================
--- 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnparseableExtraFieldData.java
 (added)
+++ 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnparseableExtraFieldData.java
 Tue Feb 16 11:37:44 2010
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.commons.compress.archivers.zip;
+
+/**
+ * Wrapper for extra field data that doesn't conform to the recommended format 
of header-tag + size + data.
+ *
+ * <p>The header-id is artificial (and not listed as a know ID in
+ * {...@link http://www.pkware.com/documents/casestudies/APPNOTE.TXT
+ * APPNOTE.TXT}.  Since it isn't used anywhere except to satisfy the
+ * ZipExtraField contract it shouldn't matter anyway.</p>
+ * @NotThreadSafe
+ */
+public final class UnparseableExtraFieldData implements ZipExtraField {
+    private static final ZipShort HEADER_ID = new ZipShort(0xACC1);
+
+    private byte[] localFileData;
+    private byte[] centralDirectoryData;
+
+    /**
+     * The Header-ID.
+     *
+     * @return a completely arbitrary value that should be ignored.
+     */
+    public ZipShort getHeaderId() {
+        return HEADER_ID;
+    }
+
+    /**
+     * Length of the complete extra field in the local file data.
+     *
+     * @return The LocalFileDataLength value
+     */
+    public ZipShort getLocalFileDataLength() {
+        return new ZipShort(localFileData == null ? 0 : localFileData.length);
+    }
+
+    /**
+     * Length of the complete extra field in the central directory.
+     *
+     * @return The CentralDirectoryLength value
+     */
+    public ZipShort getCentralDirectoryLength() {
+        return centralDirectoryData == null
+            ? getLocalFileDataLength()
+            : new ZipShort(centralDirectoryData.length);
+    }
+
+    /**
+     * The actual data to put into local file data.
+     *
+     * @return The LocalFileDataData value
+     */
+    public byte[] getLocalFileDataData() {
+        return ZipUtil.copy(localFileData);
+    }
+
+    /**
+     * The actual data to put into central directory.
+     *
+     * @return The CentralDirectoryData value
+     */
+    public byte[] getCentralDirectoryData() {
+        return centralDirectoryData == null
+            ? getLocalFileDataData() : ZipUtil.copy(centralDirectoryData);
+    }
+
+    /**
+     * Populate data from this array as if it was in local file data.
+     *
+     * @param buffer the buffer to read data from
+     * @param offset offset into buffer to read data
+     * @param length the length of data
+     */
+    public void parseFromLocalFileData(byte[] buffer, int offset, int length) {
+        localFileData = new byte[length];
+        System.arraycopy(buffer, offset, localFileData, 0, length);
+    }
+
+    /**
+     * Populate data from this array as if it was in central directory data.
+     *
+     * @param buffer the buffer to read data from
+     * @param offset offset into buffer to read data
+     * @param length the length of data
+     * @exception ZipException on error
+     */
+    public void parseFromCentralDirectoryData(byte[] buffer, int offset,
+                                              int length) {
+        centralDirectoryData = new byte[length];
+        System.arraycopy(buffer, offset, centralDirectoryData, 0, length);
+        if (localFileData == null) {
+            parseFromLocalFileData(buffer, offset, length);
+        }
+    }
+
+}

Propchange: 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnparseableExtraFieldData.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnrecognizedExtraField.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnrecognizedExtraField.java?rev=910483&r1=910482&r2=910483&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnrecognizedExtraField.java
 (original)
+++ 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnrecognizedExtraField.java
 Tue Feb 16 11:37:44 2010
@@ -61,7 +61,7 @@
      * @param data the field data to use
      */
     public void setLocalFileDataData(byte[] data) {
-        localData = copy(data);
+        localData = ZipUtil.copy(data);
     }
 
     /**
@@ -77,7 +77,7 @@
      * @return the local data
      */
     public byte[] getLocalFileDataData() {
-        return copy(localData);
+        return ZipUtil.copy(localData);
     }
 
     /**
@@ -91,7 +91,7 @@
      * @param data the data to use
      */
     public void setCentralDirectoryData(byte[] data) {
-        centralData = copy(data);
+        centralData = ZipUtil.copy(data);
     }
 
     /**
@@ -112,7 +112,7 @@
      */
     public byte[] getCentralDirectoryData() {
         if (centralData != null) {
-            return copy(centralData);
+            return ZipUtil.copy(centralData);
         }
         return getLocalFileDataData();
     }
@@ -145,12 +145,4 @@
         }
     }
 
-    private static byte[] copy(byte[] from) {
-        if (from != null) {
-            byte[] to = new byte[from.length];
-            System.arraycopy(from, 0, to, 0, to.length);
-            return to;
-        }
-        return null;
-    }
 }

Modified: 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipUtil.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipUtil.java?rev=910483&r1=910482&r2=910483&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipUtil.java
 (original)
+++ 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipUtil.java
 Tue Feb 16 11:37:44 2010
@@ -167,5 +167,17 @@
         return null;
     }
 
+    /**
+     * Create a copy of the given array - or return null if the
+     * argument is null.
+     */
+    static byte[] copy(byte[] from) {
+        if (from != null) {
+            byte[] to = new byte[from.length];
+            System.arraycopy(from, 0, to, 0, to.length);
+            return to;
+        }
+        return null;
+    }
 
 }
\ No newline at end of file

Modified: 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtilsTest.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtilsTest.java?rev=910483&r1=910482&r2=910483&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtilsTest.java
 (original)
+++ 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtilsTest.java
 Tue Feb 16 11:37:44 2010
@@ -18,6 +18,7 @@
 
 package org.apache.commons.compress.archivers.zip;
 
+import java.util.Arrays;
 import junit.framework.TestCase;
 
 /**
@@ -84,6 +85,59 @@
         }
     }
 
+    public void testParseWithRead() throws Exception {
+        ZipExtraField[] ze =
+            ExtraFieldUtils.parse(data, true,
+                                  ExtraFieldUtils.UnparseableExtraField.READ);
+        assertEquals("number of fields", 2, ze.length);
+        assertTrue("type field 1", ze[0] instanceof AsiExtraField);
+        assertEquals("mode field 1", 040755,
+                     ((AsiExtraField) ze[0]).getMode());
+        assertTrue("type field 2", ze[1] instanceof UnrecognizedExtraField);
+        assertEquals("data length field 2", 1,
+                     ze[1].getLocalFileDataLength().getValue());
+
+        byte[] data2 = new byte[data.length-1];
+        System.arraycopy(data, 0, data2, 0, data2.length);
+        ze = ExtraFieldUtils.parse(data2, true,
+                                   ExtraFieldUtils.UnparseableExtraField.READ);
+        assertEquals("number of fields", 2, ze.length);
+        assertTrue("type field 1", ze[0] instanceof AsiExtraField);
+        assertEquals("mode field 1", 040755,
+                     ((AsiExtraField) ze[0]).getMode());
+        assertTrue("type field 2", ze[1] instanceof UnparseableExtraFieldData);
+        assertEquals("data length field 2", 4,
+                     ze[1].getLocalFileDataLength().getValue());
+        byte[] expectedData = new byte[4];
+        for (int i = 0; i < 4; i++) {
+            assertEquals("byte number " + i,
+                         data2[data.length - 5 + i],
+                         ze[1].getLocalFileDataData()[i]);
+        }
+    }
+
+    public void testParseWithSkip() throws Exception {
+        ZipExtraField[] ze =
+            ExtraFieldUtils.parse(data, true,
+                                  ExtraFieldUtils.UnparseableExtraField.SKIP);
+        assertEquals("number of fields", 2, ze.length);
+        assertTrue("type field 1", ze[0] instanceof AsiExtraField);
+        assertEquals("mode field 1", 040755,
+                     ((AsiExtraField) ze[0]).getMode());
+        assertTrue("type field 2", ze[1] instanceof UnrecognizedExtraField);
+        assertEquals("data length field 2", 1,
+                     ze[1].getLocalFileDataLength().getValue());
+
+        byte[] data2 = new byte[data.length-1];
+        System.arraycopy(data, 0, data2, 0, data2.length);
+        ze = ExtraFieldUtils.parse(data2, true,
+                                   ExtraFieldUtils.UnparseableExtraField.SKIP);
+        assertEquals("number of fields", 1, ze.length);
+        assertTrue("type field 1", ze[0] instanceof AsiExtraField);
+        assertEquals("mode field 1", 040755,
+                     ((AsiExtraField) ze[0]).getMode());
+    }
+
     /**
      * Test merge methods
      */
@@ -112,4 +166,30 @@
         }
 
     }
+
+    public void testMergeWithUnparseableData() throws Exception {
+        ZipExtraField d = new UnparseableExtraFieldData();
+        d.parseFromLocalFileData(new byte[] {1, 0, 1, 0}, 0, 4);
+        byte[] local =
+            ExtraFieldUtils.mergeLocalFileDataData(new ZipExtraField[] {a, d});
+        assertEquals("local length", data.length - 1, local.length);
+        for (int i = 0; i < local.length; i++) {
+            assertEquals("local byte " + i, data[i], local[i]);
+        }
+
+        byte[] dCentral = d.getCentralDirectoryData();
+        byte[] data2 = new byte[4 + aLocal.length + dCentral.length];
+        System.arraycopy(data, 0, data2, 0, 4 + aLocal.length + 2);
+        System.arraycopy(dCentral, 0, data2,
+                         4 + aLocal.length, dCentral.length);
+
+
+        byte[] central =
+            ExtraFieldUtils.mergeCentralDirectoryData(new ZipExtraField[] {a, 
d});
+        assertEquals("central length", data2.length, central.length);
+        for (int i = 0; i < central.length; i++) {
+            assertEquals("central byte " + i, data2[i], central[i]);
+        }
+
+    }
 }


Reply via email to