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