Author: bodewig
Date: Sat May 25 17:47:00 2013
New Revision: 1486348

URL: http://svn.apache.org/r1486348
Log:
provide access to all entries of a given name in ZipFile, COMPRESS-227

Modified:
    
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java
    
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java

Modified: 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java?rev=1486348&r1=1486347&r2=1486348&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java
 (original)
+++ 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java
 Sat May 25 17:47:00 2013
@@ -25,9 +25,12 @@ import java.io.RandomAccessFile;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Deque;
 import java.util.Enumeration;
 import java.util.HashMap;
-import java.util.LinkedHashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 import java.util.zip.Inflater;
 import java.util.zip.InflaterInputStream;
@@ -83,17 +86,17 @@ public class ZipFile {
     private static final int POS_3 = 3;
 
     /**
-     * Maps ZipArchiveEntrys to two longs, recording the offsets of
-     * the local file headers and the start of entry data.
+     * List of entries in the order they appear inside the central
+     * directory.
      */
-    private final Map<ZipArchiveEntry, OffsetEntry> entries =
-        new LinkedHashMap<ZipArchiveEntry, OffsetEntry>(HASH_SIZE);
+    private final List<ZipArchiveEntry> entries =
+        new LinkedList<ZipArchiveEntry>();
 
     /**
-     * Maps String to ZipArchiveEntrys, name -> actual entry.
+     * Maps String to list of ZipArchiveEntrys, name -> actual entries.
      */
-    private final Map<String, ZipArchiveEntry> nameMap =
-        new HashMap<String, ZipArchiveEntry>(HASH_SIZE);
+    private final Map<String, Deque<ZipArchiveEntry>> nameMap =
+        new HashMap<String, Deque<ZipArchiveEntry>>(HASH_SIZE);
 
     private static final class OffsetEntry {
         private long headerOffset = -1;
@@ -273,7 +276,7 @@ public class ZipFile {
      * @return all entries as {@link ZipArchiveEntry} instances
      */
     public Enumeration<ZipArchiveEntry> getEntries() {
-        return Collections.enumeration(entries.keySet());
+        return Collections.enumeration(entries);
     }
 
     /**
@@ -287,8 +290,7 @@ public class ZipFile {
      * @since 1.1
      */
     public Enumeration<ZipArchiveEntry> getEntriesInPhysicalOrder() {
-        ZipArchiveEntry[] allEntries =
-            entries.keySet().toArray(new ZipArchiveEntry[0]);
+        ZipArchiveEntry[] allEntries = entries.toArray(new ZipArchiveEntry[0]);
         Arrays.sort(allEntries, OFFSET_COMPARATOR);
         return Collections.enumeration(Arrays.asList(allEntries));
     }
@@ -296,12 +298,51 @@ public class ZipFile {
     /**
      * Returns a named entry - or {@code null} if no entry by
      * that name exists.
+     *
+     * <p>If multiple entries with the same name exist the first entry
+     * in the archive's central directory by that name is
+     * returned.</p>
+     *
      * @param name name of the entry.
      * @return the ZipArchiveEntry corresponding to the given name - or
      * {@code null} if not present.
      */
     public ZipArchiveEntry getEntry(String name) {
-        return nameMap.get(name);
+        Deque<ZipArchiveEntry> entriesOfThatName = nameMap.get(name);
+        return entriesOfThatName != null ? entriesOfThatName.getFirst() : null;
+    }
+
+    /**
+     * Returns all named entries in the same order they appear within
+     * the archive's central directory.
+     *
+     * @param name name of the entry.
+     * @return the Iterator<ZipArchiveEntry> corresponding to the
+     * given name
+     * @since 1.6
+     */
+    public Iterator<ZipArchiveEntry> getEntries(String name) {
+        Deque<ZipArchiveEntry> entriesOfThatName = nameMap.get(name);
+        return entriesOfThatName != null ? entriesOfThatName.iterator()
+            : Collections.<ZipArchiveEntry>emptyList().iterator();
+    }
+
+    /**
+     * Returns all named entries in the same order their contents
+     * appear within the archive.
+     *
+     * @param name name of the entry.
+     * @return the Iterator<ZipArchiveEntry> corresponding to the
+     * given name
+     * @since 1.6
+     */
+    public Iterator<ZipArchiveEntry> getEntriesInPhysicalOrder(String name) {
+        ZipArchiveEntry[] entriesOfThatName = new ZipArchiveEntry[0];
+        if (nameMap.containsKey(name)) {
+            entriesOfThatName = nameMap.get(name).toArray(entriesOfThatName);
+            Arrays.sort(entriesOfThatName, OFFSET_COMPARATOR);
+        }
+        return Arrays.asList(entriesOfThatName).iterator();
     }
 
     /**
@@ -325,10 +366,12 @@ public class ZipFile {
      */
     public InputStream getInputStream(ZipArchiveEntry ze)
         throws IOException, ZipException {
-        OffsetEntry offsetEntry = entries.get(ze);
-        if (offsetEntry == null) {
+        if (!(ze instanceof Entry)) {
             return null;
         }
+        // checked just above
+        @SuppressWarnings("unchecked") OffsetEntry offsetEntry =
+            ((Entry) ze).getOffsetEntry();
         ZipUtil.checkRequestedFeatures(ze);
         long start = offsetEntry.dataOffset;
         BoundedInputStream bis =
@@ -474,7 +517,8 @@ public class ZipFile {
         throws IOException {
         archive.readFully(CFH_BUF);
         int off = 0;
-        ZipArchiveEntry ze = new ZipArchiveEntry();
+        OffsetEntry offset = new OffsetEntry();
+        Entry ze = new Entry(offset);
 
         int versionMadeBy = ZipShort.getValue(CFH_BUF, off);
         off += SHORT;
@@ -529,10 +573,9 @@ public class ZipFile {
         ze.setName(entryEncoding.decode(fileName), fileName);
 
         // LFH offset,
-        OffsetEntry offset = new OffsetEntry();
         offset.headerOffset = ZipLong.getValue(CFH_BUF, off);
         // data offset will be filled later
-        entries.put(ze, offset);
+        entries.add(ze);
 
         byte[] cdExtraData = new byte[extraLen];
         archive.readFully(cdExtraData);
@@ -851,16 +894,11 @@ public class ZipFile {
     private void resolveLocalFileHeaderData(Map<ZipArchiveEntry, 
NameAndComment>
                                             entriesWithoutUTF8Flag)
         throws IOException {
-        // changing the name of a ZipArchiveEntry is going to change
-        // the hashcode - see COMPRESS-164
-        // Map needs to be reconstructed in order to keep central
-        // directory order
-        Map<ZipArchiveEntry, OffsetEntry> origMap =
-            new LinkedHashMap<ZipArchiveEntry, OffsetEntry>(entries);
-        entries.clear();
-        for (Map.Entry<ZipArchiveEntry, OffsetEntry> ent : origMap.entrySet()) 
{
-            ZipArchiveEntry ze = ent.getKey();
-            OffsetEntry offsetEntry = ent.getValue();
+        for (Iterator<ZipArchiveEntry> it = entries.iterator(); it.hasNext(); 
) {
+            // entries is filled in populateFromCentralDirectory and
+            // never modified
+            @SuppressWarnings("unchecked") Entry ze = (Entry) it.next();
+            OffsetEntry offsetEntry = ze.getOffsetEntry();
             long offset = offsetEntry.headerOffset;
             archive.seek(offset + LFH_OFFSET_FOR_FILENAME_LENGTH);
             archive.readFully(SHORT_BUF);
@@ -883,13 +921,18 @@ public class ZipFile {
                 + SHORT + SHORT + fileNameLen + extraFieldLen;
 
             if (entriesWithoutUTF8Flag.containsKey(ze)) {
-                String orig = ze.getName();
                 NameAndComment nc = entriesWithoutUTF8Flag.get(ze);
                 ZipUtil.setNameAndCommentFromExtraFields(ze, nc.name,
                                                          nc.comment);
             }
-            entries.put(ze, offsetEntry);
-            nameMap.put(ze.getName(), ze);
+
+            String name = ze.getName();
+            Deque<ZipArchiveEntry> entriesOfThatName = nameMap.get(name);
+            if (entriesOfThatName == null) {
+                entriesOfThatName = new LinkedList<ZipArchiveEntry>();
+                nameMap.put(name, entriesOfThatName);
+            }
+            entriesOfThatName.addLast(ze);
         }
     }
 
@@ -996,16 +1039,54 @@ public class ZipFile {
                 return 0;
             }
 
-            OffsetEntry off1 = entries.get(e1);
-            OffsetEntry off2 = entries.get(e2);
-            if (off1 == null) {
+            @SuppressWarnings("unchecked") Entry ent1 =
+                e1 instanceof Entry ? (Entry) e1 : null;
+            @SuppressWarnings("unchecked") Entry ent2 =
+                e2 instanceof Entry ? (Entry) e2 : null;
+            if (ent1 == null) {
                 return 1;
             }
-            if (off2 == null) {
+            if (ent2 == null) {
                 return -1;
             }
-            long val = (off1.headerOffset - off2.headerOffset);
+            long val = (ent1.getOffsetEntry().headerOffset
+                        - ent2.getOffsetEntry().headerOffset);
             return val == 0 ? 0 : val < 0 ? -1 : +1;
         }
     };
+
+    /**
+     * Extends ZipArchiveEntry to store the offset within the archive.
+     */
+    private static class Entry extends ZipArchiveEntry {
+
+        private final OffsetEntry offsetEntry;
+
+        Entry(OffsetEntry offset) {
+            this.offsetEntry = offset;
+        }
+
+        OffsetEntry getOffsetEntry() {
+            return offsetEntry;
+        }
+
+        @Override
+        public int hashCode() {
+            return 3 * super.hashCode()
+                + (int) (offsetEntry.headerOffset % Integer.MAX_VALUE);
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (super.equals(other)) {
+                // super.equals would return false otherwise
+                @SuppressWarnings("unchecked") Entry otherEntry = (Entry) 
other;
+                return offsetEntry.headerOffset
+                        == otherEntry.offsetEntry.headerOffset
+                    && offsetEntry.dataOffset
+                        == otherEntry.offsetEntry.dataOffset;
+            }
+            return false;
+        }
+    }
 }

Modified: 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java?rev=1486348&r1=1486347&r2=1486348&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
 (original)
+++ 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
 Sat May 25 17:47:00 2013
@@ -27,6 +27,7 @@ import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Enumeration;
+import java.util.Iterator;
 import java.util.TreeMap;
 import java.util.zip.ZipEntry;
 
@@ -214,6 +215,12 @@ public class ZipFileTest extends TestCas
         ZipArchiveEntry ze = zf.getEntry("test1.txt");
         assertNotNull(ze);
         assertNotNull(zf.getInputStream(ze));
+
+        int numberOfEntries = 0;
+        for (Iterator it = zf.getEntries("test1.txt"); it.hasNext(); 
it.next()) {
+            numberOfEntries++;
+        }
+        assertEquals(2, numberOfEntries);
     }
 
     /*


Reply via email to