    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.io.input;
    018
    019import static org.apache.commons.io.IOUtils.EOF;
    020
    021import java.io.IOException;
    022import java.io.Reader;
    023import java.io.Writer;
    024import java.nio.CharBuffer;
    025
    026/**
    027 * Reader proxy that transparently writes a copy of all characters read from the proxied reader to a given Reader. Using
    028 * {@link #skip(long)} or {@link #mark(int)}/{@link #reset()} on the reader will result on some characters from the
    029 * reader being skipped or duplicated in the writer.
    030 * <p>
    031 * The proxied reader is closed when the {@link #close()} method is called on this proxy. You may configure whether the
    032 * reader closes the writer.
    033 * </p>
    034 *
    035 * @since 2.7
    036 */
    037public class TeeReader extends ProxyReader {
    038
    039    /**
    040     * The writer that will receive a copy of all characters read from the proxied reader.
    041     */
    042    private final Writer branch;
    043
    044    /**
    045     * Flag for closing the associated writer when this reader is closed.
    046     */
    047    private final boolean closeBranch;
    048
    049    /**
    050     * Creates a TeeReader that proxies the given {@link Reader} and copies all read characters to the given
    051     * {@link Writer}. The given writer will not be closed when this reader gets closed.
    052     *
    053     * @param input  reader to be proxied
    054     * @param branch writer that will receive a copy of all characters read
    055     */
    056    public TeeReader(final Reader input, final Writer branch) {
    057        this(input, branch, false);
    058    }
    059
    060    /**
    061     * Creates a TeeReader that proxies the given {@link Reader} and copies all read characters to the given
    062     * {@link Writer}. The given writer will be closed when this reader gets closed if the closeBranch parameter is
    063     * {@code true}.
    064     *
    065     * @param input       reader to be proxied
    066     * @param branch      writer that will receive a copy of all characters read
    067     * @param closeBranch flag for closing also the writer when this reader is closed
    068     */
    069    public TeeReader(final Reader input, final Writer branch, final boolean closeBranch) {
    070        super(input);
    071        this.branch = branch;
    072        this.closeBranch = closeBranch;
    073    }
    074
    075    /**
    076     * Closes the proxied reader and, if so configured, the associated writer. An exception thrown from the reader will
    077     * not prevent closing of the writer.
    078     *
    079     * @throws IOException if either the reader or writer could not be closed
    080     */
    081    @Override
    082    public void close() throws IOException {
    083        try {
    084            super.close();
    085        } finally {
    086            if (closeBranch) {
    087                branch.close();
    088            }
    089        }
    090    }
    091
    092    /**
    093     * Reads a single character from the proxied reader and writes it to the associated writer.
    094     *
    095     * @return next character from the reader, or -1 if the reader has ended
    096     * @throws IOException if the reader could not be read (or written)
    097     */
    098    @Override
    099    public int read() throws IOException {
    100        final int ch = super.read();
    101        if (ch != EOF) {
    102            branch.write(ch);
    103        }
    104        return ch;
    105    }
    106
    107    /**
    108     * Reads characters from the proxied reader and writes the read characters to the associated writer.
    109     *
    110     * @param chr character buffer
    111     * @return number of characters read, or -1 if the reader has ended
    112     * @throws IOException if the reader could not be read (or written)
    113     */
    114    @Override
    115    public int read(final char[] chr) throws IOException {
    116        final int n = super.read(chr);
    117        if (n != EOF) {
    118            branch.write(chr, 0, n);
    119        }
    120        return n;
    121    }
    122
    123    /**
    124     * Reads characters from the proxied reader and writes the read characters to the associated writer.
    125     *
    126     * @param chr character buffer
    127     * @param st  start offset within the buffer
    128     * @param end maximum number of characters to read
    129     * @return number of characters read, or -1 if the reader has ended
    130     * @throws IOException if the reader could not be read (or written)
    131     */
    132    @Override
    133    public int read(final char[] chr, final int st, final int end) throws IOException {
    134        final int n = super.read(chr, st, end);
    135        if (n != EOF) {
    136            branch.write(chr, st, n);
    137        }
    138        return n;
    139    }
    140
    141    /**
    142     * Reads characters from the proxied reader and writes the read characters to the associated writer.
    143     *
    144     * @param target character buffer
    145     * @return number of characters read, or -1 if the reader has ended
    146     * @throws IOException if the reader could not be read (or written)
    147     */
    148    @Override
    149    public int read(final CharBuffer target) throws IOException {
    150        final int originalPosition = target.position();
    151        final int n = super.read(target);
    152        if (n != EOF) {
    153            // Appending can only be done after resetting the CharBuffer to the
    154            // right position and limit.
    155            final int newPosition = target.position();
    156            final int newLimit = target.limit();
    157            try {
    158                target.position(originalPosition).limit(newPosition);
    159                branch.append(target);
    160            } finally {
    161                // Reset the CharBuffer as if the appending never happened.
    162                target.position(newPosition).limit(newLimit);
    163            }
    164        }
    165        return n;
    166    }
    167
    168}