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