TeeReader.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.IOException;
  20. import java.io.Reader;
  21. import java.io.Writer;
  22. import java.nio.CharBuffer;

  23. /**
  24.  * Reader proxy that transparently writes a copy of all characters read from the proxied reader to a given Reader. Using
  25.  * {@link #skip(long)} or {@link #mark(int)}/{@link #reset()} on the reader will result on some characters from the
  26.  * reader being skipped or duplicated in the writer.
  27.  * <p>
  28.  * The proxied reader is closed when the {@link #close()} method is called on this proxy. You may configure whether the
  29.  * reader closes the writer.
  30.  * </p>
  31.  *
  32.  * @since 2.7
  33.  */
  34. public class TeeReader extends ProxyReader {

  35.     /**
  36.      * The writer that will receive a copy of all characters read from the proxied reader.
  37.      */
  38.     private final Writer branch;

  39.     /**
  40.      * Flag for closing the associated writer when this reader is closed.
  41.      */
  42.     private final boolean closeBranch;

  43.     /**
  44.      * Constructs a TeeReader that proxies the given {@link Reader} and copies all read characters to the given
  45.      * {@link Writer}. The given writer will not be closed when this reader gets closed.
  46.      *
  47.      * @param input  reader to be proxied
  48.      * @param branch writer that will receive a copy of all characters read
  49.      */
  50.     public TeeReader(final Reader input, final Writer branch) {
  51.         this(input, branch, false);
  52.     }

  53.     /**
  54.      * Constructs a TeeReader that proxies the given {@link Reader} and copies all read characters to the given
  55.      * {@link Writer}. The given writer will be closed when this reader gets closed if the closeBranch parameter is
  56.      * {@code true}.
  57.      *
  58.      * @param input       reader to be proxied
  59.      * @param branch      writer that will receive a copy of all characters read
  60.      * @param closeBranch flag for closing also the writer when this reader is closed
  61.      */
  62.     public TeeReader(final Reader input, final Writer branch, final boolean closeBranch) {
  63.         super(input);
  64.         this.branch = branch;
  65.         this.closeBranch = closeBranch;
  66.     }

  67.     /**
  68.      * Closes the proxied reader and, if so configured, the associated writer. An exception thrown from the reader will
  69.      * not prevent closing of the writer.
  70.      *
  71.      * @throws IOException if either the reader or writer could not be closed
  72.      */
  73.     @Override
  74.     public void close() throws IOException {
  75.         try {
  76.             super.close();
  77.         } finally {
  78.             if (closeBranch) {
  79.                 branch.close();
  80.             }
  81.         }
  82.     }

  83.     /**
  84.      * Reads a single character from the proxied reader and writes it to the associated writer.
  85.      *
  86.      * @return next character from the reader, or -1 if the reader has ended
  87.      * @throws IOException if the reader could not be read (or written)
  88.      */
  89.     @Override
  90.     public int read() throws IOException {
  91.         final int ch = super.read();
  92.         if (ch != EOF) {
  93.             branch.write(ch);
  94.         }
  95.         return ch;
  96.     }

  97.     /**
  98.      * Reads characters from the proxied reader and writes the read characters to the associated writer.
  99.      *
  100.      * @param chr character buffer
  101.      * @return number of characters read, or -1 if the reader has ended
  102.      * @throws IOException if the reader could not be read (or written)
  103.      */
  104.     @Override
  105.     public int read(final char[] chr) throws IOException {
  106.         final int n = super.read(chr);
  107.         if (n != EOF) {
  108.             branch.write(chr, 0, n);
  109.         }
  110.         return n;
  111.     }

  112.     /**
  113.      * Reads characters from the proxied reader and writes the read characters to the associated writer.
  114.      *
  115.      * @param chr character buffer
  116.      * @param st  start offset within the buffer
  117.      * @param end maximum number of characters to read
  118.      * @return number of characters read, or -1 if the reader has ended
  119.      * @throws IOException if the reader could not be read (or written)
  120.      */
  121.     @Override
  122.     public int read(final char[] chr, final int st, final int end) throws IOException {
  123.         final int n = super.read(chr, st, end);
  124.         if (n != EOF) {
  125.             branch.write(chr, st, n);
  126.         }
  127.         return n;
  128.     }

  129.     /**
  130.      * Reads characters from the proxied reader and writes the read characters to the associated writer.
  131.      *
  132.      * @param target character buffer
  133.      * @return number of characters read, or -1 if the reader has ended
  134.      * @throws IOException if the reader could not be read (or written)
  135.      */
  136.     @Override
  137.     public int read(final CharBuffer target) throws IOException {
  138.         final int originalPosition = target.position();
  139.         final int n = super.read(target);
  140.         if (n != EOF) {
  141.             // Appending can only be done after resetting the CharBuffer to the
  142.             // right position and limit.
  143.             final int newPosition = target.position();
  144.             final int newLimit = target.limit();
  145.             try {
  146.                 target.position(originalPosition).limit(newPosition);
  147.                 branch.append(target);
  148.             } finally {
  149.                 // Reset the CharBuffer as if the appending never happened.
  150.                 target.position(newPosition).limit(newLimit);
  151.             }
  152.         }
  153.         return n;
  154.     }

  155. }