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 java.io.IOException;
020import java.io.InputStream;
021import java.io.OutputStream;
022
023/**
024 * InputStream proxy that transparently writes a copy of all bytes read
025 * from the proxied stream to a given OutputStream. Using {@link #skip(long)}
026 * or {@link #mark(int)}/{@link #reset()} on the stream will result on some
027 * bytes from the input stream being skipped or duplicated in the output
028 * stream.
029 * <p>
030 * The proxied input stream is closed when the {@link #close()} method is
031 * called on this proxy. It is configurable whether the associated output
032 * stream will also closed.
033 *
034 * @version $Id: TeeInputStream.java 1307461 2012-03-30 15:12:29Z ggregory $
035 * @since 1.4
036 */
037public class TeeInputStream extends ProxyInputStream {
038
039    /**
040     * The output stream that will receive a copy of all bytes read from the
041     * proxied input stream.
042     */
043    private final OutputStream branch;
044
045    /**
046     * Flag for closing also the associated output stream when this
047     * stream is closed.
048     */
049    private final boolean closeBranch;
050
051    /**
052     * Creates a TeeInputStream that proxies the given {@link InputStream}
053     * and copies all read bytes to the given {@link OutputStream}. The given
054     * output stream will not be closed when this stream gets closed.
055     *
056     * @param input input stream to be proxied
057     * @param branch output stream that will receive a copy of all bytes read
058     */
059    public TeeInputStream(InputStream input, OutputStream branch) {
060        this(input, branch, false);
061    }
062
063    /**
064     * Creates a TeeInputStream that proxies the given {@link InputStream}
065     * and copies all read bytes to the given {@link OutputStream}. The given
066     * output stream will be closed when this stream gets closed if the
067     * closeBranch parameter is {@code true}.
068     *
069     * @param input input stream to be proxied
070     * @param branch output stream that will receive a copy of all bytes read
071     * @param closeBranch flag for closing also the output stream when this
072     *                    stream is closed
073     */
074    public TeeInputStream(
075            InputStream input, OutputStream branch, boolean closeBranch) {
076        super(input);
077        this.branch = branch;
078        this.closeBranch = closeBranch;
079    }
080
081    /**
082     * Closes the proxied input stream and, if so configured, the associated
083     * output stream. An exception thrown from one stream will not prevent
084     * closing of the other stream.
085     *
086     * @throws IOException if either of the streams could not be closed
087     */
088    @Override
089    public void close() throws IOException {
090        try {
091            super.close();
092        } finally {
093            if (closeBranch) {
094                branch.close();
095            }
096        }
097    }
098
099    /**
100     * Reads a single byte from the proxied input stream and writes it to
101     * the associated output stream.
102     *
103     * @return next byte from the stream, or -1 if the stream has ended
104     * @throws IOException if the stream could not be read (or written) 
105     */
106    @Override
107    public int read() throws IOException {
108        int ch = super.read();
109        if (ch != -1) {
110            branch.write(ch);
111        }
112        return ch;
113    }
114
115    /**
116     * Reads bytes from the proxied input stream and writes the read bytes
117     * to the associated output stream.
118     *
119     * @param bts byte buffer
120     * @param st start offset within the buffer
121     * @param end maximum number of bytes to read
122     * @return number of bytes read, or -1 if the stream has ended
123     * @throws IOException if the stream could not be read (or written) 
124     */
125    @Override
126    public int read(byte[] bts, int st, int end) throws IOException {
127        int n = super.read(bts, st, end);
128        if (n != -1) {
129            branch.write(bts, st, n);
130        }
131        return n;
132    }
133
134    /**
135     * Reads bytes from the proxied input stream and writes the read bytes
136     * to the associated output stream.
137     *
138     * @param bts byte buffer
139     * @return number of bytes read, or -1 if the stream has ended
140     * @throws IOException if the stream could not be read (or written) 
141     */
142    @Override
143    public int read(byte[] bts) throws IOException {
144        int n = super.read(bts);
145        if (n != -1) {
146            branch.write(bts, 0, n);
147        }
148        return n;
149    }
150
151}