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