BoundedReader.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one
  3.  * or more contributor license agreements.  See the NOTICE file
  4.  * distributed with this work for additional information
  5.  * regarding copyright ownership.  The ASF licenses this file
  6.  * to you under the Apache License, Version 2.0 (the
  7.  * "License"); you may not use this file except in compliance
  8.  * with the License.  You may obtain a copy of the License at
  9.  *
  10.  *    http://www.apache.org/licenses/LICENSE-2.0
  11.  *
  12.  * Unless required by applicable law or agreed to in writing,
  13.  * software distributed under the License is distributed on an
  14.  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15.  * KIND, either express or implied.  See the License for the
  16.  * specific language governing permissions and limitations
  17.  * under the License.
  18.  */
  19. package org.apache.commons.io.input;

  20. import static org.apache.commons.io.IOUtils.EOF;

  21. import java.io.IOException;
  22. import java.io.Reader;

  23. /**
  24.  * A reader that imposes a limit to the number of characters that can be read from an underlying reader, returning EOF
  25.  * when this limit is reached, regardless of state of underlying reader.
  26.  *
  27.  * <p>
  28.  * One use case is to avoid overrunning the readAheadLimit supplied to {@link Reader#mark(int)}, since reading
  29.  * too many characters removes the ability to do a successful reset.
  30.  * </p>
  31.  *
  32.  * @since 2.5
  33.  */
  34. public class BoundedReader extends Reader {

  35.     private static final int INVALID = -1;

  36.     private final Reader target;

  37.     private int charsRead;

  38.     private int markedAt = INVALID;

  39.     private int readAheadLimit; // Internally, this value will never exceed the allowed size

  40.     private final int maxCharsFromTargetReader;

  41.     /**
  42.      * Constructs a bounded reader
  43.      *
  44.      * @param target                   The target stream that will be used
  45.      * @param maxCharsFromTargetReader The maximum number of characters that can be read from target
  46.      */
  47.     public BoundedReader(final Reader target, final int maxCharsFromTargetReader) {
  48.         this.target = target;
  49.         this.maxCharsFromTargetReader = maxCharsFromTargetReader;
  50.     }

  51.     /**
  52.      * Closes the target
  53.      *
  54.      * @throws IOException If an I/O error occurs while calling the underlying reader's close method
  55.      */
  56.     @Override
  57.     public void close() throws IOException {
  58.         target.close();
  59.     }

  60.     /**
  61.      * marks the target stream
  62.      *
  63.      * @param readAheadLimit The number of characters that can be read while still retaining the ability to do #reset().
  64.      *                       Note that this parameter is not validated with respect to maxCharsFromTargetReader. There
  65.      *                       is no way to pass past maxCharsFromTargetReader, even if this value is greater.
  66.      *
  67.      * @throws IOException If an I/O error occurs while calling the underlying reader's mark method
  68.      * @see java.io.Reader#mark(int)
  69.      */
  70.     @Override
  71.     public void mark(final int readAheadLimit) throws IOException {
  72.         this.readAheadLimit = readAheadLimit - charsRead;

  73.         markedAt = charsRead;

  74.         target.mark(readAheadLimit);
  75.     }

  76.     /**
  77.      * Reads a single character
  78.      *
  79.      * @return -1 on EOF or the character read
  80.      * @throws IOException If an I/O error occurs while calling the underlying reader's read method
  81.      * @see java.io.Reader#read()
  82.      */
  83.     @Override
  84.     public int read() throws IOException {

  85.         if (charsRead >= maxCharsFromTargetReader) {
  86.             return EOF;
  87.         }

  88.         if (markedAt >= 0 && charsRead - markedAt >= readAheadLimit) {
  89.             return EOF;
  90.         }
  91.         charsRead++;
  92.         return target.read();
  93.     }

  94.     /**
  95.      * Reads into an array
  96.      *
  97.      * @param cbuf The buffer to fill
  98.      * @param off  The offset
  99.      * @param len  The number of chars to read
  100.      * @return the number of chars read
  101.      * @throws IOException If an I/O error occurs while calling the underlying reader's read method
  102.      * @see java.io.Reader#read(char[], int, int)
  103.      */
  104.     @Override
  105.     public int read(final char[] cbuf, final int off, final int len) throws IOException {
  106.         int c;
  107.         for (int i = 0; i < len; i++) {
  108.             c = read();
  109.             if (c == EOF) {
  110.                 return i == 0 ? EOF : i;
  111.             }
  112.             cbuf[off + i] = (char) c;
  113.         }
  114.         return len;
  115.     }

  116.     /**
  117.      * Resets the target to the latest mark,
  118.      *
  119.      * @throws IOException If an I/O error occurs while calling the underlying reader's reset method
  120.      * @see java.io.Reader#reset()
  121.      */
  122.     @Override
  123.     public void reset() throws IOException {
  124.         charsRead = markedAt;
  125.         target.reset();
  126.     }
  127. }