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 Commons IO 1.3
61 * @version $Revision: 463529 $
62 */
63 public class NullReader extends Reader {
64
65 private long size;
66 private long position;
67 private long mark = -1;
68 private long readlimit;
69 private boolean eof;
70 private boolean throwEofException;
71 private 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(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(long size, boolean markSupported, 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 public void close() throws IOException {
125 eof = false;
126 position = 0;
127 mark = -1;
128 }
129
130 /**
131 * Mark the current position.
132 *
133 * @param readlimit The number of characters before this marked position
134 * is invalid.
135 * @throws UnsupportedOperationException if mark is not supported.
136 */
137 public synchronized void mark(int readlimit) {
138 if (!markSupported) {
139 throw new UnsupportedOperationException("Mark not supported");
140 }
141 mark = position;
142 this.readlimit = readlimit;
143 }
144
145 /**
146 * Indicates whether <i>mark</i> is supported.
147 *
148 * @return Whether <i>mark</i> is supported or not.
149 */
150 public boolean markSupported() {
151 return markSupported;
152 }
153
154 /**
155 * Read a character.
156 *
157 * @return Either The character value returned by <code>processChar()</code>
158 * or <code>-1</code> if the end of file has been reached and
159 * <code>throwEofException</code> is set to <code>false</code>.
160 * @throws EOFException if the end of file is reached and
161 * <code>throwEofException</code> is set to <code>true</code>.
162 * @throws IOException if trying to read past the end of file.
163 */
164 public int read() throws IOException {
165 if (eof) {
166 throw new IOException("Read after end of file");
167 }
168 if (position == size) {
169 return doEndOfFile();
170 }
171 position++;
172 return processChar();
173 }
174
175 /**
176 * Read some characters into the specified array.
177 *
178 * @param chars The character array to read into
179 * @return The number of characters read or <code>-1</code>
180 * if the end of file has been reached and
181 * <code>throwEofException</code> is set to <code>false</code>.
182 * @throws EOFException if the end of file is reached and
183 * <code>throwEofException</code> is set to <code>true</code>.
184 * @throws IOException if trying to read past the end of file.
185 */
186 public int read(char[] chars) throws IOException {
187 return read(chars, 0, chars.length);
188 }
189
190 /**
191 * Read the specified number characters into an array.
192 *
193 * @param chars The character array to read into.
194 * @param offset The offset to start reading characters into.
195 * @param length The number of characters to read.
196 * @return The number of characters read or <code>-1</code>
197 * if the end of file has been reached and
198 * <code>throwEofException</code> is set to <code>false</code>.
199 * @throws EOFException if the end of file is reached and
200 * <code>throwEofException</code> is set to <code>true</code>.
201 * @throws IOException if trying to read past the end of file.
202 */
203 public int read(char[] chars, int offset, int length) throws IOException {
204 if (eof) {
205 throw new IOException("Read after end of file");
206 }
207 if (position == size) {
208 return doEndOfFile();
209 }
210 position += length;
211 int returnLength = length;
212 if (position > size) {
213 returnLength = length - (int)(position - size);
214 position = size;
215 }
216 processChars(chars, offset, returnLength);
217 return returnLength;
218 }
219
220 /**
221 * Reset the stream to the point when mark was last called.
222 *
223 * @throws UnsupportedOperationException if mark is not supported.
224 * @throws IOException If no position has been marked
225 * or the read limit has been exceed since the last position was
226 * marked.
227 */
228 public synchronized void reset() throws IOException {
229 if (!markSupported) {
230 throw new UnsupportedOperationException("Mark not supported");
231 }
232 if (mark < 0) {
233 throw new IOException("No position has been marked");
234 }
235 if (position > (mark + readlimit)) {
236 throw new IOException("Marked position [" + mark +
237 "] is no longer valid - passed the read limit [" +
238 readlimit + "]");
239 }
240 position = mark;
241 eof = false;
242 }
243
244 /**
245 * Skip a specified number of characters.
246 *
247 * @param numberOfChars The number of characters to skip.
248 * @return The number of characters skipped or <code>-1</code>
249 * if the end of file has been reached and
250 * <code>throwEofException</code> is set to <code>false</code>.
251 * @throws EOFException if the end of file is reached and
252 * <code>throwEofException</code> is set to <code>true</code>.
253 * @throws IOException if trying to read past the end of file.
254 */
255 public long skip(long numberOfChars) throws IOException {
256 if (eof) {
257 throw new IOException("Skip after end of file");
258 }
259 if (position == size) {
260 return doEndOfFile();
261 }
262 position += numberOfChars;
263 long returnLength = numberOfChars;
264 if (position > size) {
265 returnLength = numberOfChars - (position - size);
266 position = size;
267 }
268 return returnLength;
269 }
270
271 /**
272 * Return a character value for the <code>read()</code> method.
273 * <p>
274 * This implementation returns zero.
275 *
276 * @return This implementation always returns zero.
277 */
278 protected int processChar() {
279 // do nothing - overridable by subclass
280 return 0;
281 }
282
283 /**
284 * Process the characters for the <code>read(char[], offset, length)</code>
285 * method.
286 * <p>
287 * This implementation leaves the character array unchanged.
288 *
289 * @param chars The character array
290 * @param offset The offset to start at.
291 * @param length The number of characters.
292 */
293 protected void processChars(char[] chars, int offset, int length) {
294 // do nothing - overridable by subclass
295 }
296
297 /**
298 * Handle End of File.
299 *
300 * @return <code>-1</code> if <code>throwEofException</code> is
301 * set to <code>false</code>
302 * @throws EOFException if <code>throwEofException</code> is set
303 * to <code>true</code>.
304 */
305 private int doEndOfFile() throws EOFException {
306 eof = true;
307 if (throwEofException) {
308 throw new EOFException();
309 }
310 return -1;
311 }
312
313 }