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 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 Commons IO 1.3
61 * @version $Revision: 463529 $
62 */
63 public class NullInputStream extends InputStream {
64
65 private long size;
66 private long position;
67 private long mark = -1;
68 private long readlimit;
69 private boolean eof;
70 private boolean throwEofException;
71 private boolean markSupported;
72
73 /**
74 * Create an {@link InputStream} that emulates a specified size
75 * which supports marking and does not throw EOFException.
76 *
77 * @param size The size of the input stream to emulate.
78 */
79 public NullInputStream(long size) {
80 this(size, true, false);
81 }
82
83 /**
84 * Create an {@link InputStream} that emulates a specified
85 * size with option settings.
86 *
87 * @param size The size of the input stream to emulate.
88 * @param markSupported Whether this instance will support
89 * the <code>mark()</code> functionality.
90 * @param throwEofException Whether this implementation
91 * will throw an {@link EOFException} or return -1 when the
92 * end of file is reached.
93 */
94 public NullInputStream(long size, boolean markSupported, boolean throwEofException) {
95 this.size = size;
96 this.markSupported = markSupported;
97 this.throwEofException = throwEofException;
98 }
99
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 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 public void close() throws IOException {
141 eof = false;
142 position = 0;
143 mark = -1;
144 }
145
146 /**
147 * Mark the current position.
148 *
149 * @param readlimit The number of bytes before this marked position
150 * is invalid.
151 * @throws UnsupportedOperationException if mark is not supported.
152 */
153 public synchronized void mark(int readlimit) {
154 if (!markSupported) {
155 throw new UnsupportedOperationException("Mark not supported");
156 }
157 mark = position;
158 this.readlimit = readlimit;
159 }
160
161 /**
162 * Indicates whether <i>mark</i> is supported.
163 *
164 * @return Whether <i>mark</i> is supported or not.
165 */
166 public boolean markSupported() {
167 return markSupported;
168 }
169
170 /**
171 * Read a byte.
172 *
173 * @return Either The byte value returned by <code>processByte()</code>
174 * or <code>-1</code> if the end of file has been reached and
175 * <code>throwEofException</code> is set to <code>false</code>.
176 * @throws EOFException if the end of file is reached and
177 * <code>throwEofException</code> is set to <code>true</code>.
178 * @throws IOException if trying to read past the end of file.
179 */
180 public int read() throws IOException {
181 if (eof) {
182 throw new IOException("Read after end of file");
183 }
184 if (position == size) {
185 return doEndOfFile();
186 }
187 position++;
188 return processByte();
189 }
190
191 /**
192 * Read some bytes into the specified array.
193 *
194 * @param bytes The byte array to read into
195 * @return The number of bytes read or <code>-1</code>
196 * if the end of file has been reached and
197 * <code>throwEofException</code> is set to <code>false</code>.
198 * @throws EOFException if the end of file is reached and
199 * <code>throwEofException</code> is set to <code>true</code>.
200 * @throws IOException if trying to read past the end of file.
201 */
202 public int read(byte[] bytes) throws IOException {
203 return read(bytes, 0, bytes.length);
204 }
205
206 /**
207 * Read the specified number bytes into an array.
208 *
209 * @param bytes The byte array to read into.
210 * @param offset The offset to start reading bytes into.
211 * @param length The number of bytes to read.
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</code>.
215 * @throws EOFException if the end of file is reached and
216 * <code>throwEofException</code> is set to <code>true</code>.
217 * @throws IOException if trying to read past the end of file.
218 */
219 public int read(byte[] bytes, int offset, int length) throws IOException {
220 if (eof) {
221 throw new IOException("Read after end of file");
222 }
223 if (position == size) {
224 return doEndOfFile();
225 }
226 position += length;
227 int returnLength = length;
228 if (position > size) {
229 returnLength = length - (int)(position - size);
230 position = size;
231 }
232 processBytes(bytes, offset, returnLength);
233 return returnLength;
234 }
235
236 /**
237 * Reset the stream to the point when mark was last called.
238 *
239 * @throws UnsupportedOperationException if mark is not supported.
240 * @throws IOException If no position has been marked
241 * or the read limit has been exceed since the last position was
242 * marked.
243 */
244 public synchronized void reset() throws IOException {
245 if (!markSupported) {
246 throw new UnsupportedOperationException("Mark not supported");
247 }
248 if (mark < 0) {
249 throw new IOException("No position has been marked");
250 }
251 if (position > (mark + readlimit)) {
252 throw new IOException("Marked position [" + mark +
253 "] is no longer valid - passed the read limit [" +
254 readlimit + "]");
255 }
256 position = mark;
257 eof = false;
258 }
259
260 /**
261 * Skip a specified number of bytes.
262 *
263 * @param numberOfBytes The number of bytes to skip.
264 * @return The number of bytes skipped or <code>-1</code>
265 * if the end of file has been reached and
266 * <code>throwEofException</code> is set to <code>false</code>.
267 * @throws EOFException if the end of file is reached and
268 * <code>throwEofException</code> is set to <code>true</code>.
269 * @throws IOException if trying to read past the end of file.
270 */
271 public long skip(long numberOfBytes) throws IOException {
272 if (eof) {
273 throw new IOException("Skip after end of file");
274 }
275 if (position == size) {
276 return doEndOfFile();
277 }
278 position += numberOfBytes;
279 long returnLength = numberOfBytes;
280 if (position > size) {
281 returnLength = numberOfBytes - (position - size);
282 position = size;
283 }
284 return returnLength;
285 }
286
287 /**
288 * Return a byte value for the <code>read()</code> method.
289 * <p>
290 * This implementation returns zero.
291 *
292 * @return This implementation always returns zero.
293 */
294 protected int processByte() {
295 // do nothing - overridable by subclass
296 return 0;
297 }
298
299 /**
300 * Process the bytes for the <code>read(byte[], offset, length)</code>
301 * method.
302 * <p>
303 * This implementation leaves the byte array unchanged.
304 *
305 * @param bytes The byte array
306 * @param offset The offset to start at.
307 * @param length The number of bytes.
308 */
309 protected void processBytes(byte[] bytes, int offset, int length) {
310 // do nothing - overridable by subclass
311 }
312
313 /**
314 * Handle End of File.
315 *
316 * @return <code>-1</code> if <code>throwEofException</code> is
317 * set to <code>false</code>
318 * @throws EOFException if <code>throwEofException</code> is set
319 * to <code>true</code>.
320 */
321 private int doEndOfFile() throws EOFException {
322 eof = true;
323 if (throwEofException) {
324 throw new EOFException();
325 }
326 return -1;
327 }
328
329 }