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