package de.skuzzle.polly.core.parser; import java.io.IOException; import java.io.LineNumberReader; import java.io.Reader; import java.util.Arrays; /** * A {@link Reader} implementation which keeps track of current line number and the * position within the current line. It also remembers the length of every completely * read line. * * @author Simon Taddiken */ public class PositioningReader extends LineNumberReader { private int col; private int markedCol; private int[] colLengths; public PositioningReader(Reader in) { super(in); this.colLengths = new int[256]; } /** * Gets the index within current line. The beginning of a line as index 0. * @return 0 based column index within current line. */ public int getCol() { return this.col; } /** * Sets the current column position to the given value. * @param col The new column position. */ protected void setCol(int col) { this.col = col; } /** * Gets the length in characters of the given line. The new line character is not * counted. Thus, in the String <code>"\n"</code>, line * 0 would have length 0. Note that line numbers can only be retrieved for lines that * have been completely read so far. Those are all positive numbers (and 0) * < {@link #getLineNumber()}. * * @param lineNumber 0 based line index. * @return Number of characters in that line. */ public int getCols(int lineNumber) { if (lineNumber < 0 || lineNumber > this.getLineNumber()) { throw new IllegalArgumentException("invalid line number: " + lineNumber); //$NON-NLS-1$ } return this.colLengths[lineNumber]; } private void ensureIndexExists(int idx) { if (this.colLengths.length < idx + 1) { this.colLengths = Arrays.copyOf(this.colLengths, Math.max(idx, this.colLengths.length + 512)); } } @Override public int read() throws IOException { int lineBefore = this.getLineNumber(); final int c = super.read(); if (c == -1) { this.colLengths[this.getLineNumber()] = this.col; } else if (lineBefore != this.getLineNumber()) { this.ensureIndexExists(lineBefore); this.colLengths[lineBefore] = this.col; this.col = 0; } else { ++this.col; } return c; } @Override public void mark(int readAheadLimit) throws IOException { synchronized (lock) { super.mark(readAheadLimit); this.markedCol = this.col; } } @Override public void reset() throws IOException { synchronized (lock) { super.reset(); this.col = this.markedCol; } } }