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.tika.io;
18  
19  import java.io.EOFException;
20  import java.io.IOException;
21  import java.io.InputStream;
22  
23  /**
24   * A functional, light weight {@link InputStream} that emulates
25   * a stream of a specified size.
26   * <p>
27   * This implementation provides a light weight
28   * object for testing with an {@link InputStream}
29   * where the contents don't matter.
30   * <p>
31   * One use case would be for testing the handling of
32   * large {@link InputStream} as it can emulate that
33   * scenario without the overhead of actually processing
34   * large numbers of bytes - significantly speeding up
35   * test execution times.
36   * <p>
37   * This implementation returns zero from the method that
38   * reads a byte and leaves the array unchanged in the read
39   * methods that are passed a byte array.
40   * If alternative data is required the <code>processByte()</code> and
41   * <code>processBytes()</code> methods can be implemented to generate
42   * data, for example:
43   *
44   * <pre>
45   *  public class TestInputStream extends NullInputStream {
46   *      public TestInputStream(int size) {
47   *          super(size);
48   *      }
49   *      protected int processByte() {
50   *          return ... // return required value here
51   *      }
52   *      protected void processBytes(byte[] bytes, int offset, int length) {
53   *          for (int i = offset; i < length; i++) {
54   *              bytes[i] = ... // set array value here
55   *          }
56   *      }
57   *  }
58   * </pre>
59   *
60   * @since Apache Tika 0.4, copied from Commons IO 1.4
61   */
62  public class NullInputStream extends InputStream {
63  
64      private final long size;
65      private long position;
66      private long mark = -1;
67      private long readlimit;
68      private boolean eof;
69      private final boolean throwEofException;
70      private final boolean markSupported;
71  
72      /**
73       * Create an {@link InputStream} that emulates a specified size
74       * which supports marking and does not throw EOFException.
75       *
76       * @param size The size of the input stream to emulate.
77       */
78      public NullInputStream(long size) {
79         this(size, true, false);
80      }
81  
82      /**
83       * Create an {@link InputStream} that emulates a specified
84       * size with option settings.
85       *
86       * @param size The size of the input stream to emulate.
87       * @param markSupported Whether this instance will support
88       * the <code>mark()</code> functionality.
89       * @param throwEofException Whether this implementation
90       * will throw an {@link EOFException} or return -1 when the
91       * end of file is reached.
92       */
93      public NullInputStream(long size, boolean markSupported, boolean throwEofException) {
94         this.size = size;
95         this.markSupported = markSupported;
96         this.throwEofException = throwEofException;
97      }
98  
99      /**
100      * Return the current position.
101      *
102      * @return the current position.
103      */
104     public long getPosition() {
105         return position;
106     }
107 
108     /**
109      * Return the size this {@link InputStream} emulates.
110      *
111      * @return The size of the input stream to emulate.
112      */
113     public long getSize() {
114         return size;
115     }
116 
117     /**
118      * Return the number of bytes that can be read.
119      *
120      * @return The number of bytes that can be read.
121      */
122     @Override
123     public int available() {
124         long avail = size - position;
125         if (avail <= 0) {
126             return 0;
127         } else if (avail > Integer.MAX_VALUE) {
128             return Integer.MAX_VALUE;
129         } else {
130             return (int)avail;
131         }
132     }
133 
134     /**
135      * Close this input stream - resets the internal state to
136      * the initial values.
137      *
138      * @throws IOException If an error occurs.
139      */
140     @Override
141     public void close() throws IOException {
142         eof = false;
143         position = 0;
144         mark = -1;
145     }
146 
147     /**
148      * Mark the current position.
149      *
150      * @param readlimit The number of bytes before this marked position
151      * is invalid.
152      * @throws UnsupportedOperationException if mark is not supported.
153      */
154     @Override
155     public synchronized void mark(int readlimit) {
156         if (!markSupported) {
157             throw new UnsupportedOperationException("Mark not supported");
158         }
159         mark = position;
160         this.readlimit = readlimit;
161     }
162 
163     /**
164      * Indicates whether <i>mark</i> is supported.
165      *
166      * @return Whether <i>mark</i> is supported or not.
167      */
168     @Override
169     public boolean markSupported() {
170         return markSupported;
171     }
172 
173     /**
174      * Read a byte.
175      *
176      * @return Either The byte value returned by <code>processByte()</code>
177      * or <code>-1</code> if the end of file has been reached and
178      * <code>throwEofException</code> is set to <code>false</code>.
179      * @throws EOFException if the end of file is reached and
180      * <code>throwEofException</code> is set to <code>true</code>.
181      * @throws IOException if trying to read past the end of file.
182      */
183     @Override
184     public int read() throws IOException {
185         if (eof) {
186             throw new IOException("Read after end of file");
187         }
188         if (position == size) {
189             return doEndOfFile();
190         }
191         position++;
192         return processByte();
193     }
194 
195     /**
196      * Read some bytes into the specified array.
197      *
198      * @param bytes The byte array to read into
199      * @return The number of bytes read or <code>-1</code>
200      * if the end of file has been reached and
201      * <code>throwEofException</code> is set to <code>false</code>.
202      * @throws EOFException if the end of file is reached and
203      * <code>throwEofException</code> is set to <code>true</code>.
204      * @throws IOException if trying to read past the end of file.
205      */
206     @Override
207     public int read(byte[] bytes) throws IOException {
208         return read(bytes, 0, bytes.length);
209     }
210 
211     /**
212      * Read the specified number bytes into an array.
213      *
214      * @param bytes The byte array to read into.
215      * @param offset The offset to start reading bytes into.
216      * @param length The number of bytes to read.
217      * @return The number of bytes read or <code>-1</code>
218      * if the end of file has been reached and
219      * <code>throwEofException</code> is set to <code>false</code>.
220      * @throws EOFException if the end of file is reached and
221      * <code>throwEofException</code> is set to <code>true</code>.
222      * @throws IOException if trying to read past the end of file.
223      */
224     @Override
225     public int read(byte[] bytes, int offset, int length) throws IOException {
226         if (eof) {
227             throw new IOException("Read after end of file");
228         }
229         if (position == size) {
230             return doEndOfFile();
231         }
232         position += length;
233         int returnLength = length;
234         if (position > size) {
235             returnLength = length - (int)(position - size);
236             position = size;
237         }
238         processBytes(bytes, offset, returnLength);
239         return returnLength;
240     }
241 
242     /**
243      * Reset the stream to the point when mark was last called.
244      *
245      * @throws UnsupportedOperationException if mark is not supported.
246      * @throws IOException If no position has been marked
247      * or the read limit has been exceed since the last position was
248      * marked.
249      */
250     @Override
251     public synchronized void reset() throws IOException {
252         if (!markSupported) {
253             throw new UnsupportedOperationException("Mark not supported");
254         }
255         if (mark < 0) {
256             throw new IOException("No position has been marked");
257         }
258         if (position > (mark + readlimit)) {
259             throw new IOException("Marked position [" + mark +
260                     "] is no longer valid - passed the read limit [" +
261                     readlimit + "]");
262         }
263         position = mark;
264         eof = false;
265     }
266 
267     /**
268      * Skip a specified number of bytes.
269      *
270      * @param numberOfBytes The number of bytes to skip.
271      * @return The number of bytes skipped or <code>-1</code>
272      * if the end of file has been reached and
273      * <code>throwEofException</code> is set to <code>false</code>.
274      * @throws EOFException if the end of file is reached and
275      * <code>throwEofException</code> is set to <code>true</code>.
276      * @throws IOException if trying to read past the end of file.
277      */
278     @Override
279     public long skip(long numberOfBytes) throws IOException {
280         if (eof) {
281             throw new IOException("Skip after end of file");
282         }
283         if (position == size) {
284             return doEndOfFile();
285         }
286         position += numberOfBytes;
287         long returnLength = numberOfBytes;
288         if (position > size) {
289             returnLength = numberOfBytes - (position - size);
290             position = size;
291         }
292         return returnLength;
293     }
294 
295     /**
296      * Return a byte value for the  <code>read()</code> method.
297      * <p>
298      * This implementation returns zero.
299      *
300      * @return This implementation always returns zero.
301      */
302     protected int processByte() {
303         // do nothing - overridable by subclass
304         return 0;
305     }
306 
307     /**
308      * Process the bytes for the <code>read(byte[], offset, length)</code>
309      * method.
310      * <p>
311      * This implementation leaves the byte array unchanged.
312      *
313      * @param bytes The byte array
314      * @param offset The offset to start at.
315      * @param length The number of bytes.
316      */
317     protected void processBytes(byte[] bytes, int offset, int length) {
318         // do nothing - overridable by subclass
319     }
320 
321     /**
322      * Handle End of File.
323      *
324      * @return <code>-1</code> if <code>throwEofException</code> is
325      * set to <code>false</code>
326      * @throws EOFException if <code>throwEofException</code> is set
327      * to <code>true</code>.
328      */
329     private int doEndOfFile() throws EOFException {
330         eof = true;
331         if (throwEofException) {
332             throw new EOFException();
333         }
334         return -1;
335     }
336 
337 }