1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.io.input; 18 19 import static org.apache.commons.io.IOUtils.EOF; 20 21 import java.io.FilterReader; 22 import java.io.IOException; 23 import java.io.Reader; 24 import java.util.function.IntPredicate; 25 26 /** 27 * A filter reader that filters out characters where subclasses decide which characters to filter out. 28 */ 29 public abstract class AbstractCharacterFilterReader extends FilterReader { 30 31 /** 32 * Skips nothing. 33 * 34 * @since 2.9.0 35 */ 36 protected static final IntPredicate SKIP_NONE = ch -> false; 37 38 private final IntPredicate skip; 39 40 /** 41 * Constructs a new reader. 42 * 43 * @param reader the reader to filter 44 */ AbstractCharacterFilterReader(final Reader reader)45 protected AbstractCharacterFilterReader(final Reader reader) { 46 this(reader, SKIP_NONE); 47 } 48 49 /** 50 * Constructs a new reader. 51 * 52 * @param reader the reader to filter. 53 * @param skip Skip test. 54 * @since 2.9.0 55 */ AbstractCharacterFilterReader(final Reader reader, final IntPredicate skip)56 protected AbstractCharacterFilterReader(final Reader reader, final IntPredicate skip) { 57 super(reader); 58 this.skip = skip == null ? SKIP_NONE : skip; 59 } 60 61 /** 62 * Returns true if the given character should be filtered out, false to keep the character. 63 * 64 * @param ch the character to test. 65 * @return true if the given character should be filtered out, false to keep the character. 66 */ filter(final int ch)67 protected boolean filter(final int ch) { 68 return skip.test(ch); 69 } 70 71 @Override read()72 public int read() throws IOException { 73 int ch; 74 do { 75 ch = in.read(); 76 } while (ch != EOF && filter(ch)); 77 return ch; 78 } 79 80 @Override read(final char[] cbuf, final int off, final int len)81 public int read(final char[] cbuf, final int off, final int len) throws IOException { 82 final int read = super.read(cbuf, off, len); 83 if (read == EOF) { 84 return EOF; 85 } 86 int pos = off - 1; 87 for (int readPos = off; readPos < off + read; readPos++) { 88 if (filter(cbuf[readPos])) { 89 continue; 90 } 91 pos++; 92 if (pos < readPos) { 93 cbuf[pos] = cbuf[readPos]; 94 } 95 } 96 return pos - off + 1; 97 } 98 } 99