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