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 } 330