1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.io.input;
18
19 import java.io.EOFException;
20 import java.io.IOException;
21 import java.io.Reader;
22
23 /**
24 * A functional, light weight {@link Reader} that emulates
25 * a reader of a specified size.
26 * <p>
27 * This implementation provides a light weight
28 * object for testing with an {@link Reader}
29 * where the contents don't matter.
30 * <p>
31 * One use case would be for testing the handling of
32 * large {@link Reader} as it can emulate that
33 * scenario without the overhead of actually processing
34 * large numbers of characters - significantly speeding up
35 * test execution times.
36 * <p>
37 * This implementation returns a space from the method that
38 * reads a character and leaves the array unchanged in the read
39 * methods that are passed a character array.
40 * If alternative data is required the <code>processChar()</code> and
41 * <code>processChars()</code> methods can be implemented to generate
42 * data, for example:
43 *
44 * <pre>
45 * public class TestReader extends NullReader {
46 * public TestReader(int size) {
47 * super(size);
48 * }
49 * protected char processChar() {
50 * return ... // return required value here
51 * }
52 * protected void processChars(char[] chars, int offset, int length) {
53 * for (int i = offset; i < length; i++) {
54 * chars[i] = ... // set array value here
55 * }
56 * }
57 * }
58 * </pre>
59 *
60 * @since 1.3
61 * @version $Id: NullReader.java 1415850 2012-11-30 20:51:39Z ggregory $
62 */
63 public class NullReader extends Reader {
64
65 private final long size;
66 private long position;
67 private long mark = -1;
68 private long readlimit;
69 private boolean eof;
70 private final boolean throwEofException;
71 private final boolean markSupported;
72
73 /**
74 * Create a {@link Reader} that emulates a specified size
75 * which supports marking and does not throw EOFException.
76 *
77 * @param size The size of the reader to emulate.
78 */
79 public NullReader(final long size) {
80 this(size, true, false);
81 }
82
83 /**
84 * Create a {@link Reader} that emulates a specified
85 * size with option settings.
86 *
87 * @param size The size of the reader to emulate.
88 * @param markSupported Whether this instance will support
89 * the <code>mark()</code> functionality.
90 * @param throwEofException Whether this implementation
91 * will throw an {@link EOFException} or return -1 when the
92 * end of file is reached.
93 */
94 public NullReader(final long size, final boolean markSupported, final boolean throwEofException) {
95 this.size = size;
96 this.markSupported = markSupported;
97 this.throwEofException = throwEofException;
98 }
99
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(final 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}.
163 * @throws EOFException if the end of file is reached and
164 * <code>throwEofException</code> is set to {@code true}.
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}.
186 * @throws EOFException if the end of file is reached and
187 * <code>throwEofException</code> is set to {@code true}.
188 * @throws IOException if trying to read past the end of file.
189 */
190 @Override
191 public int read(final 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}.
204 * @throws EOFException if the end of file is reached and
205 * <code>throwEofException</code> is set to {@code true}.
206 * @throws IOException if trying to read past the end of file.
207 */
208 @Override
209 public int read(final char[] chars, final int offset, final 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}.
258 * @throws EOFException if the end of file is reached and
259 * <code>throwEofException</code> is set to {@code true}.
260 * @throws IOException if trying to read past the end of file.
261 */
262 @Override
263 public long skip(final 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(final char[] chars, final int offset, final 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}
310 * @throws EOFException if <code>throwEofException</code> is set
311 * to {@code true}.
312 */
313 private int doEndOfFile() throws EOFException {
314 eof = true;
315 if (throwEofException) {
316 throw new EOFException();
317 }
318 return -1;
319 }
320
321 }