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