Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 8b947b0b072960345902212e6eff4082d2e5f440 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/*******************************************************************************
 * Copyright (c) 2006, 2008 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.equinox.internal.transforms;

import java.io.*;

/**
 * This class facilitates the moving of data from one input stream to another.  
 * Subclasses may customize the behavior of this move by overriding the {@link #pipeInput(InputStream, OutputStream)} method.
 */
public class Pipe {

	protected InputStream input;
	private PipedInputStream pipedInputStream;
	protected PipedOutputStream pipedOutputStream;

	/**
	 * Create a new Pipe based on the provided input stream.
	 * @param original the original stream.
	 * @throws IOException thrown if there is an issue establishing the pipe.
	 */
	public Pipe(InputStream original) throws IOException {
		this.input = original;

		// The following streams do the majority of the work.  
		// The first operation on the input stream will provoke a new thread to start.
		// This thread will invoke pipeInput and push the data from the original input stream to the output stream.
		// The output stream is tied to this input stream via PipedI/OStream properties so the data is available to callers on the input stream.
		// Any IOException thrown from within the thread will be caught and rethrown to callers of methods on this stream.
		this.pipedInputStream = new PipedInputStream() {
			protected IOException failure;
			private boolean started = false;
			protected Object lock = this;

			private void start() throws IOException {
				synchronized (lock) {
					if (failure != null) {
						IOException e = new IOException("Problem piping the stream."); //$NON-NLS-1$
						e.fillInStackTrace();
						e.initCause(failure);
						throw e;
					}
					if (!started) {
						started = true;
						Thread pipeThread = new Thread(new Runnable() {
							public void run() {
								try {
									pipeInput(input, pipedOutputStream);
									pipedOutputStream.close();
								} catch (IOException e) {
									synchronized (lock) {
										failure = e;
									}
								}
							}
						});
						pipeThread.start();
					}
				}
			}

			public synchronized int available() throws IOException {
				start();
				return super.available();
			}

			public synchronized int read() throws IOException {
				start();
				int c = super.read();
				return c;
			}

			public int read(byte[] b) throws IOException {
				start();
				return super.read(b);
			}

			public synchronized int read(byte[] b, int off, int len) throws IOException {
				start();
				return super.read(b, off, len);
			}

			public synchronized void reset() throws IOException {
				started = false;
				failure = null;
				input.reset();
				super.reset();
			}
		};
		this.pipedOutputStream = new PipedOutputStream(pipedInputStream);

	}

	/**
	 * Get the stream that has resulted from piping the original input stream through {@link #pipeInput(InputStream, OutputStream)}.
	 * @return the new stream.
	 */
	public InputStream getPipedInputStream() {
		return pipedInputStream;
	}

	/**
	 * Pipe the input stream to the output stream.
	 * The default implementation of this method does a simple copy operations.  
	 * Subclasses may elaborate on this behavior.
	 * @param original the original stream
	 * @param result the result stream
	 * @throws IOException thrown if there is an issue reading from the input stream or writing to the output stream.
	 */
	protected void pipeInput(InputStream original, OutputStream result) throws IOException {
		byte[] buffer = new byte[2048];
		int len = 0;
		while ((len = original.read(buffer)) != 0) {
			result.write(buffer, 0, len);
		}
	}
}

Back to the top