NullInputStream.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.InputStream;

  22. /**
  23.  * A light weight {@link InputStream} that emulates a stream of a specified size.
  24.  * <p>
  25.  * This implementation provides a light weight object for testing with an {@link InputStream} where the contents don't matter.
  26.  * </p>
  27.  * <p>
  28.  * One use case would be for testing the handling of large {@link InputStream} as it can emulate that scenario without the overhead of actually processing large
  29.  * numbers of bytes - significantly speeding up test execution times.
  30.  * </p>
  31.  * <p>
  32.  * This implementation returns zero from the method that reads a byte and leaves the array unchanged in the read methods that are passed a byte array. If
  33.  * alternative data is required the {@code processByte()} and {@code processBytes()} methods can be implemented to generate data, for example:
  34.  * </p>
  35.  *
  36.  * <pre>
  37.  *  public class TestInputStream extends NullInputStream {
  38.  *
  39.  *      public TestInputStream(int size) {
  40.  *          super(size);
  41.  *      }
  42.  *
  43.  *      protected int processByte() {
  44.  *          return ... // return required value here
  45.  *      }
  46.  *
  47.  *      protected void processBytes(byte[] bytes, int offset, int length) {
  48.  *          for (int i = offset; i &lt; length; i++) {
  49.  *              bytes[i] = ... // set array value here
  50.  *          }
  51.  *      }
  52.  *  }
  53.  * </pre>
  54.  *
  55.  * @since 1.3
  56.  */
  57. public class NullInputStream extends AbstractInputStream {

  58.     /**
  59.      * The singleton instance.
  60.      *
  61.      * <p>
  62.      * Since instances hold state, call {@link #init()} to reuse.
  63.      * </p>
  64.      *
  65.      * @since 2.12.0
  66.      * @deprecated Not reusable without calling {@link #init()} to reset state.
  67.      */
  68.     @Deprecated
  69.     public static final NullInputStream INSTANCE = new NullInputStream();

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

  76.     /**
  77.      * Constructs an {@link InputStream} that emulates a size 0 stream which supports marking and does not throw EOFException.
  78.      * <p>
  79.      * This is an "empty" input stream.
  80.      * </p>
  81.      *
  82.      * @since 2.7
  83.      */
  84.     public NullInputStream() {
  85.         this(0, true, false);
  86.     }

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

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

  107.     @Override
  108.     public int available() {
  109.         if (isClosed()) {
  110.             return 0;
  111.         }
  112.         final long avail = size - position;
  113.         if (avail <= 0) {
  114.             return 0;
  115.         }
  116.         if (avail > Integer.MAX_VALUE) {
  117.             return Integer.MAX_VALUE;
  118.         }
  119.         return (int) avail;
  120.     }

  121.     /**
  122.      * Throws {@link EOFException} if {@code throwEofException} is enabled.
  123.      *
  124.      * @param message The {@link EOFException} message.
  125.      * @throws EOFException Thrown if {@code throwEofException} is enabled.
  126.      */
  127.     private void checkThrowEof(final String message) throws EOFException {
  128.         if (throwEofException) {
  129.             throw new EOFException(message);
  130.         }
  131.     }

  132.     /**
  133.      * Closes this input stream.
  134.      *
  135.      * @throws IOException If an error occurs.
  136.      */
  137.     @Override
  138.     public void close() throws IOException {
  139.         super.close();
  140.         mark = -1;
  141.     }

  142.     /**
  143.      * Gets the current position.
  144.      *
  145.      * @return the current position.
  146.      */
  147.     public long getPosition() {
  148.         return position;
  149.     }

  150.     /**
  151.      * Gets the size this {@link InputStream} emulates.
  152.      *
  153.      * @return The size of the input stream to emulate.
  154.      */
  155.     public long getSize() {
  156.         return size;
  157.     }

  158.     /**
  159.      * Handles End of File.
  160.      *
  161.      * @return {@code -1} if {@code throwEofException} is set to {@code false}
  162.      * @throws IOException if {@code throwEofException} is set to {@code true}.
  163.      */
  164.     private int handleEof() throws IOException {
  165.         checkThrowEof("handleEof()");
  166.         return EOF;
  167.     }

  168.     /**
  169.      * Initializes or re-initializes this instance for reuse.
  170.      *
  171.      * @return this instance.
  172.      * @since 2.17.0
  173.      */
  174.     public NullInputStream init() {
  175.         setClosed(false);
  176.         position = 0;
  177.         mark = -1;
  178.         readLimit = 0;
  179.         return this;
  180.     }

  181.     /**
  182.      * Marks the current position.
  183.      *
  184.      * @param readLimit The number of bytes before this marked position is invalid.
  185.      * @throws UnsupportedOperationException if mark is not supported.
  186.      */
  187.     @Override
  188.     public synchronized void mark(final int readLimit) {
  189.         if (!markSupported) {
  190.             throw UnsupportedOperationExceptions.mark();
  191.         }
  192.         mark = position;
  193.         this.readLimit = readLimit;
  194.     }

  195.     /**
  196.      * Tests whether <em>mark</em> is supported.
  197.      *
  198.      * @return Whether <em>mark</em> is supported or not.
  199.      */
  200.     @Override
  201.     public boolean markSupported() {
  202.         return markSupported;
  203.     }

  204.     /**
  205.      * Returns a byte value for the {@code read()} method.
  206.      * <p>
  207.      * This implementation returns zero.
  208.      *
  209.      * @return This implementation always returns zero.
  210.      */
  211.     protected int processByte() {
  212.         // do nothing - overridable by subclass
  213.         return 0;
  214.     }

  215.     /**
  216.      * Processes the bytes for the {@code read(byte[], offset, length)} method.
  217.      * <p>
  218.      * This implementation leaves the byte array unchanged.
  219.      * </p>
  220.      *
  221.      * @param bytes  The byte array
  222.      * @param offset The offset to start at.
  223.      * @param length The number of bytes.
  224.      */
  225.     protected void processBytes(final byte[] bytes, final int offset, final int length) {
  226.         // do nothing - overridable by subclass
  227.     }

  228.     /**
  229.      * Reads a byte.
  230.      *
  231.      * @return Either The byte value returned by {@code processByte()} or {@code -1} if the end of file has been reached and {@code throwEofException} is set to
  232.      *         {@code false}.
  233.      * @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}.
  234.      * @throws IOException  if trying to read past the end of file.
  235.      */
  236.     @Override
  237.     public int read() throws IOException {
  238.         checkOpen();
  239.         if (position == size) {
  240.             return handleEof();
  241.         }
  242.         position++;
  243.         return processByte();
  244.     }

  245.     /**
  246.      * Reads some bytes into the specified array.
  247.      *
  248.      * @param bytes The byte array to read into
  249.      * @return The number of bytes read or {@code -1} if the end of file has been reached and {@code throwEofException} is set to {@code false}.
  250.      * @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}.
  251.      * @throws IOException  if trying to read past the end of file.
  252.      */
  253.     @Override
  254.     public int read(final byte[] bytes) throws IOException {
  255.         return read(bytes, 0, bytes.length);
  256.     }

  257.     /**
  258.      * Reads the specified number bytes into an array.
  259.      *
  260.      * @param bytes  The byte array to read into.
  261.      * @param offset The offset to start reading bytes into.
  262.      * @param length The number of bytes to read.
  263.      * @return The number of bytes read or {@code -1} if the end of file has been reached and {@code throwEofException} is set to {@code false}.
  264.      * @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}.
  265.      * @throws IOException  if trying to read past the end of file.
  266.      */
  267.     @Override
  268.     public int read(final byte[] bytes, final int offset, final int length) throws IOException {
  269.         if (bytes.length == 0 || length == 0) {
  270.             return 0;
  271.         }
  272.         checkOpen();
  273.         if (position == size) {
  274.             return handleEof();
  275.         }
  276.         position += length;
  277.         int returnLength = length;
  278.         if (position > size) {
  279.             returnLength = length - (int) (position - size);
  280.             position = size;
  281.         }
  282.         processBytes(bytes, offset, returnLength);
  283.         return returnLength;
  284.     }

  285.     /**
  286.      * Resets the stream to the point when mark was last called.
  287.      *
  288.      * @throws UnsupportedOperationException if mark is not supported.
  289.      * @throws IOException                   If no position has been marked or the read limit has been exceeded since the last position was marked.
  290.      */
  291.     @Override
  292.     public synchronized void reset() throws IOException {
  293.         if (!markSupported) {
  294.             throw UnsupportedOperationExceptions.reset();
  295.         }
  296.         if (mark < 0) {
  297.             throw new IOException("No position has been marked");
  298.         }
  299.         if (position > mark + readLimit) {
  300.             throw new IOException("Marked position [" + mark + "] is no longer valid - passed the read limit [" + readLimit + "]");
  301.         }
  302.         position = mark;
  303.         setClosed(false);
  304.     }

  305.     /**
  306.      * Skips a specified number of bytes.
  307.      *
  308.      * @param numberOfBytes The number of bytes to skip.
  309.      * @return The number of bytes skipped or {@code -1} if the end of file has been reached and {@code throwEofException} is set to {@code false}.
  310.      * @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}.
  311.      * @throws IOException  if trying to read past the end of file.
  312.      */
  313.     @Override
  314.     public long skip(final long numberOfBytes) throws IOException {
  315.         if (isClosed()) {
  316.             checkThrowEof("skip(long)");
  317.             return EOF;
  318.         }
  319.         if (position == size) {
  320.             return handleEof();
  321.         }
  322.         position += numberOfBytes;
  323.         long returnLength = numberOfBytes;
  324.         if (position > size) {
  325.             returnLength = numberOfBytes - (position - size);
  326.             position = size;
  327.         }
  328.         return returnLength;
  329.     }

  330. }