+
+ private class ParseContext {
+
+ public static final int CONTEXT_LENGTH = 20;
+
+ private Reader reader;
+
+ public final TemplateParseException parseException = new TemplateParseException(source);
+
+ int line = 1;
+
+ int column = 0;
+
+ private int curChar = -1;
+
+ private int[] charContext = new int[CONTEXT_LENGTH];
+
+ protected int contextPosition = 0;
+
+ public ParseContext(Reader reader) {
+ this.reader = reader;
+ }
+
+ public void addError(String message) {
+ addError(line, column, message);
+ }
+
+ public void addError(int line, int column, String message) {
+ StringBuffer charContextBuffer = new StringBuffer();
+ int j = contextPosition;
+ for (int i = 0; i < CONTEXT_LENGTH; i++) {
+ if (charContext[j] != 0) {
+ if (charContext[j] == '\n') {
+ charContextBuffer.append("\\n");
+ } else {
+ charContextBuffer.appendCodePoint(charContext[j]);
+ }
+ }
+ j = (j + 1) % CONTEXT_LENGTH;
+ }
+ parseException.addError(line, column, message, charContextBuffer.toString());
+ }
+
+ public void merge(ParseContext other) {
+ line = other.line;
+ column = other.column;
+ parseException.append(other.parseException);
+ }
+
+ public void append(ParseContext other) {
+ parseException.append(other.parseException);
+ }
+
+ public int read() throws IOException {
+ int ch;
+ while ((ch = reader.read()) == '\r') {
+ }
+ curChar = ch;
+ if (ch == '\n') {
+ line++;
+ column = 0;
+ } else {
+ column++;
+ }
+ if (ch != -1) {
+ charContext[contextPosition] = ch;
+ contextPosition = (contextPosition + 1) % CONTEXT_LENGTH;
+ }
+ return ch;
+ }
+
+ public ParseContext copy() {
+ ParseContext newParseContext = new ParseContext(reader);
+ newParseContext.line = line;
+ newParseContext.column = column;
+ newParseContext.charContext = Arrays.copyOf(charContext, charContext.length);
+ newParseContext.contextPosition = contextPosition;
+ return newParseContext;
+ }
+ }