Record position in parse exception
authorJakub Skoczen <jakub@indexdata.dk>
Fri, 16 Mar 2012 15:57:07 +0000 (16:57 +0100)
committerJakub Skoczen <jakub@indexdata.dk>
Fri, 16 Mar 2012 15:57:07 +0000 (16:57 +0100)
src/main/java/org/z3950/zing/cql/CQLParseException.java
src/main/java/org/z3950/zing/cql/CQLParser.java
src/main/java/org/z3950/zing/cql/PositionAwareReader.java [new file with mode: 0644]

index ee237f5..ceb97de 100644 (file)
@@ -9,14 +9,24 @@ package org.z3950.zing.cql;
  * @version    $Id: CQLParseException.java,v 1.2 2002-11-06 20:13:45 mike Exp $
  */
 public class CQLParseException extends Exception {
+    private int pos;
     /**
      * Creates a new <TT>CQLParseException</TT>.
      * @param s
      * An error message describing the problem with the query,
      * usually a syntax error of some kind.
      */
-    public CQLParseException(String s) {
+    public CQLParseException(String s, int pos) {
        super(s);
+        this.pos = pos;
+    }
+    
+    /**
+     * Character position of the parsing error.
+     * @return 
+     */
+    public int getPosition() {
+      return pos;
     }
 }
 
index 795b2f1..62a51b6 100644 (file)
@@ -21,6 +21,7 @@ import java.util.List;
  */
 public class CQLParser {
     private CQLLexer lexer;
+    private PositionAwareReader par; //active reader with position
     private int compat;        // When false, implement CQL 1.2
     public static final int V1POINT1 = 12368;
     public static final int V1POINT2 = 12369;
@@ -96,14 +97,16 @@ public class CQLParser {
      * tree representing the query.  */
     public CQLNode parse(Reader cql)
        throws CQLParseException, IOException {
-       lexer = new CQLLexer(cql, LEXDEBUG);
+        par = new PositionAwareReader(cql);
+       lexer = new CQLLexer(par, LEXDEBUG);
 
        lexer.nextToken();
        debug("about to parseQuery()");
        CQLNode root = parseTopLevelPrefixes("cql.serverChoice",
                new CQLRelation(compat == V1POINT2 ? "=" : "scr"));
        if (lexer.ttype != CQLLexer.TT_EOF)
-           throw new CQLParseException("junk after end: " + lexer.render());
+           throw new CQLParseException("junk after end: " + lexer.render(), 
+              par.getPosition());
 
        return root;
     }
@@ -130,7 +133,7 @@ public class CQLParser {
            }
 
            if (sortnode.keys.size() == 0) {
-               throw new CQLParseException("no sort keys");
+               throw new CQLParseException("no sort keys", par.getPosition());
            }
 
            node = sortnode;
@@ -162,7 +165,7 @@ public class CQLParser {
                                                 new CQLProxNode(term, term2, ms));
            } else {
                throw new CQLParseException("expected boolean, got " +
-                                           lexer.render());
+                                           lexer.render(), par.getPosition());
            }
        }
 
@@ -179,7 +182,8 @@ public class CQLParser {
            match('/');
            if (lexer.ttype != CQLLexer.TT_WORD)
                throw new CQLParseException("expected modifier, "
-                                           + "got " + lexer.render());
+                                           + "got " + lexer.render(), 
+                  par.getPosition());
            String type = lexer.sval.toLowerCase();
            match(lexer.ttype);
            if (!isSymbolicRelation()) {
@@ -294,7 +298,8 @@ public class CQLParser {
        if (lexer.ttype != token)
            throw new CQLParseException("expected " +
                                        lexer.render(token, true) +
-                                       ", " + "got " + lexer.render());
+                                       ", " + "got " + lexer.render(), 
+              par.getPosition());
        int tmp = lexer.nextToken();
        debug("match() got token=" + lexer.ttype + ", " +
              "nval=" + lexer.nval + ", sval='" + lexer.sval + "'" +
@@ -325,7 +330,7 @@ public class CQLParser {
        }
 
        throw new CQLParseException("expected " + expected + ", " +
-                                   "got " + lexer.render());
+                                   "got " + lexer.render(), par.getPosition());
     }
 
 
diff --git a/src/main/java/org/z3950/zing/cql/PositionAwareReader.java b/src/main/java/org/z3950/zing/cql/PositionAwareReader.java
new file mode 100644 (file)
index 0000000..b3294c0
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 1995-2012, Index Data
+ * All rights reserved.
+ * See the file LICENSE for details.
+ */
+package org.z3950.zing.cql;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.nio.CharBuffer;
+
+/**
+ * Reader proxy to count how many characters has been read so far.
+ * @author jakub
+ */
+public class PositionAwareReader extends Reader {
+  protected Reader reader;
+  protected int pos = -1;
+  
+  public PositionAwareReader(Reader reader) {
+    this.reader = reader;
+  }
+
+  /*
+   * Position of the last read character or -1 if either reading from an empty 
+   * stream or no 'read' has been invoked for this reader.
+   */
+  public int getPosition() {
+    return pos;
+  }
+
+  @Override
+  public void mark(int readAheadLimit) throws IOException {
+    reader.mark(readAheadLimit);
+  }
+
+  @Override
+  public boolean markSupported() {
+    return reader.markSupported();
+  }
+
+  @Override
+  public int read() throws IOException {
+    int c = reader.read();
+    if (c != -1) pos++;
+    return c;
+  }
+
+  @Override
+  public int read(char[] cbuf) throws IOException {
+    int c = reader.read(cbuf);
+    if (c != -1) pos+=c;
+    return c;
+  }
+
+  @Override
+  public int read(CharBuffer target) throws IOException {
+    int c = reader.read(target);
+    if (c != -1) pos+=c;
+    return c;
+  }
+  
+  @Override
+  public int read(char[] cbuf, int off, int len) throws IOException {
+    int c = reader.read(cbuf, off, len);
+    if (c != -1) pos+=c;
+    return c;
+  }
+
+  @Override
+  public boolean ready() throws IOException {
+    return reader.ready();
+  }
+
+  @Override
+  public long skip(long n) throws IOException {
+    return reader.skip(n);
+  }
+  
+  @Override
+  public void close() throws IOException {
+    reader.close();
+  }
+
+  @Override
+  public void reset() throws IOException {
+    reader.reset();
+  }
+  
+  //override object methods, to be on the safe-side
+
+  @Override
+  public boolean equals(Object obj) {
+    return reader.equals(obj);
+  }
+
+  @Override
+  public String toString() {
+    return reader.toString();
+  }
+
+  @Override
+  public int hashCode() {
+    return reader.hashCode();
+  }
+  
+}