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 java.io.IOException; 022import java.io.Reader; 023 024/** 025 * A reader that imposes a limit to the number of characters that can be read from 026 * an underlying reader, returning eof when this limit is reached -regardless of state of 027 * underlying reader. 028 * 029 * <p> 030 * One use case is to avoid overrunning the readAheadLimit supplied to 031 * java.io.Reader#mark(int), since reading too many characters removes the 032 * ability to do a successful reset. 033 * </p> 034 * 035 * @since 2.5 036 */ 037public class BoundedReader 038 extends Reader 039{ 040 041 private static final int INVALID = -1; 042 043 private final Reader target; 044 045 private int charsRead = 0; 046 047 private int markedAt = INVALID; 048 049 private int readAheadLimit; // Internally, this value will never exceed the allowed size 050 051 private final int maxCharsFromTargetReader; 052 053 /** 054 * Constructs a bounded reader 055 * 056 * @param target The target stream that will be used 057 * @param maxCharsFromTargetReader The maximum number of characters that can be read from target 058 * @throws IOException if mark fails 059 */ 060 public BoundedReader( Reader target, int maxCharsFromTargetReader ) throws IOException { 061 this.target = target; 062 this.maxCharsFromTargetReader = maxCharsFromTargetReader; 063 } 064 065 /** 066 * Closes the target 067 * 068 * @throws IOException If an I/O error occurs while calling the underlying reader's close method 069 */ 070 @Override 071 public void close() throws IOException { 072 target.close(); 073 } 074 075 /** 076 * Resets the target to the latest mark, @see java.io.Reader#reset() 077 * 078 * @throws IOException If an I/O error occurs while calling the underlying reader's reset method 079 */ 080 @Override 081 public void reset() throws IOException { 082 charsRead = markedAt; 083 target.reset(); 084 } 085 086 /** 087 * marks the target stream, @see java.io.Reader#mark(int). 088 * 089 * @param readAheadLimit The number of characters that can be read while 090 * still retaining the ability to do #reset(). 091 * Note that this parameter is not validated with respect to 092 * maxCharsFromTargetReader. There is no way to pass 093 * past maxCharsFromTargetReader, even if this value is 094 * greater. 095 * 096 * @throws IOException If an I/O error occurs while calling the underlying reader's mark method 097 */ 098 @Override 099 public void mark( int readAheadLimit ) throws IOException { 100 this.readAheadLimit = readAheadLimit - charsRead; 101 102 markedAt = charsRead; 103 104 target.mark( readAheadLimit ); 105 } 106 107 /** 108 * Reads a single character, @see java.io.Reader#read() 109 * 110 * @return -1 on eof or the character read 111 * @throws IOException If an I/O error occurs while calling the underlying reader's read method 112 */ 113 @Override 114 public int read() throws IOException { 115 116 if ( charsRead >= maxCharsFromTargetReader ) { 117 return -1; 118 } 119 120 if ( markedAt >= 0 && ( charsRead - markedAt ) >= readAheadLimit ) { 121 return -1; 122 } 123 charsRead++; 124 return target.read(); 125 } 126 127 /** 128 * Reads into an array, @see java.io.Reader#read(char[], int, int) 129 * 130 * @param cbuf The buffer to fill 131 * @param off The offset 132 * @param len The number of chars to read 133 * @return the number of chars read 134 * @throws IOException If an I/O error occurs while calling the underlying reader's read method 135 */ 136 @Override 137 public int read( char[] cbuf, int off, int len ) throws IOException { 138 int c; 139 for ( int i = 0; i < len; i++ ) { 140 c = read(); 141 if ( c == -1 ) { 142 return i; 143 } 144 cbuf[off + i] = (char) c; 145 } 146 return len; 147 } 148}