1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 package org.apache.commons.io.input; 20 21 import static org.apache.commons.io.IOUtils.EOF; 22 23 import java.io.IOException; 24 import java.io.Reader; 25 26 /** 27 * A reader that imposes a limit to the number of characters that can be read from an underlying reader, returning EOF 28 * when this limit is reached, regardless of state of underlying reader. 29 * 30 * <p> 31 * One use case is to avoid overrunning the readAheadLimit supplied to {@link java.io.Reader#mark(int)}, since reading 32 * too many characters removes the ability to do a successful reset. 33 * </p> 34 * 35 * @since 2.5 36 */ 37 public class BoundedReader extends Reader { 38 39 private static final int INVALID = -1; 40 41 private final Reader target; 42 43 private int charsRead; 44 45 private int markedAt = INVALID; 46 47 private int readAheadLimit; // Internally, this value will never exceed the allowed size 48 49 private final int maxCharsFromTargetReader; 50 51 /** 52 * Constructs a bounded reader 53 * 54 * @param target The target stream that will be used 55 * @param maxCharsFromTargetReader The maximum number of characters that can be read from target 56 */ 57 public BoundedReader(final Reader target, final int maxCharsFromTargetReader) { 58 this.target = target; 59 this.maxCharsFromTargetReader = maxCharsFromTargetReader; 60 } 61 62 /** 63 * Closes the target 64 * 65 * @throws IOException If an I/O error occurs while calling the underlying reader's close method 66 */ 67 @Override 68 public void close() throws IOException { 69 target.close(); 70 } 71 72 /** 73 * marks the target stream 74 * 75 * @param readAheadLimit The number of characters that can be read while still retaining the ability to do #reset(). 76 * Note that this parameter is not validated with respect to maxCharsFromTargetReader. There 77 * is no way to pass past maxCharsFromTargetReader, even if this value is greater. 78 * 79 * @throws IOException If an I/O error occurs while calling the underlying reader's mark method 80 * @see java.io.Reader#mark(int) 81 */ 82 @Override 83 public void mark(final int readAheadLimit) throws IOException { 84 this.readAheadLimit = readAheadLimit - charsRead; 85 86 markedAt = charsRead; 87 88 target.mark(readAheadLimit); 89 } 90 91 /** 92 * Reads a single character 93 * 94 * @return -1 on EOF or the character read 95 * @throws IOException If an I/O error occurs while calling the underlying reader's read method 96 * @see java.io.Reader#read() 97 */ 98 @Override 99 public int read() throws IOException { 100 101 if (charsRead >= maxCharsFromTargetReader) { 102 return EOF; 103 } 104 105 if (markedAt >= 0 && charsRead - markedAt >= readAheadLimit) { 106 return EOF; 107 } 108 charsRead++; 109 return target.read(); 110 } 111 112 /** 113 * Reads into an array 114 * 115 * @param cbuf The buffer to fill 116 * @param off The offset 117 * @param len The number of chars to read 118 * @return the number of chars read 119 * @throws IOException If an I/O error occurs while calling the underlying reader's read method 120 * @see java.io.Reader#read(char[], int, int) 121 */ 122 @Override 123 public int read(final char[] cbuf, final int off, final int len) throws IOException { 124 int c; 125 for (int i = 0; i < len; i++) { 126 c = read(); 127 if (c == EOF) { 128 return i == 0 ? EOF : i; 129 } 130 cbuf[off + i] = (char) c; 131 } 132 return len; 133 } 134 135 /** 136 * Resets the target to the latest mark, 137 * 138 * @throws IOException If an I/O error occurs while calling the underlying reader's reset method 139 * @see java.io.Reader#reset() 140 */ 141 @Override 142 public void reset() throws IOException { 143 charsRead = markedAt; 144 target.reset(); 145 } 146 }