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