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 */
17package org.apache.commons.io.input;
18
19import java.io.EOFException;
20import java.io.IOException;
21import 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 */
63public 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}
314