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