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    *      http://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 light weight {@link InputStream} that emulates a stream of a specified size.
27   * <p>
28   * This implementation provides a light weight 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   *
58   * @since 1.3
59   */
60  public class NullInputStream extends AbstractInputStream {
61  
62      /**
63       * The singleton instance.
64       *
65       * <p>
66       * Since instances hold state, call {@link #init()} to reuse.
67       * </p>
68       *
69       * @since 2.12.0
70       * @deprecated Not reusable without calling {@link #init()} to reset state.
71       */
72      @Deprecated
73      public static final NullInputStream INSTANCE = new NullInputStream();
74  
75      private final long size;
76      private long position;
77      private long mark = -1;
78      private long readLimit;
79      private final boolean throwEofException;
80      private final boolean markSupported;
81  
82      /**
83       * Constructs an {@link InputStream} that emulates a size 0 stream which supports marking and does not throw EOFException.
84       * <p>
85       * This is an "empty" input stream.
86       * </p>
87       *
88       * @since 2.7
89       */
90      public NullInputStream() {
91          this(0, true, false);
92      }
93  
94      /**
95       * Constructs an {@link InputStream} that emulates a specified size which supports marking and does not throw EOFException.
96       *
97       * @param size The size of the input stream to emulate.
98       */
99      public NullInputStream(final long size) {
100         this(size, true, false);
101     }
102 
103     /**
104      * Constructs an {@link InputStream} that emulates a specified size with option settings.
105      *
106      * @param size              The size of the input stream to emulate.
107      * @param markSupported     Whether this instance will support the {@code mark()} functionality.
108      * @param throwEofException Whether this implementation will throw an {@link EOFException} or return -1 when the end of file is reached.
109      */
110     public NullInputStream(final long size, final boolean markSupported, final boolean throwEofException) {
111         this.size = size;
112         this.markSupported = markSupported;
113         this.throwEofException = throwEofException;
114     }
115 
116     @Override
117     public int available() {
118         if (isClosed()) {
119             return 0;
120         }
121         final long avail = size - position;
122         if (avail <= 0) {
123             return 0;
124         }
125         if (avail > Integer.MAX_VALUE) {
126             return Integer.MAX_VALUE;
127         }
128         return (int) avail;
129     }
130 
131     /**
132      * Throws {@link EOFException} if {@code throwEofException} is enabled.
133      *
134      * @param message The {@link EOFException} message.
135      * @throws EOFException Thrown if {@code throwEofException} is enabled.
136      */
137     private void checkThrowEof(final String message) throws EOFException {
138         if (throwEofException) {
139             throw new EOFException(message);
140         }
141     }
142 
143     /**
144      * Closes this input stream.
145      *
146      * @throws IOException If an error occurs.
147      */
148     @Override
149     public void close() throws IOException {
150         super.close();
151         mark = -1;
152     }
153 
154     /**
155      * Gets the current position.
156      *
157      * @return the current position.
158      */
159     public long getPosition() {
160         return position;
161     }
162 
163     /**
164      * Gets the size this {@link InputStream} emulates.
165      *
166      * @return The size of the input stream to emulate.
167      */
168     public long getSize() {
169         return size;
170     }
171 
172     /**
173      * Handles End of File.
174      *
175      * @return {@code -1} if {@code throwEofException} is set to {@code false}
176      * @throws IOException if {@code throwEofException} is set to {@code true}.
177      */
178     private int handleEof() throws IOException {
179         checkThrowEof("handleEof()");
180         return EOF;
181     }
182 
183     /**
184      * Initializes or re-initializes this instance for reuse.
185      *
186      * @return this instance.
187      * @since 2.17.0
188      */
189     public NullInputStream init() {
190         setClosed(false);
191         position = 0;
192         mark = -1;
193         readLimit = 0;
194         return this;
195     }
196 
197     /**
198      * Marks the current position.
199      *
200      * @param readLimit The number of bytes before this marked position is invalid.
201      * @throws UnsupportedOperationException if mark is not supported.
202      */
203     @Override
204     public synchronized void mark(final int readLimit) {
205         if (!markSupported) {
206             throw UnsupportedOperationExceptions.mark();
207         }
208         mark = position;
209         this.readLimit = readLimit;
210     }
211 
212     /**
213      * Tests whether <em>mark</em> is supported.
214      *
215      * @return Whether <em>mark</em> is supported or not.
216      */
217     @Override
218     public boolean markSupported() {
219         return markSupported;
220     }
221 
222     /**
223      * Returns a byte value for the {@code read()} method.
224      * <p>
225      * This implementation returns zero.
226      *
227      * @return This implementation always returns zero.
228      */
229     protected int processByte() {
230         // do nothing - overridable by subclass
231         return 0;
232     }
233 
234     /**
235      * Processes the bytes for the {@code read(byte[], offset, length)} method.
236      * <p>
237      * This implementation leaves the byte array unchanged.
238      * </p>
239      *
240      * @param bytes  The byte array
241      * @param offset The offset to start at.
242      * @param length The number of bytes.
243      */
244     protected void processBytes(final byte[] bytes, final int offset, final int length) {
245         // do nothing - overridable by subclass
246     }
247 
248     /**
249      * Reads a byte.
250      *
251      * @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
252      *         {@code false}.
253      * @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}.
254      * @throws IOException  if trying to read past the end of file.
255      */
256     @Override
257     public int read() throws IOException {
258         checkOpen();
259         if (position == size) {
260             return handleEof();
261         }
262         position++;
263         return processByte();
264     }
265 
266     /**
267      * Reads some bytes into the specified array.
268      *
269      * @param bytes The byte array to read into
270      * @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}.
271      * @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}.
272      * @throws IOException  if trying to read past the end of file.
273      */
274     @Override
275     public int read(final byte[] bytes) throws IOException {
276         return read(bytes, 0, bytes.length);
277     }
278 
279     /**
280      * Reads the specified number bytes into an array.
281      *
282      * @param bytes  The byte array to read into.
283      * @param offset The offset to start reading bytes into.
284      * @param length The number of bytes to read.
285      * @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}.
286      * @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}.
287      * @throws IOException  if trying to read past the end of file.
288      */
289     @Override
290     public int read(final byte[] bytes, final int offset, final int length) throws IOException {
291         if (bytes.length == 0 || length == 0) {
292             return 0;
293         }
294         checkOpen();
295         if (position == size) {
296             return handleEof();
297         }
298         position += length;
299         int returnLength = length;
300         if (position > size) {
301             returnLength = length - (int) (position - size);
302             position = size;
303         }
304         processBytes(bytes, offset, returnLength);
305         return returnLength;
306     }
307 
308     /**
309      * Resets the stream to the point when mark was last called.
310      *
311      * @throws UnsupportedOperationException if mark is not supported.
312      * @throws IOException                   If no position has been marked or the read limit has been exceeded since the last position was marked.
313      */
314     @Override
315     public synchronized void reset() throws IOException {
316         if (!markSupported) {
317             throw UnsupportedOperationExceptions.reset();
318         }
319         if (mark < 0) {
320             throw new IOException("No position has been marked");
321         }
322         if (position > mark + readLimit) {
323             throw new IOException("Marked position [" + mark + "] is no longer valid - passed the read limit [" + readLimit + "]");
324         }
325         position = mark;
326         setClosed(false);
327     }
328 
329     /**
330      * Skips a specified number of bytes.
331      *
332      * @param numberOfBytes The number of bytes to skip.
333      * @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}.
334      * @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}.
335      * @throws IOException  if trying to read past the end of file.
336      */
337     @Override
338     public long skip(final long numberOfBytes) throws IOException {
339         if (isClosed()) {
340             checkThrowEof("skip(long)");
341             return EOF;
342         }
343         if (position == size) {
344             return handleEof();
345         }
346         position += numberOfBytes;
347         long returnLength = numberOfBytes;
348         if (position > size) {
349             returnLength = numberOfBytes - (position - size);
350             position = size;
351         }
352         return returnLength;
353     }
354 
355 }