    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    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()} 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 UnsupportedOperationExceptions.mark();
    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()}
    175     * or {@code -1} if the end of file has been reached and
    176     * {@code throwEofException} is set to {@code false}.
    177     * @throws EOFException if the end of file is reached and
    178     * {@code throwEofException} 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}
    198     * if the end of file has been reached and
    199     * {@code throwEofException} is set to {@code false}.
    200     * @throws EOFException if the end of file is reached and
    201     * {@code throwEofException} 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}
    216     * if the end of file has been reached and
    217     * {@code throwEofException} is set to {@code false}.
    218     * @throws EOFException if the end of file is reached and
    219     * {@code throwEofException} 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 UnsupportedOperationExceptions.reset();
    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}
    270     * if the end of file has been reached and
    271     * {@code throwEofException} is set to {@code false}.
    272     * @throws EOFException if the end of file is reached and
    273     * {@code throwEofException} 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()} 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)}
    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} if {@code throwEofException} is
    325     * set to {@code false}
    326     * @throws EOFException if {@code throwEofException} 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}