Skip to content

Commit 422ce6f

Browse files
author
Niall Pemberton
committed
IO-129 add new TeeInputStream implementation - thanks to Jukka Zitting
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/io/trunk@584407 13f79535-47bb-0310-9956-ffa450edef68
1 parent 1b4e77f commit 422ce6f

3 files changed

Lines changed: 205 additions & 0 deletions

File tree

RELEASE-NOTES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ Enhancements from 1.3.2
4848
- CloseShieldInputStream - prevents the underlying input stream from being closed.
4949
- CloseShieldOutputStream - prevents the underlying output stream from being closed.
5050

51+
- TeeInputStream [IO-129]
52+
- Add new Tee input stream implementation
53+
5154

5255
Feedback
5356
--------
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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+
19+
import java.io.IOException;
20+
import java.io.InputStream;
21+
import 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+
* Unlike the proxied input stream (that gets closed when {@link #close()}
31+
* is called), the associated output stream is never closed by this class.
32+
*
33+
* @since Commons IO 1.4
34+
*/
35+
public class TeeInputStream extends ProxyInputStream {
36+
37+
/**
38+
* The output stream that will receive a copy of all bytes read from the
39+
* proxied input stream.
40+
*/
41+
private final OutputStream branch;
42+
43+
/**
44+
* Creates a TeeInputStream that proxies the given {@link InputStream}
45+
* and copies all read bytes to the given {@link OutputStream}.
46+
*
47+
* @param input input stream to be proxied
48+
* @param branch output stream that will receive a copy of all bytes read
49+
*/
50+
public TeeInputStream(InputStream input, OutputStream branch) {
51+
super(input);
52+
this.branch = branch;
53+
}
54+
55+
/**
56+
* Reads a single byte from the proxied input stream and writes it to
57+
* the associated output stream.
58+
*
59+
* @return next byte from the stream, or -1 if the stream has ended
60+
* @throws IOException if the stream could not be read (or written)
61+
*/
62+
public int read() throws IOException {
63+
int ch = super.read();
64+
if (ch != -1) {
65+
branch.write(ch);
66+
}
67+
return ch;
68+
}
69+
70+
/**
71+
* Reads bytes from the proxied input stream and writes the read bytes
72+
* to the associated output stream.
73+
*
74+
* @param bts byte buffer
75+
* @param st start offset within the buffer
76+
* @param end maximum number of bytes to read
77+
* @return number of bytes read, or -1 if the stream has ended
78+
* @throws IOException if the stream could not be read (or written)
79+
*/
80+
public int read(byte[] bts, int st, int end) throws IOException {
81+
int n = super.read(bts, st, end);
82+
if (n != -1) {
83+
branch.write(bts, st, n);
84+
}
85+
return n;
86+
}
87+
88+
/**
89+
* Reads bytes from the proxied input stream and writes the read bytes
90+
* to the associated output stream.
91+
*
92+
* @param bts byte buffer
93+
* @return number of bytes read, or -1 if the stream has ended
94+
* @throws IOException if the stream could not be read (or written)
95+
*/
96+
public int read(byte[] bts) throws IOException {
97+
int n = super.read(bts);
98+
if (n != -1) {
99+
branch.write(bts, 0, n);
100+
}
101+
return n;
102+
}
103+
104+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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+
19+
import java.io.ByteArrayInputStream;
20+
import java.io.ByteArrayOutputStream;
21+
import java.io.InputStream;
22+
23+
import junit.framework.TestCase;
24+
25+
/**
26+
* JUnit Test Case for {@link TeeInputStream}.
27+
*/
28+
public class TeeInputStreamTest extends TestCase {
29+
30+
private final String ASCII = "US-ASCII";
31+
32+
private InputStream tee;
33+
34+
private ByteArrayOutputStream output;
35+
36+
protected void setUp() throws Exception {
37+
InputStream input = new ByteArrayInputStream("abc".getBytes(ASCII));
38+
output = new ByteArrayOutputStream();
39+
tee = new TeeInputStream(input, output);
40+
}
41+
42+
public void testReadNothing() throws Exception {
43+
assertEquals("", new String(output.toString(ASCII)));
44+
}
45+
46+
public void testReadOneByte() throws Exception {
47+
assertEquals('a', tee.read());
48+
assertEquals("a", new String(output.toString(ASCII)));
49+
}
50+
51+
public void testReadEverything() throws Exception {
52+
assertEquals('a', tee.read());
53+
assertEquals('b', tee.read());
54+
assertEquals('c', tee.read());
55+
assertEquals(-1, tee.read());
56+
assertEquals("abc", new String(output.toString(ASCII)));
57+
}
58+
59+
public void testReadToArray() throws Exception {
60+
byte[] buffer = new byte[8];
61+
assertEquals(3, tee.read(buffer));
62+
assertEquals('a', buffer[0]);
63+
assertEquals('b', buffer[1]);
64+
assertEquals('c', buffer[2]);
65+
assertEquals(-1, tee.read(buffer));
66+
assertEquals("abc", new String(output.toString(ASCII)));
67+
}
68+
69+
public void testReadToArrayWithOffset() throws Exception {
70+
byte[] buffer = new byte[8];
71+
assertEquals(3, tee.read(buffer, 4, 4));
72+
assertEquals('a', buffer[4]);
73+
assertEquals('b', buffer[5]);
74+
assertEquals('c', buffer[6]);
75+
assertEquals(-1, tee.read(buffer, 4, 4));
76+
assertEquals("abc", new String(output.toString(ASCII)));
77+
}
78+
79+
public void testSkip() throws Exception {
80+
assertEquals('a', tee.read());
81+
assertEquals(1, tee.skip(1));
82+
assertEquals('c', tee.read());
83+
assertEquals(-1, tee.read());
84+
assertEquals("ac", new String(output.toString(ASCII)));
85+
}
86+
87+
public void testMarkReset() throws Exception {
88+
assertEquals('a', tee.read());
89+
tee.mark(1);
90+
assertEquals('b', tee.read());
91+
tee.reset();
92+
assertEquals('b', tee.read());
93+
assertEquals('c', tee.read());
94+
assertEquals(-1, tee.read());
95+
assertEquals("abbc", new String(output.toString(ASCII)));
96+
}
97+
98+
}

0 commit comments

Comments
 (0)