24 March 2007

Composition in Java: sequential stream concatenation

Raymond Chen wrote a short article about concatenating two streams, a simple example of the composite pattern. More interestingly, he followed up with a longer post about how this is useful for implementing a rewindable stream. The latter case seemed so useful, I decided to have a go at implementing it in Java.

For the first part of this implementation, I'll just do what Raymond did: implement a composite stream class. Once this is working properly, the second part will cover how the composite stream is used to create a rewindable stream — resettable in Java parlance.

To develop a CompositeInputStream, we start with a test that will show us when we're done:

public class TestCompositeInputStream extends TestCase
{
    public void testRead() throws Exception
    {
        InputStream first = new ByteArrayInputStream(new byte[]{ 0, 1, 2, 3 });
        InputStream second = new ByteArrayInputStream(new byte[]{ 4, 5, 6, 7 });
        InputStream composite = new CompositeInputStream(first, second);

        byte[] buffer = new byte[8];
        int bytesRead = composite.read(buffer, 0, 8);
        assertEquals(8, bytesRead);
        for (int i = 0; i < 8; i++)
            assertEquals(i, buffer[i]);
    }
}

The test creates two InputStreams, the first with the numbers 0-3, and the second with the numbers 4-7. It then creates a composite stream containing the two inputs, and ensures that reading from the composite stream returns all 8 numbers in the correct order.

Thanks to automatic garbage collection and an absence of macros, the Java implementation is even simpler than Raymond's C++ one:

public class CompositeInputStream extends InputStream
{
    private final InputStream first;
    private final InputStream second;

    public CompositeInputStream(InputStream first, InputStream second)
    {
        this.first = first;
        this.second = second;
    }

    public int read() throws IOException
    {
        int result = first.read();
        if (result == -1)
            result = second.read();
        return result;
    }
}

To create a subclass of InputStream, it's as easy as overriding one method, read(), which returns the next byte from the stream or -1 if none remain. This method in our CompositeInputStream reads a byte from the first source if it is available, otherwise it reads from the second.

While I've decided to implement this myself, for production use I'd recommend the API class SequenceInputStream, which is designed to do exactly this task.

Next time, we'll use this class to implement a resettable stream.