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