This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-compress.git
commit 1bf03bd699013c231a24e340b9887b4a662d7f5a Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Fri May 24 12:47:33 2024 -0400 Add ArchiveInputStream.forEach(IOConsumer) Add ArchiveInputStream.iterator() --- src/changes/changes.xml | 3 +- .../compress/archivers/ArchiveInputStream.java | 63 +++++++++++++++++++++- .../apache/commons/compress/archivers/Lister.java | 5 +- .../org/apache/commons/compress/AbstractTest.java | 5 +- .../apache/commons/compress/archivers/ArTest.java | 1 - .../commons/compress/archivers/CpioTest.java | 5 +- .../commons/compress/archivers/DumpTest.java | 11 ++-- .../apache/commons/compress/archivers/JarTest.java | 12 ++--- .../apache/commons/compress/archivers/ZipTest.java | 9 ++-- .../archivers/tar/TarArchiveInputStreamTest.java | 18 ++++--- .../commons/compress/compressors/Pack200Test.java | 13 ++--- .../compressors/pack200/Pack200UtilsTest.java | 22 +++----- 12 files changed, 101 insertions(+), 66 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index c61f6a9f7..78607af9e 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -47,6 +47,8 @@ The <action> type attribute can be add,update,fix,remove. <release version="1.27.0" date="YYYY-MM-DD" description="This is a feature and maintenance release. Java 8 or later is required."> <!-- FIX --> <!-- ADD --> + <action type="add" dev="ggregory" due-to="Gary Gregory">Add ArchiveInputStream.forEach(IOConsumer).</action> + <action type="add" dev="ggregory" due-to="Gary Gregory">Add ArchiveInputStream.iterator().</action> <!-- UPDATE --> </release> <release version="1.26.2" date="2024-05-18" description="Minor feature and maintenance release."> @@ -65,7 +67,6 @@ The <action> type attribute can be add,update,fix,remove. <action type="fix" dev="ggregory" due-to="Gary Gregory">Avoid NullPointerException in java.nio.channels.spi.AbstractInterruptibleChannel.close() when calling org.apache.commons.compress.archivers.zip.FileRandomAccessOutputStream.close().</action> <action type="fix" dev="ggregory" due-to="Gary Gregory">Fix SpotBugs NP_NULL_PARAM_DEREF in Sets.newHashSet().</action> <action type="fix" dev="ggregory" due-to="Gary Gregory">Private class' method PackingLogger.setVerbose(boolean) should be private.</action> - <!-- UPDATE --> <action type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump org.apache.commons:commons-parent from 66 to 69 #495, #508.</action> <action type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump org.ow2.asm:asm from 9.6 to 9.7 #504.</action> diff --git a/src/main/java/org/apache/commons/compress/archivers/ArchiveInputStream.java b/src/main/java/org/apache/commons/compress/archivers/ArchiveInputStream.java index d5ebf6059..d8f312faf 100644 --- a/src/main/java/org/apache/commons/compress/archivers/ArchiveInputStream.java +++ b/src/main/java/org/apache/commons/compress/archivers/ArchiveInputStream.java @@ -22,8 +22,12 @@ import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; +import java.util.Iterator; +import java.util.Objects; import org.apache.commons.io.Charsets; +import org.apache.commons.io.function.IOConsumer; +import org.apache.commons.io.function.IOIterator; import org.apache.commons.io.input.NullInputStream; /** @@ -46,6 +50,40 @@ import org.apache.commons.io.input.NullInputStream; */ public abstract class ArchiveInputStream<E extends ArchiveEntry> extends FilterInputStream { + class ArchiveEntryIOIterator implements IOIterator<E> { + + private E next; + + @Override + public boolean hasNext() throws IOException { + if (next == null) { + next = getNextEntry(); + } + return next != null; + } + + @Override + public synchronized E next() throws IOException { + if (next != null) { + final E e = next; + next = null; + return e; + } + return getNextEntry(); + } + + /** + * Always returns null, this is a "native" IOIterator. + * + * @return null. + */ + @Override + public Iterator<E> unwrap() { + return null; + } + + } + private static final int BYTE_MASK = 0xFF; private final byte[] single = new byte[1]; @@ -122,6 +160,23 @@ public abstract class ArchiveInputStream<E extends ArchiveEntry> extends FilterI } } + /** + * Performs the given action for each element of the stream until all elements have been processed or the action throws an exception. Actions are performed + * in the order of iteration. Exceptions thrown by the action are relayed to the caller. + * <p> + * The behavior of this method is unspecified if the action performs side-effects that modify the underlying source of elements, unless an overriding class + * has specified a concurrent modification policy. + * </p> + * + * @param action The action to be performed for each element + * @throws IOException if an I/O error occurs. + * @throws NullPointerException if the specified action is null + * @since 2.17.0 + */ + public void forEach(final IOConsumer<? super E> action) throws IOException { + iterator().forEachRemaining(Objects.requireNonNull(action)); + } + /** * Gets the current number of bytes read from this stream. * @@ -155,11 +210,15 @@ public abstract class ArchiveInputStream<E extends ArchiveEntry> extends FilterI /** * Gets the next Archive Entry in this Stream. * - * @return the next entry, or {@code null} if there are no more entries - * @throws IOException if the next entry could not be read + * @return the next entry, or {@code null} if there are no more entries. + * @throws IOException if the next entry could not be read. */ public abstract E getNextEntry() throws IOException; + public IOIterator<E> iterator() { + return new ArchiveEntryIOIterator(); + } + /** * Does nothing. * diff --git a/src/main/java/org/apache/commons/compress/archivers/Lister.java b/src/main/java/org/apache/commons/compress/archivers/Lister.java index f5d87855d..f84cecada 100644 --- a/src/main/java/org/apache/commons/compress/archivers/Lister.java +++ b/src/main/java/org/apache/commons/compress/archivers/Lister.java @@ -149,10 +149,7 @@ public final class Lister { try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(file)); ArchiveInputStream<?> archiveInputStream = createArchiveInputStream(args, inputStream)) { println("Created " + archiveInputStream.toString()); - ArchiveEntry entry; - while ((entry = archiveInputStream.getNextEntry()) != null) { - println(entry); - } + archiveInputStream.forEach(this::println); } } diff --git a/src/test/java/org/apache/commons/compress/AbstractTest.java b/src/test/java/org/apache/commons/compress/AbstractTest.java index 1772b81ca..d90bf8a4d 100644 --- a/src/test/java/org/apache/commons/compress/AbstractTest.java +++ b/src/test/java/org/apache/commons/compress/AbstractTest.java @@ -154,8 +154,7 @@ public abstract class AbstractTest extends AbstractTempDirTest { final Path targetDir = createTempDirectory("dir-result"); final Path result = targetDir.resolve("result"); try { - ArchiveEntry entry; - while ((entry = inputStream.getNextEntry()) != null) { + inputStream.iterator().forEachRemaining(entry -> { final Path outputFile = entry.resolveIn(result); long bytesCopied = 0; if (entry.isDirectory()) { @@ -175,7 +174,7 @@ public abstract class AbstractTest extends AbstractTempDirTest { if (expected != null && !expected.remove(getExpectedString(entry))) { fail("Unexpected entry: " + getExpectedString(entry)); } - } + }); inputStream.close(); if (expected != null && !expected.isEmpty()) { fail(expected.size() + " missing entries: " + Arrays.toString(expected.toArray())); diff --git a/src/test/java/org/apache/commons/compress/archivers/ArTest.java b/src/test/java/org/apache/commons/compress/archivers/ArTest.java index 607d5f225..a9f5f32c9 100644 --- a/src/test/java/org/apache/commons/compress/archivers/ArTest.java +++ b/src/test/java/org/apache/commons/compress/archivers/ArTest.java @@ -97,7 +97,6 @@ public final class ArTest extends AbstractTest { if (entry == null) { break; } - if ("test1.xml".equals(entry.getName())) { aos.putArchiveEntry(entry); IOUtils.copy(ais, aos); diff --git a/src/test/java/org/apache/commons/compress/archivers/CpioTest.java b/src/test/java/org/apache/commons/compress/archivers/CpioTest.java index 626d8504d..d9914fbc7 100644 --- a/src/test/java/org/apache/commons/compress/archivers/CpioTest.java +++ b/src/test/java/org/apache/commons/compress/archivers/CpioTest.java @@ -88,12 +88,11 @@ public final class CpioTest extends AbstractTest { final Map<String, File> result = new HashMap<>(); try (InputStream inputStream = Files.newInputStream(output.toPath()); ArchiveInputStream<?> archiveInputStream = ArchiveStreamFactory.DEFAULT.createArchiveInputStream("cpio", inputStream)) { - ArchiveEntry entry; - while ((entry = archiveInputStream.getNextEntry()) != null) { + archiveInputStream.forEach(entry -> { final File cpioget = newTempFile(entry.getName()); Files.copy(archiveInputStream, cpioget.toPath()); result.put(entry.getName(), cpioget); - } + }); } File testFile = result.get("test1.xml"); diff --git a/src/test/java/org/apache/commons/compress/archivers/DumpTest.java b/src/test/java/org/apache/commons/compress/archivers/DumpTest.java index 580fb5106..5c0068589 100644 --- a/src/test/java/org/apache/commons/compress/archivers/DumpTest.java +++ b/src/test/java/org/apache/commons/compress/archivers/DumpTest.java @@ -85,18 +85,15 @@ public final class DumpTest extends AbstractTest { private void unarchiveAll(final File input) throws Exception { try (InputStream is = Files.newInputStream(input.toPath()); ArchiveInputStream<?> in = ArchiveStreamFactory.DEFAULT.createArchiveInputStream("dump", is)) { - ArchiveEntry entry = in.getNextEntry(); - while (entry != null) { + in.forEach(entry -> { final File archiveEntry = newTempFile(entry.getName()); archiveEntry.getParentFile().mkdirs(); if (entry.isDirectory()) { archiveEntry.mkdir(); - entry = in.getNextEntry(); - continue; + } else { + Files.copy(in, archiveEntry.toPath()); } - Files.copy(in, archiveEntry.toPath()); - entry = in.getNextEntry(); - } + }); } } } diff --git a/src/test/java/org/apache/commons/compress/archivers/JarTest.java b/src/test/java/org/apache/commons/compress/archivers/JarTest.java index b321abcc1..6cba5ebf5 100644 --- a/src/test/java/org/apache/commons/compress/archivers/JarTest.java +++ b/src/test/java/org/apache/commons/compress/archivers/JarTest.java @@ -77,19 +77,15 @@ public final class JarTest extends AbstractTest { final File input = getFile("bla.jar"); try (InputStream is = Files.newInputStream(input.toPath()); ArchiveInputStream<?> in = ArchiveStreamFactory.DEFAULT.createArchiveInputStream("jar", is)) { - - ArchiveEntry entry = in.getNextEntry(); - while (entry != null) { + in.forEach(entry -> { final File archiveEntry = newTempFile(entry.getName()); archiveEntry.getParentFile().mkdirs(); if (entry.isDirectory()) { archiveEntry.mkdir(); - entry = in.getNextEntry(); - continue; + } else { + Files.copy(in, archiveEntry.toPath()); } - Files.copy(in, archiveEntry.toPath()); - entry = in.getNextEntry(); - } + }); } } diff --git a/src/test/java/org/apache/commons/compress/archivers/ZipTest.java b/src/test/java/org/apache/commons/compress/archivers/ZipTest.java index 3ea64fcd8..42c9fb11d 100644 --- a/src/test/java/org/apache/commons/compress/archivers/ZipTest.java +++ b/src/test/java/org/apache/commons/compress/archivers/ZipTest.java @@ -576,9 +576,7 @@ public final class ZipTest extends AbstractTest { // stream access try (InputStream fis = Files.newInputStream(input.toPath()); ArchiveInputStream<?> in = ArchiveStreamFactory.DEFAULT.createArchiveInputStream("zip", fis)) { - for (ArchiveEntry entry; (entry = in.getNextEntry()) != null;) { - readStream(in, entry, actualStatistics); - } + in.forEach(entry -> readStream(in, entry, actualStatistics)); } // file access try (ZipFile zf = newZipFile(input)) { @@ -767,13 +765,12 @@ public final class ZipTest extends AbstractTest { final List<File> results = new ArrayList<>(); try (InputStream fileInputStream = Files.newInputStream(output.toPath())) { try (ArchiveInputStream<ZipArchiveEntry> archiveInputStream = ArchiveStreamFactory.DEFAULT.createArchiveInputStream("zip", fileInputStream)) { - ZipArchiveEntry entry; - while ((entry = archiveInputStream.getNextEntry()) != null) { + archiveInputStream.forEach(entry -> { final File outfile = new File(tempResultDir.getCanonicalPath() + "/result/" + entry.getName()); outfile.getParentFile().mkdirs(); Files.copy(archiveInputStream, outfile.toPath()); results.add(outfile); - } + }); } } assertEquals(results.size(), 2); diff --git a/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStreamTest.java b/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStreamTest.java index 50b02c1fd..1dc765353 100644 --- a/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStreamTest.java +++ b/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStreamTest.java @@ -47,6 +47,7 @@ import org.apache.commons.compress.AbstractTest; import org.apache.commons.compress.archivers.ArchiveException; import org.apache.commons.compress.archivers.ArchiveStreamFactory; import org.apache.commons.io.IOUtils; +import org.apache.commons.io.function.IOConsumer; import org.junit.jupiter.api.Test; public class TarArchiveInputStreamTest extends AbstractTest { @@ -65,11 +66,7 @@ public class TarArchiveInputStreamTest extends AbstractTest { } private void getNextEntryUntilIOException(final TarArchiveInputStream archive) { - assertThrows(IOException.class, () -> { - while (archive.getNextTarEntry() != null) { - // noop - } - }); + assertThrows(IOException.class, () -> archive.forEach(IOConsumer.noop())); } @SuppressWarnings("resource") // Caller closes @@ -78,14 +75,19 @@ public class TarArchiveInputStreamTest extends AbstractTest { } @Test - public void testCompress197() { + public void testCompress197() throws IOException { try (TarArchiveInputStream tar = getTestStream("/COMPRESS-197.tar")) { TarArchiveEntry entry = tar.getNextTarEntry(); while (entry != null) { entry = tar.getNextTarEntry(); } - } catch (final IOException e) { - fail("COMPRESS-197: " + e.getMessage()); + } + } + + @Test + public void testCompress197ForEach() throws IOException { + try (TarArchiveInputStream tar = getTestStream("/COMPRESS-197.tar")) { + tar.forEach(IOConsumer.noop()); } } diff --git a/src/test/java/org/apache/commons/compress/compressors/Pack200Test.java b/src/test/java/org/apache/commons/compress/compressors/Pack200Test.java index a4131418d..b8679e904 100644 --- a/src/test/java/org/apache/commons/compress/compressors/Pack200Test.java +++ b/src/test/java/org/apache/commons/compress/compressors/Pack200Test.java @@ -32,7 +32,6 @@ import java.util.List; import java.util.Map; import org.apache.commons.compress.AbstractTest; -import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.ArchiveInputStream; import org.apache.commons.compress.archivers.ArchiveStreamFactory; import org.apache.commons.compress.archivers.jar.JarArchiveOutputStream; @@ -77,19 +76,15 @@ public final class Pack200Test extends AbstractTest { try (InputStream is = useFile ? new Pack200CompressorInputStream(input, mode) : new Pack200CompressorInputStream(Files.newInputStream(input.toPath()), mode); ArchiveInputStream<?> in = ArchiveStreamFactory.DEFAULT.createArchiveInputStream("jar", is)) { - - ArchiveEntry entry = in.getNextEntry(); - while (entry != null) { + in.forEach(entry -> { final File archiveEntry = newTempFile(entry.getName()); archiveEntry.getParentFile().mkdirs(); if (entry.isDirectory()) { archiveEntry.mkdir(); - entry = in.getNextEntry(); - continue; + } else { + Files.copy(in, archiveEntry.toPath()); } - Files.copy(in, archiveEntry.toPath()); - entry = in.getNextEntry(); - } + }); } } diff --git a/src/test/java/org/apache/commons/compress/compressors/pack200/Pack200UtilsTest.java b/src/test/java/org/apache/commons/compress/compressors/pack200/Pack200UtilsTest.java index da961040f..bef45c094 100644 --- a/src/test/java/org/apache/commons/compress/compressors/pack200/Pack200UtilsTest.java +++ b/src/test/java/org/apache/commons/compress/compressors/pack200/Pack200UtilsTest.java @@ -117,18 +117,15 @@ public final class Pack200UtilsTest extends AbstractTest { Pack200Utils.normalize(input, output, new HashMap<>()); try (InputStream is = Files.newInputStream(output.toPath()); ArchiveInputStream<?> in = ArchiveStreamFactory.DEFAULT.createArchiveInputStream("jar", is)) { - ArchiveEntry entry = in.getNextEntry(); - while (entry != null) { + in.forEach(entry -> { final File archiveEntry = newTempFile(entry.getName()); archiveEntry.getParentFile().mkdirs(); if (entry.isDirectory()) { archiveEntry.mkdir(); - entry = in.getNextEntry(); - continue; + } else { + Files.copy(in, archiveEntry.toPath()); } - Files.copy(in, archiveEntry.toPath()); - entry = in.getNextEntry(); - } + }); } } @@ -142,18 +139,15 @@ public final class Pack200UtilsTest extends AbstractTest { Pack200Utils.normalize(output); try (InputStream is = Files.newInputStream(output.toPath()); ArchiveInputStream<?> in = ArchiveStreamFactory.DEFAULT.createArchiveInputStream("jar", is)) { - ArchiveEntry entry = in.getNextEntry(); - while (entry != null) { + in.forEach(entry -> { final File archiveEntry = newTempFile(entry.getName()); archiveEntry.getParentFile().mkdirs(); if (entry.isDirectory()) { archiveEntry.mkdir(); - entry = in.getNextEntry(); - continue; + } else { + Files.copy(in, archiveEntry.toPath()); } - Files.copy(in, archiveEntry.toPath()); - entry = in.getNextEntry(); - } + }); } }