package nl.helixsoft.util; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; /** * A {@link Reader} with buffer and a peek method to just see the next character. Useful in many parser designs. * Unlike {@link ParseBuffer} this is a Reader so it doesn't ignore text encodings. */ public class PeekReader extends Reader { static final int BUFFER_SIZE = 0x10000; private final Reader parent; private final char[] buf; final int size; int pos = 0; int fill = 0; boolean eof = false; public PeekReader (InputStream parent) throws UnsupportedEncodingException { this (new InputStreamReader (parent, "UTF-8"), BUFFER_SIZE); } public PeekReader (InputStream parent, String charsetName) throws UnsupportedEncodingException { this (new InputStreamReader(parent, charsetName), BUFFER_SIZE); } public PeekReader (InputStream parent, int size) throws UnsupportedEncodingException { this (new InputStreamReader(parent, "UTF-8"), size); } public PeekReader(Reader parent, int size) { this.parent = parent; buf = new char[size]; this.size = size; } @Override public int read() throws IOException { int result = peek(); if (result < 0) return result; pos++; pos %= size; return result; } public int peek() throws IOException { if (pos == fill) { if (eof) return -1; fillNext(); if (pos == fill && eof) return -1; } int result = buf[pos]; // check for characters above 127 if (result < 0) throw new IllegalStateException ("ParseBuffer.peek() does not yet support anything non-ascii!"); return result; } private void fillNext() throws IOException { int end = Math.min (size, pos + (size / 2)); int delta = end - pos; int result = parent.read(buf, fill, delta); if (result < 0) { eof = true; return; } fill += result; fill %= size; } public int getPos() { return pos; } public String subString(int start, int end) { String result; if (start < end) result = new String(buf, start, end - start); else { StringBuilder builder = new StringBuilder(size + end - start); builder.append (new String(buf, start, (size - start))); builder.append (new String(buf, 0, end)); result = builder.toString(); } return result; } // for testing int getFill() { return fill; } @Override public int read(char[] cbuf, int off, int len) throws IOException { if (pos == fill && eof) return -1; for (int i = 0; i < len; ++i) { int r = read(); if (r == -1) return i; cbuf[off + i] = (char)r; } return len; } @Override public void close() throws IOException { } }