View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.io.input;
18  
19  import static org.apache.commons.io.IOUtils.EOF;
20  
21  import java.io.EOFException;
22  import java.io.IOException;
23  import java.io.InputStream;
24  
25  /**
26   * A lightweight {@link InputStream} that emulates a stream of a specified size.
27   * <p>
28   * This implementation provides a lightweight object for testing with an {@link InputStream} where the contents don't matter.
29   * </p>
30   * <p>
31   * One use case would be for testing the handling of large {@link InputStream} as it can emulate that scenario without the overhead of actually processing large
32   * numbers of bytes - significantly speeding up test execution times.
33   * </p>
34   * <p>
35   * This implementation returns zero from the method that reads a byte and leaves the array unchanged in the read methods that are passed a byte array. If
36   * alternative data is required the {@code processByte()} and {@code processBytes()} methods can be implemented to generate data, for example:
37   * </p>
38   *
39   * <pre>
40   *  public class TestInputStream extends NullInputStream {
41   *
42   *      public TestInputStream(int size) {
43   *          super(size);
44   *      }
45   *
46   *      protected int processByte() {
47   *          return ... // return required value here
48   *      }
49   *
50   *      protected void processBytes(byte[] bytes, int offset, int length) {
51   *          for (int i = offset; i &lt; length; i++) {
52   *              bytes[i] = ... // set array value here
53   *          }
54   *      }
55   *  }
56   * </pre>
57   * <p>
58   * This class is not thread-safe.
59   * </p>
60   *
61   * @since 1.3
62   */
63  public class NullInputStream extends AbstractInputStream {
64  
65      /**
66       * The singleton instance.
67       *
68       * <p>
69       * Since instances hold state, call {@link #init()} to reuse.
70       * </p>
71       *
72       * @since 2.12.0
73       * @deprecated Not reusable without calling {@link #init()} to reset state.
74       */
75      @Deprecated
76      public static final NullInputStream INSTANCE = new NullInputStream();
77  
78      private final long size;
79      private long position;
80      private long mark = -1;
81      private long readLimit;
82      private final boolean throwEofException;
83      private final boolean markSupported;
84  
85      /**
86       * Constructs an {@link InputStream} that emulates a size 0 stream which supports marking and does not throw EOFException.
87       * <p>
88       * This is an "empty" input stream.
89       * </p>
90       *
91       * @since 2.7
92       */
93      public NullInputStream() {
94          this(0, true, false);
95      }
96  
97      /**
98       * Constructs an {@link InputStream} that emulates a specified size which supports marking and does not throw EOFException.
99       *
100      * @param size The size of the input stream to emulate.
101      */
102     public NullInputStream(final long size) {
103         this(size, true, false);
104     }
105 
106     /**
107      * Constructs an {@link InputStream} that emulates a specified size with option settings.
108      *
109      * @param size              The size of the input stream to emulate.
110      * @param markSupported     Whether this instance will support the {@code mark()} functionality.
111      * @param throwEofException Whether this implementation will throw an {@link EOFException} or return -1 when the end of file is reached.
112      */
113     public NullInputStream(final long size, final boolean markSupported, final boolean throwEofException) {
114         this.size = size;
115         this.markSupported = markSupported;
116         this.throwEofException = throwEofException;
117     }
118 
119     @Override
120     public int available() {
121         if (isClosed()) {
122             return 0;
123         }
124         final long avail = size - position;
125         if (avail <= 0) {
126             return 0;
127         }
128         if (avail > Integer.MAX_VALUE) {
129             return Integer.MAX_VALUE;
130         }
131         return (int) avail;
132     }
133 
134     /**
135      * Throws {@link EOFException} if {@code throwEofException} is enabled.
136      *
137      * @param message The {@link EOFException} message.
138      * @throws EOFException Thrown if {@code throwEofException} is enabled.
139      */
140     private void checkThrowEof(final String message) throws EOFException {
141         if (throwEofException) {
142             throw new EOFException(message);
143         }
144     }
145 
146     /**
147      * Closes this input stream.
148      *
149      * @throws IOException If an error occurs.
150      */
151     @Override
152     public void close() throws IOException {
153         super.close();
154         mark = -1;
155     }
156 
157     /**
158      * Gets the current position.
159      *
160      * @return the current position.
161      */
162     public long getPosition() {
163         return position;
164     }
165 
166     /**
167      * Gets the size this {@link InputStream} emulates.
168      *
169      * @return The size of the input stream to emulate.
170      */
171     public long getSize() {
172         return size;
173     }
174 
175     /**
176      * Handles End of File.
177      *
178      * @return {@code -1} if {@code throwEofException} is set to {@code false}
179      * @throws IOException if {@code throwEofException} is set to {@code true}.
180      */
181     private int handleEof() throws IOException {
182         checkThrowEof("handleEof()");
183         return EOF;
184     }
185 
186     /**
187      * Initializes or re-initializes this instance for reuse.
188      *
189      * @return this instance.
190      * @since 2.17.0
191      */
192     public NullInputStream init() {
193         setClosed(false);
194         position = 0;
195         mark = -1;
196         readLimit = 0;
197         return this;
198     }
199 
200     /**
201      * Marks the current position.
202      *
203      * @param readLimit The number of bytes before this marked position is invalid.
204      * @throws UnsupportedOperationException if mark is not supported.
205      */
206     @Override
207     public synchronized void mark(final int readLimit) {
208         if (!markSupported) {
209             throw UnsupportedOperationExceptions.mark();
210         }
211         mark = position;
212         this.readLimit = readLimit;
213     }
214 
215     /**
216      * Tests whether <em>mark</em> is supported.
217      *
218      * @return Whether <em>mark</em> is supported or not.
219      */
220     @Override
221     public boolean markSupported() {
222         return markSupported;
223     }
224 
225     /**
226      * Returns a byte value for the {@code read()} method.
227      * <p>
228      * This implementation returns zero.
229      * </p>
230      *
231      * @return This implementation always returns zero.
232      */
233     protected int processByte() {
234         // do nothing - overridable by subclass
235         return 0;
236     }
237 
238     /**
239      * Processes the bytes for the {@code read(byte[], offset, length)} method.
240      * <p>
241      * This implementation leaves the byte array unchanged.
242      * </p>
243      *
244      * @param bytes  The byte array
245      * @param offset The offset to start at.
246      * @param length The number of bytes.
247      */
248     protected void processBytes(final byte[] bytes, final int offset, final int length) {
249         // do nothing - overridable by subclass
250     }
251 
252     /**
253      * Reads a byte.
254      *
255      * @return Either The byte value returned by {@code processByte()} or {@code -1} if the end of file has been reached and {@code throwEofException} is set to
256      *         {@code false}.
257      * @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}.
258      * @throws IOException  if trying to read past the end of file.
259      */
260     @Override
261     public int read() throws IOException {
262         checkOpen();
263         if (position == size) {
264             return handleEof();
265         }
266         position++;
267         return processByte();
268     }
269 
270     /**
271      * Reads some bytes into the specified array.
272      *
273      * @param bytes The byte array to read into
274      * @return The number of bytes read or {@code -1} if the end of file has been reached and {@code throwEofException} is set to {@code false}.
275      * @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}.
276      * @throws IOException  if trying to read past the end of file.
277      */
278     @Override
279     public int read(final byte[] bytes) throws IOException {
280         return read(bytes, 0, bytes.length);
281     }
282 
283     /**
284      * Reads the specified number bytes into an array.
285      *
286      * @param bytes  The byte array to read into.
287      * @param offset The offset to start reading bytes into.
288      * @param length The number of bytes to read.
289      * @return The number of bytes read or {@code -1} if the end of file has been reached and {@code throwEofException} is set to {@code false}.
290      * @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}.
291      * @throws IOException  if trying to read past the end of file.
292      */
293     @Override
294     public int read(final byte[] bytes, final int offset, final int length) throws IOException {
295         if (bytes.length == 0 || length == 0) {
296             return 0;
297         }
298         checkOpen();
299         if (position == size) {
300             return handleEof();
301         }
302         position += length;
303         int returnLength = length;
304         if (position > size) {
305             returnLength = length - (int) (position - size);
306             position = size;
307         }
308         processBytes(bytes, offset, returnLength);
309         return returnLength;
310     }
311 
312     /**
313      * Resets the stream to the point when mark was last called.
314      *
315      * @throws UnsupportedOperationException if mark is not supported.
316      * @throws IOException                   If no position has been marked or the read limit has been exceeded since the last position was marked.
317      */
318     @Override
319     public synchronized void reset() throws IOException {
320         if (!markSupported) {
321             throw UnsupportedOperationExceptions.reset();
322         }
323         if (mark < 0) {
324             throw new IOException("No position has been marked");
325         }
326         if (position > mark + readLimit) {
327             throw new IOException("Marked position [" + mark + "] is no longer valid - passed the read limit [" + readLimit + "]");
328         }
329         position = mark;
330         setClosed(false);
331     }
332 
333     /**
334      * Skips a specified number of bytes.
335      *
336      * @param numberOfBytes The number of bytes to skip.
337      * @return The number of bytes skipped or {@code -1} if the end of file has been reached and {@code throwEofException} is set to {@code false}.
338      * @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}.
339      * @throws IOException  if trying to read past the end of file.
340      */
341     @Override
342     public long skip(final long numberOfBytes) throws IOException {
343         if (isClosed()) {
344             checkThrowEof("skip(long)");
345             return EOF;
346         }
347         if (position == size) {
348             return handleEof();
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 }