NullReader.java

  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. import static org.apache.commons.io.IOUtils.EOF;

  19. import java.io.EOFException;
  20. import java.io.IOException;
  21. import java.io.Reader;

  22. /**
  23.  * A functional, light weight {@link Reader} that emulates
  24.  * a reader of a specified size.
  25.  * <p>
  26.  * This implementation provides a light weight
  27.  * object for testing with an {@link Reader}
  28.  * where the contents don't matter.
  29.  * </p>
  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.  * <p>
  38.  * This implementation returns a space from the method that
  39.  * reads a character and leaves the array unchanged in the read
  40.  * methods that are passed a character array.
  41.  * If alternative data is required the {@code processChar()} and
  42.  * {@code processChars()} methods can be implemented to generate
  43.  * data, for example:
  44.  * </p>
  45.  *
  46.  * <pre>
  47.  *  public class TestReader extends NullReader {
  48.  *      public TestReader(int size) {
  49.  *          super(size);
  50.  *      }
  51.  *      protected char processChar() {
  52.  *          return ... // return required value here
  53.  *      }
  54.  *      protected void processChars(char[] chars, int offset, int length) {
  55.  *          for (int i = offset; i &lt; length; i++) {
  56.  *              chars[i] = ... // set array value here
  57.  *          }
  58.  *      }
  59.  *  }
  60.  * </pre>
  61.  *
  62.  * @since 1.3
  63.  */
  64. public class NullReader extends Reader {

  65.     /**
  66.      * The singleton instance.
  67.      *
  68.      * @since 2.12.0
  69.      */
  70.     public static final NullReader INSTANCE = new NullReader();

  71.     private final long size;
  72.     private long position;
  73.     private long mark = -1;
  74.     private long readLimit;
  75.     private boolean eof;
  76.     private final boolean throwEofException;
  77.     private final boolean markSupported;

  78.     /**
  79.      * Constructs a {@link Reader} that emulates a size 0 reader
  80.      * which supports marking and does not throw EOFException.
  81.      *
  82.      * @since 2.7
  83.      */
  84.     public NullReader() {
  85.        this(0, true, false);
  86.     }

  87.     /**
  88.      * Constructs a {@link Reader} that emulates a specified size
  89.      * which supports marking and does not throw EOFException.
  90.      *
  91.      * @param size The size of the reader to emulate.
  92.      */
  93.     public NullReader(final long size) {
  94.        this(size, true, false);
  95.     }

  96.     /**
  97.      * Constructs a {@link Reader} that emulates a specified
  98.      * size with option settings.
  99.      *
  100.      * @param size The size of the reader to emulate.
  101.      * @param markSupported Whether this instance will support
  102.      * the {@code mark()} functionality.
  103.      * @param throwEofException Whether this implementation
  104.      * will throw an {@link EOFException} or return -1 when the
  105.      * end of file is reached.
  106.      */
  107.     public NullReader(final long size, final boolean markSupported, final boolean throwEofException) {
  108.        this.size = size;
  109.        this.markSupported = markSupported;
  110.        this.throwEofException = throwEofException;
  111.     }

  112.     /**
  113.      * Closes this Reader - resets the internal state to
  114.      * the initial values.
  115.      *
  116.      * @throws IOException If an error occurs.
  117.      */
  118.     @Override
  119.     public void close() throws IOException {
  120.         eof = false;
  121.         position = 0;
  122.         mark = -1;
  123.     }

  124.     /**
  125.      * Handles End of File.
  126.      *
  127.      * @return {@code -1} if {@code throwEofException} is
  128.      * set to {@code false}
  129.      * @throws EOFException if {@code throwEofException} is set
  130.      * to {@code true}.
  131.      */
  132.     private int doEndOfFile() throws EOFException {
  133.         eof = true;
  134.         if (throwEofException) {
  135.             throw new EOFException();
  136.         }
  137.         return EOF;
  138.     }

  139.     /**
  140.      * Returns the current position.
  141.      *
  142.      * @return the current position.
  143.      */
  144.     public long getPosition() {
  145.         return position;
  146.     }

  147.     /**
  148.      * Returns the size this {@link Reader} emulates.
  149.      *
  150.      * @return The size of the reader to emulate.
  151.      */
  152.     public long getSize() {
  153.         return size;
  154.     }

  155.     /**
  156.      * Marks the current position.
  157.      *
  158.      * @param readLimit The number of characters before this marked position
  159.      * is invalid.
  160.      * @throws UnsupportedOperationException if mark is not supported.
  161.      */
  162.     @Override
  163.     public synchronized void mark(final int readLimit) {
  164.         if (!markSupported) {
  165.             throw UnsupportedOperationExceptions.mark();
  166.         }
  167.         mark = position;
  168.         this.readLimit = readLimit;
  169.     }

  170.     /**
  171.      * Indicates whether <em>mark</em> is supported.
  172.      *
  173.      * @return Whether <em>mark</em> is supported or not.
  174.      */
  175.     @Override
  176.     public boolean markSupported() {
  177.         return markSupported;
  178.     }

  179.     /**
  180.      * Returns a character value for the  {@code read()} method.
  181.      * <p>
  182.      * This implementation returns zero.
  183.      * </p>
  184.      *
  185.      * @return This implementation always returns zero.
  186.      */
  187.     protected int processChar() {
  188.         // do nothing - overridable by subclass
  189.         return 0;
  190.     }

  191.     /**
  192.      * Process the characters for the {@code read(char[], offset, length)}
  193.      * method.
  194.      * <p>
  195.      * This implementation leaves the character array unchanged.
  196.      * </p>
  197.      *
  198.      * @param chars The character array
  199.      * @param offset The offset to start at.
  200.      * @param length The number of characters.
  201.      */
  202.     protected void processChars(final char[] chars, final int offset, final int length) {
  203.         // do nothing - overridable by subclass
  204.     }

  205.     /**
  206.      * Reads a character.
  207.      *
  208.      * @return Either The character value returned by {@code processChar()}
  209.      * or {@code -1} if the end of file has been reached and
  210.      * {@code throwEofException} is set to {@code false}.
  211.      * @throws EOFException if the end of file is reached and
  212.      * {@code throwEofException} is set to {@code true}.
  213.      * @throws IOException if trying to read past the end of file.
  214.      */
  215.     @Override
  216.     public int read() throws IOException {
  217.         if (eof) {
  218.             throw new IOException("Read after end of file");
  219.         }
  220.         if (position == size) {
  221.             return doEndOfFile();
  222.         }
  223.         position++;
  224.         return processChar();
  225.     }

  226.     /**
  227.      * Reads some characters into the specified array.
  228.      *
  229.      * @param chars The character array to read into
  230.      * @return The number of characters read or {@code -1}
  231.      * if the end of file has been reached and
  232.      * {@code throwEofException} is set to {@code false}.
  233.      * @throws EOFException if the end of file is reached and
  234.      * {@code throwEofException} is set to {@code true}.
  235.      * @throws IOException if trying to read past the end of file.
  236.      */
  237.     @Override
  238.     public int read(final char[] chars) throws IOException {
  239.         return read(chars, 0, chars.length);
  240.     }

  241.     /**
  242.      * Reads the specified number characters into an array.
  243.      *
  244.      * @param chars The character array to read into.
  245.      * @param offset The offset to start reading characters into.
  246.      * @param length The number of characters to read.
  247.      * @return The number of characters read or {@code -1}
  248.      * if the end of file has been reached and
  249.      * {@code throwEofException} is set to {@code false}.
  250.      * @throws EOFException if the end of file is reached and
  251.      * {@code throwEofException} is set to {@code true}.
  252.      * @throws IOException if trying to read past the end of file.
  253.      */
  254.     @Override
  255.     public int read(final char[] chars, final int offset, final int length) throws IOException {
  256.         if (eof) {
  257.             throw new IOException("Read after end of file");
  258.         }
  259.         if (position == size) {
  260.             return doEndOfFile();
  261.         }
  262.         position += length;
  263.         int returnLength = length;
  264.         if (position > size) {
  265.             returnLength = length - (int) (position - size);
  266.             position = size;
  267.         }
  268.         processChars(chars, offset, returnLength);
  269.         return returnLength;
  270.     }

  271.     /**
  272.      * Resets the stream to the point when mark was last called.
  273.      *
  274.      * @throws UnsupportedOperationException if mark is not supported.
  275.      * @throws IOException If no position has been marked
  276.      * or the read limit has been exceeded since the last position was
  277.      * marked.
  278.      */
  279.     @Override
  280.     public synchronized void reset() throws IOException {
  281.         if (!markSupported) {
  282.             throw UnsupportedOperationExceptions.reset();
  283.         }
  284.         if (mark < 0) {
  285.             throw new IOException("No position has been marked");
  286.         }
  287.         if (position > mark + readLimit) {
  288.             throw new IOException("Marked position [" + mark +
  289.                     "] is no longer valid - passed the read limit [" +
  290.                     readLimit + "]");
  291.         }
  292.         position = mark;
  293.         eof = false;
  294.     }

  295.     /**
  296.      * Skips a specified number of characters.
  297.      *
  298.      * @param numberOfChars The number of characters to skip.
  299.      * @return The number of characters skipped or {@code -1}
  300.      * if the end of file has been reached and
  301.      * {@code throwEofException} is set to {@code false}.
  302.      * @throws EOFException if the end of file is reached and
  303.      * {@code throwEofException} is set to {@code true}.
  304.      * @throws IOException if trying to read past the end of file.
  305.      */
  306.     @Override
  307.     public long skip(final long numberOfChars) throws IOException {
  308.         if (eof) {
  309.             throw new IOException("Skip after end of file");
  310.         }
  311.         if (position == size) {
  312.             return doEndOfFile();
  313.         }
  314.         position += numberOfChars;
  315.         long returnLength = numberOfChars;
  316.         if (position > size) {
  317.             returnLength = numberOfChars - (position - size);
  318.             position = size;
  319.         }
  320.         return returnLength;
  321.     }

  322. }