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