Author: julius
Date: Mon Jan 14 21:42:48 2013
New Revision: 1433151

URL: http://svn.apache.org/viewvc?rev=1433151&view=rev
Log:
COMPRESS-214 - better support for unix symlinks

Added:
    
commons/proper/compress/trunk/src/test/resources/COMPRESS-214_unix_symlinks.zip 
  (with props)
Modified:
    
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
    
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/utils/IOUtils.java
    
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntryTest.java

Modified: 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java?rev=1433151&r1=1433150&r2=1433151&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
 (original)
+++ 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
 Mon Jan 14 21:42:48 2013
@@ -17,14 +17,18 @@
  */
 package org.apache.commons.compress.archivers.zip;
 
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.utils.IOUtils;
+
 import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.zip.ZipException;
-import org.apache.commons.compress.archivers.ArchiveEntry;
 
 /**
  * Extension that adds better handling of extra fields and provides
@@ -267,6 +271,83 @@ public class ZipArchiveEntry extends jav
     }
 
     /**
+     * Returns true if this entry represents a unix symlink,
+     * in which case the entry's content contains the target path
+     * for the symlink.
+     *
+     * @return true if the entry represents a unix symlink, false otherwise.
+     */
+    public boolean isUnixSymlink() {
+        return (getUnixMode() & UnixStat.LINK_FLAG) == UnixStat.LINK_FLAG;
+    }
+
+    /**
+     * <p>
+     * Convenience method to return this entry's content as a String if 
isUnixSymlink()
+     * returns true, otherwise returns null.
+     * This method assumes the symlink's target path has been stored in the 
zip file using the
+     * UTF-8 character encoding.
+     * </p><p>
+     * Unfortunately, there is no reliable way to automatically obtain the 
target path's
+     * character encoding (within the zip file) should it differ from UTF-8.  
Zip files
+     * do not remember the character set used to encode zip contents.
+     * Entry names do not have this problem (thanks to various flags and extra 
fields),
+     * but symlink targets are not so lucky.  If you happen to know the 
character set that was used
+     * to encode symlink paths in the zip file, feel free to use the 
overloaded version:
+     * <code>getUnixSymlink(ZipFile zf, String pathCharset)</code>.
+     * </p><p>
+     * This method can only be used in conjunction with a ZipFile object 
(since we need
+     * the entry's contents); users of ZipArchiveInputStream will need to 
create their
+     * own logic.
+     * </p>
+     *
+     * @param zf ZipFile object that contains this ZipArchiveEntry's contents.
+     * @return entry's content as a String (UTF-8 character set assumed).
+     * @throws IOException problem with content's input stream
+     */
+    public String getUnixSymlink(ZipFile zf) throws IOException {
+        return getUnixSymlink(zf, "UTF-8");
+    }
+
+    /**
+     * <p>
+     * Convenience method to return this entry's content as a String if 
isUnixSymlink()
+     * returns true, otherwise returns null.
+     * </p><p>
+     * Unfortunately, there is no reliable way to automatically obtain the 
target path's
+     * character encoding (within the zip file) should it differ from UTF-8.  
Zip files
+     * do not remember the character set used to encode zip contents.
+     * Entry names do not have this problem (thanks to various flags and extra 
fields),
+     * but symlink targets are not so lucky.
+     * </p><p>
+     * This method can only be used in conjunction with a ZipFile object 
(since we need
+     * the entry's contents); users of ZipArchiveInputStream will need to 
create their
+     * own logic.
+     * </p>
+     *
+     * @param zf          ZipFile object that contains this ZipArchiveEntry's 
contents.
+     * @param pathCharset the character set used to encode the symlink's 
target path.
+     * @return entry's content as a String (using the provided character set).
+     * @throws IOException problem with content's input stream
+     */
+    public String getUnixSymlink(ZipFile zf, String pathCharset) throws 
IOException {
+        if (isUnixSymlink()) {
+            InputStream in = null;
+            try {
+                in = zf.getInputStream(this);
+                byte[] symlinkBytes = IOUtils.toByteArray(in);
+                return new String(symlinkBytes, pathCharset);
+            } finally {
+                if (in != null) {
+                    in.close();
+                }
+            }
+        } else {
+            return null;
+        }
+    }
+
+    /**
      * Platform specification to put into the &quot;version made
      * by&quot; part of the central file header.
      *

Modified: 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/utils/IOUtils.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/utils/IOUtils.java?rev=1433151&r1=1433150&r2=1433151&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/utils/IOUtils.java
 (original)
+++ 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/utils/IOUtils.java
 Mon Jan 14 21:42:48 2013
@@ -18,6 +18,7 @@
  */
 package org.apache.commons.compress.utils;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -69,4 +70,28 @@ public final class IOUtils {
         }
         return count;
     }
