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/09/15 13:37:20 UTC

(commons-io) branch master updated: Add UnsynchronizedBufferedReader.peek()

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


The following commit(s) were added to refs/heads/master by this push:
     new e9d28f723 Add UnsynchronizedBufferedReader.peek()
e9d28f723 is described below

commit e9d28f723a60991f0601b076be67b1354d6836c8
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sun Sep 15 09:37:14 2024 -0400

    Add UnsynchronizedBufferedReader.peek()
---
 .../io/input/UnsynchronizedBufferedReader.java     |  30 +++
 .../io/input/UnsynchronizedBufferedReaderTest.java | 223 ++++++++++++++++-----
 2 files changed, 208 insertions(+), 45 deletions(-)

diff --git a/src/main/java/org/apache/commons/io/input/UnsynchronizedBufferedReader.java b/src/main/java/org/apache/commons/io/input/UnsynchronizedBufferedReader.java
index c0ded3212..f19b102c3 100644
--- a/src/main/java/org/apache/commons/io/input/UnsynchronizedBufferedReader.java
+++ b/src/main/java/org/apache/commons/io/input/UnsynchronizedBufferedReader.java
@@ -209,6 +209,35 @@ public class UnsynchronizedBufferedReader extends UnsynchronizedReader {
         return true;
     }
 
+    /**
+     * Returns the next character in the current reader without consuming it. So the next call to {@link #read()} will still return this value.
+     *
+     * @return the next character
+     * @throws IOException If an I/O error occurs
+     */
+    public int peek() throws IOException {
+        mark(1);
+        final int c = read();
+        reset();
+        return c;
+    }
+
+    /**
+     * Populates the buffer with the next {@code buf.length} characters in the current reader without consuming them. The next call to {@link #read()} will
+     * still return the next value.
+     *
+     * @param buf the buffer to fill for the look ahead.
+     * @return the buffer itself
+     * @throws IOException If an I/O error occurs
+     */
+    public int peek(final char[] buf) throws IOException {
+        final int n = buf.length;
+        mark(n);
+        final int c = read(buf, 0, n);
+        reset();
+        return c;
+    }
+
     /**
      * Reads a single character from this reader and returns it with the two higher-order bytes set to 0. If possible, BufferedReader returns a character from
      * the buffer. If there are no characters available in the buffer, it fills the buffer and then returns a character. It returns -1 if there are no more
@@ -445,4 +474,5 @@ public class UnsynchronizedBufferedReader extends UnsynchronizedReader {
         }
         return amount;
     }
+
 }
diff --git a/src/test/java/org/apache/commons/io/input/UnsynchronizedBufferedReaderTest.java b/src/test/java/org/apache/commons/io/input/UnsynchronizedBufferedReaderTest.java
index 909a20cc0..de1cc2297 100644
--- a/src/test/java/org/apache/commons/io/input/UnsynchronizedBufferedReaderTest.java
+++ b/src/test/java/org/apache/commons/io/input/UnsynchronizedBufferedReaderTest.java
@@ -17,6 +17,8 @@
 
 package org.apache.commons.io.input;
 
+import static org.apache.commons.io.IOUtils.EOF;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -29,6 +31,7 @@ import java.io.InputStreamReader;
 import java.io.PipedReader;
 import java.io.Reader;
 import java.io.StringReader;
+import java.util.function.Supplier;
 
 import org.apache.commons.io.IOUtils;
 import org.junit.jupiter.api.AfterEach;
@@ -79,8 +82,45 @@ public class UnsynchronizedBufferedReaderTest {
         IOUtils.closeQuietly(br);
     }
 
-    private void assertLines(final String in, final String... lines) throws IOException {
-        try (UnsynchronizedBufferedReader bufferedReader = new UnsynchronizedBufferedReader(new StringReader(in))) {
+    private void assertLines(final String input, final String... lines) throws IOException {
+        assertReadLines(input, lines);
+        assertPeek(input, lines);
+    }
+
+    private void assertPeek(final String input, final String... lines) throws IOException {
+        try (UnsynchronizedBufferedReader bufferedReader = new UnsynchronizedBufferedReader(new StringReader(input))) {
+            for (final String line : lines) {
+                // all
+                final char[] bufAFull = new char[line.length()];
+                assertEquals(bufAFull.length, bufferedReader.peek(bufAFull));
+                assertArrayEquals(line.toCharArray(), bufAFull);
+                if (!line.isEmpty()) {
+                    // one
+                    assertEquals(line.charAt(0), bufferedReader.peek());
+                    // array
+                    for (int peekLen = 0; peekLen < line.length(); peekLen++) {
+                        assertPeekArray(bufferedReader, peekLen, line);
+                    }
+                }
+                // move test to the next fixture
+                assertEquals(line, bufferedReader.readLine());
+            }
+            assertNull(bufferedReader.readLine());
+        }
+    }
+
+    private void assertPeekArray(final UnsynchronizedBufferedReader bufferedReader, final int peekLen, final String line) throws IOException {
+        final char[] expectedBuf = new char[peekLen];
+        final int srcPeekLen = Math.min(peekLen, line.length());
+        line.getChars(0, srcPeekLen, expectedBuf, 0);
+        final char[] actualBuf = new char[peekLen];
+        final Supplier<String> msg = () -> String.format("len=%,d, line='%s'", peekLen, line);
+        assertEquals(actualBuf.length, bufferedReader.peek(actualBuf), msg);
+        assertArrayEquals(expectedBuf, actualBuf, msg);
+    }
+
+    private void assertReadLines(final String input, final String... lines) throws IOException {
+        try (UnsynchronizedBufferedReader bufferedReader = new UnsynchronizedBufferedReader(new StringReader(input))) {
             for (final String line : lines) {
                 assertEquals(line, bufferedReader.readLine());
             }
@@ -94,20 +134,30 @@ public class UnsynchronizedBufferedReaderTest {
      * @throws IOException test failure.
      */
     @Test
