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-io.git

commit 935fb384a783f45144e8bf2dbfd1a93916481350
Author: Gary Gregory <garydgreg...@gmail.com>
AuthorDate: Tue Dec 10 15:30:00 2024 -0500

    ReversedLinesFileReader implements IOIterable<String>
---
 src/changes/changes.xml                            |  1 +
 .../commons/io/input/ReversedLinesFileReader.java  | 59 +++++++++++++++++++---
 .../ReversedLinesFileReaderParamBlockSizeTest.java | 17 +++----
 .../ReversedLinesFileReaderParamFileTest.java      | 14 ++---
 4 files changed, 65 insertions(+), 26 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 295203491..8283618e8 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -51,6 +51,7 @@ The <action> type attribute can be add,update,fix,remove.
       <!-- ADD -->
       <action dev="ggregory" type="add" issue="IO-860" due-to="Nico Strecker, 
Gary Gregory">Add ThrottledInputStream.Builder.setMaxBytes(long, 
ChronoUnit).</action>
       <action dev="ggregory" type="add"                due-to="Gary 
Gregory">Add IOIterable.</action>
+      <action dev="ggregory" type="add"                due-to="Gary 
Gregory">ReversedLinesFileReader implements IOIterable&lt;String&gt;.</action>
       <!-- UPDATE -->
     </release>
     <release version="2.18.0" date="2024-11-16" description="Version 2.18.0: 
Java 8 is required.">
diff --git 
a/src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java 
b/src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java
index 51ffb64ef..9649fac2c 100644
--- a/src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java
+++ b/src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java
@@ -31,23 +31,41 @@ import java.nio.file.StandardOpenOption;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 
 import org.apache.commons.io.Charsets;
 import org.apache.commons.io.FileSystem;
 import org.apache.commons.io.StandardLineSeparator;
 import org.apache.commons.io.build.AbstractStreamBuilder;
+import org.apache.commons.io.function.IOIterable;
+import org.apache.commons.io.function.IOIterator;
 
 /**
  * Reads lines in a file reversely (similar to a BufferedReader, but starting 
at the last line). Useful for e.g. searching in log files.
  * <p>
  * To build an instance, use {@link Builder}.
  * </p>
+ * <p>
+ * For example:
+ * </p>
+ *
+ * <pre>
+ * <code>
+ * try (ReversedLinesFileReader reader = ReversedLinesFileReader.builder()
+ *   .setPath(path)
+ *   .setBufferSize(4096)
+ *   .setCharset(StandardCharsets.UTF_8)
+ *   .get()) {
+ *      reader.forEach(line -> System.out.println(line));
+ * }
+ * </code>
+ * </pre>
  *
  * @see Builder
  * @since 2.2
  */
