001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.io.input; 020 021import static org.apache.commons.io.IOUtils.EOF; 022 023import java.io.IOException; 024import java.io.Reader; 025 026/** 027 * A reader that imposes a limit to the number of characters that can be read from an underlying reader, returning EOF 028 * when this limit is reached, regardless of state of underlying reader. 029 * 030 * <p> 031 * One use case is to avoid overrunning the readAheadLimit supplied to {@link java.io.Reader#mark(int)}, since reading 032 * too many characters removes the ability to do a successful reset. 033 * </p> 034 * 035 * @since 2.5 036 */ 037public class BoundedReader extends Reader { 038 039 private static final int INVALID = -1; 040 041 private final Reader target; 042 043 private int charsRead; 044 045 private int markedAt = INVALID; 046 047 private int readAheadLimit; // Internally, this value will never exceed the allowed size 048 049 private final int maxCharsFromTargetReader; 050 051 /** 052 * Constructs a bounded reader 053 * 054 * @param target The target stream that will be used 055 * @param maxCharsFromTargetReader The maximum number of characters that can be read from target 056 * @throws IOException Never thrown. 057 */ 058 public BoundedReader(final Reader target, final int maxCharsFromTargetReader) throws IOException { 059 this.target = target; 060 this.maxCharsFromTargetReader = maxCharsFromTargetReader; 061 } 062 063 /** 064 * Closes the target 065 * 066 * @throws IOException If an I/O error occurs while calling the underlying reader's close method 067 */ 068 @Override 069 public void close() throws IOException { 070 target.close(); 071 } 072 073 /** 074 * Resets the target to the latest mark, 075 * 076 * @throws IOException If an I/O error occurs while calling the underlying reader's reset method 077 * @see java.io.Reader#reset() 078 */ 079 @Override 080 public void reset() throws IOException { 081 charsRead = markedAt; 082 target.reset(); 083 } 084 085 /** 086 * marks the target stream 087 * 088 * @param readAheadLimit The number of characters that can be read while still retaining the ability to do #reset(). 089 * Note that this parameter is not validated with respect to maxCharsFromTargetReader. There 090 * is no way to pass past maxCharsFromTargetReader, even if this value is greater. 091 * 092 * @throws IOException If an I/O error occurs while calling the underlying reader's mark method 093 * @see java.io.Reader#mark(int) 094 */ 095 @Override 096 public void mark(final int readAheadLimit) throws IOException { 097 this.readAheadLimit = readAheadLimit - charsRead; 098 099 markedAt = charsRead; 100 101 target.mark(readAheadLimit); 102 } 103 104 /** 105 * Reads a single character 106 * 107 * @return -1 on EOF or the character read 108 * @throws IOException If an I/O error occurs while calling the underlying reader's read method 109 * @see java.io.Reader#read() 110 */ 111 @Override 112 public int read() throws IOException { 113 114 if (charsRead >= maxCharsFromTargetReader) { 115 return EOF; 116 } 117 118 if (markedAt >= 0 && (charsRead - markedAt) >= readAheadLimit) { 119 return EOF; 120 } 121 charsRead++; 122 return target.read(); 123 } 124 125 /** 126 * Reads into an array 127 * 128 * @param cbuf The buffer to fill 129 * @param off The offset 130 * @param len The number of chars to read 131 * @return the number of chars read 132 * @throws IOException If an I/O error occurs while calling the underlying reader's read method 133 * @see java.io.Reader#read(char[], int, int) 134 */ 135 @Override 136 public int read(final char[] cbuf, final int off, final int len) throws IOException { 137 int c; 138 for (int i = 0; i < len; i++) { 139 c = read(); 140 if (c == EOF) { 141 return i == 0 ? EOF : i; 142 } 143 cbuf[off + i] = (char) c; 144 } 145 return len; 146 } 147}