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 2021/05/12 15:05:49 UTC
[commons-io] branch master updated: Prevent infinite loop with
AbstractCharacterFilterReader if EOF is filtered out #226.
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 0f6da44 Prevent infinite loop with AbstractCharacterFilterReader if EOF is filtered out #226.
0f6da44 is described below
commit 0f6da4475714d869feab5768f34536f8855e3bd8
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Wed May 12 11:05:44 2021 -0400
Prevent infinite loop with AbstractCharacterFilterReader if EOF is
filtered out #226.
- Based on PR #226 by Rob Spoor, with an additional (missing) test, and
clean ups.
- Add CharacterSetFilterReader.CharacterSetFilterReader(Reader,
Integer...).
---
src/changes/changes.xml | 8 ++++-
.../io/input/AbstractCharacterFilterReader.java | 8 ++---
.../commons/io/input/CharacterSetFilterReader.java | 27 ++++++++++++-----
.../io/input/CharacterFilterReaderTest.java | 29 +++++++++++++++---
.../io/input/CharacterSetFilterReaderTest.java | 34 ++++++++++++++++++----
5 files changed, 82 insertions(+), 24 deletions(-)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index a385d2a..77f9567 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -123,6 +123,9 @@ The <action> type attribute can be add,update,fix,remove.
<action issue="IO-720" dev="ggregory" type="fix" due-to="XenoAmess">
Fix error about usage of DirectBuffer in JRE 16/17 #205.
</action>
+ <action dev="ggregory" type="fix" due-to="Rob Spoor, Gary Gregory">
+ Prevent infinite loop with AbstractCharacterFilterReader if EOF is filtered out #226.
+ </action>
<!-- ADD -->
<action dev="ggregory" type="add" due-to="Gary Gregory">
Add FileSystemProviders class.
@@ -193,9 +196,12 @@ The <action> type attribute can be add,update,fix,remove.
<action dev="ggregory" type="add" due-to="Gary Gregory">
Add copy(URL, OutputStream).
</action>
- <action issue="IO-651" dev="ggregory" type="add" due-to="jmark109, Gary Gregory">
+ <action issue="IO-651" dev="ggregory" type="add" due-to="jmark109, Gary Gregory">
Add DeferredFileOutputStream.toInputStream() #206.
</action>
+ <action dev="ggregory" type="add" due-to="Gary Gregory">
+ Add CharacterSetFilterReader.CharacterSetFilterReader(Reader, Integer...).
+ </action>
<!-- UPDATES -->
<action dev="ggregory" type="update" due-to="Dependabot">
Update junit-jupiter from 5.6.2 to 5.7.0 #153.
diff --git a/src/main/java/org/apache/commons/io/input/AbstractCharacterFilterReader.java b/src/main/java/org/apache/commons/io/input/AbstractCharacterFilterReader.java
index b829dfc..70f92a2 100644
--- a/src/main/java/org/apache/commons/io/input/AbstractCharacterFilterReader.java
+++ b/src/main/java/org/apache/commons/io/input/AbstractCharacterFilterReader.java
@@ -30,8 +30,7 @@ public abstract class AbstractCharacterFilterReader extends FilterReader {
/**
* Constructs a new reader.
*
- * @param reader
- * the reader to filter
+ * @param reader the reader to filter
*/
protected AbstractCharacterFilterReader(final Reader reader) {
super(reader);
@@ -42,15 +41,14 @@ public abstract class AbstractCharacterFilterReader extends FilterReader {
int ch;
do {
ch = in.read();
- } while (filter(ch));
+ } while (ch != EOF && filter(ch));
return ch;
}
/**
* Returns true if the given character should be filtered out, false to keep the character.
*
- * @param ch
- * the character to test.
+ * @param ch the character to test.
* @return true if the given character should be filtered out, false to keep the character.
*/
protected abstract boolean filter(int ch);
diff --git a/src/main/java/org/apache/commons/io/input/CharacterSetFilterReader.java b/src/main/java/org/apache/commons/io/input/CharacterSetFilterReader.java
index 1016ed2..064cab6 100644
--- a/src/main/java/org/apache/commons/io/input/CharacterSetFilterReader.java
+++ b/src/main/java/org/apache/commons/io/input/CharacterSetFilterReader.java
@@ -17,15 +17,17 @@
package org.apache.commons.io.input;
import java.io.Reader;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Set;
/**
- * A filter reader that removes a given set of characters represented as {@code int} code points, handy to remove
- * known junk characters from CSV files for example.
+ * A filter reader that removes a given set of characters represented as {@code int} code points, handy to remove known
+ * junk characters from CSV files for example.
* <p>
- * This class must convert each {@code int} read to an {@code Integer}. You can increase the Integer cache
- * with a system property, see {@link Integer}.
+ * This class must convert each {@code int} read to an {@code Integer}. You can increase the Integer cache with a system
+ * property, see {@link Integer}.
* </p>
*/
public class CharacterSetFilterReader extends AbstractCharacterFilterReader {
@@ -36,10 +38,19 @@ public class CharacterSetFilterReader extends AbstractCharacterFilterReader {
/**
* Constructs a new reader.
*
- * @param reader
- * the reader to filter.
- * @param skip
- * the set of characters to filter out.
+ * @param reader the reader to filter.
+ * @param skip the set of characters to filter out.
+ * @since 2.9.0
+ */
+ public CharacterSetFilterReader(final Reader reader, final Integer... skip) {
+ this(reader, new HashSet<>(Arrays.asList(skip)));
+ }
+
+ /**
+ * Constructs a new reader.
+ *
+ * @param reader the reader to filter.
+ * @param skip the set of characters to filter out.
*/
public CharacterSetFilterReader(final Reader reader, final Set<Integer> skip) {
super(reader);
diff --git a/src/test/java/org/apache/commons/io/input/CharacterFilterReaderTest.java b/src/test/java/org/apache/commons/io/input/CharacterFilterReaderTest.java
index 80a4f45..c2efad7 100644
--- a/src/test/java/org/apache/commons/io/input/CharacterFilterReaderTest.java
+++ b/src/test/java/org/apache/commons/io/input/CharacterFilterReaderTest.java
@@ -16,10 +16,13 @@
*/
package org.apache.commons.io.input;
+import static org.apache.commons.io.IOUtils.EOF;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
import java.io.IOException;
import java.io.StringReader;
+import java.time.Duration;
import java.util.HashSet;
import org.apache.commons.io.IOUtils;
@@ -28,6 +31,8 @@ import org.junit.jupiter.api.Test;
public class CharacterFilterReaderTest {
+ private static final String STRING_FIXTURE = "ababcabcd";
+
@Test
public void testInputSize0FilterSize1() throws IOException {
final StringReader input = new StringReader("");
@@ -41,7 +46,7 @@ public class CharacterFilterReaderTest {
@Test
public void testInputSize1FilterSize1() throws IOException {
try (StringReader input = new StringReader("a");
- CharacterFilterReader reader = new CharacterFilterReader(input, 'a')) {
+ CharacterFilterReader reader = new CharacterFilterReader(input, 'a')) {
assertEquals(-1, reader.read());
}
}
@@ -73,8 +78,23 @@ public class CharacterFilterReaderTest {
}
@Test
+ public void testReadFilteringEOF() throws IOException {
+ final StringReader input = new StringReader(STRING_FIXTURE);
+ assertTimeoutPreemptively(Duration.ofMillis(500), () -> {
+ try (StringBuilderWriter output = new StringBuilderWriter();
+ CharacterFilterReader reader = new CharacterFilterReader(input, EOF)) {
+ int c;
+ while ((c = reader.read()) != EOF) {
+ output.write(c);
+ }
+ assertEquals(STRING_FIXTURE, output.toString());
+ }
+ });
+ }
+
+ @Test
public void testReadIntoBuffer() throws IOException {
- final StringReader input = new StringReader("ababcabcd");
+ final StringReader input = new StringReader(STRING_FIXTURE);
try (CharacterFilterReader reader = new CharacterFilterReader(input, 'b')) {
final char[] buff = new char[9];
final int charCount = reader.read(buff);
@@ -85,8 +105,9 @@ public class CharacterFilterReaderTest {
@Test
public void testReadUsingReader() throws IOException {
- final StringReader input = new StringReader("ababcabcd");
- try (StringBuilderWriter output = new StringBuilderWriter(); CharacterFilterReader reader = new CharacterFilterReader(input, 'b')) {
+ final StringReader input = new StringReader(STRING_FIXTURE);
+ try (StringBuilderWriter output = new StringBuilderWriter();
+ CharacterFilterReader reader = new CharacterFilterReader(input, 'b')) {
IOUtils.copy(reader, output);
assertEquals("aacacd", output.toString());
}
diff --git a/src/test/java/org/apache/commons/io/input/CharacterSetFilterReaderTest.java b/src/test/java/org/apache/commons/io/input/CharacterSetFilterReaderTest.java
index 680021b..925491a 100644
--- a/src/test/java/org/apache/commons/io/input/CharacterSetFilterReaderTest.java
+++ b/src/test/java/org/apache/commons/io/input/CharacterSetFilterReaderTest.java
@@ -16,16 +16,23 @@
*/
package org.apache.commons.io.input;
+import static org.apache.commons.io.IOUtils.EOF;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
import java.io.IOException;
import java.io.StringReader;
+import java.time.Duration;
import java.util.HashSet;
+import java.util.Set;
+import org.apache.commons.io.output.StringBuilderWriter;
import org.junit.jupiter.api.Test;
public class CharacterSetFilterReaderTest {
+ private static final String STRING_FIXTURE = "ab";
+
@Test
public void testInputSize0FilterSize0() throws IOException {
final StringReader input = new StringReader("");
@@ -47,7 +54,7 @@ public class CharacterSetFilterReaderTest {
@Test
public void testInputSize0NullFilter() throws IOException {
final StringReader input = new StringReader("");
- try (CharacterSetFilterReader reader = new CharacterSetFilterReader(input, null)) {
+ try (CharacterSetFilterReader reader = new CharacterSetFilterReader(input, (Set<Integer>) null)) {
assertEquals(-1, reader.read());
}
}
@@ -75,7 +82,7 @@ public class CharacterSetFilterReaderTest {
@Test
public void testInputSize2FilterSize1FilterFirst() throws IOException {
- final StringReader input = new StringReader("ab");
+ final StringReader input = new StringReader(STRING_FIXTURE);
final HashSet<Integer> codePoints = new HashSet<>();
codePoints.add(Integer.valueOf('a'));
try (CharacterSetFilterReader reader = new CharacterSetFilterReader(input, codePoints)) {
@@ -86,7 +93,7 @@ public class CharacterSetFilterReaderTest {
@Test
public void testInputSize2FilterSize1FilterLast() throws IOException {
- final StringReader input = new StringReader("ab");
+ final StringReader input = new StringReader(STRING_FIXTURE);
final HashSet<Integer> codePoints = new HashSet<>();
codePoints.add(Integer.valueOf('b'));
try (CharacterSetFilterReader reader = new CharacterSetFilterReader(input, codePoints)) {
@@ -97,7 +104,7 @@ public class CharacterSetFilterReaderTest {
@Test
public void testInputSize2FilterSize2FilterFirst() throws IOException {
- final StringReader input = new StringReader("ab");
+ final StringReader input = new StringReader(STRING_FIXTURE);
final HashSet<Integer> codePoints = new HashSet<>();
codePoints.add(Integer.valueOf('a'));
codePoints.add(Integer.valueOf('y'));
@@ -109,7 +116,7 @@ public class CharacterSetFilterReaderTest {
@Test
public void testInputSize2FilterSize2FilterLast() throws IOException {
- final StringReader input = new StringReader("ab");
+ final StringReader input = new StringReader(STRING_FIXTURE);
final HashSet<Integer> codePoints = new HashSet<>();
codePoints.add(Integer.valueOf('x'));
codePoints.add(Integer.valueOf('b'));
@@ -121,7 +128,7 @@ public class CharacterSetFilterReaderTest {
@Test
public void testInputSize2FilterSize2FilterNone() throws IOException {
- final StringReader input = new StringReader("ab");
+ final StringReader input = new StringReader(STRING_FIXTURE);
final HashSet<Integer> codePoints = new HashSet<>();
codePoints.add(Integer.valueOf('x'));
codePoints.add(Integer.valueOf('y'));
@@ -130,4 +137,19 @@ public class CharacterSetFilterReaderTest {
assertEquals('b', reader.read());
}
}
+
+ @Test
+ public void testReadFilteringEOF() throws IOException {
+ final StringReader input = new StringReader(STRING_FIXTURE);
+ assertTimeoutPreemptively(Duration.ofMillis(500), () -> {
+ try (StringBuilderWriter output = new StringBuilderWriter();
+ CharacterSetFilterReader reader = new CharacterSetFilterReader(input, EOF)) {
+ int c;
+ while ((c = reader.read()) != EOF) {
+ output.write(c);
+ }
+ assertEquals(STRING_FIXTURE, output.toString());
+ }
+ });
+ }
}