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     * Create 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     * Create 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     * Create 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     * Return 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     * Close 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     * Handle 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     * Return 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     * Mark 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     * Indicates 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     * Return 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     * Process the bytes for the {@code read(byte[], offset, length)}
225     * method.
226     * <p>
227     * This implementation leaves the byte array unchanged.
228     *
229     * @param bytes The byte array
230     * @param offset The offset to start at.
231     * @param length The number of bytes.
232     */
233    protected void processBytes(final byte[] bytes, final int offset, final int length) {
234        // do nothing - overridable by subclass
235    }
236
237    /**
238     * Read a byte.
239     *
240     * @return Either The byte value returned by {@code processByte()}
241     * or {@code -1} if the end of file has been reached and
242     * {@code throwEofException} is set to {@code false}.
243     * @throws EOFException if the end of file is reached and
244     * {@code throwEofException} is set to {@code true}.
245     * @throws IOException if trying to read past the end of file.
246     */
247    @Override
248    public int read() throws IOException {
249        if (eof) {
250            throw new IOException("Read after end of file");
251        }
252        if (position == size) {
253            return doEndOfFile();
254        }
255        position++;
256        return processByte();
257    }
258
259    /**
260     * Read some bytes into the specified array.
261     *
262     * @param bytes The byte array to read into
263     * @return The number of bytes read or {@code -1}
264     * if the end of file has been reached and
265     * {@code throwEofException} is set to {@code false}.
266     * @throws EOFException if the end of file is reached and
267     * {@code throwEofException} is set to {@code true}.
268     * @throws IOException if trying to read past the end of file.
269     */
270    @Override
271    public int read(final byte[] bytes) throws IOException {
272        return read(bytes, 0, bytes.length);
273    }
274
275    /**
276     * Read the specified number bytes into an array.
277     *
278     * @param bytes The byte array to read into.
279     * @param offset The offset to start reading bytes into.
280     * @param length The number of bytes to read.
281     * @return The number of bytes read or {@code -1}
282     * if the end of file has been reached and
283     * {@code throwEofException} is set to {@code false}.
284     * @throws EOFException if the end of file is reached and
285     * {@code throwEofException} is set to {@code true}.
286     * @throws IOException if trying to read past the end of file.
287     */
288    @Override
289    public int read(final byte[] bytes, final int offset, final int length) throws IOException {
290        if (eof) {
291            throw new IOException("Read after end of file");
292        }
293        if (position == size) {
294            return doEndOfFile();
295        }
296        position += length;
297        int returnLength = length;
298        if (position > size) {
299            returnLength = length - (int)(position - size);
300            position = size;
301        }
302        processBytes(bytes, offset, returnLength);
303        return returnLength;
304    }
305
306    /**
307     * Reset the stream to the point when mark was last called.
308     *
309     * @throws UnsupportedOperationException if mark is not supported.
310     * @throws IOException If no position has been marked
311     * or the read limit has been exceeded since the last position was
312     * 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 +
324                    "] is no longer valid - passed the read limit [" +
325                    readlimit + "]");
326        }
327        position = mark;
328        eof = false;
329    }
330
331    /**
332     * Skip a specified number of bytes.
333     *
334     * @param numberOfBytes The number of bytes to skip.
335     * @return The number of bytes skipped or {@code -1}
336     * if the end of file has been reached and
337     * {@code throwEofException} is set to {@code false}.
338     * @throws EOFException if the end of file is reached and
339     * {@code throwEofException} is set to {@code true}.
340     * @throws IOException if trying to read past the end of file.
341     */
342    @Override
343    public long skip(final long numberOfBytes) throws IOException {
344        if (eof) {
345            throw new IOException("Skip after end of file");
346        }
347        if (position == size) {
348            return doEndOfFile();
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}