    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 <i>mark</i> is supported.
    186     *
    187     * @return Whether <i>mark</i> 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}