// This example is from the book _Java in a Nutshell_ by David Flanagan.
// Written by David Flanagan. Copyright (c) 1996 O'Reilly & Associates.
// You may study, use, modify, and distribute this example for any purpose.
// This example is provided WITHOUT WARRANTY either expressed or implied.
import java.io.*;
// This interface defines the methods that the source of data for a pipe
// must define. These methods are used to allow the source to easily be
// connected to a sink.
// Note that a PipeSource is Runnable--it is a thread body.
interface PipeSource extends Runnable {
abstract PipedOutputStream getPipedOutputStream();
abstract void connectOutputTo(PipeSink sink) throws IOException;
abstract PipeSink getSink();
abstract void start();
}
// This interface defines the methods required for a sink of data from
// a pipe. The methods are used when connecting a source to the sink.
// A PipeSink is also Runnable.
interface PipeSink extends Runnable {
abstract PipedInputStream getPipedInputStream();
abstract void connectInputTo(PipeSource source) throws IOException;
}
// A filter in a pipe behaves like both a source of data and a sink of data.
interface PipeFilter extends PipeSource, PipeSink {
};
// This is an implementation of the PipeSource interface that uses an
// InputStream as the source of data. It creates a PipedOutputStream
// to write the data to. The run() method copies data from the stream
// to the pipe.
class StreamPipeSource implements PipeSource {
protected PipedOutputStream out = new PipedOutputStream();
protected InputStream in;
protected PipeSink sink;
public StreamPipeSource(InputStream in) { this.in = in; }
public PipedOutputStream getPipedOutputStream() { return out; }
public PipeSink getSink() { return sink; }
public void connectOutputTo(PipeSink sink) throws IOException {
this.sink = sink;
out.connect(sink.getPipedInputStream());
sink.connectInputTo(this);
}
public void start() {
// Start ourselves
new Thread(this).start();
// If our output is a filter, then call its start() method.
// otherwise, just start our sink
if (sink instanceof PipeFilter) ((PipeFilter)sink).start();
else new Thread(sink).start();
}
public void run() {
byte[] buffer = new byte[512];
int bytes_read;
try {
for(;;) {
bytes_read = in.read(buffer);
if (bytes_read == -1) { return; }
out.write(buffer, 0, bytes_read);
}
}
catch (IOException e) {
if (e instanceof EOFException) return;
else System.out.println(e);
}
finally { try {out.close();} catch (IOException e) {;} }
}
}
// This is an implementation of the PipeSink interface that uses an output
// stream as the source of data. It creates a PipedInputStream to read
// data from the pipe. The run() method reads data from that pipe and
// writes it to the output stream (which might be a file or System.out, e.g.)
class StreamPipeSink implements PipeSink {
protected PipedInputStream in = new PipedInputStream();
protected OutputStream out;
public StreamPipeSink(OutputStream out) {
this.out = out;
}
public PipedInputStream getPipedInputStream() { return in; }
public void connectInputTo(PipeSource source) throws IOException {
in.connect(source.getPipedOutputStream());
}
public void run() {
byte[] buffer = new byte[512];
int bytes_read;
try {
for(;;) {
bytes_read = in.read(buffer);
if (bytes_read == -1) return;
out.write(buffer, 0, bytes_read);
}
}
catch (IOException e) {
if (e instanceof EOFException) return;
else System.out.println(e);
}
finally { try {in.close();} catch (IOException e) {;} }
}
}
// This is an abstract implementation of the PipeFilter interface.
// It creates both a PipedInputStream to read from and a PipedOutputStream
// to write to. The abstract method filter() needs to be defined by
// a subclass to read data from one pipe, filter it, and write the filtered
// data to the other pipe.
abstract class BasicPipeFilter implements PipeFilter {
protected PipedInputStream in = new PipedInputStream();
protected PipedOutputStream out = new PipedOutputStream();
protected PipeSink sink;
public PipedInputStream getPipedInputStream() { return in; }
public PipedOutputStream getPipedOutputStream() { return out; }
public void connectOutputTo(PipeSink sink) throws IOException {
this.sink = sink;
out.connect(sink.getPipedInputStream());
sink.connectInputTo((PipeSource) this);
}
public void start() {
// Start ourselves
new Thread(this).start();
// If our output is a filter, then call its start() method.
// otherwise, just start our sink
if (sink instanceof PipeFilter) ((PipeFilter)sink).start();
else new Thread(sink).start();
}
public PipeSink getSink() { return sink; }
public void connectInputTo(PipeSource source) throws IOException {
in.connect(source.getPipedOutputStream());
}
public void run() {
try { filter(); }
catch (IOException e) {
if (e instanceof EOFException) return;
else System.out.println(e);
}
finally {
try { out.close(); in.close(); }
catch (IOException e) { ; }
}
}
abstract public void filter() throws IOException;
}
// This is a non-abstract implementation of the PipeFilter interface.
// It uses the GrepInputStream we defined elsewhere to do the filtering.
class GrepFilter extends BasicPipeFilter {
protected GrepInputStream gis;
protected PrintStream pout = new PrintStream(out);
public GrepFilter(String pattern) {
gis = new GrepInputStream(new DataInputStream(in), pattern);
}
public void filter() throws IOException {
String line;
for(;;) {
line = gis.readLine();
if (line == null) return;
pout.println(line);
}
}
}
// This is another implementation fo PipeFilter. It implements the
// trival rot13 cypher on the letters A-Z and a-z.
class Rot13Filter extends BasicPipeFilter {
public void filter() throws IOException {
byte[] buffer = new byte[512];
int bytes_read;
for(;;) {
bytes_read = in.read(buffer);
if (bytes_read == -1) return;
for(int i = 0; i < bytes_read; i++) {
if ((buffer[i] >= 'a') && (buffer[i] <= 'z')) {
buffer[i] = (byte) ('a' + ((buffer[i]-'a') + 13) % 26);
}
if ((buffer[i] >= 'A') && (buffer[i] <= 'Z')) {
buffer[i] = (byte) ('A' + ((buffer[i]-'A') + 13) % 26);
}
}
out.write(buffer, 0, bytes_read);
}
}
}
// This class demonstrates how you might use these pipe classes.
// It is another implementation of a Unix-like grep command.
// Note that it frivolously passes the output of the grep pipe through two
// rot13 pipes (which, combined, leave the output unchanged).
// With the source, filter, and sink infrastructure defined above, it
// is easy to define new filters and create pipes to perform many useful
// operations. Other filter possibilities include sorting lines, removing
// duplicate lines, and doing search-and-replace.
public class Pipes {
public static void main(String[] args) throws IOException {
if (args.length != 2) {
System.out.println("Usage: java Pipes <pattern> <filename>");
System.exit(0);
}
// Create a source, three filters, and a sink for the pipe.
PipeSource source =
new StreamPipeSource(new FileInputStream(args[1]));
PipeFilter filter = new GrepFilter(args[0]);
PipeFilter filter2 = new Rot13Filter();
PipeFilter filter3 = new Rot13Filter();
PipeSink sink = new StreamPipeSink(System.out);
// Connect them all up.
source.connectOutputTo(filter);
filter.connectOutputTo(filter2);
filter2.connectOutputTo(filter3);
filter3.connectOutputTo(sink);
// And start their threads running.
source.start();
}
}
|