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.Reader; 024 025import org.apache.commons.io.IOUtils; 026 027/** 028 * A functional, lightweight {@link Reader} that emulates 029 * a reader of a specified size. 030 * <p> 031 * This implementation provides a lightweight 032 * object for testing with an {@link Reader} 033 * where the contents don't matter. 034 * </p> 035 * <p> 036 * One use case would be for testing the handling of 037 * large {@link Reader} as it can emulate that 038 * scenario without the overhead of actually processing 039 * large numbers of characters - significantly speeding up 040 * test execution times. 041 * </p> 042 * <p> 043 * This implementation returns a space from the method that 044 * reads a character and leaves the array unchanged in the read 045 * methods that are passed a character array. 046 * If alternative data is required the {@code processChar()} and 047 * {@code processChars()} methods can be implemented to generate 048 * data, for example: 049 * </p> 050 * 051 * <pre> 052 * public class TestReader extends NullReader { 053 * public TestReader(int size) { 054 * super(size); 055 * } 056 * protected char processChar() { 057 * return ... // return required value here 058 * } 059 * protected void processChars(char[] chars, int offset, int length) { 060 * for (int i = offset; i < length; i++) { 061 * chars[i] = ... // set array value here 062 * } 063 * } 064 * } 065 * </pre> 066 * <p> 067 * This class is not thread-safe. 068 * </p> 069 * 070 * @since 1.3 071 */ 072public class NullReader extends Reader { 073 074 /** 075 * The singleton instance. 076 * 077 * @since 2.12.0 078 */ 079 public static final NullReader INSTANCE = new NullReader(); 080 081 private final long size; 082 private final boolean throwEofException; 083 private final boolean markSupported; 084 085 private long position; 086 private long mark = -1; 087 private long readLimit; 088 private boolean eof; 089 090 /** 091 * Constructs a {@link Reader} that emulates a size 0 reader 092 * which supports marking and does not throw EOFException. 093 * 094 * @since 2.7 095 */ 096 public NullReader() { 097 this(0, true, false); 098 } 099 100 /** 101 * Constructs a {@link Reader} that emulates a specified size 102 * which supports marking and does not throw EOFException. 103 * 104 * @param size The size of the reader to emulate. 105 */ 106 public NullReader(final long size) { 107 this(size, true, false); 108 } 109 110 /** 111 * Constructs a {@link Reader} that emulates a specified 112 * size with option settings. 113 * 114 * @param size The size of the reader to emulate. 115 * @param markSupported Whether this instance will support 116 * the {@code mark()} functionality. 117 * @param throwEofException Whether this implementation 118 * will throw an {@link EOFException} or return -1 when the 119 * end of file is reached. 120 */ 121 public NullReader(final long size, final boolean markSupported, final boolean throwEofException) { 122 this.size = size; 123 this.markSupported = markSupported; 124 this.throwEofException = throwEofException; 125 } 126 127 /** 128 * Closes this Reader. Resets the internal state to 129 * the initial values. 130 * 131 * @throws IOException If an error occurs. 132 */ 133 @Override 134 public void close() throws IOException { 135 eof = false; 136 position = 0; 137 mark = -1; 138 } 139 140 /** 141 * Handles End of File. 142 * 143 * @return {@code -1} if {@code throwEofException} is 144 * set to {@code false} 145 * @throws EOFException if {@code throwEofException} is set 146 * to {@code true}. 147 */ 148 private int doEndOfFile() throws EOFException { 149 eof = true; 150 if (throwEofException) { 151 throw new EOFException(); 152 } 153 return EOF; 154 } 155 156 /** 157 * Gets the current position. 158 * 159 * @return the current position. 160 */ 161 public long getPosition() { 162 return position; 163 } 164 165 /** 166 * Gets the size this {@link Reader} emulates. 167 * 168 * @return The size of the reader to emulate. 169 */ 170 public long getSize() { 171 return size; 172 } 173 174 /** 175 * Marks the current position. 176 * 177 * @param readLimit The number of characters before this marked position 178 * is invalid. 179 * @throws UnsupportedOperationException if mark is not supported. 180 */ 181 @Override 182 public synchronized void mark(final int readLimit) { 183 if (!markSupported) { 184 throw UnsupportedOperationExceptions.mark(); 185 } 186 mark = position; 187 this.readLimit = readLimit; 188 } 189 190 /** 191 * Tests whether <em>mark</em> is supported. 192 * 193 * @return Whether <em>mark</em> is supported or not. 194 */ 195 @Override 196 public boolean markSupported() { 197 return markSupported; 198 } 199 200 /** 201 * Returns a character value for the {@code read()} method. 202 * <p> 203 * This implementation returns zero. 204 * </p> 205 * 206 * @return This implementation always returns zero. 207 */ 208 protected int processChar() { 209 // do nothing - overridable by subclass 210 return 0; 211 } 212 213 /** 214 * Process the characters for the {@code read(char[], offset, length)} 215 * method. 216 * <p> 217 * This implementation leaves the character array unchanged. 218 * </p> 219 * 220 * @param chars The character array 221 * @param offset The offset to start at. 222 * @param length The number of characters. 223 */ 224 protected void processChars(final char[] chars, final int offset, final int length) { 225 // do nothing - overridable by subclass 226 } 227 228 /** 229 * Reads a character. 230 * 231 * @return Either The character value returned by {@code processChar()} 232 * or {@code -1} if the end of file has been reached and 233 * {@code throwEofException} is set to {@code false}. 234 * @throws EOFException if the end of file is reached and 235 * {@code throwEofException} is set to {@code true}. 236 * @throws IOException if trying to read past the end of file. 237 */ 238 @Override 239 public int read() throws IOException { 240 if (eof) { 241 throw new IOException("Read after end of file"); 242 } 243 if (position == size) { 244 return doEndOfFile(); 245 } 246 position++; 247 return processChar(); 248 } 249 250 /** 251 * Reads some characters into the specified array. 252 * 253 * @param chars The character array to read into 254 * @return The number of characters read or {@code -1} 255 * if the end of file has been reached and 256 * {@code throwEofException} is set to {@code false}. 257 * @throws EOFException if the end of file is reached and 258 * {@code throwEofException} is set to {@code true}. 259 * @throws IOException if trying to read past the end of file. 260 */ 261 @Override 262 public int read(final char[] chars) throws IOException { 263 return read(chars, 0, chars.length); 264 } 265 266 /** 267 * Reads the specified number characters into an array. 268 * 269 * @param chars The character array to read into. 270 * @param offset The offset to start reading characters into. 271 * @param length The number of characters to read. 272 * @return The number of characters read or {@code -1} 273 * if the end of file has been reached and 274 * {@code throwEofException} is set to {@code false}. 275 * @throws NullPointerException if the array is {@code null}. 276 * @throws IndexOutOfBoundsException if {@code offset} or {@code length} are negative, or if {@code offset + length} is greater than {@code chars.length}. 277 * @throws EOFException if the end of file is reached and 278 * {@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 char[] chars, final int offset, final int length) throws IOException { 283 IOUtils.checkFromIndexSize(chars, offset, length); 284 if (length == 0) { 285 return 0; 286 } 287 if (eof) { 288 throw new IOException("Read after end of file"); 289 } 290 if (position == size) { 291 return doEndOfFile(); 292 } 293 position += length; 294 int returnLength = length; 295 if (position > size) { 296 returnLength = length - (int) (position - size); 297 position = size; 298 } 299 processChars(chars, offset, returnLength); 300 return returnLength; 301 } 302 303 /** 304 * Resets the stream to the point when mark was last called. 305 * 306 * @throws UnsupportedOperationException if mark is not supported. 307 * @throws IOException If no position has been marked 308 * or the read limit has been exceeded since the last position was 309 * marked. 310 */ 311 @Override 312 public synchronized void reset() throws IOException { 313 if (!markSupported) { 314 throw UnsupportedOperationExceptions.reset(); 315 } 316 if (mark < 0) { 317 throw new IOException("No position has been marked"); 318 } 319 if (position > mark + readLimit) { 320 throw new IOException("Marked position [" + mark + 321 "] is no longer valid - passed the read limit [" + 322 readLimit + "]"); 323 } 324 position = mark; 325 eof = false; 326 } 327 328 /** 329 * Skips a specified number of characters. 330 * 331 * @param numberOfChars The number of characters to skip. 332 * @return The number of characters skipped or {@code -1} 333 * if the end of file has been reached and 334 * {@code throwEofException} is set to {@code false}. 335 * @throws EOFException if the end of file is reached and 336 * {@code throwEofException} is set to {@code true}. 337 * @throws IOException if trying to read past the end of file. 338 */ 339 @Override 340 public long skip(final long numberOfChars) throws IOException { 341 if (eof) { 342 throw new IOException("Skip after end of file"); 343 } 344 if (position == size) { 345 return doEndOfFile(); 346 } 347 position += numberOfChars; 348 long returnLength = numberOfChars; 349 if (position > size) { 350 returnLength = numberOfChars - (position - size); 351 position = size; 352 } 353 return returnLength; 354 } 355 356}