Variation of PR from Rob Spoor (robtimus) but does not add a new
class.
Add AbstractCharacterFilterReader(Reader, IntPredicate).
Add CharacterFilterReader(Reader, IntPredicate).
Add AbstractCharacterFilterReader(Reader, IntPredicate).
Add CharacterFilterReaderIntPredicateTest.
MODIFY
src/main/java/org/apache/commons/io/input/AbstractCharacterFilterReader.java
src/main/java/org/apache/commons/io/input/AbstractCharacterFilterReader.java
import java.io.FilterReader;
import java.io.IOException;
import java.io.Reader;
+import java.util.function.IntPredicate;
/**
* A filter reader that filters out characters where subclasses decide which characters to filter out.
*/
public abstract class AbstractCharacterFilterReader extends FilterReader {
+ private static final IntPredicate SKIP_NONE = ch -> false;
+
+ private final IntPredicate skip;
+
/**
* Constructs a new reader.
*
* @param reader the reader to filter
*/
protected AbstractCharacterFilterReader(final Reader reader) {
+ this(reader, SKIP_NONE);
+ }
+
+ /**
+ * Constructs a new reader.
+ *
+ * @param reader the reader to filter.
+ * @param skip Skip test.
+ * @since 2.9.0
+ */
+ protected AbstractCharacterFilterReader(final Reader reader, final IntPredicate skip) {
super(reader);
+ this.skip = skip == null ? SKIP_NONE : skip;
+ }
+
+ /**
+ * Returns true if the given character should be filtered out, false to keep the character.
+ *
+ * @param ch the character to test.
+ * @return true if the given character should be filtered out, false to keep the character.
+ */
+ protected boolean filter(final int ch) {
+ return skip.test(ch);
}
@Override
@@ -45,14 +72,6 @@ public int read() throws IOException {
return ch;
}
- /**
- * Returns true if the given character should be filtered out, false to keep the character.
- *
- * @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);
-
@Override
public int read(final char[] cbuf, final int off, final int len) throws IOException {
final int read = super.read(cbuf, off, len);

MODIFY
src/main/java/org/apache/commons/io/input/CharacterFilterReader.java
src/main/java/org/apache/commons/io/input/CharacterFilterReader.java
package org.apache.commons.io.input;
import java.io.Reader;
+import java.util.function.IntPredicate;
/**
* A filter reader that filters out a given character represented as an {@code int} code point, handy to remove
*/
public class CharacterFilterReader extends AbstractCharacterFilterReader {
- private final int skip;
-
/**
* Constructs a new reader.
*
* the character to filter out.
*/
public CharacterFilterReader(final Reader reader, final int skip) {
- super(reader);
- this.skip = skip;
+ super(reader, c -> c == skip);
}
- @Override
- protected boolean filter(final int ch) {
- return ch == skip;
+ /**
+ * Constructs a new reader.
+ *
+ * @param reader the reader to filter.
+ * @param skip Skip test.
+ * @since 2.9.0
+ */
+ public CharacterFilterReader(final Reader reader, final IntPredicate skip) {
+ super(reader, skip);
}
}

MODIFY
src/main/java/org/apache/commons/io/input/CharacterSetFilterReader.java
src/main/java/org/apache/commons/io/input/CharacterSetFilterReader.java
*/
public class CharacterSetFilterReader extends AbstractCharacterFilterReader {
- private static final Set<Integer> EMPTY_SET = Collections.emptySet();
- private final Set<Integer> skipSet;
-
/**
* Constructs a new reader.
*
@@ -53,14 +50,7 @@ public CharacterSetFilterReader(final Reader reader, final Integer... skip) {
* @param skip the set of characters to filter out.
*/
public CharacterSetFilterReader(final Reader reader, final Set<Integer> skip) {
- super(reader);
- this.skipSet = skip == null ? EMPTY_SET : Collections.unmodifiableSet(skip);
- }
-
- @Override
- protected boolean filter(final int ch) {
- // Note WRT Integer.valueOf(): You can increase the Integer cache with a system property, see {@link Integer}.
- return skipSet.contains(Integer.valueOf(ch));
+ super(reader, c -> skip == null ? null : Collections.unmodifiableSet(skip).contains(Integer.valueOf(c)));
}
}

ADD
src/test/java/org/apache/commons/io/input/CharacterFilterReaderIntPredicateTest.java
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.io.input;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.function.IntPredicate;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.output.StringBuilderWriter;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link CharacterFilterReader} with an {@link IntPredicate}.
+ */
+public class CharacterFilterReaderIntPredicateTest {
+
+ @Test
+ public void testInputSize0FilterAll() throws IOException {
+ final StringReader input = new StringReader(StringUtils.EMPTY);
+ try (CharacterFilterReader reader = new CharacterFilterReader(input, ch -> true)) {
+ assertEquals(-1, reader.read());
+ }
+ }
+
+ @Test
+ public void testInputSize1FilterAll() throws IOException {
+ try (StringReader input = new StringReader("a");
+ CharacterFilterReader reader = new CharacterFilterReader(input, ch -> true)) {
+ assertEquals(-1, reader.read());
+ }
+ }
+
+ @Test
+ public void testInputSize2FilterAll() throws IOException {
+ final StringReader input = new StringReader("aa");
+ try (CharacterFilterReader reader = new CharacterFilterReader(input, ch -> true)) {
+ assertEquals(-1, reader.read());
+ }
+ }
+
+ @Test
+ public void testInputSize2FilterFirst() throws IOException {
+ final StringReader input = new StringReader("ab");
+ try (CharacterFilterReader reader = new CharacterFilterReader(input, ch -> ch == 'a')) {
+ assertEquals('b', reader.read());
+ assertEquals(-1, reader.read());
+ }
+ }
+
+ @Test
+ public void testInputSize2FilterLast() throws IOException {
+ final StringReader input = new StringReader("ab");
+ try (CharacterFilterReader reader = new CharacterFilterReader(input, ch -> ch == 'b')) {
+ assertEquals('a', reader.read());
+ assertEquals(-1, reader.read());
+ }
+ }
+
+ @Test
+ public void testInputSize5FilterWhitespace() throws IOException {
+ final StringReader input = new StringReader(" a b ");
+ try (CharacterFilterReader reader = new CharacterFilterReader(input, Character::isWhitespace)) {
+ assertEquals('a', reader.read());
+ assertEquals('b', reader.read());
+ assertEquals(-1, reader.read());
+ }
+ }
+
+ @Test
+ public void testReadIntoBuffer() throws IOException {
+ final StringReader input = new StringReader("ababcabcd");
+ try (CharacterFilterReader reader = new CharacterFilterReader(input, ch -> ch == 'b')) {
+ final char[] buff = new char[9];
+ final int charCount = reader.read(buff);
+ assertEquals(6, charCount);
+ assertEquals("aacacd", new String(buff, 0, charCount));
+ }
+ }
+
+ @Test
+ public void testReadIntoBufferFilterWhitespace() throws IOException {
+ final StringReader input = new StringReader(" a b a b c a b c d ");
+ try (CharacterFilterReader reader = new CharacterFilterReader(input, Character::isWhitespace)) {
+ final char[] buff = new char[19];
+ final int charCount = reader.read(buff);
+ assertEquals(9, charCount);
+ assertEquals("ababcabcd", new String(buff, 0, charCount));
+ }
+ }
+
+ @Test
+ public void testReadUsingReader() throws IOException {
+ final StringReader input = new StringReader("ababcabcd");
+ try (StringBuilderWriter output = new StringBuilderWriter();
+ CharacterFilterReader reader = new CharacterFilterReader(input, ch -> ch == 'b')) {
+ IOUtils.copy(reader, output);
+ assertEquals("aacacd", output.toString());
+ }
+ }
+
+ @Test
+ public void testReadUsingReaderFilterWhitespace() throws IOException {
+ final StringReader input = new StringReader(" a b a b c a b c d ");
+ try (StringBuilderWriter output = new StringBuilderWriter();
+ CharacterFilterReader reader = new CharacterFilterReader(input, Character::isWhitespace)) {
+ IOUtils.copy(reader, output);
+ assertEquals("ababcabcd", output.toString());
+ }
+ }
+
+}
\ No newline at end of file

Variation of PR #227 from Rob Spoor (robtimus) but does not add a new
class.

- Add AbstractCharacterFilterReader(Reader, IntPredicate).
- Add CharacterFilterReader(Reader, IntPredicate).
- Add AbstractCharacterFilterReader(Reader, IntPredicate).
- Add CharacterFilterReaderIntPredicateTest.