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 static org.apache.commons.io.IOUtils.EOF;
020
021import java.io.EOFException;
022import java.io.IOException;
023import java.io.Reader;
024
025/**
026 * A functional, light weight {@link Reader} that emulates
027 * a reader of a specified size.
028 * <p>
029 * This implementation provides a light weight
030 * object for testing with an {@link Reader}
031 * where the contents don't matter.
032 * <p>
033 * One use case would be for testing the handling of
034 * large {@link Reader} as it can emulate that
035 * scenario without the overhead of actually processing
036 * large numbers of characters - significantly speeding up
037 * test execution times.
038 * <p>
039 * This implementation returns a space from the method that
040 * reads a character and leaves the array unchanged in the read
041 * methods that are passed a character array.
042 * If alternative data is required the <code>processChar()</code> and
043 * <code>processChars()</code> methods can be implemented to generate
044 * data, for example:
045 *
046 * <pre>
047 *  public class TestReader extends NullReader {
048 *      public TestReader(int size) {
049 *          super(size);
050 *      }
051 *      protected char processChar() {
052 *          return ... // return required value here
053 *      }
054 *      protected void processChars(char[] chars, int offset, int length) {
055 *          for (int i = offset; i &lt; length; i++) {
056 *              chars[i] = ... // set array value here
057 *          }
058 *      }
059 *  }
060 * </pre>
061 *
062 * @since 1.3
063 * @version $Id: NullReader.java 1586350 2014-04-10 15:57:20Z ggregory $
064 */
065public class NullReader extends Reader {
066
067    private final long size;
068    private long position;
069    private long mark = -1;
070    private long readlimit;
071    private boolean eof;
072    private final boolean throwEofException;
073    private final boolean markSupported;
074
075    /**
076     * Create a {@link Reader} that emulates a specified size
077     * which supports marking and does not throw EOFException.
078     *
079     * @param size The size of the reader to emulate.
080     */
081    public NullReader(final long size) {
082       this(size, true, false);
083    }
084
085    /**
086     * Create a {@link Reader} that emulates a specified
087     * size with option settings.
088     *
089     * @param size The size of the reader to emulate.
090     * @param markSupported Whether this instance will support
091     * the <code>mark()</code> functionality.
092     * @param throwEofException Whether this implementation
093     * will throw an {@link EOFException} or return -1 when the
094     * end of file is reached.
095     */
096    public NullReader(final long size, final boolean markSupported, final boolean throwEofException) {
097       this.size = size;
098       this.markSupported = markSupported;
099       this.throwEofException = throwEofException;
100    }
101
102    /**
103     * Return the current position.
104     *
105     * @return the current position.
106     */
107    public long getPosition() {
108        return position;
109    }
110
111    /**
112     * Return the size this {@link Reader} emulates.
113     *
114     * @return The size of the reader to emulate.
115     */
116    public long getSize() {
117        return size;
118    }
119
120    /**
121     * Close this Reader - resets the internal state to
122     * the initial values.
123     *
124     * @throws IOException If an error occurs.
125     */
126    @Override
127    public void close() throws IOException {
128        eof = false;
129        position = 0;
130        mark = -1;
131    }
132
133    /**
134     * Mark the current position.
135     *
136     * @param readlimit The number of characters before this marked position
137     * is invalid.
138     * @throws UnsupportedOperationException if mark is not supported.
139     */
140    @Override
141    public synchronized void mark(final int readlimit) {
142        if (!markSupported) {
143            throw new UnsupportedOperationException("Mark not supported");
144        }
145        mark = position;
146        this.readlimit = readlimit;
147    }
148
149    /**
150     * Indicates whether <i>mark</i> is supported.
151     *
152     * @return Whether <i>mark</i> is supported or not.
153     */
154    @Override
155    public boolean markSupported() {
156        return markSupported;
157    }
158
159    /**
160     * Read a character.
161     *
162     * @return Either The character value returned by <code>processChar()</code>
163     * or <code>-1</code> if the end of file has been reached and
164     * <code>throwEofException</code> is set to {@code false}.
165     * @throws EOFException if the end of file is reached and
166     * <code>throwEofException</code> is set to {@code true}.
167     * @throws IOException if trying to read past the end of file.
168     */
169    @Override
170    public int read() throws IOException {
171        if (eof) {
172            throw new IOException("Read after end of file");
173        }
174        if (position == size) {
175            return doEndOfFile();
176        }
177        position++;
178        return processChar();
179    }
180
181    /**
182     * Read some characters into the specified array.
183     *
184     * @param chars The character array to read into
185     * @return The number of characters read or <code>-1</code>
186     * if the end of file has been reached and
187     * <code>throwEofException</code> is set to {@code false}.
188     * @throws EOFException if the end of file is reached and
189     * <code>throwEofException</code> is set to {@code true}.
190     * @throws IOException if trying to read past the end of file.
191     */
192    @Override
193    public int read(final char[] chars) throws IOException {
194        return read(chars, 0, chars.length);
195    }
196
197    /**
198     * Read the specified number characters into an array.
199     *
200     * @param chars The character array to read into.
201     * @param offset The offset to start reading characters into.
202     * @param length The number of characters to read.
203     * @return The number of characters read or <code>-1</code>
204     * if the end of file has been reached and
205     * <code>throwEofException</code> is set to {@code false}.
206     * @throws EOFException if the end of file is reached and
207     * <code>throwEofException</code> is set to {@code true}.
208     * @throws IOException if trying to read past the end of file.
209     */
210    @Override
211    public int read(final char[] chars, final int offset, final int length) throws IOException {
212        if (eof) {
213            throw new IOException("Read after end of file");
214        }
215        if (position == size) {
216            return doEndOfFile();
217        }
218        position += length;
219        int returnLength = length;
220        if (position > size) {
221            returnLength = length - (int)(position - size);
222            position = size;
223        }
224        processChars(chars, offset, returnLength);
225        return returnLength;
226    }
227
228    /**
229     * Reset the stream to the point when mark was last called.
230     *
231     * @throws UnsupportedOperationException if mark is not supported.
232     * @throws IOException If no position has been marked
233     * or the read limit has been exceed since the last position was
234     * marked.
235     */
236    @Override
237    public synchronized void reset() throws IOException {
238        if (!markSupported) {
239            throw new UnsupportedOperationException("Mark not supported");
240        }
241        if (mark < 0) {
242            throw new IOException("No position has been marked");
243        }
244        if (position > mark + readlimit) {
245            throw new IOException("Marked position [" + mark +
246                    "] is no longer valid - passed the read limit [" +
247                    readlimit + "]");
248        }
249        position = mark;
250        eof = false;
251    }
252
253    /**
254     * Skip a specified number of characters.
255     *
256     * @param numberOfChars The number of characters to skip.
257     * @return The number of characters skipped or <code>-1</code>
258     * if the end of file has been reached and
259     * <code>throwEofException</code> is set to {@code false}.
260     * @throws EOFException if the end of file is reached and
261     * <code>throwEofException</code> is set to {@code true}.
262     * @throws IOException if trying to read past the end of file.
263     */
264    @Override
265    public long skip(final long numberOfChars) throws IOException {
266        if (eof) {
267            throw new IOException("Skip after end of file");
268        }
269        if (position == size) {
270            return doEndOfFile();
271        }
272        position += numberOfChars;
273        long returnLength = numberOfChars;
274        if (position > size) {
275            returnLength = numberOfChars - (position - size);
276            position = size;
277        }
278        return returnLength;
279    }
280
281    /**
282     * Return a character value for the  <code>read()</code> method.
283     * <p>
284     * This implementation returns zero.
285     *
286     * @return This implementation always returns zero.
287     */
288    protected int processChar() {
289        // do nothing - overridable by subclass
290        return 0;
291    }
292
293    /**
294     * Process the characters for the <code>read(char[], offset, length)</code>
295     * method.
296     * <p>
297     * This implementation leaves the character array unchanged.
298     *
299     * @param chars The character array
300     * @param offset The offset to start at.
301     * @param length The number of characters.
302     */
303    protected void processChars(final char[] chars, final int offset, final int length) {
304        // do nothing - overridable by subclass
305    }
306
307    /**
308     * Handle End of File.
309     *
310     * @return <code>-1</code> if <code>throwEofException</code> is
311     * set to {@code false}
312     * @throws EOFException if <code>throwEofException</code> is set
313     * to {@code true}.
314     */
315    private int doEndOfFile() throws EOFException {
316        eof = true;
317        if (throwEofException) {
318            throw new EOFException();
319        }
320        return EOF;
321    }
322
323}