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 */
017package org.apache.commons.io.input;
018
019import java.io.EOFException;
020import java.io.IOException;
021import 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 1307462 2012-03-30 15:13:11Z ggregory $
062 */
063public 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}.
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(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(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}.
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(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}
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}