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