001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * https://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.io.input; 018 019import static org.apache.commons.io.IOUtils.EOF; 020 021import java.io.EOFException; 022import java.io.IOException; 023import java.io.InputStream; 024 025import org.apache.commons.io.IOUtils; 026 027/** 028 * A lightweight {@link InputStream} that emulates a stream of a specified size. 029 * <p> 030 * This implementation provides a lightweight object for testing with an {@link InputStream} where the contents don't matter. 031 * </p> 032 * <p> 033 * 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 034 * numbers of bytes - significantly speeding up test execution times. 035 * </p> 036 * <p> 037 * 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 038 * alternative data is required the {@code processByte()} and {@code processBytes()} methods can be implemented to generate data, for example: 039 * </p> 040 * 041 * <pre> 042 * public class TestInputStream extends NullInputStream { 043 * 044 * public TestInputStream(int size) { 045 * super(size); 046 * } 047 * 048 * protected int processByte() { 049 * return ... // return required value here 050 * } 051 * 052 * protected void processBytes(byte[] bytes, int offset, int length) { 053 * for (int i = offset; i < length; i++) { 054 * bytes[i] = ... // set array value here 055 * } 056 * } 057 * } 058 * </pre> 059 * <p> 060 * This class is not thread-safe. 061 * </p> 062 * 063 * @since 1.3 064 */ 065public class NullInputStream extends AbstractInputStream { 066 067 /** 068 * The singleton instance. 069 * 070 * <p> 071 * Since instances hold state, call {@link #init()} to reuse. 072 * </p> 073 * 074 * @since 2.12.0 075 * @deprecated Not reusable without calling {@link #init()} to reset state. 076 */ 077 @Deprecated 078 public static final NullInputStream INSTANCE = new NullInputStream(); 079 080 private final long size; 081 private long position; 082 private long mark = -1; 083 private long readLimit; 084 private final boolean throwEofException; 085 private final boolean markSupported; 086 087 /** 088 * Constructs an {@link InputStream} that emulates a size 0 stream which supports marking and does not throw EOFException. 089 * <p> 090 * This is an "empty" input stream. 091 * </p> 092 * 093 * @since 2.7 094 */ 095 public NullInputStream() { 096 this(0, true, false); 097 } 098 099 /** 100 * Constructs an {@link InputStream} that emulates a specified size which supports marking and does not throw EOFException. 101 * 102 * @param size The size of the input stream to emulate. 103 */ 104 public NullInputStream(final long size) { 105 this(size, true, false); 106 } 107 108 /** 109 * Constructs an {@link InputStream} that emulates a specified size with option settings. 110 * 111 * @param size The size of the input stream to emulate. 112 * @param markSupported Whether this instance will support the {@code mark()} functionality. 113 * @param throwEofException Whether this implementation will throw an {@link EOFException} or return -1 when the end of file is reached. 114 */ 115 public NullInputStream(final long size, final boolean markSupported, final boolean throwEofException) { 116 this.size = size; 117 this.markSupported = markSupported; 118 this.throwEofException = throwEofException; 119 } 120 121 @Override 122 public int available() { 123 if (isClosed()) { 124 return 0; 125 } 126 final long avail = size - position; 127 if (avail <= 0) { 128 return 0; 129 } 130 if (avail > Integer.MAX_VALUE) { 131 return Integer.MAX_VALUE; 132 } 133 return (int) avail; 134 } 135 136 /** 137 * Throws {@link EOFException} if {@code throwEofException} is enabled. 138 * 139 * @param message The {@link EOFException} message. 140 * @throws EOFException Thrown if {@code throwEofException} is enabled. 141 */ 142 private void checkThrowEof(final String message) throws EOFException { 143 if (throwEofException) { 144 throw new EOFException(message); 145 } 146 } 147 148 /** 149 * Closes this input stream. 150 * 151 * @throws IOException If an error occurs. 152 */ 153 @Override 154 public void close() throws IOException { 155 super.close(); 156 mark = -1; 157 } 158 159 /** 160 * Gets the current position. 161 * 162 * @return the current position. 163 */ 164 public long getPosition() { 165 return position; 166 } 167 168 /** 169 * Gets the size this {@link InputStream} emulates. 170 * 171 * @return The size of the input stream to emulate. 172 */ 173 public long getSize() { 174 return size; 175 } 176 177 /** 178 * Handles End of File. 179 * 180 * @return {@code -1} if {@code throwEofException} is set to {@code false} 181 * @throws IOException if {@code throwEofException} is set to {@code true}. 182 */ 183 private int handleEof() throws IOException { 184 checkThrowEof("handleEof()"); 185 return EOF; 186 } 187 188 /** 189 * Initializes or re-initializes this instance for reuse. 190 * 191 * @return {@code this} instance. 192 * @since 2.17.0 193 */ 194 public NullInputStream init() { 195 setClosed(false); 196 position = 0; 197 mark = -1; 198 readLimit = 0; 199 return this; 200 } 201 202 /** 203 * Marks the current position. 204 * 205 * @param readLimit The number of bytes before this marked position is invalid. 206 * @throws UnsupportedOperationException if mark is not supported. 207 */ 208 @Override 209 public synchronized void mark(final int readLimit) { 210 if (!markSupported) { 211 throw UnsupportedOperationExceptions.mark(); 212 } 213 mark = position; 214 this.readLimit = readLimit; 215 } 216 217 /** 218 * Tests whether <em>mark</em> is supported. 219 * 220 * @return Whether <em>mark</em> is supported or not. 221 */ 222 @Override 223 public boolean markSupported() { 224 return markSupported; 225 } 226 227 /** 228 * Returns a byte value for the {@code read()} method. 229 * <p> 230 * This implementation returns zero. 231 * </p> 232 * 233 * @return This implementation always returns zero. 234 */ 235 protected int processByte() { 236 // do nothing - overridable by subclass 237 return 0; 238 } 239 240 /** 241 * Processes the bytes for the {@code read(byte[], offset, length)} method. 242 * <p> 243 * This implementation leaves the byte array unchanged. 244 * </p> 245 * 246 * @param bytes The byte array, never {@code null}. 247 * @param offset The offset to start at, always non-negative. 248 * @param length The number of bytes to process, always non-negative and at most {@code bytes.length - offset}. 249 */ 250 protected void processBytes(final byte[] bytes, final int offset, final int length) { 251 // do nothing - overridable by subclass 252 } 253 254 /** 255 * Reads a byte. 256 * 257 * @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 258 * {@code false}. 259 * @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}. 260 * @throws IOException if trying to read past the end of file. 261 */ 262 @Override 263 public int read() throws IOException { 264 checkOpen(); 265 if (position == size) { 266 return handleEof(); 267 } 268 position++; 269 return processByte(); 270 } 271 272 /** 273 * Reads some bytes into the specified array. 274 * 275 * @param bytes The byte array to read into 276 * @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}. 277 * @throws NullPointerException if the byte array is {@code null}. 278 * @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}. 279 * @throws IOException if trying to read past the end of file. 280 */ 281 @Override 282 public int read(final byte[] bytes) throws IOException { 283 return read(bytes, 0, bytes.length); 284 } 285 286 /** 287 * Reads the specified number bytes into an array. 288 * 289 * @param bytes The byte array to read into. 290 * @param offset The offset to start reading bytes into. 291 * @param length The number of bytes to read. 292 * @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}. 293 * @throws NullPointerException if the byte array is {@code null}. 294 * @throws IndexOutOfBoundsException if {@code offset} or {@code length} are negative, or if {@code offset + length} is greater than {@code bytes.length}. 295 * @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}. 296 * @throws IOException if trying to read past the end of file. 297 */ 298 @Override 299 public int read(final byte[] bytes, final int offset, final int length) throws IOException { 300 IOUtils.checkFromIndexSize(bytes, offset, length); 301 if (length == 0) { 302 return 0; 303 } 304 checkOpen(); 305 if (position == size) { 306 return handleEof(); 307 } 308 position += length; 309 int returnLength = length; 310 if (position > size) { 311 returnLength = length - (int) (position - size); 312 position = size; 313 } 314 processBytes(bytes, offset, returnLength); 315 return returnLength; 316 } 317 318 /** 319 * Resets the stream to the point when mark was last called. 320 * 321 * @throws UnsupportedOperationException if mark is not supported. 322 * @throws IOException If no position has been marked or the read limit has been exceeded since the last position was marked. 323 */ 324 @Override 325 public synchronized void reset() throws IOException { 326 if (!markSupported) { 327 throw UnsupportedOperationExceptions.reset(); 328 } 329 if (mark < 0) { 330 throw new IOException("No position has been marked"); 331 } 332 if (position > mark + readLimit) { 333 throw new IOException("Marked position [" + mark + "] is no longer valid - passed the read limit [" + readLimit + "]"); 334 } 335 position = mark; 336 setClosed(false); 337 } 338 339 /** 340 * Skips a specified number of bytes. 341 * 342 * @param numberOfBytes The number of bytes to skip. 343 * @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}. 344 * @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}. 345 * @throws IOException if trying to read past the end of file. 346 */ 347 @Override 348 public long skip(final long numberOfBytes) throws IOException { 349 if (isClosed()) { 350 checkThrowEof("skip(long)"); 351 return EOF; 352 } 353 if (position == size) { 354 return handleEof(); 355 } 356 position += numberOfBytes; 357 long returnLength = numberOfBytes; 358 if (position > size) { 359 returnLength = numberOfBytes - (position - size); 360 position = size; 361 } 362 return returnLength; 363 } 364 365}