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 */
17package org.apache.commons.io.input;
18
19import java.io.IOException;
20import java.io.InputStream;
21import java.io.OutputStream;
22
23/**
24 * InputStream proxy that transparently writes a copy of all bytes read
25 * from the proxied stream to a given OutputStream. Using {@link #skip(long)}
26 * or {@link #mark(int)}/{@link #reset()} on the stream will result on some
27 * bytes from the input stream being skipped or duplicated in the output
28 * stream.
29 * <p>
30 * The proxied input stream is closed when the {@link #close()} method is
31 * called on this proxy. It is configurable whether the associated output
32 * stream will also closed.
33 *
34 * @version $Id: TeeInputStream.java 587913 2007-10-24 15:47:30Z niallp $
35 * @since Commons IO 1.4
36 */
37public class TeeInputStream extends ProxyInputStream {
38
39    /**
40     * The output stream that will receive a copy of all bytes read from the
41     * proxied input stream.
42     */
43    private final OutputStream branch;
44
45    /**
46     * Flag for closing also the associated output stream when this
47     * stream is closed.
48     */
49    private final boolean closeBranch;
50
51    /**
52     * Creates a TeeInputStream that proxies the given {@link InputStream}
53     * and copies all read bytes to the given {@link OutputStream}. The given
54     * output stream will not be closed when this stream gets closed.
55     *
56     * @param input input stream to be proxied
57     * @param branch output stream that will receive a copy of all bytes read
58     */
59    public TeeInputStream(InputStream input, OutputStream branch) {
60        this(input, branch, false);
61    }
62
63    /**
64     * Creates a TeeInputStream that proxies the given {@link InputStream}
65     * and copies all read bytes to the given {@link OutputStream}. The given
66     * output stream will be closed when this stream gets closed if the
67     * closeBranch parameter is <code>true</code>.
68     *
69     * @param input input stream to be proxied
70     * @param branch output stream that will receive a copy of all bytes read
71     * @param closeBranch flag for closing also the output stream when this
72     *                    stream is closed
73     */
74    public TeeInputStream(
75            InputStream input, OutputStream branch, boolean closeBranch) {
76        super(input);
77        this.branch = branch;
78        this.closeBranch = closeBranch;
79    }
80
81    /**
82     * Closes the proxied input stream and, if so configured, the associated
83     * output stream. An exception thrown from one stream will not prevent
84     * closing of the other stream.
85     *
86     * @throws IOException if either of the streams could not be closed
87     */
88    public void close() throws IOException {
89        try {
90            super.close();
91        } finally {
92            if (closeBranch) {
93                branch.close();
94            }
95        }
96    }
97
98    /**
99     * Reads a single byte from the proxied input stream and writes it to
100     * the associated output stream.
101     *
102     * @return next byte from the stream, or -1 if the stream has ended
103     * @throws IOException if the stream could not be read (or written)
104     */
105    public int read() throws IOException {
106        int ch = super.read();
107        if (ch != -1) {
108            branch.write(ch);
109        }
110        return ch;
111    }
112
113    /**
114     * Reads bytes from the proxied input stream and writes the read bytes
115     * to the associated output stream.
116     *
117     * @param bts byte buffer
118     * @param st start offset within the buffer
119     * @param end maximum number of bytes to read
120     * @return number of bytes read, or -1 if the stream has ended
121     * @throws IOException if the stream could not be read (or written)
122     */
123    public int read(byte[] bts, int st, int end) throws IOException {
124        int n = super.read(bts, st, end);
125        if (n != -1) {
126            branch.write(bts, st, n);
127        }
128        return n;
129    }
130
131    /**
132     * Reads bytes from the proxied input stream and writes the read bytes
133     * to the associated output stream.
134     *
135     * @param bts byte buffer
136     * @return number of bytes read, or -1 if the stream has ended
137     * @throws IOException if the stream could not be read (or written)
138     */
139    public int read(byte[] bts) throws IOException {
140        int n = super.read(bts);
141        if (n != -1) {
142            branch.write(bts, 0, n);
143        }
144        return n;
145    }
146
147}
148