views:

50

answers:

2

Hello,

How can I "modify" an InputStream? I have a file as input, I would like to modify some variable and forward a new InputStream.

For example, the initial InputStream contains Hello ${var}. Then I want to "modify" this InputStream with var = "world", resulting an InputStream Hello world.

What's the best practice to do this? Thanks.

+3  A: 

The java.io is one and all decorator pattern. Make use of it and create a class which extends InputStream (maybe DataInputStream or better, some Reader since you're actually interested in characters, not in bytes, but ala), add a constructor which takes the original InputStream and override the read() methods to read the original stream in, buffer it to a certain degree (e.g. from ${ until with firstnext }) and then determine the key and return the modified data instead.

If you call your new class FormattedInputStream or so, then you could return new FormattedInputStream(originalInputStream) to the enduser instead and have the enduser still just assign and use it as an InputStream.

BalusC
+1 - this answer made me delete my own text immediatly.
Andreas_D
A: 

You could try subclassing FilterInputStream.

From the docs:

A FilterInputStream contains some other input stream, which it uses as its basic source of data, possibly transforming the data along the way or providing additional functionality. The class FilterInputStream itself simply overrides all methods of InputStream with versions that pass all requests to the contained input stream. Subclasses of FilterInputStream may further override some of these methods and may also provide additional methods and fields.

Here's an initial stab at it. Not the best way to solve it. You probably want to override a few more methods, and perhaps go with a reader instead. (Or perhaps even use a Scanner and process the file line by line.)

import java.io.*;
import java.util.*;

public class Test {
    public static void main(String args[]) throws IOException {
        String str = "Hello world, this is the value one ${bar} and this " +
                     "is the value two ${foo}";

        // The "original" input stream could just as well be a FileInputStream.
        InputStream someInputStream = new StringBufferInputStream(str);

        InputStream modified = new SubstitutionStream(someInputStream);
        int c;
        while ((c = modified.read()) != -1)
            System.out.print((char) c);

        modified.close();
    }
}


class SubstitutionStream extends FilterInputStream {

    Map<String, String> valuation = new HashMap<String, String>() {{
        put("foo", "123");
        put("bar", "789");
    }};

    public SubstitutionStream(InputStream src) {
        super(src);
    }

    LinkedList<Character> buf = new LinkedList<Character>();

    public int read() throws IOException {

        if (!buf.isEmpty())
            return buf.remove();

        int c = super.read();

        if (c != '$')
            return c;

        int c2 = super.read();

        if (c2 == '{') {
            StringBuffer varId = new StringBuffer();
            while ((c2 = super.read()) != '}')
                varId.append((char) c2);

            for (char vc : valuation.get(varId.toString()).toCharArray())
                buf.add(vc);

            return buf.remove();

        } else {
            buf.add((char) c2);
            return c;
        }
    }
}

Output:

Hello world, this is the value one 789 and this is the value two 123
aioobe
That's exactly why I suggested `DataInputStream` :) (which is a subclass of it). Though, after all, internally using a `Reader` (`InputStreamReader`) is easier.
BalusC
Aha, I didn't realize that DataInputStream was a FilterInputStream. (Makes sense though.) So what does DataInputStream provide that FilterInputStream doesn't (thats useful in this situation.)
aioobe