X-Git-Url: http://git.indexdata.com/?a=blobdiff_plain;f=src%2Fmain%2Fjava%2Forg%2Fz3950%2Fzing%2Fcql%2FCQLParser.java;h=6bd6d017d77c0febec905529df98d29cf199e74d;hb=beabf8e9a3c5bd13dfafb0fb4bac0eb9fa0a90e5;hp=7ed9f593c8d4f322c5f8e104a9d9aeb2e301a6d6;hpb=85a4cca539b075ae52cfed13e2c52da89e0bad9c;p=cql-java-moved-to-github.git diff --git a/src/main/java/org/z3950/zing/cql/CQLParser.java b/src/main/java/org/z3950/zing/cql/CQLParser.java index 7ed9f59..6bd6d01 100644 --- a/src/main/java/org/z3950/zing/cql/CQLParser.java +++ b/src/main/java/org/z3950/zing/cql/CQLParser.java @@ -1,4 +1,3 @@ -// $Id: CQLParser.java,v 1.39 2007-08-06 15:54:48 mike Exp $ package org.z3950.zing.cql; import java.io.IOException; @@ -6,20 +5,26 @@ import java.util.Properties; import java.io.InputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.Reader; +import java.io.StringReader; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * Compiles CQL strings into parse trees of CQLNode subtypes. * - * @version $Id: CQLParser.java,v 1.39 2007-08-06 15:54:48 mike Exp $ * @see http://zing.z3950.org/cql/index.html */ public class CQLParser { private CQLLexer lexer; + private PositionAwareReader par; //active reader with position private int compat; // When false, implement CQL 1.2 + private final Set customRelations = new HashSet(); + public static final int V1POINT1 = 12368; public static final int V1POINT2 = 12369; public static final int V1POINT1SORT = 12370; @@ -55,6 +60,45 @@ public class CQLParser { if (DEBUG) System.err.println("PARSEDEBUG: " + str); } + + /** + * Registers custom relation in this parser. Note that when a custom relation + * is registered the parser is no longer strictly compliant with the chosen spec. + * @param relation + * @return true if custom relation has not been registered already + */ + public boolean registerCustomRelation(String relation) { + return customRelations.add(relation); + } + + /** + * Unregisters previously registered custom relation in this instance of the parser. + * @param relation + * @return true is relation has been previously registered + */ + public boolean unregisterCustomRelation(String relation) { + return customRelations.remove(relation); + } + + /** + * Compiles a CQL query. + *

+ * The resulting parse tree may be further processed by hand (see + * the individual node-types' documentation for details on the + * data structure) or, more often, simply rendered out in the + * desired form using one of the back-ends. toCQL() + * returns a decompiled CQL query equivalent to the one that was + * compiled in the first place; toXCQL() returns an + * XML snippet representing the query; and toPQF() + * returns the query rendered in Index Data's Prefix Query + * Format. + * + * @param cql The query + * @return A CQLNode object which is the root of a parse + * tree representing the query. */ + public CQLNode parse(String cql) throws CQLParseException, IOException { + return parse(new StringReader(cql)); + } /** * Compiles a CQL query. @@ -72,16 +116,18 @@ public class CQLParser { * @param cql The query * @return A CQLNode object which is the root of a parse * tree representing the query. */ - public CQLNode parse(String cql) + 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; } @@ -108,7 +154,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; @@ -140,7 +186,7 @@ public class CQLParser { new CQLProxNode(term, term2, ms)); } else { throw new CQLParseException("expected boolean, got " + - lexer.render()); + lexer.render(), par.getPosition()); } } @@ -157,7 +203,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()) { @@ -246,9 +293,10 @@ public class CQLParser { lexer.sval.equals("all") || lexer.sval.equals("within") || lexer.sval.equals("encloses") || - lexer.sval.equals("exact") || + (lexer.sval.equals("exact") && compat != V1POINT2) || (lexer.sval.equals("scr") && compat != V1POINT2) || - (lexer.sval.equals("adj") && compat == V1POINT2))) + (lexer.sval.equals("adj") && compat == V1POINT2) || + customRelations.contains(lexer.sval))) return true; return isSymbolicRelation(); @@ -272,7 +320,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 + "'" + @@ -303,7 +352,7 @@ public class CQLParser { } throw new CQLParseException("expected " + expected + ", " + - "got " + lexer.render()); + "got " + lexer.render(), par.getPosition()); } @@ -419,6 +468,7 @@ public class CQLParser { } CQLParser parser = new CQLParser(compat); + parser.registerCustomRelation("@"); CQLNode root = null; try { root = parser.parse(cql); @@ -443,7 +493,7 @@ public class CQLParser { f.close(); System.out.println(root.toPQF(config)); } else { - System.out.print(root.toXCQL(0)); + System.out.print(root.toXCQL()); } } catch (IOException ex) { System.err.println("Can't render query: " + ex.getMessage());