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( final Reader target, final 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, 077 * 078 * @throws IOException If an I/O error occurs while calling the underlying reader's reset method 079 * @see java.io.Reader#reset() 080 */ 081 @Override 082 public void reset() throws IOException { 083 charsRead = markedAt; 084 target.reset(); 085 } 086 087 /** 088 * marks the target stream 089 * 090 * @param readAheadLimit The number of characters that can be read while 091 * still retaining the ability to do #reset(). 092 * Note that this parameter is not validated with respect to 093 * maxCharsFromTargetReader. There is no way to pass 094 * past maxCharsFromTargetReader, even if this value is 095 * greater. 096 * 097 * @throws IOException If an I/O error occurs while calling the underlying reader's mark method 098 * @see java.io.Reader#mark(int) 099 */ 100 @Override 101 public void mark( final int readAheadLimit ) throws IOException { 102 this.readAheadLimit = readAheadLimit - charsRead; 103 104 markedAt = charsRead; 105 106 target.mark( readAheadLimit ); 107 } 108 109 /** 110 * Reads a single character 111 * 112 * @return -1 on eof or the character read 113 * @throws IOException If an I/O error occurs while calling the underlying reader's read method 114 * @see java.io.Reader#read() 115 */ 116 @Override 117 public int read() throws IOException { 118 119 if ( charsRead >= maxCharsFromTargetReader ) { 120 return -1; 121 } 122 123 if ( markedAt >= 0 && ( charsRead - markedAt ) >= readAheadLimit ) { 124 return -1; 125 } 126 charsRead++; 127 return target.read(); 128 } 129 130 /** 131 * Reads into an array 132 * 133 * @param cbuf The buffer to fill 134 * @param off The offset 135 * @param len The number of chars to read 136 * @return the number of chars read 137 * @throws IOException If an I/O error occurs while calling the underlying reader's read method 138 * @see java.io.Reader#read(char[], int, int) 139 */ 140 @Override 141 public int read( final char[] cbuf, final int off, final int len ) throws IOException { 142 int c; 143 for ( int i = 0; i < len; i++ ) { 144 c = read(); 145 if ( c == -1 ) { 146 return i == 0 ? -1 : i; 147 } 148 cbuf[off + i] = (char) c; 149 } 150 return len; 151 } 152}