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