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