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