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();
-            }
+            });
         }
     }
 

Reply via email to