001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.io.input;
018
019 import java.io.EOFException;
020 import java.io.IOException;
021 import java.io.Reader;
022
023 /**
024 * A functional, light weight {@link Reader} that emulates
025 * a reader of a specified size.
026 * <p>
027 * This implementation provides a light weight
028 * object for testing with an {@link Reader}
029 * where the contents don't matter.
030 * <p>
031 * One use case would be for testing the handling of
032 * large {@link Reader} as it can emulate that
033 * scenario without the overhead of actually processing
034 * large numbers of characters - significantly speeding up
035 * test execution times.
036 * <p>
037 * This implementation returns a space from the method that
038 * reads a character and leaves the array unchanged in the read
039 * methods that are passed a character array.
040 * If alternative data is required the <code>processChar()</code> and
041 * <code>processChars()</code> methods can be implemented to generate
042 * data, for example:
043 *
044 * <pre>
045 * public class TestReader extends NullReader {
046 * public TestReader(int size) {
047 * super(size);
048 * }
049 * protected char processChar() {
050 * return ... // return required value here
051 * }
052 * protected void processChars(char[] chars, int offset, int length) {
053 * for (int i = offset; i < length; i++) {
054 * chars[i] = ... // set array value here
055 * }
056 * }
057 * }
058 * </pre>
059 *
060 * @since 1.3
061 * @version $Id: NullReader.java 1304052 2012-03-22 20:55:29Z ggregory $
062 */
063 public class NullReader extends Reader {
064
065 private final long size;
066 private long position;
067 private long mark = -1;
068 private long readlimit;
069 private boolean eof;
070 private final boolean throwEofException;
071 private final boolean markSupported;
072
073 /**
074 * Create a {@link Reader} that emulates a specified size
075 * which supports marking and does not throw EOFException.
076 *
077 * @param size The size of the reader to emulate.
078 */
079 public NullReader(long size) {
080 this(size, true, false);
081 }
082
083 /**
084 * Create a {@link Reader} that emulates a specified
085 * size with option settings.
086 *
087 * @param size The size of the reader to emulate.
088 * @param markSupported Whether this instance will support
089 * the <code>mark()</code> functionality.
090 * @param throwEofException Whether this implementation
091 * will throw an {@link EOFException} or return -1 when the
092 * end of file is reached.
093 */
094 public NullReader(long size, boolean markSupported, boolean throwEofException) {
095 this.size = size;
096 this.markSupported = markSupported;
097 this.throwEofException = throwEofException;
098 }
099
100 /**
101 * Return the current position.
102 *
103 * @return the current position.
104 */
105 public long getPosition() {
106 return position;
107 }
108
109 /**
110 * Return the size this {@link Reader} emulates.
111 *
112 * @return The size of the reader to emulate.
113 */
114 public long getSize() {
115 return size;
116 }
117
118 /**
119 * Close this Reader - resets the internal state to
120 * the initial values.
121 *
122 * @throws IOException If an error occurs.
123 */
124 @Override
125 public void close() throws IOException {
126 eof = false;
127 position = 0;
128 mark = -1;
129 }
130
131 /**
132 * Mark the current position.
133 *
134 * @param readlimit The number of characters before this marked position
135 * is invalid.
136 * @throws UnsupportedOperationException if mark is not supported.
137 */
138 @Override
139 public synchronized void mark(int readlimit) {
140 if (!markSupported) {
141 throw new UnsupportedOperationException("Mark not supported");
142 }
143 mark = position;
144 this.readlimit = readlimit;
145 }
146
147 /**
148 * Indicates whether <i>mark</i> is supported.
149 *
150 * @return Whether <i>mark</i> is supported or not.
151 */
152 @Override
153 public boolean markSupported() {
154 return markSupported;
155 }
156
157 /**
158 * Read a character.
159 *
160 * @return Either The character value returned by <code>processChar()</code>
161 * or <code>-1</code> if the end of file has been reached and
162 * <code>throwEofException</code> is set to <code>false</code>.
163 * @throws EOFException if the end of file is reached and
164 * <code>throwEofException</code> is set to <code>true</code>.
165 * @throws IOException if trying to read past the end of file.
166 */
167 @Override
168 public int read() throws IOException {
169 if (eof) {
170 throw new IOException("Read after end of file");
171 }
172 if (position == size) {
173 return doEndOfFile();
174 }
175 position++;
176 return processChar();
177 }
178
179 /**
180 * Read some characters into the specified array.
181 *
182 * @param chars The character array to read into
183 * @return The number of characters read or <code>-1</code>
184 * if the end of file has been reached and
185 * <code>throwEofException</code> is set to <code>false</code>.
186 * @throws EOFException if the end of file is reached and
187 * <code>throwEofException</code> is set to <code>true</code>.
188 * @throws IOException if trying to read past the end of file.
189 */
190 @Override
191 public int read(char[] chars) throws IOException {
192 return read(chars, 0, chars.length);
193 }
194
195 /**
196 * Read the specified number characters into an array.
197 *
198 * @param chars The character array to read into.
199 * @param offset The offset to start reading characters into.
200 * @param length The number of characters to read.
201 * @return The number of characters read or <code>-1</code>
202 * if the end of file has been reached and
203 * <code>throwEofException</code> is set to <code>false</code>.
204 * @throws EOFException if the end of file is reached and
205 * <code>throwEofException</code> is set to <code>true</code>.
206 * @throws IOException if trying to read past the end of file.
207 */
208 @Override
209 public int read(char[] chars, int offset, int length) throws IOException {
210 if (eof) {
211 throw new IOException("Read after end of file");
212 }
213 if (position == size) {
214 return doEndOfFile();
215 }
216 position += length;
217 int returnLength = length;
218 if (position > size) {
219 returnLength = length - (int)(position - size);
220 position = size;
221 }
222 processChars(chars, offset, returnLength);
223 return returnLength;
224 }
225
226 /**
227 * Reset the stream to the point when mark was last called.
228 *
229 * @throws UnsupportedOperationException if mark is not supported.
230 * @throws IOException If no position has been marked
231 * or the read limit has been exceed since the last position was
232 * marked.
233 */
234 @Override
235 public synchronized void reset() throws IOException {
236 if (!markSupported) {
237 throw new UnsupportedOperationException("Mark not supported");
238 }
239 if (mark < 0) {
240 throw new IOException("No position has been marked");
241 }
242 if (position > mark + readlimit) {
243 throw new IOException("Marked position [" + mark +
244 "] is no longer valid - passed the read limit [" +
245 readlimit + "]");
246 }
247 position = mark;
248 eof = false;
249 }
250
251 /**
252 * Skip a specified number of characters.
253 *
254 * @param numberOfChars The number of characters to skip.
255 * @return The number of characters skipped or <code>-1</code>
256 * if the end of file has been reached and
257 * <code>throwEofException</code> is set to <code>false</code>.
258 * @throws EOFException if the end of file is reached and
259 * <code>throwEofException</code> is set to <code>true</code>.
260 * @throws IOException if trying to read past the end of file.
261 */
262 @Override
263 public long skip(long numberOfChars) throws IOException {
264 if (eof) {
265 throw new IOException("Skip after end of file");
266 }
267 if (position == size) {
268 return doEndOfFile();
269 }
270 position += numberOfChars;
271 long returnLength = numberOfChars;
272 if (position > size) {
273 returnLength = numberOfChars - (position - size);
274 position = size;
275 }
276 return returnLength;
277 }
278
279 /**
280 * Return a character value for the <code>read()</code> method.
281 * <p>
282 * This implementation returns zero.
283 *
284 * @return This implementation always returns zero.
285 */
286 protected int processChar() {
287 // do nothing - overridable by subclass
288 return 0;
289 }
290
291 /**
292 * Process the characters for the <code>read(char[], offset, length)</code>
293 * method.
294 * <p>
295 * This implementation leaves the character array unchanged.
296 *
297 * @param chars The character array
298 * @param offset The offset to start at.
299 * @param length The number of characters.
300 */
301 protected void processChars(char[] chars, int offset, int length) {
302 // do nothing - overridable by subclass
303 }
304
305 /**
306 * Handle End of File.
307 *
308 * @return <code>-1</code> if <code>throwEofException</code> is
309 * set to <code>false</code>
310 * @throws EOFException if <code>throwEofException</code> is set
311 * to <code>true</code>.
312 */
313 private int doEndOfFile() throws EOFException {
314 eof = true;
315 if (throwEofException) {
316 throw new EOFException();
317 }
318 return -1;
319 }
320
321 }