You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by bo...@apache.org on 2018/04/30 04:32:59 UTC

[1/2] commons-compress git commit: COMPRESS-118 add archuve expansion API

Repository: commons-compress
Updated Branches:
  refs/heads/master 44980ffc7 -> 75a7edc74


COMPRESS-118 add archuve expansion API


Project: http://git-wip-us.apache.org/repos/asf/commons-compress/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-compress/commit/33558253
Tree: http://git-wip-us.apache.org/repos/asf/commons-compress/tree/33558253
Diff: http://git-wip-us.apache.org/repos/asf/commons-compress/diff/33558253

Branch: refs/heads/master
Commit: 335582530a7f0dbb137e42655716962abae3addf
Parents: 44980ff
Author: Stefan Bodewig <bo...@apache.org>
Authored: Mon Apr 30 06:23:59 2018 +0200
Committer: Stefan Bodewig <bo...@apache.org>
Committed: Mon Apr 30 06:23:59 2018 +0200

----------------------------------------------------------------------
 .../compress/archivers/examples/Archive.java    |  19 +--
 .../archivers/examples/ArchiveEntrySource.java  |  34 +++++
 .../archivers/examples/ArchiveSources.java      | 112 +++++++++++++++++
 .../archivers/examples/ChainDefinition.java     |   8 +-
 .../archivers/examples/ChainRunner.java         |  46 +++++++
 .../archivers/examples/DirectorySink.java       |  69 +++++++++++
 .../compress/archivers/examples/Expand.java     | 110 ++++++++++++++++
 .../compress/archivers/examples/ExpandCli.java  |  48 +++++++
 .../examples/SevenZArchiveEntrySource.java      | 124 +++++++++++++++++++
 .../examples/StreamBasedArchiveEntrySource.java |  97 +++++++++++++++
 .../examples/ZipArchiveEntrySource.java         |  96 ++++++++++++++
 .../compress/utils/NoCloseInputStream.java      |  43 +++++++
 12 files changed, 784 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-compress/blob/33558253/src/main/java/org/apache/commons/compress/archivers/examples/Archive.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/examples/Archive.java b/src/main/java/org/apache/commons/compress/archivers/examples/Archive.java
index a929442..8b5a5d2 100644
--- a/src/main/java/org/apache/commons/compress/archivers/examples/Archive.java
+++ b/src/main/java/org/apache/commons/compress/archivers/examples/Archive.java
@@ -124,25 +124,8 @@ public class Archive {
         public void to(Sink<File> sink) throws IOException, ArchiveException {
             chainDef.add(sink);
             chainDef.freeze();
-            new Archive(source, chainDef, sink).run();
+            new ChainRunner<File>(source, chainDef, sink).run();
         }
     }
 
