You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2024/12/10 20:30:34 UTC
(commons-io) 08/09: ReversedLinesFileReader implements IOIterable
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 <ga...@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<String>.</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}")