/* * $Id:$ * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. */ package oms3.io; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; /** * ExtendedBufferedReader * * A special reader decorater which supports more * sophisticated access to the underlying reader object. * * In particular the reader supports a look-ahead option, * which allows you to see the next char returned by * next(). * Furthermore the skip-method supports skipping until * (but excluding) a given char. Similar functionality * is supported by the reader as well. * */ class ExtendedBufferedReader extends BufferedReader { /** the end of stream symbol */ public static final int END_OF_STREAM = -1; /** undefined state for the lookahead char */ public static final int UNDEFINED = -2; /** the lookahead chars */ private int lookaheadChar = UNDEFINED; /** the last char returned */ private int lastChar = UNDEFINED; /** the line counter */ private int lineCounter = 0; private CharBuffer line = new CharBuffer(); /** * Created extended buffered reader using default buffer-size * */ public ExtendedBufferedReader(Reader r) { super(r); /* note uh: do not fetch the first char here, * because this might block the method! */ } /** * Create extended buffered reader using the given buffer-size */ public ExtendedBufferedReader(Reader r, int bufSize) { super(r, bufSize); /* note uh: do not fetch the first char here, * because this might block the method! */ } /** * Reads the next char from the input stream. * @return the next char or END_OF_STREAM if end of stream has been reached. */ @Override public int read() throws IOException { // initalize the lookahead if (lookaheadChar == UNDEFINED) { lookaheadChar = super.read(); } lastChar = lookaheadChar; if (super.ready()) { lookaheadChar = super.read(); } else { lookaheadChar = UNDEFINED; } if (lastChar == '\n') { lineCounter++; } return lastChar; } /** * Returns the last read character again. * * @return the last read char or UNDEFINED */ public int readAgain() { return lastChar; } /** * Non-blocking reading of len chars into buffer buf starting * at bufferposition off. * * performs an iteratative read on the underlying stream * as long as the following conditions hold: * - less than len chars have been read * - end of stream has not been reached * - next read is not blocking * * @return nof chars actually read or END_OF_STREAM */ public int read(char[] buf, int off, int len) throws IOException { // do not claim if len == 0 if (len == 0) { return 0; } // init lookahead, but do not block !! if (lookaheadChar == UNDEFINED) { if (ready()) { lookaheadChar = super.read(); } else { return -1; } } // 'first read of underlying stream' if (lookaheadChar == -1) { return -1; } // continue until the lookaheadChar would block int cOff = off; while (len > 0 && ready()) { if (lookaheadChar == -1) { // eof stream reached, do not continue return cOff - off; } else { buf[cOff++] = (char) lookaheadChar; if (lookaheadChar == '\n') { lineCounter++; } lastChar = lookaheadChar; lookaheadChar = super.read(); len--; } } return cOff - off; } /** * Reads all characters up to (but not including) the given character. * * @param c the character to read up to * @return the string up to the character <code>c</code> * @throws IOException */ public String readUntil(char c) throws IOException { if (lookaheadChar == UNDEFINED) { lookaheadChar = super.read(); } line.clear(); // reuse while (lookaheadChar != c && lookaheadChar != END_OF_STREAM) { line.append((char) lookaheadChar); if (lookaheadChar == '\n') { lineCounter++; } lastChar = lookaheadChar; lookaheadChar = super.read(); } return line.toString(); } /** * @return A String containing the contents of the line, not * including any line-termination characters, or null * if the end of the stream has been reached */ public String readLine() throws IOException { if (lookaheadChar == UNDEFINED) { lookaheadChar = super.read(); } line.clear(); //reuse // return null if end of stream has been reached if (lookaheadChar == END_OF_STREAM) { return null; } // do we have a line termination already char laChar = (char) lookaheadChar; if (laChar == '\n' || laChar == '\r') { lastChar = lookaheadChar; lookaheadChar = super.read(); // ignore '\r\n' as well if ((char) lookaheadChar == '\n') { lastChar = lookaheadChar; lookaheadChar = super.read(); } lineCounter++; return line.toString(); } // create the rest-of-line return and update the lookahead line.append(laChar); String restOfLine = super.readLine(); // TODO involves copying lastChar = lookaheadChar; lookaheadChar = super.read(); if (restOfLine != null) { line.append(restOfLine); } lineCounter++; return line.toString(); } /** * Skips char in the stream * * ATTENTION: invalidates the line-counter !!!!! * * @return nof skiped chars */ public long skip(long n) throws IllegalArgumentException, IOException { if (lookaheadChar == UNDEFINED) { lookaheadChar = super.read(); } // illegal argument if (n < 0) { throw new IllegalArgumentException("negative argument not supported"); } // no skipping if (n == 0 || lookaheadChar == END_OF_STREAM) { return 0; } // skip and reread the lookahead-char long skiped = 0; if (n > 1) { skiped = super.skip(n - 1); } lookaheadChar = super.read(); // fixme uh: we should check the skiped sequence for line-terminations... lineCounter = Integer.MIN_VALUE; return skiped + 1; } /** * Skips all chars in the input until (but excluding) the given char * * @param c * @return * @throws IllegalArgumentException * @throws IOException */ public long skipUntil(char c) throws IllegalArgumentException, IOException { if (lookaheadChar == UNDEFINED) { lookaheadChar = super.read(); } long counter = 0; while (lookaheadChar != c && lookaheadChar != END_OF_STREAM) { if (lookaheadChar == '\n') { lineCounter++; } lookaheadChar = super.read(); counter++; } return counter; } /** * Returns the next char in the stream without consuming it. * * Remember the next char read by read(..) will always be * identical to lookAhead(). * * @return the next char (without consuming it) or END_OF_STREAM */ public int lookAhead() throws IOException { if (lookaheadChar == UNDEFINED) { lookaheadChar = super.read(); } return lookaheadChar; } /** * Returns the nof line read * ATTENTION: the skip-method does invalidate the line-number counter * * @return the current-line-number (or -1) */ public int getLineNumber() { if (lineCounter > -1) { return lineCounter; } else { return -1; } } @Override public boolean markSupported() { /* note uh: marking is not supported, cause we cannot * see into the future... */ return false; } }