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