-    private final Source<File> source;
-    private final ChainDefinition<File> chainDef;
-    private final Sink<File> sink;
-
-    private Archive(Source<File> source, ChainDefinition<File> chainDef, Sink<File> sink) {
-        this.source = source;
-        this.chainDef = chainDef;
-        this.sink = sink;
-    }
-
-    private void run() throws IOException, ArchiveException {
-        ThrowingIterator<ChainPayload<File>> iter = source.get();
-        while (iter.hasNext()) {
-            chainDef.chain().next(iter.next());
-        }
-        sink.finish();
-    }
 }

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/33558253/src/main/java/org/apache/commons/compress/archivers/examples/ArchiveEntrySource.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/examples/ArchiveEntrySource.java b/src/main/java/org/apache/commons/compress/archivers/examples/ArchiveEntrySource.java
new file mode 100644
index 0000000..f114eb6
--- /dev/null
+++ b/src/main/java/org/apache/commons/compress/archivers/examples/ArchiveEntrySource.java
@@ -0,0 +1,34 @@
+/*
+ * 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.examples;
+
+import org.apache.commons.compress.archivers.ArchiveEntry;
+
+/**
+ * Combines Source and a factory for filter that skips unreadable entries.
+ * @since 1.17
+ */
+public interface ArchiveEntrySource extends Source<ArchiveEntry> {
+
+    /**
+     * Provides a filter that can be used to skip entries the
+     * underlying source is unable to read the content of.
+     */
+    Filter<ArchiveEntry> skipUnreadable();
+}

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/33558253/src/main/java/org/apache/commons/compress/archivers/examples/ArchiveSources.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/examples/ArchiveSources.java b/src/main/java/org/apache/commons/compress/archivers/examples/ArchiveSources.java
new file mode 100644
index 0000000..39ca236
--- /dev/null
+++ b/src/main/java/org/apache/commons/compress/archivers/examples/ArchiveSources.java
@@ -0,0 +1,112 @@
+/*
+ * 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.examples;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.FileInputStream;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.StandardOpenOption;
+import org.apache.commons.compress.archivers.ArchiveException;
+import org.apache.commons.compress.archivers.ArchiveInputStream;
+import org.apache.commons.compress.archivers.ArchiveStreamFactory;
+import org.apache.commons.compress.archivers.sevenz.SevenZFile;
+import org.apache.commons.compress.archivers.zip.ZipFile;
+
+/**
+ * Supplies factory methods for ArchiveEntry sources that read from archives,
+ * @since 1.17
+ */
+public class ArchiveSources {
+    /**
+     * Builder for {@link ArchiveEntrySource} that needs to know its format.
+     * @since 1.17
+     */
+    public interface PendingFormat {
+        ArchiveEntrySource detectFormat() throws IOException, ArchiveException;
+        ArchiveEntrySource withFormat(String format) throws IOException, ArchiveException;
+    }
+
+    /**
+     * Uses {@link ArchiveFactory#createArchiveInputStream} unless special handling for ZIP or /z is required.
+     */
+    public static PendingFormat forFile(final File f) {
+        return new PendingFormat() {
+            @Override
+            public ArchiveEntrySource detectFormat() throws IOException, ArchiveException {
+                String format = null;
+                try (InputStream i = new BufferedInputStream(new FileInputStream(f))) {
+                    format = new ArchiveStreamFactory().detect(i);
+                }
+                return withFormat(format);
+            }
+            @Override
+            public ArchiveEntrySource withFormat(String format) throws IOException, ArchiveException {
+                if (prefersSeekableByteChannel(format)) {
+                    return forChannel(format, FileChannel.open(f.toPath(), StandardOpenOption.READ));
+                }
+                return new StreamBasedArchiveEntrySource(new ArchiveStreamFactory()
+                    .createArchiveInputStream(format, new BufferedInputStream(new FileInputStream(f))));
+            }
+        };
+    }
+
+    /**
+     * Uses {@link ArchiveFactory#createArchiveInputStream} unless special handling for ZIP or /z is required.
+     */
+    public static ArchiveEntrySource forChannel(String format, SeekableByteChannel c)
+        throws IOException, ArchiveException {
+        if (!prefersSeekableByteChannel(format)) {
+            return new StreamBasedArchiveEntrySource(new ArchiveStreamFactory()
+                .createArchiveInputStream(format, Channels.newInputStream(c)));
+        } else if (ArchiveStreamFactory.ZIP.equalsIgnoreCase(format)) {
+            return new ZipArchiveEntrySource(c);
+        } else if (ArchiveStreamFactory.SEVEN_Z.equalsIgnoreCase(format)) {
+            return new SevenZArchiveEntrySource(c);
+        }
+        throw new ArchiveException("don't know how to handle format " + format);
+    }
+
+    /**
+     * Uses {@link ArchiveFactory#createArchiveInputStream}.
+     *
+     * <p>Will not support 7z.</p>
+     */
+    public static PendingFormat forStream(final InputStream in) {
+        return new PendingFormat() {
+            @Override
+            public ArchiveEntrySource detectFormat() throws IOException, ArchiveException {
+                return new StreamBasedArchiveEntrySource(new ArchiveStreamFactory().createArchiveInputStream(in));
+            }
+            @Override
+            public ArchiveEntrySource withFormat(String format) throws IOException, ArchiveException {
+                return new StreamBasedArchiveEntrySource(new ArchiveStreamFactory()
+                    .createArchiveInputStream(format, in));
+            }
+        };
+    }
+
+    private static boolean prefersSeekableByteChannel(String format) {
+        return ArchiveStreamFactory.ZIP.equalsIgnoreCase(format) || ArchiveStreamFactory.SEVEN_Z.equalsIgnoreCase(format);
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/33558253/src/main/java/org/apache/commons/compress/archivers/examples/ChainDefinition.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/examples/ChainDefinition.java b/src/main/java/org/apache/commons/compress/archivers/examples/ChainDefinition.java
index 5136b1d..d8a387f 100644
--- a/src/main/java/org/apache/commons/compress/archivers/examples/ChainDefinition.java
+++ b/src/main/java/org/apache/commons/compress/archivers/examples/ChainDefinition.java
@@ -25,7 +25,7 @@ import java.util.LinkedList;
  * The recipe for building a {@link Chain}.
  * @since 1.17
  */
-public class ChainDefinition<T> {
+class ChainDefinition<T> {
     private final Deque<ChainStep<T>> steps = new LinkedList<>();
     private volatile boolean frozen = false;
 
@@ -33,7 +33,7 @@ public class ChainDefinition<T> {
      * Adds a step.
      * @throws IllegalStateException if the definition is already frozen.
      */
-    public void add(ChainStep<T> step) {
+    void add(ChainStep<T> step) {
         if (frozen) {
             throw new IllegalStateException("the definition is already frozen");
         }
@@ -47,7 +47,7 @@ public class ChainDefinition<T> {
      *
      * @throws IllegalStateException if the last step of the definition is not a sink.
      */
-    public void freeze() {
+    void freeze() {
         if (!frozen) {
             frozen = true;
             if (!(steps.getLast() instanceof Sink)) {
@@ -61,7 +61,7 @@ public class ChainDefinition<T> {
      *
      * @throws IllegalStateException if the definition is not frozen.
      */
-    public Chain<T> chain() {
+    Chain<T> chain() {
         if (!frozen) {
             throw new IllegalStateException("the definition hasn't been frozen, yet");
         }

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/33558253/src/main/java/org/apache/commons/compress/archivers/examples/ChainRunner.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/examples/ChainRunner.java b/src/main/java/org/apache/commons/compress/archivers/examples/ChainRunner.java
new file mode 100644
index 0000000..5f43589
--- /dev/null
+++ b/src/main/java/org/apache/commons/compress/archivers/examples/ChainRunner.java
@@ -0,0 +1,46 @@
+/*
+ * 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.examples;
+
+import java.io.IOException;
+import org.apache.commons.compress.archivers.ArchiveException;
+
+/**
+ * Contains the execution logic of a full chain including a source.
+ * @since 1.17
+ */
+class ChainRunner<T> {
+    private final Source<T> source;
+    private final ChainDefinition<T> chainDef;
+    private final Sink<T> sink;
+
+    ChainRunner(Source<T> source, ChainDefinition<T> chainDef, Sink<T> sink) {
+        this.source = source;
+        this.chainDef = chainDef;
+        this.sink = sink;
+    }
+
+    void run() throws IOException, ArchiveException {
+        ThrowingIterator<ChainPayload<T>> iter = source.get();
+        while (iter.hasNext()) {
+            chainDef.chain().next(iter.next());
+        }
+        sink.finish();
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/33558253/src/main/java/org/apache/commons/compress/archivers/examples/DirectorySink.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/examples/DirectorySink.java b/src/main/java/org/apache/commons/compress/archivers/examples/DirectorySink.java
new file mode 100644
index 0000000..9cc1597
--- /dev/null
+++ b/src/main/java/org/apache/commons/compress/archivers/examples/DirectorySink.java
@@ -0,0 +1,69 @@
+/*
+ * 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.examples;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.archivers.ArchiveException;
+import org.apache.commons.compress.utils.IOUtils;
+
+/**
+ * A sink that expands archive entries into a directory.
+ * @since 1.17
+ */
+public class DirectorySink extends Sink<ArchiveEntry> {
+    private final File dir;
+    private final String dirPath;
+
+    /**
+     * @param dir the directory to provide entries from.
+     */
+    public DirectorySink(File dir) throws IOException {
+        if (!dir.isDirectory()) {
+            throw new IllegalArgumentException("dir is not a readable directory");
+        }
+        this.dir = dir;
+        dirPath = dir.getCanonicalPath();
+    }
+
+    @Override
+    public void consume(ChainPayload<ArchiveEntry> payload) throws IOException, ArchiveException {
+        File f = new File(dir, payload.getEntryName());
+        if (!f.getCanonicalPath().startsWith(dirPath)) {
+            throw new IOException("expanding " + payload.getEntryName() + " would create file outside of " + dir);
+        }
+        if (payload.getEntry().isDirectory()) {
+            f.mkdirs();
+        } else {
+            f.getParentFile().mkdirs();
+            try (OutputStream o = new FileOutputStream(f);
+                 InputStream i = payload.getInput().get()) {
+                IOUtils.copy(i, o);
+            }
+        }
+    }
+
+    @Override
+    public void close() {
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/33558253/src/main/java/org/apache/commons/compress/archivers/examples/Expand.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/examples/Expand.java b/src/main/java/org/apache/commons/compress/archivers/examples/Expand.java
new file mode 100644
index 0000000..99e1f34
--- /dev/null
+++ b/src/main/java/org/apache/commons/compress/archivers/examples/Expand.java
@@ -0,0 +1,110 @@
+/*
+ * 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.examples;
+
+import java.io.IOException;
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.archivers.ArchiveException;
+
+/**
+ * Consumes archive entries and passes them to a sink, usually used to
+ * expand an archive.
+ * @since 1.17
+ */
+public class Expand {
+    /**
+     * Sets up a chain of operations and consumes the entries from a source of archive entries.
+     * @since 1.17
+     */
+    public interface ChainBuilder {
+        /**
+         * Adds a filter to the chain.
+         */
+        ChainBuilder filter(Filter<ArchiveEntry> filter);
+        /**
+         * Adds a filter to the chain that filters out entries that cannot be read.
+         */
+        ChainBuilder skipUnreadable();
+        /**
+         * Adds a filter to the chain that suppresses all directory entries.
+         */
+        ChainBuilder skipDirectories();
+        /**
+         * Adds a transformer to the chain.
+         */
+        ChainBuilder map(Transformer<ArchiveEntry> transformer);
+        /**
+         * Adds a generic step to the chain.
+         */
+        ChainBuilder withStep(ChainStep<ArchiveEntry> step);
+        /**
+         * Actually consumes all the entries supplied.
+         */
+        void to(Sink<ArchiveEntry> sink) throws IOException, ArchiveException;
+    }
+
+    /**
+     * Sets the source of entries to process.
+     */
+    public static ChainBuilder source(ArchiveEntrySource source) {
+        return new Builder(source);
+    }
+
+    private static class Builder implements ChainBuilder {
+        private final ArchiveEntrySource source;
+        private ChainDefinition<ArchiveEntry> chainDef = new ChainDefinition<>();
+
+        Builder(ArchiveEntrySource source) {
+            this.source = source;
+        }
+
+        @Override
+        public ChainBuilder filter(Filter<ArchiveEntry> filter) {
+            return withStep(filter);
+        }
+        @Override
+        public ChainBuilder skipUnreadable() {
+            return filter(source.skipUnreadable());
+        }
+        @Override
+        public ChainBuilder skipDirectories() {
+            return filter(new Filter<ArchiveEntry>() {
+                @Override
+                public boolean accept(String entryName, ArchiveEntry e) {
+                    return !e.isDirectory();
+                }
+            });
+        }
+        @Override
+        public ChainBuilder map(Transformer<ArchiveEntry> transformer) {
+            return withStep(transformer);
+        }
+        @Override
+        public ChainBuilder withStep(ChainStep<ArchiveEntry> step) {
+            chainDef.add(step);
+            return this;
+        }
+        @Override
+        public void to(Sink<ArchiveEntry> sink) throws IOException, ArchiveException {
+            chainDef.add(sink);
+            chainDef.freeze();
+            new ChainRunner<ArchiveEntry>(source, chainDef, sink).run();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/33558253/src/main/java/org/apache/commons/compress/archivers/examples/ExpandCli.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/examples/ExpandCli.java b/src/main/java/org/apache/commons/compress/archivers/examples/ExpandCli.java
new file mode 100644
index 0000000..fd264ce
--- /dev/null
+++ b/src/main/java/org/apache/commons/compress/archivers/examples/ExpandCli.java
@@ -0,0 +1,48 @@
+/*
+ * 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.examples;
+
+import java.io.File;
+import java.io.IOException;
+import org.apache.commons.compress.archivers.ArchiveException;
+
+/**
+ * Simple command line tool that extracts an archive into a directory.
+ *
+ * <p>Usage: <code>ExpandCli archive dir [format]</code></p>
+ * @since 1.17
+ */
+public class ExpandCli {
+
+    public static void main(String[] args) throws IOException, ArchiveException {
+        if (args.length < 2 || args.length > 3) {
+            System.err.println("Usage: ExpandCli dir archive [format]");
+            System.exit(1);
+        } else if (args.length == 2) {
+            try (ArchiveEntrySource source = ArchiveSources.forFile(new File(args[0])).detectFormat()) {
+                Expand.source(source).to(new DirectorySink(new File(args[1])));
+            }
+        } else {
+            try (ArchiveEntrySource source = ArchiveSources.forFile(new File(args[0])).withFormat(args[2])) {
+                Expand.source(source).to(new DirectorySink(new File(args[1])));
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/33558253/src/main/java/org/apache/commons/compress/archivers/examples/SevenZArchiveEntrySource.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/examples/SevenZArchiveEntrySource.java b/src/main/java/org/apache/commons/compress/archivers/examples/SevenZArchiveEntrySource.java
new file mode 100644
index 0000000..9f38b38
--- /dev/null
+++ b/src/main/java/org/apache/commons/compress/archivers/examples/SevenZArchiveEntrySource.java
@@ -0,0 +1,124 @@
+/*
+ * 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.examples;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.channels.SeekableByteChannel;
+import java.util.NoSuchElementException;
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.archivers.ArchiveException;
+import org.apache.commons.compress.utils.NoCloseInputStream;
+import org.apache.commons.compress.archivers.sevenz.SevenZFile;
+
+/**
+ * Supplier based on {@link SevenZFile}s.
+ * @since 1.17
+ */
+public class SevenZArchiveEntrySource implements ArchiveEntrySource {
+
+    private final SevenZFile sf;
+
+    public SevenZArchiveEntrySource(File f) throws IOException {
+        this(new SevenZFile(f));
+    }
+
+    public SevenZArchiveEntrySource(SeekableByteChannel c) throws IOException {
+        this(new SevenZFile(c));
+    }
+
+    public SevenZArchiveEntrySource(SevenZFile sf) {
+        this.sf = sf;
+    }
+
+    @Override
+    public ThrowingIterator<ChainPayload<ArchiveEntry>> get() throws IOException {
+        return new SevenZFileIterator(sf);
+    }
+
+    @Override
+    public void close() throws IOException {
+        sf.close();
+    }
+
+    @Override
+    public Filter<ArchiveEntry> skipUnreadable() {
+        return new Filter<ArchiveEntry>() {
+            @Override
+            public boolean accept(String entryName, ArchiveEntry entry) {
+                return true;
+            }
+        };
+    }
+
+    private static class SevenZFileIterator implements ThrowingIterator<ChainPayload<ArchiveEntry>> {
+        private final SevenZFile sf;
+        private ArchiveEntry nextEntry;
+        private boolean nextEntryConsumed;
+        SevenZFileIterator(SevenZFile sf) throws IOException {
+            this.sf = sf;
+            nextEntry = sf.getNextEntry();
+            nextEntryConsumed = false;
+        }
+
+        @Override
+        public boolean hasNext() throws IOException {
+            if (nextEntry == null || nextEntryConsumed) {
+                nextEntry = sf.getNextEntry();
+                nextEntryConsumed = false;
+            }
+            return nextEntry != null && !nextEntryConsumed;
+        }
+
+        @Override
+        public ChainPayload<ArchiveEntry> next() throws IOException {
+            if (!hasNext()) {
+                throw new NoSuchElementException();
+            }
+            nextEntryConsumed = true;
+            return new ChainPayload(nextEntry, nextEntry.getName(), new Supplier<InputStream>() {
+                    @Override
+                    public InputStream get() throws IOException {
+                        return new SevenZFileInputStream(sf);
+                    }
+                });
+        }
+
+    }
+
+    private static class SevenZFileInputStream extends InputStream {
+        private final SevenZFile sf;
+        SevenZFileInputStream(SevenZFile sf) {
+            this.sf = sf;
+        }
+        @Override
+        public int read() throws IOException {
+            return sf.read();
+        }
+        @Override
+        public int read(byte[] b) throws IOException {
+            return read(b, 0, b.length);
+        }
+        @Override
+        public int read(final byte[] b, final int off, final int len) throws IOException {
+            return sf.read(b, off, len);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/33558253/src/main/java/org/apache/commons/compress/archivers/examples/StreamBasedArchiveEntrySource.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/examples/StreamBasedArchiveEntrySource.java b/src/main/java/org/apache/commons/compress/archivers/examples/StreamBasedArchiveEntrySource.java
new file mode 100644
index 0000000..19aa55b
--- /dev/null
+++ b/src/main/java/org/apache/commons/compress/archivers/examples/StreamBasedArchiveEntrySource.java
@@ -0,0 +1,97 @@
+/*
+ * 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.examples;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.NoSuchElementException;
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.archivers.ArchiveException;
+import org.apache.commons.compress.archivers.ArchiveInputStream;
+import org.apache.commons.compress.archivers.ArchiveStreamFactory;
+import org.apache.commons.compress.utils.NoCloseInputStream;
+
+/**
+ * Supplier based on {@link ArchiveInputStream}s.
+ * @since 1.17
+ */
+public class StreamBasedArchiveEntrySource implements ArchiveEntrySource {
+
+    private final ArchiveInputStream in;
+
+    public StreamBasedArchiveEntrySource(ArchiveInputStream in) {
+        this.in = in;
+    }
+
+    @Override
+    public ThrowingIterator<ChainPayload<ArchiveEntry>> get() throws IOException {
+        return new ArchiveInputStreamIterator(in);
+    }
+
+    @Override
+    public void close() throws IOException {
+        in.close();
+    }
+
+    @Override
+    public Filter<ArchiveEntry> skipUnreadable() {
+        return new Filter<ArchiveEntry>() {
+            @Override
+            public boolean accept(String entryName, ArchiveEntry entry) {
+                return in.canReadEntryData(entry);
+            }
+        };
+    }
+
+    private static class ArchiveInputStreamIterator implements ThrowingIterator<ChainPayload<ArchiveEntry>> {
+        private final ArchiveInputStream in;
+        private ArchiveEntry nextEntry;
+        private boolean nextEntryConsumed;
+        ArchiveInputStreamIterator(ArchiveInputStream in) throws IOException {
+            this.in = in;
+            nextEntry = in.getNextEntry();
+            nextEntryConsumed = false;
+        }
+
+        @Override
+        public boolean hasNext() throws IOException {
+            if (nextEntry == null || nextEntryConsumed) {
+                nextEntry = in.getNextEntry();
+                nextEntryConsumed = false;
+            }
+            return nextEntry != null && !nextEntryConsumed;
+        }
+
+        @Override
+        public ChainPayload<ArchiveEntry> next() throws IOException {
+            if (!hasNext()) {
+                throw new NoSuchElementException();
+            }
+            nextEntryConsumed = true;
+            return new ChainPayload(nextEntry, nextEntry.getName(), new Supplier<InputStream>() {
+                    @Override
+                    public InputStream get() throws IOException {
+                        return new NoCloseInputStream(in);
+                    }
+                });
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/33558253/src/main/java/org/apache/commons/compress/archivers/examples/ZipArchiveEntrySource.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/examples/ZipArchiveEntrySource.java b/src/main/java/org/apache/commons/compress/archivers/examples/ZipArchiveEntrySource.java
new file mode 100644
index 0000000..d5e84bc
--- /dev/null
+++ b/src/main/java/org/apache/commons/compress/archivers/examples/ZipArchiveEntrySource.java
@@ -0,0 +1,96 @@
+/*
+ * 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.examples;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.channels.SeekableByteChannel;
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipFile;
+
+/**
+ * Supplier based on {@link ZipFile}s.
+ * @since 1.17
+ */
+public class ZipArchiveEntrySource implements ArchiveEntrySource {
+
+    private final ZipFile zf;
+
+    public ZipArchiveEntrySource(File f) throws IOException {
+        this(new ZipFile(f));
+    }
+
+    public ZipArchiveEntrySource(SeekableByteChannel c) throws IOException {
+        this(new ZipFile(c));
+    }
+
+    public ZipArchiveEntrySource(ZipFile file) {
+        zf = file;
+    }
+
+    @Override
+    public ThrowingIterator<ChainPayload<ArchiveEntry>> get() throws IOException {
+        return new ZipFileIterator(zf, zf.getEntries());
+    }
+
+    @Override
+    public void close() throws IOException {
+        zf.close();
+    }
+
+    @Override
+    public Filter<ArchiveEntry> skipUnreadable() {
+        return new Filter<ArchiveEntry>() {
+            @Override
+            public boolean accept(String entryName, ArchiveEntry entry) {
+                return entry instanceof ZipArchiveEntry && zf.canReadEntryData((ZipArchiveEntry) entry);
+            }
+        };
+    }
+
+    private static class ZipFileIterator implements ThrowingIterator<ChainPayload<ArchiveEntry>> {
+        private final ZipFile zf;
+        private final Enumeration<ZipArchiveEntry> iter;
+        ZipFileIterator(ZipFile zf, Enumeration<ZipArchiveEntry> iter) {
+            this.zf = zf;
+            this.iter = iter;
+        }
+
+        @Override
+        public boolean hasNext() throws IOException {
+            return iter.hasMoreElements();
+        }
+
+        @Override
+        public ChainPayload<ArchiveEntry> next() throws IOException {
+            final ZipArchiveEntry z = iter.nextElement();
+            return new ChainPayload(z, z.getName(), new Supplier<InputStream>() {
+                    @Override
+                    public InputStream get() throws IOException {
+                        return zf.getInputStream(z);
+                    }
+                });
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/33558253/src/main/java/org/apache/commons/compress/utils/NoCloseInputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/utils/NoCloseInputStream.java b/src/main/java/org/apache/commons/compress/utils/NoCloseInputStream.java
new file mode 100644
index 0000000..bdc0ee9
--- /dev/null
+++ b/src/main/java/org/apache/commons/compress/utils/NoCloseInputStream.java
@@ -0,0 +1,43 @@
+/*
+ * 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.utils;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Wrapper that overrides {@link #close} so that it doesn't close the
+ * underlying stream.
+ *
+ * @since 1.17
+ */
+public class NoCloseInputStream extends FilterInputStream {
+
+    public NoCloseInputStream(InputStream in) {
+        super(in);
+    }
+
+    /**
+     * This method does nothing.
+     */
+    public void close() {
+        // do not close the stream
+    }
+}


[2/2] commons-compress git commit: COMPRESS-118 make listing just another sink for expanding

Posted by bo...@apache.org.
COMPRESS-118 make listing just another sink for expanding


Project: http://git-wip-us.apache.org/repos/asf/commons-compress/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-compress/commit/75a7edc7
Tree: http://git-wip-us.apache.org/repos/asf/commons-compress/tree/75a7edc7
Diff: http://git-wip-us.apache.org/repos/asf/commons-compress/diff/75a7edc7

Branch: refs/heads/master
Commit: 75a7edc742215c3354befe8fc0ec6d4ffe3bd663
Parents: 3355825
Author: Stefan Bodewig <bo...@apache.org>
Authored: Mon Apr 30 06:32:20 2018 +0200
Committer: Stefan Bodewig <bo...@apache.org>
Committed: Mon Apr 30 06:32:20 2018 +0200

----------------------------------------------------------------------
 pom.xml                                         |  2 +-
 .../compress/archivers/examples/ListerCli.java  | 68 ++++++++++++++++++++
 2 files changed, 69 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-compress/blob/75a7edc7/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index c3eff8e..85ee514 100644
--- a/pom.xml
+++ b/pom.xml
@@ -329,7 +329,7 @@ Brotli, Zstandard and ar, cpio, jar, tar, zip, dump, 7z, arj.
         <configuration>
           <archive>
             <manifestEntries>
-              <Main-Class>org.apache.commons.compress.archivers.Lister</Main-Class>
+              <Main-Class>org.apache.commons.compress.archivers.examples.ListerCli</Main-Class>
               <Extension-Name>org.apache.commons.compress</Extension-Name>
               <Automatic-Module-Name>${commons.module.name}</Automatic-Module-Name>
             </manifestEntries>

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/75a7edc7/src/main/java/org/apache/commons/compress/archivers/examples/ListerCli.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/examples/ListerCli.java b/src/main/java/org/apache/commons/compress/archivers/examples/ListerCli.java
new file mode 100644
index 0000000..36f6efa
--- /dev/null
+++ b/src/main/java/org/apache/commons/compress/archivers/examples/ListerCli.java
@@ -0,0 +1,68 @@
+/*
+ *  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.examples;
+
+import java.io.File;
+import org.apache.commons.compress.archivers.ArchiveEntry;
+
+/**
+ * Simple command line application that lists the contents of an archive.
+ *
+ * <p>The name of the archive must be given as a command line argument.</p>
+ * <p>The optional second argument defines the archive type, in case the format is not recognized.</p>
+ *
+ * @since 1.17
+ */
+public final class ListerCli {
+
+    public static void main(final String[] args) throws Exception {
+        if (args.length == 0) {
+            usage();
+            return;
+        }
+        System.out.println("Analysing " + args[0]);
+        final Sink<ArchiveEntry> sink = new Sink<ArchiveEntry>() {
+            @Override
+            public void consume(ChainPayload<ArchiveEntry> payload) {
+                System.out.println(payload.getEntry().getName());
+            }
+            @Override
+            public void close() {
+            }
+        };
+
+        final File f = new File(args[0]);
+        if (!f.isFile()) {
+            System.err.println(f + " doesn't exist or is a directory");
+        } else if (args.length == 1) {
+            try (ArchiveEntrySource source = ArchiveSources.forFile(f).detectFormat()) {
+                Expand.source(source).to(sink);
+            }
+        } else {
+            try (ArchiveEntrySource source = ArchiveSources.forFile(f).withFormat(args[1])) {
+                Expand.source(source).to(sink);
+            }
+        }
+    }
+
+    private static void usage() {
+        System.out.println("Parameters: archive-name [archive-type]");
+    }
+
+}