blob: c6f3793838788f0044f6017db69032088542b988 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* 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);
}
}
}