    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.InputStream;
    024
    025import org.apache.commons.io.IOUtils;
    026
    027/**
    028 * A lightweight {@link InputStream} that emulates a stream of a specified size.
    029 * <p>
    030 * This implementation provides a lightweight object for testing with an {@link InputStream} where the contents don't matter.
    031 * </p>
    032 * <p>
    033 * One use case would be for testing the handling of large {@link InputStream} as it can emulate that scenario without the overhead of actually processing large
    034 * numbers of bytes - significantly speeding up test execution times.
    035 * </p>
    036 * <p>
    037 * This implementation returns zero from the method that reads a byte and leaves the array unchanged in the read methods that are passed a byte array. If
    038 * alternative data is required the {@code processByte()} and {@code processBytes()} methods can be implemented to generate data, for example:
    039 * </p>
    040 *
    041 * <pre>
    042 *  public class TestInputStream extends NullInputStream {
    043 *
    044 *      public TestInputStream(int size) {
    045 *          super(size);
    046 *      }
    047 *
    048 *      protected int processByte() {
    049 *          return ... // return required value here
    050 *      }
    051 *
    052 *      protected void processBytes(byte[] bytes, int offset, int length) {
    053 *          for (int i = offset; i &lt; length; i++) {
    054 *              bytes[i] = ... // set array value here
    055 *          }
    056 *      }
    057 *  }
    058 * </pre>
    059 * <p>
    060 * This class is not thread-safe.
    061 * </p>
    062 *
    063 * @since 1.3
    064 */
    065public class NullInputStream extends AbstractInputStream {
    066
    067    /**
    068     * The singleton instance.
    069     *
    070     * <p>
    071     * Since instances hold state, call {@link #init()} to reuse.
    072     * </p>
    073     *
    074     * @since 2.12.0
    075     * @deprecated Not reusable without calling {@link #init()} to reset state.
    076     */
    077    @Deprecated
    078    public static final NullInputStream INSTANCE = new NullInputStream();
    079
    080    private final long size;
    081    private long position;
    082    private long mark = -1;
    083    private long readLimit;
    084    private final boolean throwEofException;
    085    private final boolean markSupported;
    086
    087    /**
    088     * Constructs an {@link InputStream} that emulates a size 0 stream which supports marking and does not throw EOFException.
    089     * <p>
    090     * This is an "empty" input stream.
    091     * </p>
    092     *
    093     * @since 2.7
    094     */
    095    public NullInputStream() {
    096        this(0, true, false);
    097    }
    098
    099    /**
    100     * Constructs an {@link InputStream} that emulates a specified size which supports marking and does not throw EOFException.
    101     *
    102     * @param size The size of the input stream to emulate.
    103     */
    104    public NullInputStream(final long size) {
    105        this(size, true, false);
    106    }
    107
    108    /**
    109     * Constructs an {@link InputStream} that emulates a specified size with option settings.
    110     *
    111     * @param size              The size of the input stream to emulate.
    112     * @param markSupported     Whether this instance will support the {@code mark()} functionality.
    113     * @param throwEofException Whether this implementation will throw an {@link EOFException} or return -1 when the end of file is reached.
    114     */
    115    public NullInputStream(final long size, final boolean markSupported, final boolean throwEofException) {
    116        this.size = size;
    117        this.markSupported = markSupported;
    118        this.throwEofException = throwEofException;
    119    }
    120
    121    @Override
    122    public int available() {
    123        if (isClosed()) {
    124            return 0;
    125        }
    126        final long avail = size - position;
    127        if (avail <= 0) {
    128            return 0;
    129        }
    130        if (avail > Integer.MAX_VALUE) {
    131            return Integer.MAX_VALUE;
    132        }
    133        return (int) avail;
    134    }
    135
    136    /**
    137     * Throws {@link EOFException} if {@code throwEofException} is enabled.
    138     *
    139     * @param message The {@link EOFException} message.
    140     * @throws EOFException Thrown if {@code throwEofException} is enabled.
    141     */
    142    private void checkThrowEof(final String message) throws EOFException {
    143        if (throwEofException) {
    144            throw new EOFException(message);
    145        }
    146    }
    147
    148    /**
    149     * Closes this input stream.
    150     *
    151     * @throws IOException If an error occurs.
    152     */
    153    @Override
    154    public void close() throws IOException {
    155        super.close();
    156        mark = -1;
    157    }
    158
    159    /**
    160     * Gets the current position.
    161     *
    162     * @return the current position.
    163     */
    164    public long getPosition() {
    165        return position;
    166    }
    167
    168    /**
    169     * Gets the size this {@link InputStream} emulates.
    170     *
    171     * @return The size of the input stream to emulate.
    172     */
    173    public long getSize() {
    174        return size;
    175    }
    176
    177    /**
    178     * Handles End of File.
    179     *
    180     * @return {@code -1} if {@code throwEofException} is set to {@code false}
    181     * @throws IOException if {@code throwEofException} is set to {@code true}.
    182     */
    183    private int handleEof() throws IOException {
    184        checkThrowEof("handleEof()");
    185        return EOF;
    186    }
    187
    188    /**
    189     * Initializes or re-initializes this instance for reuse.
    190     *
    191     * @return {@code this} instance.
    192     * @since 2.17.0
    193     */
    194    public NullInputStream init() {
    195        setClosed(false);
    196        position = 0;
    197        mark = -1;
    198        readLimit = 0;
    199        return this;
    200    }
    201
    202    /**
    203     * Marks the current position.
    204     *
    205     * @param readLimit The number of bytes before this marked position is invalid.
    206     * @throws UnsupportedOperationException if mark is not supported.
    207     */
    208    @Override
    209    public synchronized void mark(final int readLimit) {
    210        if (!markSupported) {
    211            throw UnsupportedOperationExceptions.mark();
    212        }
    213        mark = position;
    214        this.readLimit = readLimit;
    215    }
    216
    217    /**
    218     * Tests whether <em>mark</em> is supported.
    219     *
    220     * @return Whether <em>mark</em> is supported or not.
    221     */
    222    @Override
    223    public boolean markSupported() {
    224        return markSupported;
    225    }
    226
    227    /**
    228     * Returns a byte value for the {@code read()} method.
    229     * <p>
    230     * This implementation returns zero.
    231     * </p>
    232     *
    233     * @return This implementation always returns zero.
    234     */
    235    protected int processByte() {
    236        // do nothing - overridable by subclass
    237        return 0;
    238    }
    239
    240    /**
    241     * Processes the bytes for the {@code read(byte[], offset, length)} method.
    242     * <p>
    243     * This implementation leaves the byte array unchanged.
    244     * </p>
    245     *
    246     * @param bytes  The byte array, never {@code null}.
    247     * @param offset The offset to start at, always non-negative.
    248     * @param length The number of bytes to process, always non-negative and at most {@code bytes.length - offset}.
    249     */
    250    protected void processBytes(final byte[] bytes, final int offset, final int length) {
    251        // do nothing - overridable by subclass
    252    }
    253
    254    /**
    255     * Reads a byte.
    256     *
    257     * @return Either The byte value returned by {@code processByte()} or {@code -1} if the end of file has been reached and {@code throwEofException} is set to
    258     *         {@code false}.
    259     * @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}.
    260     * @throws IOException  if trying to read past the end of file.
    261     */
    262    @Override
    263    public int read() throws IOException {
    264        checkOpen();
    265        if (position == size) {
    266            return handleEof();
    267        }
    268        position++;
    269        return processByte();
    270    }
    271
    272    /**
    273     * Reads some bytes into the specified array.
    274     *
    275     * @param bytes The byte array to read into.
    276     * @return The number of bytes read or {@code -1} if the end of file has been reached and {@code throwEofException} is set to {@code false}.
    277     * @throws NullPointerException if the byte array is {@code null}.
    278     * @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}.
    279     * @throws IOException  if trying to read past the end of file.
    280     */
    281    @Override
    282    public int read(final byte[] bytes) throws IOException {
    283        return read(bytes, 0, bytes.length);
    284    }
    285
    286    /**
    287     * Reads the specified number bytes into an array.
    288     *
    289     * @param bytes  The byte array to read into.
    290     * @param offset The offset to start reading bytes into.
    291     * @param length The number of bytes to read.
    292     * @return The number of bytes read or {@code -1} if the end of file has been reached and {@code throwEofException} is set to {@code false}.
    293     * @throws NullPointerException if the byte array is {@code null}.
    294     * @throws IndexOutOfBoundsException if {@code offset} or {@code length} are negative, or if {@code offset + length} is greater than {@code bytes.length}.
    295     * @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}.
    296     * @throws IOException  if trying to read past the end of file.
    297     */
    298    @Override
    299    public int read(final byte[] bytes, final int offset, final int length) throws IOException {
    300        IOUtils.checkFromIndexSize(bytes, offset, length);
    301        if (length == 0) {
    302            return 0;
    303        }
    304        checkOpen();
    305        if (position == size) {
    306            return handleEof();
    307        }
    308        position += length;
    309        int returnLength = length;
    310        if (position > size) {
    311            returnLength = length - (int) (position - size);
    312            position = size;
    313        }
    314        processBytes(bytes, offset, returnLength);
    315        return returnLength;
    316    }
    317
    318    /**
    319     * Resets the stream to the point when mark was last called.
    320     *
    321     * @throws UnsupportedOperationException if mark is not supported.
    322     * @throws IOException                   If no position has been marked or the read limit has been exceeded since the last position was marked.
    323     */
    324    @Override
    325    public synchronized void reset() throws IOException {
    326        if (!markSupported) {
    327            throw UnsupportedOperationExceptions.reset();
    328        }
    329        if (mark < 0) {
    330            throw new IOException("No position has been marked");
    331        }
    332        if (position > mark + readLimit) {
    333            throw new IOException("Marked position [" + mark + "] is no longer valid - passed the read limit [" + readLimit + "]");
    334        }
    335        position = mark;
    336        setClosed(false);
    337    }
    338
    339    /**
    340     * Skips a specified number of bytes.
    341     *
    342     * @param numberOfBytes The number of bytes to skip.
    343     * @return The number of bytes skipped or {@code -1} if the end of file has been reached and {@code throwEofException} is set to {@code false}.
    344     * @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}.
    345     * @throws IOException  if trying to read past the end of file.
    346     */
    347    @Override
    348    public long skip(final long numberOfBytes) throws IOException {
    349        if (isClosed()) {
    350            checkThrowEof("skip(long)");
    351            return EOF;
    352        }
    353        if (position == size) {
    354            return handleEof();
    355        }
    356        position += numberOfBytes;
    357        long returnLength = numberOfBytes;
    358        if (position > size) {
    359            returnLength = numberOfBytes - (position - size);
    360            position = size;
    361        }
    362        return returnLength;
    363    }
    364
    365}