-public class ReversedLinesFileReader implements Closeable {
+public class ReversedLinesFileReader implements Closeable, IOIterable<String> {
 
     // @formatter:off
     /**
@@ -57,11 +75,11 @@ public class ReversedLinesFileReader implements Closeable {
      * For example:
      * </p>
      * <pre>{@code
-     * ReversedLinesFileReader r = ReversedLinesFileReader.builder()
+     * ReversedLinesFileReader reader = ReversedLinesFileReader.builder()
      *   .setPath(path)
      *   .setBufferSize(4096)
      *   .setCharset(StandardCharsets.UTF_8)
-     *   .get();}
+     *   .get());}
      * </pre>
      *
      * @see #get()
@@ -470,7 +488,6 @@ public class ReversedLinesFileReader implements Closeable {
      * @throws IOException if an I/O error occurs.
      */
     public String readLine() throws IOException {
-
         String line = currentFilePart.readLine();
         while (line == null) {
             currentFilePart = currentFilePart.rollOver();
@@ -480,13 +497,11 @@ public class ReversedLinesFileReader implements Closeable 
{
             }
             line = currentFilePart.readLine();
         }
-
         // aligned behavior with BufferedReader that doesn't return a last, 
empty line
         if (EMPTY_STRING.equals(line) && !trailingNewlineOfFileSkipped) {
             trailingNewlineOfFileSkipped = true;
             line = readLine();
         }
-
         return line;
     }
 
@@ -538,4 +553,36 @@ public class ReversedLinesFileReader implements Closeable {
         return lines.isEmpty() ? EMPTY_STRING : 
String.join(System.lineSeparator(), lines) + System.lineSeparator();
     }
 
+    @Override
+    public IOIterator<String> iterator() {
+        return new IOIterator<String>() {
+
+            private String next;
+
+            @Override
+            public boolean hasNext() throws IOException {
+                if (next == null) {
+                    next = readLine();
+                }
+                return next != null;
+            }
+
+            @Override
+            public String next() throws IOException {
+                if (next == null) {
+                    next = readLine();
+                }
+                final String tmp = next;
+                next = null;
+                return tmp;
+            }
+
+            @Override
+            public Iterator<String> unwrap() {
+                return null;
+            }
+
+        };
+    }
+
 }
diff --git 
a/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderParamBlockSizeTest.java
 
b/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderParamBlockSizeTest.java
index c9ede92af..6c8885eab 100644
--- 
a/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderParamBlockSizeTest.java
+++ 
b/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderParamBlockSizeTest.java
@@ -28,6 +28,8 @@ import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URISyntaxException;
 import java.nio.charset.StandardCharsets;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
 import java.util.stream.IntStream;
 
 import org.apache.commons.io.TestResources;
@@ -69,15 +71,15 @@ public class ReversedLinesFileReaderParamBlockSizeTest {
     private static final String TEST_LINE_X_WINDOWS_950_2 = 
"\u7E41\u9AD4\u4E2D\u6587";
 
     static void assertEqualsAndNoLineBreaks(final String expected, final 
String actual) {
-        assertEqualsAndNoLineBreaks(null, expected, actual);
+        assertEqualsAndNoLineBreaks(expected, actual, null);
     }
 
-    static void assertEqualsAndNoLineBreaks(final String msg, final String 
expected, final String actual) {
+    static void assertEqualsAndNoLineBreaks(final String expected, final 
String actual, final Supplier<String> messageSupplier) {
         if (actual != null) {
             assertFalse(actual.contains(LF.getString()), "Line contains \\n: 
line=" + actual);
             assertFalse(actual.contains(CR.getString()), "Line contains \\r: 
line=" + actual);
         }
-        assertEquals(expected, actual, msg);
+        assertEquals(expected, actual, messageSupplier);
     }
 
     // small and uneven block sizes are not used in reality but are good to 
show that the algorithm is solid
@@ -88,12 +90,9 @@ public class ReversedLinesFileReaderParamBlockSizeTest {
     private ReversedLinesFileReader reversedLinesFileReader;
 
     private void assertFileWithShrinkingTestLines(final 
ReversedLinesFileReader reversedLinesFileReader) throws IOException {
-        String line = null;
-        int lineCount = 0;
-        while ((line = reversedLinesFileReader.readLine()) != null) {
-            lineCount++;
-            assertEqualsAndNoLineBreaks("Line " + lineCount + " is not 
matching", TEST_LINE.substring(0, lineCount), line);
-        }
+        final AtomicInteger count = new AtomicInteger();
+        reversedLinesFileReader.forEach(
+                line -> assertEqualsAndNoLineBreaks(TEST_LINE.substring(0, 
count.incrementAndGet()), line, () -> "Line " + count + " is not matching"));
     }
 
     @AfterEach
diff --git 
a/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderParamFileTest.java
 
b/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderParamFileTest.java
index 9c035fd19..c47888823 100644
--- 
a/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderParamFileTest.java
+++ 
b/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderParamFileTest.java
@@ -88,25 +88,17 @@ public class ReversedLinesFileReaderParamFileTest {
     private void testDataIntegrityWithBufferedReader(final Path filePath, 
final FileSystem fileSystem, final Charset charset,
             final ReversedLinesFileReader reversedLinesFileReader) throws 
IOException {
         final Stack<String> lineStack = new Stack<>();
-        String line;
-
         try (BufferedReader bufferedReader = Files.newBufferedReader(filePath, 
Charsets.toCharset(charset))) {
             // read all lines in normal order
+            String line;
             while ((line = bufferedReader.readLine()) != null) {
                 lineStack.push(line);
             }
         }
-
         // read in reverse order and compare with lines from stack
-        while ((line = reversedLinesFileReader.readLine()) != null) {
-            final String lineFromBufferedReader = lineStack.pop();
-            assertEquals(lineFromBufferedReader, line);
-        }
+        reversedLinesFileReader.forEach(line -> assertEquals(lineStack.pop(), 
line));
         assertEquals(0, lineStack.size(), "Stack should be empty");
-
-        if (fileSystem != null) {
-            fileSystem.close();
-        }
+        IOUtils.close(fileSystem);
     }
 
     @ParameterizedTest(name = "{0}, encoding={1}, blockSize={2}, 
useNonDefaultFileSystem={3}, isResource={4}")

Reply via email to