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:

  1. package com.varaneckas;  
  2.   
  3. import java.io.IOException;  
  4. import java.io.InputStream;  
  5. import java.util.LinkedList;  
  6.   
  7. /** 
  8.  * {@link InputStream} implementation that allows chaining of various  
  9.  * streams for seamless sequential reading 
  10.  *  
  11.  * @author Tomas Varaneckas <tomas.varaneckas@gmail.com> 
  12.  */  
  13. public class InputStreamChain extends InputStream {  
  14.       
  15.     /** 
  16.      * Input stream chain 
  17.      */  
  18.     private final LinkedList<InputStream> streams = new LinkedList<InputStream>();  
  19.       
  20.     /** 
  21.      * Currently active stream 
  22.      */  
  23.     private InputStream current;  
  24.       
  25.     /** 
  26.      * Default constructor 
  27.      */  
  28.     public InputStreamChain() {  
  29.         //nothing to do  
  30.     }  
  31.       
  32.     /** 
  33.      * Constructor with an initial stream 
  34.      *  
  35.      * @param first Initial InputStream 
  36.      */  
  37.     public InputStreamChain(final InputStream first) {  
  38.         addInputStream(first);  
  39.     }  
  40.       
  41.     /** 
  42.      * Constructor with an array of initial streams 
  43.      *  
  44.      * @param streams Array of initial InputStreams 
  45.      */  
  46.     public InputStreamChain(final InputStream[] streams) {  
  47.         for (InputStream stream : streams) {  
  48.             addInputStream(stream);  
  49.         }  
  50.     }  
  51.   
  52.     /** 
  53.      * Vararg constructor 
  54.      *  
  55.      * @param streams initial input streams 
  56.      */  
  57.     public InputStreamChain(final InputStream ... streams) {  
  58.         for (InputStream stream : streams) {  
  59.             addInputStream(stream);  
  60.         }  
  61.     }  
  62.   
  63.     /** 
  64.      * Adds input stream to the end of chain 
  65.      *  
  66.      * @param stream InputStream to add to chain 
  67.      * @return instance of self (for fluent calls) 
  68.      */  
  69.     public InputStreamChain addInputStream(final InputStream stream) {  
  70.         streams.addLast(stream);  
  71.         if (current == null) {  
  72.             current = streams.removeFirst();  
  73.         }  
  74.         return this;  
  75.     }  
  76.   
  77.     @Override  
  78.     public int read() throws IOException {  
  79.         int bit = current.read();  
  80.         if (bit == -1 && streams.size() > 0) {  
  81.             try {  
  82.                 current.close();  
  83.             } catch (final IOException e) {  
  84.                 //replace this with a call to logging facility  
  85.                 e.printStackTrace();  
  86.             }  
  87.             current = streams.removeFirst();  
  88.             bit = read();  
  89.         }  
  90.         return bit;  
  91.     }  
  92.       
  93.     @Override  
  94.     public int available() throws IOException {  
  95.         int available = current.available();  
  96.         for (InputStream stream : streams) {  
  97.             available += stream.available();  
  98.         }  
  99.         return available;  
  100.     }  
  101.       
  102.     @Override  
  103.     public void close() throws IOException {  
  104.         current.close();  
  105.     }  
  106.       
  107.     @Override  
  108.     public boolean markSupported() {  
  109.         return current.markSupported();  
  110.     }  
  111.       
  112.     @Override  
  113.     public synchronized void mark(int i) {  
  114.         current.mark(i);  
  115.     }  
  116.       
  117.     @Override  
  118.     public synchronized void reset() throws IOException {  
  119.         current.reset();  
  120.     }  
  121.   
  122.     @Override  
  123.     public long skip(long l) throws IOException {  
  124.         return current.skip(l);  
  125.     }  
  126.       
  127. }  


Example code:
  1. InputStream chuck = new ByteArrayInputStream("Chuck ".getBytes());  
  2. InputStream norris = new ByteArrayInputStream("Norris".getBytes());  
  3. InputStream chuckNorris = new InputStreamChain()  
  4.         .addInputStream(chuck)  
  5.         .addInputStream(norris);  
  6. //will print "Chuck Norris"  
  7. System.out.println(new BufferedReader(  
  8.         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.