+
+
+    // toByteArray(InputStream) copied from:
+    // 
commons/proper/io/trunk/src/main/java/org/apache/commons/io/IOUtils.java?revision=1428941
+    // January 8th, 2013
+    //
+    // Assuming our copy() works just as well as theirs!  :-)
+
+    /**
+     * Gets the contents of an <code>InputStream</code> as a 
<code>byte[]</code>.
+     * <p>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedInputStream</code>.
+     *
+     * @param input  the <code>InputStream</code> to read from
+     * @return the requested byte array
+     * @throws NullPointerException if the input is null
+     * @throws IOException if an I/O error occurs
+     */
+    public static byte[] toByteArray(final InputStream input) throws 
IOException {
+        final ByteArrayOutputStream output = new ByteArrayOutputStream();
+        copy(input, output);
+        return output.toByteArray();
+    }
 }

Modified: 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntryTest.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntryTest.java?rev=1433151&r1=1433150&r2=1433151&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntryTest.java
 (original)
+++ 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntryTest.java
 Mon Jan 14 21:42:48 2013
@@ -18,11 +18,16 @@
 
 package org.apache.commons.compress.archivers.zip;
 
+import junit.framework.TestCase;
+
 import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.net.URI;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.TreeMap;
 import java.util.zip.ZipEntry;
 
-import junit.framework.TestCase;
-
 /**
  * JUnit 3 testcases for org.apache.commons.compress.archivers.zip.ZipEntry.
  *
@@ -33,6 +38,54 @@ public class ZipArchiveEntryTest extends
         super(name);
     }
 
+    public void testUnixSymlinkSampleFile() throws Exception {
+        final String entryPrefix = "COMPRESS-214_unix_symlinks/";
+        final TreeMap<String, String> expectedVals = new TreeMap<String, 
String>();
+
+        // I threw in some Japanese characters to keep things interesting.
+        expectedVals.put(entryPrefix + "link1", 
"../COMPRESS-214_unix_symlinks/./a/b/c/../../../\uF999");
+        expectedVals.put(entryPrefix + "link2", 
"../COMPRESS-214_unix_symlinks/./a/b/c/../../../g");
+        expectedVals.put(entryPrefix + "link3", 
"../COMPRESS-214_unix_symlinks/././a/b/c/../../../\u76F4\u6A39");
+        expectedVals.put(entryPrefix + "link4", "\u82B1\u5B50/\u745B\u5B50");
+        expectedVals.put(entryPrefix + "\uF999", 
"./\u82B1\u5B50/\u745B\u5B50/\u5897\u8C37/\uF999");
+        expectedVals.put(entryPrefix + "g", "./a/b/c/d/e/f/g");
+        expectedVals.put(entryPrefix + "\u76F4\u6A39", "./g");
+
+        // Notice how a directory link might contain a trailing slash, or it 
might not.
+        // Also note:  symlinks are always stored as files, even if they link 
to directories.
+        expectedVals.put(entryPrefix + "link5", 
"../COMPRESS-214_unix_symlinks/././a/b");
+        expectedVals.put(entryPrefix + "link6", 
"../COMPRESS-214_unix_symlinks/././a/b/");
+
+        // I looked into creating a test with hard links, but zip does not 
appear to
+        // support hard links, so nevermind.
+
+        URL zip = getClass().getResource("/COMPRESS-214_unix_symlinks.zip");
+        File archive = new File(new URI(zip.toString()));
+        ZipFile zf = null;
+
+        try {
+            zf = new ZipFile(archive);
+            Enumeration<ZipArchiveEntry> en = zf.getEntries();
+            while (en.hasMoreElements()) {
+                ZipArchiveEntry zae = en.nextElement();
+                if (zae.isUnixSymlink()) {
+                    String name = zae.getName();
+                    String link = zae.getUnixSymlink(zf);
+                    String expected = expectedVals.get(name);
+                    assertEquals(expected, link);
+                } else {
+                    String link = zae.getUnixSymlink(zf);
+                    // Should be null if it's not a symlink!
+                    assertNull(link);
+                }
+            }
+        } finally {
+            if (zf != null) {
+                zf.close();
+            }
+        }
+    }
+
     /**
      * test handling of extra fields
      */

Added: 
commons/proper/compress/trunk/src/test/resources/COMPRESS-214_unix_symlinks.zip
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/resources/COMPRESS-214_unix_symlinks.zip?rev=1433151&view=auto
==============================================================================
Binary file - no diff available.

Propchange: 
commons/proper/compress/trunk/src/test/resources/COMPRESS-214_unix_symlinks.zip
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream


Reply via email to