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 <bode...@apache.org> Authored: Mon Apr 30 06:23:59 2018 +0200 Committer: Stefan Bodewig <bode...@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 + } +}