2008-11-11

InputStreamChain

If you have several Java InputStreams that you want to queue up into a single InputStream object, you can use this:

package com.varaneckas;

import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;

/**
* {@link InputStream} implementation that allows chaining of various
* streams for seamless sequential reading
*
* @author Tomas Varaneckas <tomas.varaneckas@gmail.com>
*/
public class InputStreamChain extends InputStream {

/**
* Input stream chain
*/
private final LinkedList<InputStream> streams = new LinkedList<InputStream>();

/**
* Currently active stream
*/
private InputStream current;

/**
* Default constructor
*/
public InputStreamChain() {
//nothing to do
}

/**
* Constructor with an initial stream
*
* @param first Initial InputStream
*/
public InputStreamChain(final InputStream first) {
addInputStream(first);
}

/**
* Constructor with an array of initial streams
*
* @param streams Array of initial InputStreams
*/
public InputStreamChain(final InputStream[] streams) {
for (InputStream stream : streams) {
addInputStream(stream);
}
}

/**
* Vararg constructor
*
* @param streams initial input streams
*/
public InputStreamChain(final InputStream ... streams) {
for (InputStream stream : streams) {
addInputStream(stream);
}
}

/**
* Adds input stream to the end of chain
*
* @param stream InputStream to add to chain
* @return instance of self (for fluent calls)
*/
public InputStreamChain addInputStream(final InputStream stream) {
streams.addLast(stream);
if (current == null) {
current = streams.removeFirst();
}
return this;
}

@Override
public int read() throws IOException {
int bit = current.read();
if (bit == -1 && streams.size() > 0) {
try {
current.close();
} catch (final IOException e) {
//replace this with a call to logging facility
e.printStackTrace();
}
current = streams.removeFirst();
bit = read();
}
return bit;
}

@Override
public int available() throws IOException {
int available = current.available();
for (InputStream stream : streams) {
available += stream.available();
}
return available;
}

@Override
public void close() throws IOException {
current.close();
}

@Override
public boolean markSupported() {
return current.markSupported();
}

@Override
public synchronized void mark(int i) {
current.mark(i);
}

@Override
public synchronized void reset() throws IOException {
current.reset();
}

@Override
public long skip(long l) throws IOException {
return current.skip(l);
}

}


Example code:
InputStream chuck = new ByteArrayInputStream("Chuck ".getBytes());
InputStream norris = new ByteArrayInputStream("Norris".getBytes());
InputStream chuckNorris = new InputStreamChain()
.addInputStream(chuck)
.addInputStream(norris);
//will print "Chuck Norris"
System.out.println(new BufferedReader(
new InputStreamReader(chuckNorris)).readLine());


Have fun!

3 comments:

  1. Cool, I can see how that can be handy! But why not use vararg and allow links in the chain to be added through the constructor? Method chaining introduces mutability and a wider, more complex state space that I don't see is necessary here.

    ReplyDelete
  2. java.io.SequenceInputStream?

    ReplyDelete
  3. @casper bang
    Yeah, I guess you're right, haven't thought of adding vararg constructor in first place. I'll update the source.

    @phloser
    Damn, I knew there would be something like this done already, but I was doing this in a hurry and a quick research gave me no results so it was faster to roll my own... Anyway, I decompiled and looked at java.lang.SequenceInputStream implementation. It looks old and somewhat cumbersome, with Enumeration in the constructor, etc.
    No matter what, it was fun writing my own class. :)

    ReplyDelete

Spam comments (i.e. ones that contain links to web development services) will be reported along with user profiles!

Note: only a member of this blog may post a comment.