Improved error handling for antlr3 and antlr4 trees:
- Error listeners and message formatters
- Cosmas grammar incorporates setter method for error listener
- No more QueryExceptions, instead return empty query serialization along with error msg
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/AbstractSyntaxTree.java b/src/main/java/de/ids_mannheim/korap/query/serialize/AbstractSyntaxTree.java
index ffdceae..624e42c 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/AbstractSyntaxTree.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/AbstractSyntaxTree.java
@@ -62,6 +62,7 @@
requestMap.put("warnings", warnings);
requestMap.put("messages", messages);
requestMap.put("collection", collection);
+ requestMap.put("query", new LinkedHashMap<String, Object>());
requestMap.put("meta", new LinkedHashMap<String, Object>());
}
@@ -89,6 +90,10 @@
List<Object> error = Arrays.asList(new Object[]{code, msg});
errors.add(error);
}
+
+ public void addError(List<Object> fullErrorMsg) {
+ errors.add(fullErrorMsg);
+ }
public Map<String, Object> getRequestMap() {
return requestMap;
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/AqlTree.java b/src/main/java/de/ids_mannheim/korap/query/serialize/AqlTree.java
index d5e473c..bb56d65 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/AqlTree.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/AqlTree.java
@@ -104,16 +104,9 @@
throw new NullPointerException("Parser has not been instantiated!");
}
log.info("Processing Annis query.");
- log.info("AST is: "+tree.toStringTree(parser));
- System.out.println("Processing Annis QL");
- if (verbose) System.out.println(tree.toStringTree(parser));
- processNode(tree);
- log.info(requestMap.toString());
-
- for (String ref : nodeVariables.keySet()) {
- if (nodeReferencesTotal.get(ref) == null) {
- System.err.println(ref);
- }
+ if (tree != null) {
+ log.debug("ANTLR parse tree: "+tree.toStringTree(parser));
+ processNode(tree);
}
}
@@ -733,19 +726,12 @@
Method startRule = AqlParser.class.getMethod("start");
tree = (ParserRuleContext) startRule.invoke(parser, (Object[])null);
}
-
// Some things went wrong ...
catch (Exception e) {
- System.err.println("ERROR: "+errorListener.generateFullErrorMsg());
- log.error(e.getMessage());
+ log.error("Could not parse query. Please make sure it is well-formed.");
+ log.error(errorListener.generateFullErrorMsg().toString());
+ addError(errorListener.generateFullErrorMsg());
}
-
- if (tree == null) {
- log.error("Could not parse query. Make sure it is correct ANNIS QL syntax.");
- throw new QueryException("Could not parse query. Make sure it is correct ANNIS QL syntax.");
- }
-
- // Return the generated tree
return tree;
}
}
\ No newline at end of file
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/CosmasTree.java b/src/main/java/de/ids_mannheim/korap/query/serialize/CosmasTree.java
index 5d94bd2..9803cf0 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/CosmasTree.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/CosmasTree.java
@@ -2,7 +2,7 @@
import de.ids_mannheim.korap.query.cosmas2.c2psLexer;
import de.ids_mannheim.korap.query.cosmas2.c2psParser;
-import de.ids_mannheim.korap.query.serialize.util.CosmasCondition;
+import de.ids_mannheim.korap.query.serialize.util.Antlr3DescriptiveErrorListener;
import de.ids_mannheim.korap.query.serialize.util.ResourceMapper;
import de.ids_mannheim.korap.query.serialize.util.StatusCodes;
import de.ids_mannheim.korap.util.QueryException;
@@ -16,7 +16,6 @@
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
-import com.google.common.collect.TreeBasedTable;
import java.util.*;
import java.util.regex.Matcher;
@@ -25,7 +24,7 @@
/**
* Map representation of CosmasII syntax tree as returned by ANTLR
*
- * @author bingel
+ * @author Joachim Bingel (bingel@ids-mannheim.de)
* @version 0.2
*/
public class CosmasTree extends Antlr3AbstractSyntaxTree {
@@ -106,16 +105,12 @@
@Override
public void process(String query) throws QueryException {
Tree tree = null;
- try {
- tree = parseCosmasQuery(query);
- } catch (RecognitionException e) {
- throw new QueryException("Your query could not be processed. Please make sure it is well-formed.");
- } catch (NullPointerException e) {
- throw new QueryException("Your query could not be processed. Please make sure it is well-formed.");
- }
+ tree = parseCosmasQuery(query);
log.info("Processing CosmasII query");
- processNode(tree);
- log.info(requestMap.toString());
+ if (tree != null) {
+ log.debug("ANTLR parse tree: "+tree.toStringTree());
+ processNode(tree);
+ }
}
@SuppressWarnings("unchecked")
@@ -1025,30 +1020,6 @@
putIntoSuperObject(object, 0);
}
-
- private Tree parseCosmasQuery(String q) throws RecognitionException {
- q = rewritePositionQuery(q);
-
- Tree tree = null;
- ANTLRStringStream ss = new ANTLRStringStream(q);
- c2psLexer lex = new c2psLexer(ss);
- org.antlr.runtime.CommonTokenStream tokens = new org.antlr.runtime.CommonTokenStream(lex); //v3
- parser = new c2psParser(tokens);
- c2psParser.c2ps_query_return c2Return = ((c2psParser) parser).c2ps_query(); // statt t().
- // AST Tree anzeigen:
- tree = (Tree) c2Return.getTree();
-
- String treestring = tree.toStringTree();
- if (treestring.contains("<mismatched token") || treestring.contains("<error") || treestring.contains("<unexpected")) {
- log.error("Invalid tree. Could not parse Cosmas query. Make sure it is well-formed.");
- throw new RecognitionException();
- }
- if (verbose) {
- System.out.println(tree.toStringTree());
- }
- return tree;
- }
-
/**
* Normalises position operators to equivalents using #BED
*/
@@ -1071,4 +1042,40 @@
}
return rewrittenQuery;
}
+
+ private Tree parseCosmasQuery(String query) {
+ query = rewritePositionQuery(query);
+ Tree tree = null;
+ Antlr3DescriptiveErrorListener errorListener = new Antlr3DescriptiveErrorListener(query);
+ try {
+ ANTLRStringStream ss = new ANTLRStringStream(query);
+ c2psLexer lex = new c2psLexer(ss);
+ org.antlr.runtime.CommonTokenStream tokens = new org.antlr.runtime.CommonTokenStream(lex); //v3
+ parser = new c2psParser(tokens);
+
+ ((c2psParser) parser).setErrorReporter(errorListener); // Use the custom error reporter
+ c2psParser.c2ps_query_return c2Return = ((c2psParser) parser).c2ps_query(); // statt t().
+ // AST Tree anzeigen:
+ tree = (Tree) c2Return.getTree();
+
+ } catch (RecognitionException e) {
+ log.error("Could not parse query. Please make sure it is well-formed.");
+ addError(StatusCodes.MALFORMED_QUERY, "Could not parse query. Please make sure it is well-formed.");
+ }
+ String treestring = tree.toStringTree();
+
+ boolean erroneous = false;
+ if (parser.failed() || parser.getNumberOfSyntaxErrors() > 0) {
+ erroneous = true;
+ tree = null;
+ }
+
+ if (erroneous || treestring.contains("<mismatched token") ||
+ treestring.contains("<error") || treestring.contains("<unexpected")) {
+ log.error("Could not parse query. Please make sure it is well-formed.");
+ log.error(errorListener.generateFullErrorMsg().toString());
+ addError(errorListener.generateFullErrorMsg());
+ }
+ return tree;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/PoliqarpPlusTree.java b/src/main/java/de/ids_mannheim/korap/query/serialize/PoliqarpPlusTree.java
index 3117220..ff525a0 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/PoliqarpPlusTree.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/PoliqarpPlusTree.java
@@ -35,7 +35,6 @@
*/
public PoliqarpPlusTree(String query) throws QueryException {
process(query);
- System.out.println(">>> " + requestMap + " <<<");
log.info(">>> " + requestMap.get("query") + " <<<");
}
@@ -45,7 +44,10 @@
tree = parsePoliqarpQuery(query);
super.parser = this.parser;
log.info("Processing PoliqarpPlus");
- processNode(tree);
+ if (tree != null) {
+ log.debug("ANTLR parse tree: "+tree.toStringTree(parser));
+ processNode(tree);
+ }
}
/**
@@ -671,14 +673,14 @@
}
- private ParserRuleContext parsePoliqarpQuery(String p) throws QueryException {
+ private ParserRuleContext parsePoliqarpQuery(String query) throws QueryException {
Lexer lexer = new PoliqarpPlusLexer((CharStream) null);
ParserRuleContext tree = null;
Antlr4DescriptiveErrorListener errorListener = new Antlr4DescriptiveErrorListener(query);
// Like p. 111
try {
// Tokenize input data
- ANTLRInputStream input = new ANTLRInputStream(p);
+ ANTLRInputStream input = new ANTLRInputStream(query);
lexer.setInputStream(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
parser = new PoliqarpPlusParser(tokens);
@@ -693,21 +695,13 @@
// Get starting rule from parser
Method startRule = PoliqarpPlusParser.class.getMethod("request");
tree = (ParserRuleContext) startRule.invoke(parser, (Object[]) null);
- log.debug(tree.toStringTree(parser));
}
// Some things went wrong ...
catch (Exception e) {
- log.error("Could not parse query. Please make sure it is well-formed.");;
- log.error("Underlying error is: "+e.getMessage());
- System.err.println(e.getMessage());
+ log.error("Could not parse query. Please make sure it is well-formed.");
+ log.error(errorListener.generateFullErrorMsg().toString());
+ addError(errorListener.generateFullErrorMsg());
}
-
- if (tree == null) {
- throw new QueryException("The query you specified could not be processed. Please make sure it is well-formed.");
- }
- // Return the generated tree
- log.info("ANTLR parse tree: "+tree.toStringTree(parser));
- System.out.println("ANTLR parse tree: "+tree.toStringTree(parser));
return tree;
}
}
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/QuerySerializer.java b/src/main/java/de/ids_mannheim/korap/query/serialize/QuerySerializer.java
index a34f7c2..635b2c8 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/QuerySerializer.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/QuerySerializer.java
@@ -3,7 +3,7 @@
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
-import de.ids_mannheim.korap.query.poliqarp.PoliqarpPlusParser;
+import de.ids_mannheim.korap.query.serialize.util.StatusCodes;
import de.ids_mannheim.korap.util.QueryException;
import de.ids_mannheim.korap.utils.JsonUtils;
import de.ids_mannheim.korap.utils.KorAPLogger;
@@ -94,7 +94,7 @@
*
* @param outFile The file to which the serialization is written
* @param query The query string
- * @param queryLanguage The query language. As of 13/11/20, this must be either 'poliqarp' or 'poliqarpplus'. Some extra maven stuff needs to done to support CosmasII ('cosmas') [that maven stuff would be to tell maven how to build the cosmas grammar and where to find the classes]
+ * @param queryLanguage The query language. As of 17 Dec 2014, this must be one of 'poliqarpplus', 'cosmas2', 'annis' or 'cql'.
* @throws IOException
* @throws QueryException
*/
@@ -120,7 +120,7 @@
throws QueryException {
if (query == null || query.isEmpty())
- throw new QueryException(406, "No Content!");
+ throw new QueryException(StatusCodes.NO_QUERY, "No Content!");
try {
if (ql.equalsIgnoreCase("poliqarp")) {
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/QueryUtils.java b/src/main/java/de/ids_mannheim/korap/query/serialize/QueryUtils.java
index 61a97e2..2f5c237 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/QueryUtils.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/QueryUtils.java
@@ -1,12 +1,19 @@
package de.ids_mannheim.korap.query.serialize;
import de.ids_mannheim.korap.util.QueryException;
+
import org.apache.commons.lang.StringUtils;
+import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -15,366 +22,375 @@
* @date 10/12/2013
*/
public class QueryUtils {
-//
-// /**
-// * Returns the category (or 'label') of the root of a ParseTree (ANTLR 4).
-// *
-// * @param node
-// * @return
-// */
-// public static String getNodeCat(ParseTree node) {
-// String nodeCat = node.toStringTree(parser);
-// Pattern p = Pattern.compile("\\((.*?)\\s"); // from opening parenthesis to 1st whitespace
-// Matcher m = p.matcher(node.toStringTree(parser));
-// if (m.find()) {
-// nodeCat = m.group(1);
-// }
-// return nodeCat;
-// }
-//
-// /**
-// * Returns the category (or 'label') of the root of a ParseTree (ANTLR 3).
-// *
-// * @param node
-// * @return
-// */
-// public static String getNodeCat(Tree node) {
-// String nodeCat = node.toStringTree();
-// Pattern p = Pattern.compile("\\((.*?)\\s"); // from opening parenthesis to 1st whitespace
-// Matcher m = p.matcher(node.toStringTree());
-// if (m.find()) {
-// nodeCat = m.group(1);
-// }
-// return nodeCat;
-// }
-//
-//
-// /**
-// * Tests whether a certain node has a child by a certain name
-// *
-// * @param node The parent node.
-// * @param childCat The category of the potential child.
-// * @return true iff one or more children belong to the specified category
-// */
-// public static boolean hasChild(Tree node, String childCat) {
-// for (int i = 0; i < node.getChildCount(); i++) {
-// if (getNodeCat(node.getChild(i)).equals(childCat)) {
-// return true;
-// }
-// }
-// return false;
-// }
-//
-// /**
-// * Tests whether a certain node has a child by a certain name
-// *
-// * @param node The parent node.
-// * @param childCat The category of the potential child.
-// * @return true iff one or more children belong to the specified category
-// */
-// public static boolean hasChild(ParseTree node, String childCat) {
-// for (int i = 0; i < node.getChildCount(); i++) {
-// if (getNodeCat(node.getChild(i)).equals(childCat)) {
-// return true;
-// }
-// }
-// return false;
-// }
-//
-// public static boolean hasDescendant(ParseTree node, String childCat) {
-// for (int i = 0; i < node.getChildCount(); i++) {
-// ParseTree child = node.getChild(i);
-// if (getNodeCat(child).equals(childCat)) {
-// return true;
-// }
-// if (hasDescendant(child, childCat)) {
-// return true;
-// }
-// }
-// return false;
-// }
-//
-// public static List<Tree> getChildrenWithCat(Tree node, String nodeCat) {
-// ArrayList<Tree> children = new ArrayList<Tree>();
-// for (int i = 0; i < node.getChildCount(); i++) {
-// if (getNodeCat(node.getChild(i)).equals(nodeCat)) {
-// children.add(node.getChild(i));
-// }
-// }
-// return children;
-// }
-//
-// public static List<ParseTree> getChildrenWithCat(ParseTree node, String nodeCat) {
-// ArrayList<ParseTree> children = new ArrayList<ParseTree>();
-// for (int i = 0; i < node.getChildCount(); i++) {
-// if (getNodeCat(node.getChild(i)).equals(nodeCat)) {
-// children.add(node.getChild(i));
-// }
-// }
-// return children;
-// }
-//
-// public static List<ParseTree> getChildren(ParseTree node) {
-// ArrayList<ParseTree> children = new ArrayList<ParseTree>();
-// for (int i = 0; i < node.getChildCount(); i++) {
-// children.add(node.getChild(i));
-// }
-// return children;
-// }
-//
-// public static Tree getFirstChildWithCat(Tree node, String nodeCat) {
-// for (int i = 0; i < node.getChildCount(); i++) {
-// if (getNodeCat(node.getChild(i)).equals(nodeCat)) {
-// return node.getChild(i);
-// }
-// }
-// return null;
-// }
-//
-// public static ParseTree getFirstChildWithCat(ParseTree node, String nodeCat) {
-// for (int i = 0; i < node.getChildCount(); i++) {
-// if (getNodeCat(node.getChild(i)).equals(nodeCat)) {
-// return node.getChild(i);
-// }
-// }
-// return null;
-// }
-//
-// /**
-// * Checks whether a node only serves as a container for another node (e.g. in (cq_segment ( cg_seg_occ ...)), the cq_segment node does not contain
-// * any information and only contains the cq_seg_occ node.
-// * @param node The node to check
-// * @return true iff the node is a container only.
-// */
-// public static boolean isContainerOnly(ParseTree node) {
-// String[] validNodeNamesArray = "cq_segment sq_segment element empty_segments".split(" ");
-// List<String> validNodeNames = Arrays.asList(validNodeNamesArray);
-// List<ParseTree> children = getChildren(node);
-// for (ParseTree child : children) {
-// if (validNodeNames.contains(getNodeCat(child))) {
-// return false;
-// }
-// }
-// return true;
-// }
+ //
+ // /**
+ // * Returns the category (or 'label') of the root of a ParseTree (ANTLR 4).
+ // *
+ // * @param node
+ // * @return
+ // */
+ // public static String getNodeCat(ParseTree node) {
+ // String nodeCat = node.toStringTree(parser);
+ // Pattern p = Pattern.compile("\\((.*?)\\s"); // from opening parenthesis to 1st whitespace
+ // Matcher m = p.matcher(node.toStringTree(parser));
+ // if (m.find()) {
+ // nodeCat = m.group(1);
+ // }
+ // return nodeCat;
+ // }
+ //
+ // /**
+ // * Returns the category (or 'label') of the root of a ParseTree (ANTLR 3).
+ // *
+ // * @param node
+ // * @return
+ // */
+ // public static String getNodeCat(Tree node) {
+ // String nodeCat = node.toStringTree();
+ // Pattern p = Pattern.compile("\\((.*?)\\s"); // from opening parenthesis to 1st whitespace
+ // Matcher m = p.matcher(node.toStringTree());
+ // if (m.find()) {
+ // nodeCat = m.group(1);
+ // }
+ // return nodeCat;
+ // }
+ //
+ //
+ // /**
+ // * Tests whether a certain node has a child by a certain name
+ // *
+ // * @param node The parent node.
+ // * @param childCat The category of the potential child.
+ // * @return true iff one or more children belong to the specified category
+ // */
+ // public static boolean hasChild(Tree node, String childCat) {
+ // for (int i = 0; i < node.getChildCount(); i++) {
+ // if (getNodeCat(node.getChild(i)).equals(childCat)) {
+ // return true;
+ // }
+ // }
+ // return false;
+ // }
+ //
+ // /**
+ // * Tests whether a certain node has a child by a certain name
+ // *
+ // * @param node The parent node.
+ // * @param childCat The category of the potential child.
+ // * @return true iff one or more children belong to the specified category
+ // */
+ // public static boolean hasChild(ParseTree node, String childCat) {
+ // for (int i = 0; i < node.getChildCount(); i++) {
+ // if (getNodeCat(node.getChild(i)).equals(childCat)) {
+ // return true;
+ // }
+ // }
+ // return false;
+ // }
+ //
+ // public static boolean hasDescendant(ParseTree node, String childCat) {
+ // for (int i = 0; i < node.getChildCount(); i++) {
+ // ParseTree child = node.getChild(i);
+ // if (getNodeCat(child).equals(childCat)) {
+ // return true;
+ // }
+ // if (hasDescendant(child, childCat)) {
+ // return true;
+ // }
+ // }
+ // return false;
+ // }
+ //
+ // public static List<Tree> getChildrenWithCat(Tree node, String nodeCat) {
+ // ArrayList<Tree> children = new ArrayList<Tree>();
+ // for (int i = 0; i < node.getChildCount(); i++) {
+ // if (getNodeCat(node.getChild(i)).equals(nodeCat)) {
+ // children.add(node.getChild(i));
+ // }
+ // }
+ // return children;
+ // }
+ //
+ // public static List<ParseTree> getChildrenWithCat(ParseTree node, String nodeCat) {
+ // ArrayList<ParseTree> children = new ArrayList<ParseTree>();
+ // for (int i = 0; i < node.getChildCount(); i++) {
+ // if (getNodeCat(node.getChild(i)).equals(nodeCat)) {
+ // children.add(node.getChild(i));
+ // }
+ // }
+ // return children;
+ // }
+ //
+ // public static List<ParseTree> getChildren(ParseTree node) {
+ // ArrayList<ParseTree> children = new ArrayList<ParseTree>();
+ // for (int i = 0; i < node.getChildCount(); i++) {
+ // children.add(node.getChild(i));
+ // }
+ // return children;
+ // }
+ //
+ // public static Tree getFirstChildWithCat(Tree node, String nodeCat) {
+ // for (int i = 0; i < node.getChildCount(); i++) {
+ // if (getNodeCat(node.getChild(i)).equals(nodeCat)) {
+ // return node.getChild(i);
+ // }
+ // }
+ // return null;
+ // }
+ //
+ // public static ParseTree getFirstChildWithCat(ParseTree node, String nodeCat) {
+ // for (int i = 0; i < node.getChildCount(); i++) {
+ // if (getNodeCat(node.getChild(i)).equals(nodeCat)) {
+ // return node.getChild(i);
+ // }
+ // }
+ // return null;
+ // }
+ //
+ // /**
+ // * Checks whether a node only serves as a container for another node (e.g. in (cq_segment ( cg_seg_occ ...)), the cq_segment node does not contain
+ // * any information and only contains the cq_seg_occ node.
+ // * @param node The node to check
+ // * @return true iff the node is a container only.
+ // */
+ // public static boolean isContainerOnly(ParseTree node) {
+ // String[] validNodeNamesArray = "cq_segment sq_segment element empty_segments".split(" ");
+ // List<String> validNodeNames = Arrays.asList(validNodeNamesArray);
+ // List<ParseTree> children = getChildren(node);
+ // for (ParseTree child : children) {
+ // if (validNodeNames.contains(getNodeCat(child))) {
+ // return false;
+ // }
+ // }
+ // return true;
+ // }
- public static void checkUnbalancedPars(String q) throws QueryException {
- int openingPars = StringUtils.countMatches(q, "(");
- int closingPars = StringUtils.countMatches(q, ")");
- int openingBrkts = StringUtils.countMatches(q, "[");
- int closingBrkts = StringUtils.countMatches(q, "]");
- int openingBrcs = StringUtils.countMatches(q, "{");
- int closingBrcs = StringUtils.countMatches(q, "}");
- if (openingPars != closingPars) throw new QueryException(
- "Your query string contains an unbalanced number of parantheses.");
- if (openingBrkts != closingBrkts) throw new QueryException(
- "Your query string contains an unbalanced number of brackets.");
- if (openingBrcs != closingBrcs) throw new QueryException(
- "Your query string contains an unbalanced number of braces.");
- }
+ public static SimpleEntry<String, Integer> checkUnbalancedPars(String q) {
+ Map<Character, Character> brackets = new HashMap<Character, Character>();
+ brackets.put('[', ']');
+ brackets.put('{', '}');
+ brackets.put('(', ')');
+ Set<Character> allChars = new HashSet<Character>();
+ allChars.addAll(brackets.keySet());
+ allChars.addAll(brackets.values());
+ int lastOpenBracket = 0;
+
+ final Stack<Character> stack = new Stack<Character>();
+ for (int i = 0; i < q.length(); i++) {
+ if (!allChars.contains(q.charAt(i))) continue;
+ if (brackets.containsKey(q.charAt(i))) {
+ stack.push(q.charAt(i));
+ lastOpenBracket = i;
+ } else if (stack.empty() || (q.charAt(i) != brackets.get(stack.pop()))) {
+ return new SimpleEntry<String, Integer>("Parantheses/brackets unbalanced.",i);
+ }
+ }
+ if (!stack.empty()) return new SimpleEntry<String, Integer>("Parantheses/brackets unbalanced.", lastOpenBracket);
+ return null;
+ }
- public static List<String> parseMorph(String stringTree) {
+ public static List<String> parseMorph(String stringTree) {
- ArrayList<String> morph = new ArrayList<String>();
- return morph;
- }
+ ArrayList<String> morph = new ArrayList<String>();
+ return morph;
+ }
- public static String buildCypherQuery(String cypher, String ctypel, String ctyper,
- int cl, int cr, int page, int limit) {
- //todo: implies that there is only one type allowed!
- String sctypel = "", sctyper = "";
- switch (ctypel) {
- case "C":
- sctypel = "chars";
- break;
- case "T":
- sctypel = "tokens";
- break;
- }
- switch (ctyper) {
- case "C":
- sctyper = "chars";
- break;
- case "T":
- sctyper = "tokens";
- break;
- }
+ public static String buildCypherQuery(String cypher, String ctypel, String ctyper,
+ int cl, int cr, int page, int limit) {
+ //todo: implies that there is only one type allowed!
+ String sctypel = "", sctyper = "";
+ switch (ctypel) {
+ case "C":
+ sctypel = "chars";
+ break;
+ case "T":
+ sctypel = "tokens";
+ break;
+ }
+ switch (ctyper) {
+ case "C":
+ sctyper = "chars";
+ break;
+ case "T":
+ sctyper = "tokens";
+ break;
+ }
- StringBuffer buffer = new StringBuffer();
- buffer.append("<query><cypher><![CDATA[");
- buffer.append(cypher);
- buffer.append("]]></cypher>");
- buffer.append("<wordAliasPrefix>wtok_</wordAliasPrefix>");
- buffer.append("<contextColumn>sent</contextColumn>");
- buffer.append("<contextIdColumn>sid</contextIdColumn>");
- buffer.append("<textColumn>txt</textColumn>");
- buffer.append("<startIndex>");
- buffer.append(page);
- buffer.append("</startIndex>");
- buffer.append("<itemsPerPage>");
- buffer.append(limit);
- buffer.append("</itemsPerPage>");
- buffer.append("<context>");
- buffer.append("<left>");
- buffer.append("<" + sctypel + ">");
- buffer.append(cl);
- buffer.append("</" + sctypel + ">");
- buffer.append("</left>");
- buffer.append("<right>");
- buffer.append("<" + sctyper + ">");
- buffer.append(cr);
- buffer.append("</" + sctyper + ">");
- buffer.append("</right>");
- buffer.append("</context>");
- buffer.append("</query>");
- return buffer.toString();
- }
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("<query><cypher><![CDATA[");
+ buffer.append(cypher);
+ buffer.append("]]></cypher>");
+ buffer.append("<wordAliasPrefix>wtok_</wordAliasPrefix>");
+ buffer.append("<contextColumn>sent</contextColumn>");
+ buffer.append("<contextIdColumn>sid</contextIdColumn>");
+ buffer.append("<textColumn>txt</textColumn>");
+ buffer.append("<startIndex>");
+ buffer.append(page);
+ buffer.append("</startIndex>");
+ buffer.append("<itemsPerPage>");
+ buffer.append(limit);
+ buffer.append("</itemsPerPage>");
+ buffer.append("<context>");
+ buffer.append("<left>");
+ buffer.append("<" + sctypel + ">");
+ buffer.append(cl);
+ buffer.append("</" + sctypel + ">");
+ buffer.append("</left>");
+ buffer.append("<right>");
+ buffer.append("<" + sctyper + ">");
+ buffer.append(cr);
+ buffer.append("</" + sctyper + ">");
+ buffer.append("</right>");
+ buffer.append("</context>");
+ buffer.append("</query>");
+ return buffer.toString();
+ }
- public static String buildDotQuery(long sid, String graphdb_id) {
- StringBuffer b = new StringBuffer();
- b.append("<query>");
- b.append("<sentenceId>");
- b.append(sid);
- b.append("</sentenceId>");
- b.append("<gdbId>");
- b.append(graphdb_id);
- b.append("</gdbId>");
- b.append("<hls>");
- b.append("<hl>");
- b.append(40857);
- b.append("</hl>");
- b.append("<hl>");
- b.append(40856);
- b.append("</hl>");
- b.append("</hls>");
- b.append("</query>");
+ public static String buildDotQuery(long sid, String graphdb_id) {
+ StringBuffer b = new StringBuffer();
+ b.append("<query>");
+ b.append("<sentenceId>");
+ b.append(sid);
+ b.append("</sentenceId>");
+ b.append("<gdbId>");
+ b.append(graphdb_id);
+ b.append("</gdbId>");
+ b.append("<hls>");
+ b.append("<hl>");
+ b.append(40857);
+ b.append("</hl>");
+ b.append("<hl>");
+ b.append(40856);
+ b.append("</hl>");
+ b.append("</hls>");
+ b.append("</query>");
- return b.toString();
- }
+ return b.toString();
+ }
- public String buildaggreQuery(String query) {
- StringBuffer b = new StringBuffer();
- b.append("<query><cypher><![CDATA[");
- b.append(query);
- b.append("]]></cypher>");
- b.append("<columns>");
- b.append("<column agg='true' sum='false'>");
- b.append("<cypherAlias>");
- b.append("aggBy");
- b.append("</cypherAlias>");
- b.append("<displayName>");
- b.append("Aggregate");
- b.append("</displayName>");
- b.append("</column>");
+ public String buildaggreQuery(String query) {
+ StringBuffer b = new StringBuffer();
+ b.append("<query><cypher><![CDATA[");
+ b.append(query);
+ b.append("]]></cypher>");
+ b.append("<columns>");
+ b.append("<column agg='true' sum='false'>");
+ b.append("<cypherAlias>");
+ b.append("aggBy");
+ b.append("</cypherAlias>");
+ b.append("<displayName>");
+ b.append("Aggregate");
+ b.append("</displayName>");
+ b.append("</column>");
- b.append("<column agg='fals' sum='true'>");
- b.append("<cypherAlias>");
- b.append("cnt");
- b.append("</cypherAlias>");
- b.append("<displayName>");
- b.append("Count");
- b.append("</displayName>");
- b.append("</column>");
- b.append("</columns>");
+ b.append("<column agg='fals' sum='true'>");
+ b.append("<cypherAlias>");
+ b.append("cnt");
+ b.append("</cypherAlias>");
+ b.append("<displayName>");
+ b.append("Count");
+ b.append("</displayName>");
+ b.append("</column>");
+ b.append("</columns>");
- b.append("</query>");
- return b.toString();
- }
+ b.append("</query>");
+ return b.toString();
+ }
- @Deprecated
- public static Map addParameters(Map request, int page, int num, String cli, String cri,
- int cls, int crs, boolean cutoff) {
- Map ctx = new LinkedHashMap();
- List left = new ArrayList();
- left.add(cli);
- left.add(cls);
- List right = new ArrayList();
- right.add(cri);
- right.add(crs);
- ctx.put("left", left);
- ctx.put("right", right);
+ @Deprecated
+ public static Map addParameters(Map request, int page, int num, String cli, String cri,
+ int cls, int crs, boolean cutoff) {
+ Map ctx = new LinkedHashMap();
+ List left = new ArrayList();
+ left.add(cli);
+ left.add(cls);
+ List right = new ArrayList();
+ right.add(cri);
+ right.add(crs);
+ ctx.put("left", left);
+ ctx.put("right", right);
- request.put("startPage", page);
- request.put("count", num);
- request.put("context", ctx);
- request.put("cutOff", cutoff);
+ request.put("startPage", page);
+ request.put("count", num);
+ request.put("context", ctx);
+ request.put("cutOff", cutoff);
- return request;
- }
+ return request;
+ }
- public static void prepareContext(LinkedHashMap<String, Object> requestMap) {
- LinkedHashMap<String, Object> context = new LinkedHashMap<String, Object>();
+ public static void prepareContext(LinkedHashMap<String, Object> requestMap) {
+ LinkedHashMap<String, Object> context = new LinkedHashMap<String, Object>();
- LinkedHashMap<String, Object> classMap = new LinkedHashMap<String, Object>();
- LinkedHashMap<String, Object> operands = new LinkedHashMap<String, Object>();
- LinkedHashMap<String, Object> operation = new LinkedHashMap<String, Object>();
- LinkedHashMap<String, Object> frame = new LinkedHashMap<String, Object>();
- LinkedHashMap<String, Object> classRef = new LinkedHashMap<String, Object>();
- LinkedHashMap<String, Object> spanRef = new LinkedHashMap<String, Object>();
- LinkedHashMap<String, Object> classRefOp = new LinkedHashMap<String, Object>();
- LinkedHashMap<String, Object> min = new LinkedHashMap<String, Object>();
- LinkedHashMap<String, Object> max = new LinkedHashMap<String, Object>();
- LinkedHashMap<String, Object> exclude = new LinkedHashMap<String, Object>();
- LinkedHashMap<String, Object> distances = new LinkedHashMap<String, Object>();
- LinkedHashMap<String, Object> inOrder = new LinkedHashMap<String, Object>();
+ LinkedHashMap<String, Object> classMap = new LinkedHashMap<String, Object>();
+ LinkedHashMap<String, Object> operands = new LinkedHashMap<String, Object>();
+ LinkedHashMap<String, Object> operation = new LinkedHashMap<String, Object>();
+ LinkedHashMap<String, Object> frame = new LinkedHashMap<String, Object>();
+ LinkedHashMap<String, Object> classRef = new LinkedHashMap<String, Object>();
+ LinkedHashMap<String, Object> spanRef = new LinkedHashMap<String, Object>();
+ LinkedHashMap<String, Object> classRefOp = new LinkedHashMap<String, Object>();
+ LinkedHashMap<String, Object> min = new LinkedHashMap<String, Object>();
+ LinkedHashMap<String, Object> max = new LinkedHashMap<String, Object>();
+ LinkedHashMap<String, Object> exclude = new LinkedHashMap<String, Object>();
+ LinkedHashMap<String, Object> distances = new LinkedHashMap<String, Object>();
+ LinkedHashMap<String, Object> inOrder = new LinkedHashMap<String, Object>();
- operation.put("@id", "group:operation/");
- operation.put("@type", "@id");
+ operation.put("@id", "group:operation/");
+ operation.put("@type", "@id");
- classMap.put("@id", "group:class");
- classMap.put("@type", "xsd:integer");
+ classMap.put("@id", "group:class");
+ classMap.put("@type", "xsd:integer");
- operands.put("@id", "group:operands");
- operands.put("@container", "@list");
+ operands.put("@id", "group:operands");
+ operands.put("@container", "@list");
- frame.put("@id", "group:frame/");
- frame.put("@type", "@id");
+ frame.put("@id", "group:frame/");
+ frame.put("@type", "@id");
- classRef.put("@id", "group:classRef");
- classRef.put("@type", "xsd:integer");
+ classRef.put("@id", "group:classRef");
+ classRef.put("@type", "xsd:integer");
- spanRef.put("@id", "group:spanRef");
- spanRef.put("@type", "xsd:integer");
+ spanRef.put("@id", "group:spanRef");
+ spanRef.put("@type", "xsd:integer");
- classRefOp.put("@id", "group:classRefOp");
- classRefOp.put("@type", "@id");
+ classRefOp.put("@id", "group:classRefOp");
+ classRefOp.put("@type", "@id");
- min.put("@id", "boundary:min");
- min.put("@type", "xsd:integer");
+ min.put("@id", "boundary:min");
+ min.put("@type", "xsd:integer");
- max.put("@id", "boundary:max");
- max.put("@type", "xsd:integer");
+ max.put("@id", "boundary:max");
+ max.put("@type", "xsd:integer");
- exclude.put("@id", "group:exclude");
- exclude.put("@type", "xsd:boolean");
+ exclude.put("@id", "group:exclude");
+ exclude.put("@type", "xsd:boolean");
- distances.put("@id", "group:distances");
- distances.put("@container", "@list");
+ distances.put("@id", "group:distances");
+ distances.put("@container", "@list");
- inOrder.put("@id", "group:inOrder");
- inOrder.put("@type", "xsd:boolean");
+ inOrder.put("@id", "group:inOrder");
+ inOrder.put("@type", "xsd:boolean");
- context.put("korap", "http://korap.ids-mannheim.de/ns/KorAP/json-ld/v0.1/");
- context.put("boundary", "korap:boundary/");
- context.put("group", "korap:group/");
- context.put("operation", operation);
- context.put("class", classMap);
- context.put("operands", operands);
- context.put("frame", frame);
- context.put("classRef", classRef);
- context.put("spanRef", spanRef);
- context.put("classRefOp", classRefOp);
- context.put("min", min);
- context.put("max", max);
- context.put("exclude", exclude);
- context.put("distances", distances);
- context.put("inOrder", inOrder);
+ context.put("korap", "http://korap.ids-mannheim.de/ns/KorAP/json-ld/v0.1/");
+ context.put("boundary", "korap:boundary/");
+ context.put("group", "korap:group/");
+ context.put("operation", operation);
+ context.put("class", classMap);
+ context.put("operands", operands);
+ context.put("frame", frame);
+ context.put("classRef", classRef);
+ context.put("spanRef", spanRef);
+ context.put("classRefOp", classRefOp);
+ context.put("min", min);
+ context.put("max", max);
+ context.put("exclude", exclude);
+ context.put("distances", distances);
+ context.put("inOrder", inOrder);
- requestMap.put("@context", context);
- }
+ requestMap.put("@context", context);
+ }
- public static String escapeRegexSpecialChars(String key) {
+ public static String escapeRegexSpecialChars(String key) {
key.replace("\\", "\\\\");
Pattern p = Pattern.compile("\\.|\\^|\\$|\\||\\?|\\*|\\+|\\(|\\)|\\[|\\]|\\{|\\}");
Matcher m = p.matcher(key);
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/util/Antlr3DescriptiveErrorListener.java b/src/main/java/de/ids_mannheim/korap/query/serialize/util/Antlr3DescriptiveErrorListener.java
new file mode 100644
index 0000000..2a0fb63
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/util/Antlr3DescriptiveErrorListener.java
@@ -0,0 +1,85 @@
+package de.ids_mannheim.korap.query.serialize.util;
+
+import java.util.ArrayList;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import de.ids_mannheim.korap.query.cosmas2.IErrorReporter;
+import de.ids_mannheim.korap.query.serialize.QueryUtils;
+
+/**
+ * Custom descriptive error listener for Antlr3 grammars. Requires interface IErrorReporter to be present in
+ * grammar destination (generated source directory).
+ * @author Joachim Bingel (bingel@ids-mannheim.de)
+ *
+ */
+public class Antlr3DescriptiveErrorListener implements IErrorReporter {
+
+ private String query;
+ private String offendingSymbol;
+ private String expected;
+ private int charPosition;
+
+ public Antlr3DescriptiveErrorListener(String query) {
+ this.query = query;
+ };
+
+ @Override
+ public void reportError(String error) {
+ String charPositionStr = null;
+ String offendingSymbol = null;
+ String expected = null;
+ Pattern p = Pattern.compile("line \\d+:(\\d+).* '(.+?)' expecting (.+)");
+ Matcher m = p.matcher(error);
+ if (m.find()) {
+ charPositionStr = m.group(1);
+ offendingSymbol = m.group(2);
+ expected = m.group(3);
+ }
+ if (charPositionStr != null)
+ this.charPosition = Integer.parseInt(charPositionStr);
+ if (offendingSymbol != null)
+ this.offendingSymbol = offendingSymbol;
+ if (expected != null)
+ this.expected = expected;
+ }
+
+ public ArrayList<Object> generateFullErrorMsg() {
+ ArrayList<Object> errorSpecs = new ArrayList<Object>();
+ String msg = getDetailedErrorMessage();
+ errorSpecs.add(StatusCodes.MALFORMED_QUERY);
+ errorSpecs.add(msg);
+ errorSpecs.add(getCharPosition());
+ return errorSpecs;
+ }
+
+ private String getDetailedErrorMessage() {
+ // default message, in case no detailed info is available;
+ String msg = "Malformed query. Could not parse.";
+ char offendingSymbol = query.charAt(0);
+ if (query.length() > charPosition) offendingSymbol = query.charAt(charPosition);
+ msg = "Failing to parse at symbol: '"+offendingSymbol+"'";
+ if (expected != null) {
+ if (expected.equals("EOF") || expected.equals("<EOF>")) {
+ msg += " Expected end of query.";
+ } else {
+ msg += " Expected '"+expected+"'";
+ }
+ }
+ // check for unbalanced parantheses
+ SimpleEntry<String, Integer> unbalanced = QueryUtils.checkUnbalancedPars(query);
+ if (unbalanced != null) {
+ msg = unbalanced.getKey();
+ charPosition = unbalanced.getValue();
+ }
+
+ return msg;
+ }
+
+ public int getCharPosition() {
+ return charPosition;
+ }
+
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/util/Antlr4DescriptiveErrorListener.java b/src/main/java/de/ids_mannheim/korap/query/serialize/util/Antlr4DescriptiveErrorListener.java
new file mode 100644
index 0000000..5b7115e
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/util/Antlr4DescriptiveErrorListener.java
@@ -0,0 +1,72 @@
+package de.ids_mannheim.korap.query.serialize.util;
+
+import java.util.ArrayList;
+import java.util.AbstractMap.SimpleEntry;
+
+import org.antlr.v4.runtime.BaseErrorListener;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+
+import de.ids_mannheim.korap.query.serialize.QueryUtils;
+
+public class Antlr4DescriptiveErrorListener extends BaseErrorListener {
+
+ String query;
+ String message;
+ int line;
+ int charPosition;
+
+ public Antlr4DescriptiveErrorListener(String query) {
+ this.query = query;
+ };
+
+ @Override
+ public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol,
+ int line, int charPositionInLine,
+ String msg, RecognitionException e)
+ {
+ this.message = msg;
+ this.line = line;
+ this.charPosition = charPositionInLine;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public int getLine() {
+ return line;
+ }
+
+ public int getCharPosition() {
+ return charPosition;
+ }
+
+ public ArrayList<Object> generateFullErrorMsg() {
+ ArrayList<Object> errorSpecs = new ArrayList<Object>();
+ String msg = getDetailedErrorMessage();
+ errorSpecs.add(StatusCodes.MALFORMED_QUERY);
+ errorSpecs.add(msg);
+ errorSpecs.add(getCharPosition());
+ return errorSpecs;
+ }
+
+ private String getDetailedErrorMessage() {
+ // default message, in case no detailed info is available;
+ String msg = "Malformed query. Could not parse.";
+ char offendingSymbol = query.charAt(0);
+ if (query.length() > charPosition) offendingSymbol = query.charAt(charPosition);
+ msg = "Failing to parse at symbol: '"+offendingSymbol+"'";
+ // check for unbalanced parantheses
+ SimpleEntry<String, Integer> unbalanced = QueryUtils.checkUnbalancedPars(query);
+ if (unbalanced != null) {
+ msg = unbalanced.getKey();
+ charPosition = unbalanced.getValue();
+ }
+
+ return msg;
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/util/StatusCodes.java b/src/main/java/de/ids_mannheim/korap/query/serialize/util/StatusCodes.java
new file mode 100644
index 0000000..6bcf7e9
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/util/StatusCodes.java
@@ -0,0 +1,10 @@
+package de.ids_mannheim.korap.query.serialize.util;
+
+public class StatusCodes {
+ public final static int NO_QUERY = 301;
+ public final static int MALFORMED_QUERY = 302;
+ public final static int DEPRECATED_QUERY_ELEMENT = 303;
+ public final static int UNDEFINED_CLASS_REFERENCE = 304;
+ public final static int INCOMPATIBLE_OPERATOR_AND_OPERAND = 305;
+ public final static int UNKNOWN_QUERY_ELEMENT = 306;
+}
diff --git a/src/test/java/CosmasTreeTest.java b/src/test/java/CosmasTreeTest.java
index fe6370e..03ab986 100644
--- a/src/test/java/CosmasTreeTest.java
+++ b/src/test/java/CosmasTreeTest.java
@@ -595,6 +595,14 @@
assertEquals(true, res.at("/query/operands/0/operands/0/frames/1").isMissingNode());
assertEquals(true, res.at("/query/operands/0/operands/0/exclude").asBoolean());
+ query = "wegen #IN(FE,%,MIN) <s>";
+ qs.setQuery(query, "cosmas2");
+ res = mapper.readTree(qs.toJSON());
+ assertEquals(true, res.at("/query/reset").isMissingNode());
+ assertEquals("classRefCheck:equals", res.at("/query/operands/0/classRefCheck/0").asText());
+ assertEquals("frames:matches", res.at("/query/operands/0/operands/0/frames/0").asText());
+ assertEquals(true, res.at("/query/operands/0/operands/0/exclude").asBoolean());
+
query = "wegen #IN(FE,ALL,%,MIN) <s>";
qs.setQuery(query, "cosmas2");
res = mapper.readTree(qs.toJSON());
@@ -602,7 +610,6 @@
assertEquals("classRefCheck:equals", res.at("/query/operands/0/classRefCheck/0").asText());
assertEquals("frames:matches", res.at("/query/operands/0/operands/0/frames/0").asText());
assertEquals(true, res.at("/query/operands/0/operands/0/exclude").asBoolean());
-
}
@Test