-    public void test_close() throws IOException {
+    public void testClose() throws IOException {
         // Test for method void UnsynchronizedBufferedReader.close()
         br = new UnsynchronizedBufferedReader(new StringReader(testString));
         br.close();
         assertThrows(IOException.class, br::read);
     }
 
+    @Test
+    public void testEmptyInput() throws Exception {
+        try (final UnsynchronizedBufferedReader br = new UnsynchronizedBufferedReader(new StringReader(""))) {
+            assertEquals(EOF, br.read());
+            assertEquals(EOF, br.peek());
+            assertNull(br.readLine());
+            assertEquals(0, br.read(new char[10], 0, 0));
+        }
+    }
+
     /**
      * Tests {@link UnsynchronizedBufferedReader#mark(int)}.
      *
      * @throws IOException test failure.
      */
     @Test
-    public void test_markI() throws IOException {
+    public void testMark() throws IOException {
         // Test for method void UnsynchronizedBufferedReader.mark(int)
         char[] buf = null;
         br = new UnsynchronizedBufferedReader(new StringReader(testString));
@@ -176,7 +226,7 @@ public class UnsynchronizedBufferedReaderTest {
      * Tests {@link UnsynchronizedBufferedReader#markSupported()}.
      */
     @Test
-    public void test_markSupported() {
+    public void testMarkSupported() {
         // Test for method boolean UnsynchronizedBufferedReader.markSupported()
         br = new UnsynchronizedBufferedReader(new StringReader(testString));
         assertTrue(br.markSupported());
@@ -188,21 +238,20 @@ public class UnsynchronizedBufferedReaderTest {
      * @throws IOException test failure.
      */
     @Test
-    public void test_read() throws IOException {
+    public void testRead() throws IOException {
         // Test for method int UnsynchronizedBufferedReader.read()
         br = new UnsynchronizedBufferedReader(new StringReader(testString));
         final int r = br.read();
-        assertTrue(testString.charAt(0) == r);
+        assertEquals(testString.charAt(0), r);
         br = new UnsynchronizedBufferedReader(new StringReader(new String(new char[] { '\u8765' })));
-        assertTrue(br.read() == '\u8765');
+        assertEquals(br.read(), '\u8765');
         //
         final char[] chars = new char[256];
         for (int i = 0; i < 256; i++) {
             chars[i] = (char) i;
         }
         try (Reader in = new UnsynchronizedBufferedReader(new StringReader(new String(chars)), 12)) {
-            assertEquals(0, in.read()); // Fill the
-            // buffer
+            assertEquals(0, in.read()); // Fill the buffer
             final char[] buf = new char[14];
             in.read(buf, 0, 14); // Read greater than the buffer
             assertTrue(new String(buf).equals(new String(chars, 1, 14)));
@@ -211,40 +260,91 @@ public class UnsynchronizedBufferedReaderTest {
         //
         // regression test for HARMONY-841
         try (Reader reader = new UnsynchronizedBufferedReader(new CharArrayReader(new char[5], 1, 0), 2)) {
-            assertTrue(reader.read() == -1);
+            assertEquals(reader.read(), -1);
         }
     }
 
     /**
-     * Tests {@link UnsynchronizedBufferedReader#read(char[], int, int)}.
+     * Tests {@link UnsynchronizedBufferedReader#peek()}.
      *
      * @throws IOException test failure.
      */
     @Test
-    public void test_read_$CII_Exception() throws IOException {
+    public void testPeek() throws IOException {
+        // Test for method int UnsynchronizedBufferedReader.read()
         br = new UnsynchronizedBufferedReader(new StringReader(testString));
-        final char[] nullCharArray = null;
-        final char[] charArray = testString.toCharArray();
-        assertThrows(IndexOutOfBoundsException.class, () -> br.read(nullCharArray, -1, -1));
-        assertThrows(IndexOutOfBoundsException.class, () -> br.read(nullCharArray, -1, 0));
-        assertThrows(NullPointerException.class, () -> br.read(nullCharArray, 0, -1));
-        assertThrows(NullPointerException.class, () -> br.read(nullCharArray, 0, 0));
-        assertThrows(NullPointerException.class, () -> br.read(nullCharArray, 0, 1));
-        assertThrows(IndexOutOfBoundsException.class, () -> br.read(charArray, -1, -1));
-        assertThrows(IndexOutOfBoundsException.class, () -> br.read(charArray, -1, 0));
-
-        br.read(charArray, 0, 0);
-        br.read(charArray, 0, charArray.length);
-        br.read(charArray, charArray.length, 0);
-
-        assertThrows(IndexOutOfBoundsException.class, () -> br.read(charArray, charArray.length + 1, 0));
-        assertThrows(IndexOutOfBoundsException.class, () -> br.read(charArray, charArray.length + 1, 1));
-
-        br.close();
+        final int p = br.peek();
+        assertEquals(testString.charAt(0), p);
+        final int r = br.read();
+        assertEquals(testString.charAt(0), r);
+        br = new UnsynchronizedBufferedReader(new StringReader(new String(new char[] { '\u8765' })));
+        assertEquals(br.peek(), '\u8765');
+        assertEquals(br.read(), '\u8765');
+        // chars '\0'...'\255'
+        final char[] chars = new char[256];
+        for (int i = 0; i < 256; i++) {
+            chars[i] = (char) i;
+        }
+        try (UnsynchronizedBufferedReader in = new UnsynchronizedBufferedReader(new StringReader(new String(chars)), 12)) {
+            assertEquals(0, in.peek()); // Fill the buffer
+            assertEquals(0, in.read()); // Fill the buffer
+            final char[] buf = new char[14];
+            in.read(buf, 0, 14); // Read greater than the buffer
+            assertTrue(new String(buf).equals(new String(chars, 1, 14)));
+            assertEquals(15, in.peek()); // Check next byte
+            assertEquals(15, in.read()); // Check next byte
+        }
+        //
+        // regression test for HARMONY-841
+        try (UnsynchronizedBufferedReader reader = new UnsynchronizedBufferedReader(new CharArrayReader(new char[5], 1, 0), 2)) {
+            assertEquals(reader.peek(), -1);
+            assertEquals(reader.read(), -1);
+        }
+    }
 
-        assertThrows(IOException.class, () -> br.read(nullCharArray, -1, -1));
-        assertThrows(IOException.class, () -> br.read(charArray, -1, 0));
-        assertThrows(IOException.class, () -> br.read(charArray, 0, -1));
+    /**
+     * Tests {@link UnsynchronizedBufferedReader#peek()}.
+     *
+     * @throws IOException test failure.
+     */
+    @Test
+    public void testPeekArray() throws IOException {
+        // Test for method int UnsynchronizedBufferedReader.read()
+        final char[] peekBuf1 = new char[1];
+        br = new UnsynchronizedBufferedReader(new StringReader(testString));
+        assertEquals(peekBuf1.length, br.peek(peekBuf1));
+        assertEquals(testString.charAt(0), peekBuf1[0]);
+        final int r = br.read();
+        assertEquals(testString.charAt(0), r);
+        br = new UnsynchronizedBufferedReader(new StringReader(new String(new char[] { '\u8765' })));
+        assertEquals(peekBuf1.length, br.peek(peekBuf1));
+        assertEquals(peekBuf1[0], '\u8765');
+        assertEquals(br.read(), '\u8765');
+        // chars '\0'...'\255'
+        final char[] chars = new char[256];
+        for (int i = 0; i < 256; i++) {
+            chars[i] = (char) i;
+        }
+        try (UnsynchronizedBufferedReader in = new UnsynchronizedBufferedReader(new StringReader(new String(chars)), 12)) {
+            assertEquals(peekBuf1.length, in.peek(peekBuf1)); // Fill the buffer
+            assertEquals(peekBuf1[0], 0);
+            assertEquals(0, in.read()); // Fill the buffer
+            final char[] peekBuf14 = new char[14];
+            assertEquals(peekBuf14.length, in.peek(peekBuf14)); // Peek greater than the buffer
+            assertTrue(new String(peekBuf14).equals(new String(chars, 1, 14)));
+            final char[] buf = new char[14];
+            in.read(buf, 0, 14); // Read greater than the buffer
+            assertTrue(new String(buf).equals(new String(chars, 1, 14)));
+            assertEquals(peekBuf1.length, in.peek(peekBuf1)); // Check next byte
+            assertEquals(15, peekBuf1[0]);
+            assertEquals(15, in.read()); // Check next byte
+        }
+        //
+        // regression test for HARMONY-841
+        try (UnsynchronizedBufferedReader reader = new UnsynchronizedBufferedReader(new CharArrayReader(new char[5], 1, 0), 2)) {
+            assertEquals(reader.peek(), -1);
+            assertEquals(reader.read(), -1);
+        }
     }
 
     /**
@@ -253,7 +353,7 @@ public class UnsynchronizedBufferedReaderTest {
      * @throws IOException test failure.
      */
     @Test
-    public void test_read$CII() throws IOException {
+    public void testReadArray() throws IOException {
         final char[] ca = new char[2];
         try (UnsynchronizedBufferedReader toRet = new UnsynchronizedBufferedReader(new InputStreamReader(new ByteArrayInputStream(new byte[0])))) {
             /* Null buffer should throw NPE even when len == 0 */
@@ -289,6 +389,7 @@ public class UnsynchronizedBufferedReaderTest {
 
             @Override
             public void close() throws IOException {
+                // Empty
             }
 
             @Override
@@ -320,7 +421,7 @@ public class UnsynchronizedBufferedReaderTest {
         })) {
             bufin.read();
             final int result = bufin.read(new char[2], 0, 2);
-            assertTrue(result == 1);
+            assertEquals(result, 1);
         }
         // regression for HARMONY-831
         try (Reader reader = new UnsynchronizedBufferedReader(new PipedReader(), 9)) {
@@ -344,13 +445,45 @@ public class UnsynchronizedBufferedReaderTest {
         assertThrows(IOException.class, () -> reader.read(ch, 0, 42));
     }
 
+    /**
+     * Tests {@link UnsynchronizedBufferedReader#read(char[], int, int)}.
+     *
+     * @throws IOException test failure.
+     */
+    @Test
+    public void testReadArrayException() throws IOException {
+        br = new UnsynchronizedBufferedReader(new StringReader(testString));
+        final char[] nullCharArray = null;
+        final char[] charArray = testString.toCharArray();
+        assertThrows(IndexOutOfBoundsException.class, () -> br.read(nullCharArray, -1, -1));
+        assertThrows(IndexOutOfBoundsException.class, () -> br.read(nullCharArray, -1, 0));
+        assertThrows(NullPointerException.class, () -> br.read(nullCharArray, 0, -1));
+        assertThrows(NullPointerException.class, () -> br.read(nullCharArray, 0, 0));
+        assertThrows(NullPointerException.class, () -> br.read(nullCharArray, 0, 1));
+        assertThrows(IndexOutOfBoundsException.class, () -> br.read(charArray, -1, -1));
+        assertThrows(IndexOutOfBoundsException.class, () -> br.read(charArray, -1, 0));
+
+        br.read(charArray, 0, 0);
+        br.read(charArray, 0, charArray.length);
+        br.read(charArray, charArray.length, 0);
+
+        assertThrows(IndexOutOfBoundsException.class, () -> br.read(charArray, charArray.length + 1, 0));
+        assertThrows(IndexOutOfBoundsException.class, () -> br.read(charArray, charArray.length + 1, 1));
+
+        br.close();
+
+        assertThrows(IOException.class, () -> br.read(nullCharArray, -1, -1));
+        assertThrows(IOException.class, () -> br.read(charArray, -1, 0));
+        assertThrows(IOException.class, () -> br.read(charArray, 0, -1));
+    }
+
     /**
      * Tests {@link UnsynchronizedBufferedReader#readLine()}.
      *
      * @throws IOException test failure.
      */
     @Test
-    public void test_readLine() throws IOException {
+    public void testReadLine() throws IOException {
         // Test for method java.lang.String UnsynchronizedBufferedReader.readLine()
         br = new UnsynchronizedBufferedReader(new StringReader(testString));
         final String r = br.readLine();
@@ -364,12 +497,12 @@ public class UnsynchronizedBufferedReaderTest {
      * @throws IOException test failure.
      */
     @Test
-    public void test_readLine_IgnoresEbcdic85Characters() throws IOException {
+    public void testReadLineIgnoresEbcdic85Characters() throws IOException {
         assertLines("A\u0085B", "A\u0085B");
     }
 
     @Test
-    public void test_readLine_Separators() throws IOException {
+    public void testReadLineSeparators() throws IOException {
         assertLines("A\nB\nC", "A", "B", "C");
         assertLines("A\rB\rC", "A", "B", "C");
         assertLines("A\r\nB\r\nC", "A", "B", "C");
@@ -389,7 +522,7 @@ public class UnsynchronizedBufferedReaderTest {
      * @throws IOException test failure.
      */
     @Test
-    public void test_ready() throws IOException {
+    public void testReady() throws IOException {
         // Test for method boolean UnsynchronizedBufferedReader.ready()
         br = new UnsynchronizedBufferedReader(new StringReader(testString));
         assertTrue(br.ready());
@@ -398,10 +531,10 @@ public class UnsynchronizedBufferedReaderTest {
     /**
      * Tests {@link UnsynchronizedBufferedReader#reset()}.
      *
-     * @throws IOException
+     * @throws IOException test failure.
      */
     @Test
-    public void test_reset() throws IOException {
+    public void testReset() throws IOException {
         // Test for method void UnsynchronizedBufferedReader.reset()
         br = new UnsynchronizedBufferedReader(new StringReader(testString));
         br.skip(500);
@@ -417,7 +550,7 @@ public class UnsynchronizedBufferedReaderTest {
     }
 
     @Test
-    public void test_reset_IOException() throws Exception {
+    public void testReset_IOException() throws Exception {
         final int[] expected = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', -1 };
         br = new UnsynchronizedBufferedReader(new StringReader("1234567890"), 9);
         br.mark(9);
@@ -443,10 +576,10 @@ public class UnsynchronizedBufferedReaderTest {
     /**
      * Tests {@link UnsynchronizedBufferedReader#skip(long)}.
      *
-     * @throws IOException
+     * @throws IOException test failure.
      */
     @Test
-    public void test_skipJ() throws IOException {
+    public void testSkip() throws IOException {
         // Test for method long UnsynchronizedBufferedReader.skip(long)
         br = new UnsynchronizedBufferedReader(new StringReader(testString));
         br.skip(500);