Tuesday, March 04, 2008

Reading from a bunch of strings

This extends a java.io.Reader which will be reading from a bunch of strings.  The purpose of this is to give the basis of a reader that would read chunks of data which are already be a Strings, but not allocate the whole string information in memory.  For example, reading XML from VARCHARs that can span more than one record.

Test case

The test case shows a base cases and the general case.

public class BunchOfStringsReaderTest {
    @Test
    public void readFromBunchOfStrings() throws Exception {
        final Reader p = new BunchOfStringsReader("FooBar", "mew mew",
                "pikachu");
        final char[] buf = new char[1000];
        int c;
        int i = 0;
        do {
            c = p.read(buf, i, 10);
            i += c;
        } while (c >= 0);
        final String s = new String(buf, 0, i + 1);
        Assert.assertEquals("FooBarmew mewpikachu", s);
 
    }
 
    @Test
    public void readFromNothing() throws Exception {
        final Reader p = new BunchOfStringsReader();
        final char[] buf = new char[20];
        final int c = p.read(buf, 0, 1);
        Assert.assertEquals(c, -1);
    }
 
    @Test
    public void readFromOneString() throws Exception {
        final Reader p = new BunchOfStringsReader("FooBar");
        final char[] buf = new char[20];
        final int c = p.read(buf, 0, 1);
        Assert.assertEquals(c, 1);
        Assert.assertEquals('F', buf[0]);
 
    }
 
}

The actual code

The following shows the code to be implemented to satisfy the test case.

public class BunchOfStringsReader extends Reader {

    private StringReader currentReader = null;
    private int i = 0;
    private final String[] strings;

    public BunchOfStringsReader(final String... strings) {
        this.strings = strings;
        if (strings.length > 0) {
            currentReader = new StringReader(strings[0]);
        }
    }

    @Override
    public void close() throws IOException {

    }

    public StringReader getNextString() {
        return new StringReader(strings[++i]);
    }

    private boolean hasNextString() {
        return i + 1 < strings.length;
    }

    @Override
    public boolean markSupported() {
        return false;
    }

    @Override
    public int read(final char[] cbuf, final int off, final int len)
            throws IOException {
        if (currentReader == null) {
            return -1;
        }
        int c = currentReader.read(cbuf, off, len);
        if (c == -1 && hasNextString()) {
            currentReader = getNextString();
            c = currentReader.read(cbuf, off, len);
        }
        return c;
    }
}

Of course this is just a sample of what can be done.  A more realistic implementation such as something that will get the data from a database record would do the following:

  • modify the constructor to get a database connection/rowset/etc. passed onto it.
  • modify hasNextString() and getNextString() to get the check if there is a next record and get the data from the database.
  • modify close() to close the database connection/rowset/etc.

Further work

The performance can be further improved by not relying on the StringReader and managing it via code.  However, the resulting code would be more complicated, so it is left as an exercise for the reader.

0 comments: