Home | History | Annotate | Download | only in input
      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.Reader;
     22 
     23 /**
     24  * A functional, light weight {@link Reader} that emulates
     25  * a reader of a specified size.
     26  * <p>
     27  * This implementation provides a light weight
     28  * object for testing with an {@link Reader}
     29  * where the contents don't matter.
     30  * <p>
     31  * One use case would be for testing the handling of
     32  * large {@link Reader} as it can emulate that
     33  * scenario without the overhead of actually processing
     34  * large numbers of characters - significantly speeding up
     35  * test execution times.
     36  * <p>
     37  * This implementation returns a space from the method that
     38  * reads a character and leaves the array unchanged in the read
     39  * methods that are passed a character array.
     40  * If alternative data is required the <code>processChar()</code> and
     41  * <code>processChars()</code> methods can be implemented to generate
     42  * data, for example:
     43  *
     44  * <pre>
     45  *  public class TestReader extends NullReader {
     46  *      public TestReader(int size) {
     47  *          super(size);
     48  *      }
     49  *      protected char processChar() {
     50  *          return ... // return required value here
     51  *      }
     52  *      protected void processChars(char[] chars, int offset, int length) {
     53  *          for (int i = offset; i < length; i++) {
     54  *              chars[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 NullReader extends Reader {
     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 a {@link Reader} that emulates a specified size
     75      * which supports marking and does not throw EOFException.
     76      *
     77      * @param size The size of the reader to emulate.
     78      */
     79     public NullReader(long size) {
     80        this(size, true, false);
     81     }
     82 
     83     /**
     84      * Create a {@link Reader} that emulates a specified
     85      * size with option settings.
     86      *
     87      * @param size The size of the reader 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 NullReader(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 Reader} emulates.
    111      *
    112      * @return The size of the reader to emulate.
    113      */
    114     public long getSize() {
    115         return size;
    116     }
    117 
    118     /**
    119      * Close this Reader - resets the internal state to
    120      * the initial values.
    121      *
    122      * @throws IOException If an error occurs.
    123      */
    124     public void close() throws IOException {
    125         eof = false;
    126         position = 0;
    127         mark = -1;
    128     }
    129 
    130     /**
    131      * Mark the current position.
    132      *
    133      * @param readlimit The number of characters before this marked position
    134      * is invalid.
    135      * @throws UnsupportedOperationException if mark is not supported.
    136      */
    137     public synchronized void mark(int readlimit) {
    138         if (!markSupported) {
    139             throw new UnsupportedOperationException("Mark not supported");
    140         }
    141         mark = position;
    142         this.readlimit = readlimit;
    143     }
    144 
    145     /**
    146      * Indicates whether <i>mark</i> is supported.
    147      *
    148      * @return Whether <i>mark</i> is supported or not.
    149      */
    150     public boolean markSupported() {
    151         return markSupported;
    152     }
    153 
    154     /**
    155      * Read a character.
    156      *
    157      * @return Either The character value returned by <code>processChar()</code>
    158      * or <code>-1</code> if the end of file has been reached and
    159      * <code>throwEofException</code> is set to <code>false</code>.
    160      * @throws EOFException if the end of file is reached and
    161      * <code>throwEofException</code> is set to <code>true</code>.
    162      * @throws IOException if trying to read past the end of file.
    163      */
    164     public int read() throws IOException {
    165         if (eof) {
    166             throw new IOException("Read after end of file");
    167         }
    168         if (position == size) {
    169             return doEndOfFile();
    170         }
    171         position++;
    172         return processChar();
    173     }
    174 
    175     /**
    176      * Read some characters into the specified array.
    177      *
    178      * @param chars The character array to read into
    179      * @return The number of characters read or <code>-1</code>
    180      * if the end of file has been reached and
    181      * <code>throwEofException</code> is set to <code>false</code>.
    182      * @throws EOFException if the end of file is reached and
    183      * <code>throwEofException</code> is set to <code>true</code>.
    184      * @throws IOException if trying to read past the end of file.
    185      */
    186     public int read(char[] chars) throws IOException {
    187         return read(chars, 0, chars.length);
    188     }
    189 
    190     /**
    191      * Read the specified number characters into an array.
    192      *
    193      * @param chars The character array to read into.
    194      * @param offset The offset to start reading characters into.
    195      * @param length The number of characters to read.
    196      * @return The number of characters read or <code>-1</code>
    197      * if the end of file has been reached and
    198      * <code>throwEofException</code> is set to <code>false</code>.
    199      * @throws EOFException if the end of file is reached and
    200      * <code>throwEofException</code> is set to <code>true</code>.
    201      * @throws IOException if trying to read past the end of file.
    202      */
    203     public int read(char[] chars, int offset, int length) throws IOException {
    204         if (eof) {
    205             throw new IOException("Read after end of file");
    206         }
    207         if (position == size) {
    208             return doEndOfFile();
    209         }
    210         position += length;
    211         int returnLength = length;
    212         if (position > size) {
    213             returnLength = length - (int)(position - size);
    214             position = size;
    215         }
    216         processChars(chars, offset, returnLength);
    217         return returnLength;
    218     }
    219 
    220     /**
    221      * Reset the stream to the point when mark was last called.
    222      *
    223      * @throws UnsupportedOperationException if mark is not supported.
    224      * @throws IOException If no position has been marked
    225      * or the read limit has been exceed since the last position was
    226      * marked.
    227      */
    228     public synchronized void reset() throws IOException {
    229         if (!markSupported) {
    230             throw new UnsupportedOperationException("Mark not supported");
    231         }
    232         if (mark < 0) {
    233             throw new IOException("No position has been marked");
    234         }
    235         if (position > (mark + readlimit)) {
    236             throw new IOException("Marked position [" + mark +
    237                     "] is no longer valid - passed the read limit [" +
    238                     readlimit + "]");
    239         }
    240         position = mark;
    241         eof = false;
    242     }
    243 
    244     /**
    245      * Skip a specified number of characters.
    246      *
    247      * @param numberOfChars The number of characters to skip.
    248      * @return The number of characters skipped or <code>-1</code>
    249      * if the end of file has been reached and
    250      * <code>throwEofException</code> is set to <code>false</code>.
    251      * @throws EOFException if the end of file is reached and
    252      * <code>throwEofException</code> is set to <code>true</code>.
    253      * @throws IOException if trying to read past the end of file.
    254      */
    255     public long skip(long numberOfChars) throws IOException {
    256         if (eof) {
    257             throw new IOException("Skip after end of file");
    258         }
    259         if (position == size) {
    260             return doEndOfFile();
    261         }
    262         position += numberOfChars;
    263         long returnLength = numberOfChars;
    264         if (position > size) {
    265             returnLength = numberOfChars - (position - size);
    266             position = size;
    267         }
    268         return returnLength;
    269     }
    270 
    271     /**
    272      * Return a character value for the  <code>read()</code> method.
    273      * <p>
    274      * This implementation returns zero.
    275      *
    276      * @return This implementation always returns zero.
    277      */
    278     protected int processChar() {
    279         // do nothing - overridable by subclass
    280         return 0;
    281     }
    282 
    283     /**
    284      * Process the characters for the <code>read(char[], offset, length)</code>
    285      * method.
    286      * <p>
    287      * This implementation leaves the character array unchanged.
    288      *
    289      * @param chars The character array
    290      * @param offset The offset to start at.
    291      * @param length The number of characters.
    292      */
    293     protected void processChars(char[] chars, int offset, int length) {
    294         // do nothing - overridable by subclass
    295     }
    296 
    297     /**
    298      * Handle End of File.
    299      *
    300      * @return <code>-1</code> if <code>throwEofException</code> is
    301      * set to <code>false</code>
    302      * @throws EOFException if <code>throwEofException</code> is set
    303      * to <code>true</code>.
    304      */
    305     private int doEndOfFile() throws EOFException {
    306         eof = true;
    307         if (throwEofException) {
    308             throw new EOFException();
    309         }
    310         return -1;
    311     }
    312 
    313 }
    314