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.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