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 * https://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 static org.apache.commons.io.IOUtils.EOF;
20
21 import java.io.EOFException;
22 import java.io.IOException;
23 import java.io.Reader;
24
25 import org.apache.commons.io.IOUtils;
26
27 /**
28 * A functional, lightweight {@link Reader} that emulates
29 * a reader of a specified size.
30 * <p>
31 * This implementation provides a lightweight
32 * object for testing with an {@link Reader}
33 * where the contents don't matter.
34 * </p>
35 * <p>
36 * One use case would be for testing the handling of
37 * large {@link Reader} as it can emulate that
38 * scenario without the overhead of actually processing
39 * large numbers of characters - significantly speeding up
40 * test execution times.
41 * </p>
42 * <p>
43 * This implementation returns a space from the method that
44 * reads a character and leaves the array unchanged in the read
45 * methods that are passed a character array.
46 * If alternative data is required the {@code processChar()} and
47 * {@code processChars()} methods can be implemented to generate
48 * data, for example:
49 * </p>
50 *
51 * <pre>
52 * public class TestReader extends NullReader {
53 * public TestReader(int size) {
54 * super(size);
55 * }
56 * protected char processChar() {
57 * return ... // return required value here
58 * }
59 * protected void processChars(char[] chars, int offset, int length) {
60 * for (int i = offset; i < length; i++) {
61 * chars[i] = ... // set array value here
62 * }
63 * }
64 * }
65 * </pre>
66 * <p>
67 * This class is not thread-safe.
68 * </p>
69 *
70 * @since 1.3
71 */
72 public class NullReader extends Reader {
73
74 /**
75 * The singleton instance.
76 *
77 * @since 2.12.0
78 */
79 public static final NullReader INSTANCE = new NullReader();
80
81 private final long size;
82 private final boolean throwEofException;
83 private final boolean markSupported;
84
85 private long position;
86 private long mark = -1;
87 private long readLimit;
88 private boolean eof;
89
90 /**
91 * Constructs a {@link Reader} that emulates a size 0 reader
92 * which supports marking and does not throw EOFException.
93 *
94 * @since 2.7
95 */
96 public NullReader() {
97 this(0, true, false);
98 }
99
100 /**
101 * Constructs a {@link Reader} that emulates a specified size
102 * which supports marking and does not throw EOFException.
103 *
104 * @param size The size of the reader to emulate.
105 */
106 public NullReader(final long size) {
107 this(size, true, false);
108 }
109
110 /**
111 * Constructs a {@link Reader} that emulates a specified
112 * size with option settings.
113 *
114 * @param size The size of the reader to emulate.
115 * @param markSupported Whether this instance will support
116 * the {@code mark()} functionality.
117 * @param throwEofException Whether this implementation
118 * will throw an {@link EOFException} or return -1 when the
119 * end of file is reached.
120 */
121 public NullReader(final long size, final boolean markSupported, final boolean throwEofException) {
122 this.size = size;
123 this.markSupported = markSupported;
124 this.throwEofException = throwEofException;
125 }
126
127 /**
128 * Closes this Reader. Resets the internal state to
129 * the initial values.
130 *
131 * @throws IOException If an error occurs.
132 */
133 @Override
134 public void close() throws IOException {
135 eof = false;
136 position = 0;
137 mark = -1;
138 }
139
140 /**
141 * Handles End of File.
142 *
143 * @return {@code -1} if {@code throwEofException} is
144 * set to {@code false}
145 * @throws EOFException if {@code throwEofException} is set
146 * to {@code true}.
147 */
148 private int doEndOfFile() throws EOFException {
149 eof = true;
150 if (throwEofException) {
151 throw new EOFException();
152 }
153 return EOF;
154 }
155
156 /**
157 * Gets the current position.
158 *
159 * @return the current position.
160 */
161 public long getPosition() {
162 return position;
163 }
164
165 /**
166 * Gets the size this {@link Reader} emulates.
167 *
168 * @return The size of the reader to emulate.
169 */
170 public long getSize() {
171 return size;
172 }
173
174 /**
175 * Marks the current position.
176 *
177 * @param readLimit The number of characters before this marked position
178 * is invalid.
179 * @throws UnsupportedOperationException if mark is not supported.
180 */
181 @Override
182 public synchronized void mark(final int readLimit) {
183 if (!markSupported) {
184 throw UnsupportedOperationExceptions.mark();
185 }
186 mark = position;
187 this.readLimit = readLimit;
188 }
189
190 /**
191 * Tests whether <em>mark</em> is supported.
192 *
193 * @return Whether <em>mark</em> is supported or not.
194 */
195 @Override
196 public boolean markSupported() {
197 return markSupported;
198 }
199
200 /**
201 * Returns a character value for the {@code read()} method.
202 * <p>
203 * This implementation returns zero.
204 * </p>
205 *
206 * @return This implementation always returns zero.
207 */
208 protected int processChar() {
209 // do nothing - overridable by subclass
210 return 0;
211 }
212
213 /**
214 * Process the characters for the {@code read(char[], offset, length)}
215 * method.
216 * <p>
217 * This implementation leaves the character array unchanged.
218 * </p>
219 *
220 * @param chars The character array
221 * @param offset The offset to start at.
222 * @param length The number of characters.
223 */
224 protected void processChars(final char[] chars, final int offset, final int length) {
225 // do nothing - overridable by subclass
226 }
227
228 /**
229 * Reads a character.
230 *
231 * @return Either The character value returned by {@code processChar()}
232 * or {@code -1} if the end of file has been reached and
233 * {@code throwEofException} is set to {@code false}.
234 * @throws EOFException if the end of file is reached and
235 * {@code throwEofException} is set to {@code true}.
236 * @throws IOException if trying to read past the end of file.
237 */
238 @Override
239 public int read() throws IOException {
240 if (eof) {
241 throw new IOException("Read after end of file");
242 }
243 if (position == size) {
244 return doEndOfFile();
245 }
246 position++;
247 return processChar();
248 }
249
250 /**
251 * Reads some characters into the specified array.
252 *
253 * @param chars The character array to read into
254 * @return The number of characters read or {@code -1}
255 * if the end of file has been reached and
256 * {@code throwEofException} is set to {@code false}.
257 * @throws EOFException if the end of file is reached and
258 * {@code throwEofException} is set to {@code true}.
259 * @throws IOException if trying to read past the end of file.
260 */
261 @Override
262 public int read(final char[] chars) throws IOException {
263 return read(chars, 0, chars.length);
264 }
265
266 /**
267 * Reads the specified number characters into an array.
268 *
269 * @param chars The character array to read into.
270 * @param offset The offset to start reading characters into.
271 * @param length The number of characters to read.
272 * @return The number of characters read or {@code -1}
273 * if the end of file has been reached and
274 * {@code throwEofException} is set to {@code false}.
275 * @throws NullPointerException if the array is {@code null}.
276 * @throws IndexOutOfBoundsException if {@code offset} or {@code length} are negative, or if {@code offset + length} is greater than {@code chars.length}.
277 * @throws EOFException if the end of file is reached and
278 * {@code throwEofException} is set to {@code true}.
279 * @throws IOException if trying to read past the end of file.
280 */
281 @Override
282 public int read(final char[] chars, final int offset, final int length) throws IOException {
283 IOUtils.checkFromIndexSize(chars, offset, length);
284 if (length == 0) {
285 return 0;
286 }
287 if (eof) {
288 throw new IOException("Read after end of file");
289 }
290 if (position == size) {
291 return doEndOfFile();
292 }
293 position += length;
294 int returnLength = length;
295 if (position > size) {
296 returnLength = length - (int) (position - size);
297 position = size;
298 }
299 processChars(chars, offset, returnLength);
300 return returnLength;
301 }
302
303 /**
304 * Resets the stream to the point when mark was last called.
305 *
306 * @throws UnsupportedOperationException if mark is not supported.
307 * @throws IOException If no position has been marked
308 * or the read limit has been exceeded since the last position was
309 * marked.
310 */
311 @Override
312 public synchronized void reset() throws IOException {
313 if (!markSupported) {
314 throw UnsupportedOperationExceptions.reset();
315 }
316 if (mark < 0) {
317 throw new IOException("No position has been marked");
318 }
319 if (position > mark + readLimit) {
320 throw new IOException("Marked position [" + mark +
321 "] is no longer valid - passed the read limit [" +
322 readLimit + "]");
323 }
324 position = mark;
325 eof = false;
326 }
327
328 /**
329 * Skips a specified number of characters.
330 *
331 * @param numberOfChars The number of characters to skip.
332 * @return The number of characters skipped or {@code -1}
333 * if the end of file has been reached and
334 * {@code throwEofException} is set to {@code false}.
335 * @throws EOFException if the end of file is reached and
336 * {@code throwEofException} is set to {@code true}.
337 * @throws IOException if trying to read past the end of file.
338 */
339 @Override
340 public long skip(final long numberOfChars) throws IOException {
341 if (eof) {
342 throw new IOException("Skip after end of file");
343 }
344 if (position == size) {
345 return doEndOfFile();
346 }
347 position += numberOfChars;
348 long returnLength = numberOfChars;
349 if (position > size) {
350 returnLength = numberOfChars - (position - size);
351 position = size;
352 }
353 return returnLength;
354 }
355
356 }