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); } /*