blob: 26e86e37edc1e266e7fb77f40fd8ae33fc7e17c1 [file] [log] [blame]
Joachim Bingeldbbde772014-05-12 15:26:10 +00001package de.ids_mannheim.korap.query.serialize;
2
Joachim Bingel6003b852014-12-18 14:20:55 +00003import de.ids_mannheim.korap.query.parse.collection.CollectionQueryLexer;
4import de.ids_mannheim.korap.query.parse.collection.CollectionQueryParser;
Joachim Bingel3fa584b2014-12-17 13:35:43 +00005import de.ids_mannheim.korap.query.serialize.util.Antlr4DescriptiveErrorListener;
Joachim Bingel0e54d222015-01-12 13:22:16 +00006import de.ids_mannheim.korap.query.serialize.util.CqlfObjectGenerator;
Joachim Bingel3fa584b2014-12-17 13:35:43 +00007import de.ids_mannheim.korap.query.serialize.util.StatusCodes;
Joachim Bingel6003b852014-12-18 14:20:55 +00008import de.ids_mannheim.korap.query.serialize.util.QueryException;
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +00009
Michael Hanldf206ab2014-05-13 10:22:27 +000010import org.antlr.v4.runtime.*;
Joachim Bingel787836a2014-08-07 14:50:18 +000011import org.antlr.v4.runtime.tree.*;
Joachim Bingel017915d2014-12-16 13:03:04 +000012import org.slf4j.Logger;
13import org.slf4j.LoggerFactory;
Joachim Bingeldbbde772014-05-12 15:26:10 +000014
15import java.lang.reflect.Method;
16import java.util.*;
Joachim Bingel3e7e2c72014-12-16 16:36:50 +000017import java.util.regex.Matcher;
Joachim Bingel017915d2014-12-16 13:03:04 +000018import java.util.regex.Pattern;
Joachim Bingeldbbde772014-05-12 15:26:10 +000019
Joachim Bingeldbbde772014-05-12 15:26:10 +000020/**
Joachim Bingela3f51f72014-07-22 14:45:31 +000021 * @author hanl, bingel
Joachim Bingeldbbde772014-05-12 15:26:10 +000022 * @date 06/12/2013
23 */
Joachim Bingel1faf8a52015-01-09 13:17:34 +000024public class CollectionQueryProcessor extends Antlr4AbstractQueryProcessor {
Joachim Bingeldbbde772014-05-12 15:26:10 +000025
Joachim Bingel1faf8a52015-01-09 13:17:34 +000026 private static Logger log = LoggerFactory.getLogger(CollectionQueryProcessor.class);
Michael Hanldf206ab2014-05-13 10:22:27 +000027 private Parser parser;
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +000028 private static boolean verbose;
Michael Hanldf206ab2014-05-13 10:22:27 +000029 private List<ParseTree> visited = new ArrayList<ParseTree>();
Michael Hanl2bd00c62014-11-04 16:26:42 +000030
Michael Hanldf206ab2014-05-13 10:22:27 +000031 /**
Michael Hanldf206ab2014-05-13 10:22:27 +000032 * Keeps track of active object.
33 */
34 LinkedList<LinkedHashMap<String, Object>> objectStack = new LinkedList<LinkedHashMap<String, Object>>();
35 /**
36 * Keeps track of open node categories
37 */
38 LinkedList<String> openNodeCats = new LinkedList<String>();
39 /**
40 * Keeps track of how many objects there are to pop after every recursion of {@link #processNode(ParseTree)}
41 */
42 LinkedList<Integer> objectsToPop = new LinkedList<Integer>();
43 Integer stackedObjects = 0;
Joachim Bingelf8a578b2014-10-08 08:41:00 +000044
Joachim Bingel1faf8a52015-01-09 13:17:34 +000045 public CollectionQueryProcessor() {
Joachim Bingel0e54d222015-01-12 13:22:16 +000046 CqlfObjectGenerator.setQueryProcessor(this);
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +000047 }
48
Joachim Bingel1faf8a52015-01-09 13:17:34 +000049 public CollectionQueryProcessor(boolean verbose) {
Joachim Bingel0e54d222015-01-12 13:22:16 +000050 CqlfObjectGenerator.setQueryProcessor(this);
Joachim Bingel1faf8a52015-01-09 13:17:34 +000051 CollectionQueryProcessor.verbose = verbose;
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +000052 }
53
Joachim Bingel1faf8a52015-01-09 13:17:34 +000054 public CollectionQueryProcessor(String query) throws QueryException {
Joachim Bingel0e54d222015-01-12 13:22:16 +000055 CqlfObjectGenerator.setQueryProcessor(this);
56 process(query);
Joachim Bingelf8a578b2014-10-08 08:41:00 +000057 }
58
59 @Override
Michael Hanldf206ab2014-05-13 10:22:27 +000060 public void process(String query) throws QueryException {
61 ParseTree tree = parseCollectionQuery(query);
62 if (this.parser != null) {
63 super.parser = this.parser;
64 } else {
65 throw new NullPointerException("Parser has not been instantiated!");
66 }
Joachim Bingel4b9fbd72014-12-18 15:57:05 +000067 log.info("Processing collection query: "+query);
Michael Hanldf206ab2014-05-13 10:22:27 +000068 if (verbose) System.out.println(tree.toStringTree(parser));
69 processNode(tree);
70 }
71
72 private void processNode(ParseTree node) {
73 // Top-down processing
74 String nodeCat = getNodeCat(node);
75 openNodeCats.push(nodeCat);
76
77 stackedObjects = 0;
Michael Hanldf206ab2014-05-13 10:22:27 +000078 if (verbose) {
79 System.err.println(" " + objectStack);
80 System.out.println(openNodeCats);
81 }
Joachim Bingeldbbde772014-05-12 15:26:10 +000082
83 /*
Michael Hanldf206ab2014-05-13 10:22:27 +000084 ****************************************************************
Joachim Bingeldbbde772014-05-12 15:26:10 +000085 ****************************************************************
86 * Processing individual node categories *
87 ****************************************************************
88 ****************************************************************
89 */
Michael Hanldf206ab2014-05-13 10:22:27 +000090
Joachim Bingela3f51f72014-07-22 14:45:31 +000091 if (nodeCat.equals("relation")) {
92 String operator = node.getChild(1).getChild(0).toStringTree(parser).equals("&") ? "and" : "or";
Joachim Bingel0e54d222015-01-12 13:22:16 +000093 LinkedHashMap<String, Object> relationGroup = CqlfObjectGenerator.makeDocGroup(operator);
Joachim Bingela3f51f72014-07-22 14:45:31 +000094 putIntoSuperObject(relationGroup);
95 objectStack.push(relationGroup);
Michael Hanldf206ab2014-05-13 10:22:27 +000096 stackedObjects++;
Michael Hanldf206ab2014-05-13 10:22:27 +000097 }
98
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +000099 if (nodeCat.equals("constraint")) {
Michael Hanldf206ab2014-05-13 10:22:27 +0000100 ParseTree fieldNode = getFirstChildWithCat(node, "field");
101 String field = fieldNode.getChild(0).toStringTree(parser);
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000102 ParseTree operatorNode = getFirstChildWithCat(node, "operator");
103 ParseTree valueNode = getFirstChildWithCat(node, "value");
Joachim Bingel0e54d222015-01-12 13:22:16 +0000104 LinkedHashMap<String, Object> term = CqlfObjectGenerator.makeDoc();
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000105 term.put("key", field);
106 term.putAll(parseValue(valueNode));
107 String match = operatorNode.getText();
108 term.put("match", "match:" + interpretMatchOperator(match));
109 if (checkOperatorValueConformance(term) == false) {
110 requestMap = new LinkedHashMap<String,Object>();
111 return;
Michael Hanldf206ab2014-05-13 10:22:27 +0000112 }
Joachim Bingel017915d2014-12-16 13:03:04 +0000113 if (checkDateValidity(valueNode)) {
114 addWarning("The collection query contains a value that looks like a date ('"+valueNode.getText()+"')"
115 + " and an operator that is only defined for strings ('"+match+"'). The value is interpreted as "
116 + "a string, use a date operator to ensure the value is treated as a date");
117 }
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000118 putIntoSuperObject(term);
119 }
120
121 if (nodeCat.equals("dateconstraint")) {
122 ParseTree fieldNode = getFirstChildWithCat(node, "field");
123 String field = fieldNode.getChild(0).toStringTree(parser);
124 ParseTree dateOpNode = getFirstChildWithCat(node, "dateOp");
125 ParseTree dateNode = getFirstChildWithCat(node, "date");
Michael Hanldf206ab2014-05-13 10:22:27 +0000126
Joachim Bingel0e54d222015-01-12 13:22:16 +0000127 LinkedHashMap<String, Object> term = CqlfObjectGenerator.makeDoc();
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000128 term.put("key", field);
129 term.putAll(parseValue(dateNode));
130 String match = dateOpNode.getText();
131 term.put("match", "match:" + interpretMatchOperator(match));
132 if (checkOperatorValueConformance(term) == false) {
133 requestMap = new LinkedHashMap<String,Object>();
134 return;
135 }
136 putIntoSuperObject(term);
Michael Hanldf206ab2014-05-13 10:22:27 +0000137 }
Joachim Bingel787836a2014-08-07 14:50:18 +0000138
139 if (nodeCat.equals("token")) {
Joachim Bingel0e54d222015-01-12 13:22:16 +0000140 LinkedHashMap<String,Object> token = CqlfObjectGenerator.makeToken();
Joachim Bingel787836a2014-08-07 14:50:18 +0000141 // handle negation
142 List<ParseTree> negations = getChildrenWithCat(node, "!");
143 boolean negated = false;
144 boolean isRegex = false;
145 if (negations.size() % 2 == 1) negated = true;
146 if (getNodeCat(node.getChild(0)).equals("key")) {
147 // no 'term' child, but direct key specification: process here
Joachim Bingel0e54d222015-01-12 13:22:16 +0000148 LinkedHashMap<String,Object> term = CqlfObjectGenerator.makeTerm();
Joachim Bingel787836a2014-08-07 14:50:18 +0000149 String key = node.getChild(0).getText();
150 if (getNodeCat(node.getChild(0).getChild(0)).equals("regex")) {
151 isRegex = true;
152 term.put("type", "type:regex");
153 key = key.substring(1,key.length()-1);
154 }
155 term.put("layer", "orth");
156 term.put("key", key);
157 String matches = negated ? "ne" : "eq";
158 term.put("match", "match:"+matches);
159 ParseTree flagNode = getFirstChildWithCat(node, "flag");
160 if (flagNode != null) {
161 String flag = getNodeCat(flagNode.getChild(0)).substring(1); //substring removes leading slash '/'
162 if (flag.contains("i")) term.put("caseInsensitive", true);
163 else if (flag.contains("I")) term.put("caseInsensitive", false);
164 if (flag.contains("x")) {
165 term.put("type", "type:regex");
166 if (!isRegex) {
167 key = QueryUtils.escapeRegexSpecialChars(key);
168 }
169 term.put("key", ".*?"+key+".*?"); // overwrite key
170 }
171 }
172 token.put("wrap", term);
173 } else {
174 // child is 'term' or 'termGroup' -> process in extra method
175 LinkedHashMap<String,Object> termOrTermGroup = parseTermOrTermGroup(node.getChild(1), negated);
176 token.put("wrap", termOrTermGroup);
177 }
178 putIntoSuperObject(token);
179 visited.add(node.getChild(0));
180 visited.add(node.getChild(2));
181 }
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000182
Michael Hanldf206ab2014-05-13 10:22:27 +0000183 objectsToPop.push(stackedObjects);
Michael Hanlf1fead42014-05-14 15:13:33 +0000184
Joachim Bingeldbbde772014-05-12 15:26:10 +0000185 /*
Michael Hanlf1fead42014-05-14 15:13:33 +0000186 ****************************************************************
Joachim Bingeldbbde772014-05-12 15:26:10 +0000187 ****************************************************************
188 * recursion until 'request' node (root of tree) is processed *
189 ****************************************************************
190 ****************************************************************
191 */
Michael Hanldf206ab2014-05-13 10:22:27 +0000192 for (int i = 0; i < node.getChildCount(); i++) {
193 ParseTree child = node.getChild(i);
194 processNode(child);
195 }
Joachim Bingeldbbde772014-05-12 15:26:10 +0000196
197 /*
Michael Hanlf1fead42014-05-14 15:13:33 +0000198 **************************************************************
Joachim Bingeldbbde772014-05-12 15:26:10 +0000199 * Stuff that happens after processing the children of a node *
200 **************************************************************
201 */
Michael Hanldf206ab2014-05-13 10:22:27 +0000202 if (!objectsToPop.isEmpty()) {
Joachim Bingela3f51f72014-07-22 14:45:31 +0000203 int toPop = objectsToPop.pop();
204 for (int i = 0; i < toPop; i++) {
Michael Hanldf206ab2014-05-13 10:22:27 +0000205 objectStack.pop();
206 }
207 }
208 openNodeCats.pop();
Joachim Bingeldbbde772014-05-12 15:26:10 +0000209
Joachim Bingeldbbde772014-05-12 15:26:10 +0000210
Joachim Bingeldbbde772014-05-12 15:26:10 +0000211 }
212
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000213 /**
214 * Checks whether the combination of operator and value is legal (inequation operators <,>,<=,>= may only be used with dates).
215 */
216 private boolean checkOperatorValueConformance(LinkedHashMap<String, Object> term) {
Joachim Bingel49da7ca2014-10-07 12:26:14 +0000217 String match = (String) term.get("match");
218 String type = (String) term.get("type");
219 if (type == null || type.equals("type:regex")) {
Joachim Bingel017915d2014-12-16 13:03:04 +0000220 if (!(match.equals("match:eq") || match.equals("match:ne") || match.equals("match:contains") || match.equals("match:containsnot"))) {
Joachim Bingel3fa584b2014-12-17 13:35:43 +0000221 addError(StatusCodes.INCOMPATIBLE_OPERATOR_AND_OPERAND, "You used an inequation operator with a string value.");
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000222 return false;
Joachim Bingel49da7ca2014-10-07 12:26:14 +0000223 }
224 }
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000225 return true;
Joachim Bingel49da7ca2014-10-07 12:26:14 +0000226 }
227
228 private LinkedHashMap<String, Object> parseValue(ParseTree valueNode) {
Joachim Bingela3f51f72014-07-22 14:45:31 +0000229 LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000230 if (getNodeCat(valueNode).equals("date")) {
231 map.put("type", "type:date");
232 checkDateValidity(valueNode);
233 }
Joachim Bingela3f51f72014-07-22 14:45:31 +0000234 if (getNodeCat(valueNode.getChild(0)).equals("regex")) {
235 String regex = valueNode.getChild(0).getChild(0).toStringTree(parser);
236 map.put("value", regex.substring(1, regex.length()-1));
237 map.put("type", "type:regex");
Joachim Bingel82e4ca72014-10-27 11:03:38 +0000238 } else if (getNodeCat(valueNode.getChild(0)).equals("multiword")) {
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000239 String mw = ""; // multiword
Joachim Bingel82e4ca72014-10-27 11:03:38 +0000240 for (int i=1; i<valueNode.getChild(0).getChildCount()-1; i++) {
241 mw += valueNode.getChild(0).getChild(i).getText() + " ";
242 }
243 map.put("value", mw.substring(0, mw.length()-1));
Joachim Bingela3f51f72014-07-22 14:45:31 +0000244 } else {
245 map.put("value", valueNode.getChild(0).toStringTree(parser));
246 }
247 return map;
248 }
249
Joachim Bingel3e7e2c72014-12-16 16:36:50 +0000250 /**
251 * Checks if a date
252 * @param valueNode
253 * @return
254 */
Joachim Bingel017915d2014-12-16 13:03:04 +0000255 private boolean checkDateValidity(ParseTree valueNode) {
Joachim Bingel3e7e2c72014-12-16 16:36:50 +0000256 Pattern p = Pattern.compile("[0-9]{4}(-([0-9]{2})(-([0-9]{2}))?)?");
257 Matcher m = p.matcher(valueNode.getText());
258
259 if (!m.find()) return false;
260 String month = m.group(2);
261 String day = m.group(4);
262 if (month != null) {
263 if (Integer.parseInt(month) > 12) {
264 return false;
265 } else if (day != null) {
266 if (Integer.parseInt(day) > 31) {
267 return false;
268 }
269 }
270 }
Joachim Bingel017915d2014-12-16 13:03:04 +0000271 return true;
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000272 }
273
274 private String interpretMatchOperator(String match) {
Michael Hanldf206ab2014-05-13 10:22:27 +0000275 String out = null;
276 switch (match) {
277 case "<":
278 out = "lt";
279 break;
280 case ">":
281 out = "gt";
282 break;
283 case "<=":
284 out = "leq";
285 break;
286 case ">=":
287 out = "geq";
288 break;
289 case "=":
290 out = "eq";
291 break;
292 case "!=":
293 out = "ne";
294 break;
Joachim Bingela499e922014-10-08 13:32:50 +0000295 case "~":
296 out = "contains";
297 break;
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000298 case "!~":
Joachim Bingel017915d2014-12-16 13:03:04 +0000299 out = "containsnot";
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000300 break;
301 case "in":
302 out = "eq";
303 break;
304 case "on":
305 out = "eq";
306 break;
307 case "until":
308 out = "leq";
309 break;
310 case "since":
311 out = "geq";
312 break;
313 default:
314 out = match;
Joachim Bingel3fa584b2014-12-17 13:35:43 +0000315 addError(StatusCodes.UNKNOWN_QUERY_ELEMENT, "Unknown operator '"+match+"'.");
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000316 break;
Michael Hanldf206ab2014-05-13 10:22:27 +0000317 }
318 return out;
319 }
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000320
321 @Deprecated
Michael Hanldf206ab2014-05-13 10:22:27 +0000322 private String invertInequation(String op) {
323 String inv = null;
324 switch (op) {
325 case "lt":
326 inv = "gt";
327 break;
328 case "leq":
329 inv = "geq";
330 break;
331 case "gt":
332 inv = "lt";
333 break;
334 case "geq":
335 inv = "leq";
336 break;
337 }
338 return inv;
339 }
340
341 private void putIntoSuperObject(LinkedHashMap<String, Object> object) {
342 putIntoSuperObject(object, 0);
343 }
344
345 @SuppressWarnings({"unchecked"})
346 private void putIntoSuperObject(LinkedHashMap<String, Object> object, int objStackPosition) {
347 if (objectStack.size() > objStackPosition) {
348 ArrayList<Object> topObjectOperands = (ArrayList<Object>) objectStack.get(objStackPosition).get("operands");
Joachim Bingela3f51f72014-07-22 14:45:31 +0000349 topObjectOperands.add(object);
Michael Hanldf206ab2014-05-13 10:22:27 +0000350 } else {
Joachim Bingel017915d2014-12-16 13:03:04 +0000351// requestMap = object;
352 requestMap.put("collection", object);
Michael Hanldf206ab2014-05-13 10:22:27 +0000353 }
354 }
355
Joachim Bingel787836a2014-08-07 14:50:18 +0000356 private LinkedHashMap<String, Object> parseTermOrTermGroup(
357 ParseTree node, boolean negated) {
358 return parseTermOrTermGroup(node, negated, "token");
359 }
360
361 /**
362 * Parses a (term) or (termGroup) node
363 * @param node
364 * @param negatedGlobal Indicates whether the term/termGroup is globally negated, e.g. through a negation
365 * operator preceding the related token like "![base=foo]". Global negation affects the term's "match" parameter.
366 * @return A term or termGroup object, depending on input
367 */
368 @SuppressWarnings("unchecked")
369 private LinkedHashMap<String, Object> parseTermOrTermGroup(ParseTree node, boolean negatedGlobal, String mode) {
370 if (getNodeCat(node).equals("term")) {
371 String key = null;
Joachim Bingel0e54d222015-01-12 13:22:16 +0000372 LinkedHashMap<String,Object> term = CqlfObjectGenerator.makeTerm();
Joachim Bingel787836a2014-08-07 14:50:18 +0000373 // handle negation
374 boolean negated = negatedGlobal;
375 boolean isRegex = false;
376 List<ParseTree> negations = getChildrenWithCat(node, "!");
377 if (negations.size() % 2 == 1) negated = !negated;
378 // retrieve possible nodes
379 ParseTree keyNode = getFirstChildWithCat(node, "key");
380 ParseTree valueNode = getFirstChildWithCat(node, "value");
381 ParseTree layerNode = getFirstChildWithCat(node, "layer");
382 ParseTree foundryNode = getFirstChildWithCat(node, "foundry");
383 ParseTree termOpNode = getFirstChildWithCat(node, "termOp");
384 ParseTree flagNode = getFirstChildWithCat(node, "flag");
385 // process foundry
386 if (foundryNode != null) term.put("foundry", foundryNode.getText());
387 // process layer: map "base" -> "lemma"
388 if (layerNode != null) {
389 String layer = layerNode.getText();
390 if (layer.equals("base")) layer="lemma";
391 if (mode.equals("span")) term.put("key", layer);
392 else term.put("layer", layer);
393 }
394 // process key: 'normal' or regex?
395 key = keyNode.getText();
396 if (getNodeCat(keyNode.getChild(0)).equals("regex")) {
397 isRegex = true;
398 term.put("type", "type:regex");
Joachim Bingel82e4ca72014-10-27 11:03:38 +0000399 key = key.substring(1, key.length()-1); // remove leading and trailing slashes
Joachim Bingel787836a2014-08-07 14:50:18 +0000400 }
401 if (mode.equals("span")) term.put("value", key);
402 else term.put("key", key);
403 // process value
404 if (valueNode != null) term.put("value", valueNode.getText());
405 // process operator ("match" property)
406 if (termOpNode != null) {
407 String termOp = termOpNode.getText();
408 negated = termOp.contains("!") ? !negated : negated;
409 if (!negated) term.put("match", "match:eq");
410 else term.put("match", "match:ne");
411 }
412 // process possible flags
413 if (flagNode != null) {
414 String flag = getNodeCat(flagNode.getChild(0)).substring(1); //substring removes leading slash '/'
415 if (flag.contains("i")) term.put("caseInsensitive", true);
416 else if (flag.contains("I")) term.put("caseInsensitive", false);
417 if (flag.contains("x")) {
418 if (!isRegex) {
419 key = QueryUtils.escapeRegexSpecialChars(key);
420 }
421 term.put("key", ".*?"+key+".*?"); // flag 'x' allows submatches: overwrite key with appended .*?
422 term.put("type", "type:regex");
423 }
424 }
425 return term;
426 } else {
427 // For termGroups, establish a boolean relation between operands and recursively call this function with
428 // the term or termGroup operands
429 LinkedHashMap<String,Object> termGroup = null;
430 ParseTree leftOp = null;
431 ParseTree rightOp = null;
432 // check for leading/trailing parantheses
433 if (!getNodeCat(node.getChild(0)).equals("(")) leftOp = node.getChild(0);
434 else leftOp = node.getChild(1);
435 if (!getNodeCat(node.getChild(node.getChildCount()-1)).equals(")")) rightOp = node.getChild(node.getChildCount()-1);
436 else rightOp = node.getChild(node.getChildCount()-2);
437 // establish boolean relation
438 ParseTree boolOp = getFirstChildWithCat(node, "booleanOp");
439 String operator = boolOp.getText().equals("&") ? "and" : "or";
Joachim Bingel0e54d222015-01-12 13:22:16 +0000440 termGroup = CqlfObjectGenerator.makeTermGroup(operator);
Joachim Bingel787836a2014-08-07 14:50:18 +0000441 ArrayList<Object> operands = (ArrayList<Object>) termGroup.get("operands");
442 // recursion with left/right operands
443 operands.add(parseTermOrTermGroup(leftOp, negatedGlobal, mode));
444 operands.add(parseTermOrTermGroup(rightOp, negatedGlobal, mode));
445 return termGroup;
446 }
447 }
448
Joachim Bingel3fa584b2014-12-17 13:35:43 +0000449 private ParserRuleContext parseCollectionQuery(String query) throws QueryException {
450 Lexer lexer = new CollectionQueryLexer((CharStream) null);
Michael Hanldf206ab2014-05-13 10:22:27 +0000451 ParserRuleContext tree = null;
Joachim Bingel3fa584b2014-12-17 13:35:43 +0000452 Antlr4DescriptiveErrorListener errorListener = new Antlr4DescriptiveErrorListener(query);
Michael Hanldf206ab2014-05-13 10:22:27 +0000453 // Like p. 111
454 try {
455
456 // Tokenize input data
Joachim Bingel3fa584b2014-12-17 13:35:43 +0000457 ANTLRInputStream input = new ANTLRInputStream(query);
458 lexer.setInputStream(input);
459 CommonTokenStream tokens = new CommonTokenStream(lexer);
Michael Hanldf206ab2014-05-13 10:22:27 +0000460 parser = new CollectionQueryParser(tokens);
461
462 // Don't throw out erroneous stuff
463 parser.setErrorHandler(new BailErrorStrategy());
Joachim Bingel3fa584b2014-12-17 13:35:43 +0000464 lexer.removeErrorListeners();
465 lexer.addErrorListener(errorListener);
Michael Hanldf206ab2014-05-13 10:22:27 +0000466 parser.removeErrorListeners();
Joachim Bingel3fa584b2014-12-17 13:35:43 +0000467 parser.addErrorListener(errorListener);
Michael Hanldf206ab2014-05-13 10:22:27 +0000468 // Get starting rule from parser
469 Method startRule = CollectionQueryParser.class.getMethod("start");
470 tree = (ParserRuleContext) startRule.invoke(parser, (Object[]) null);
Michael Hanldf206ab2014-05-13 10:22:27 +0000471 }
472 // Some things went wrong ...
473 catch (Exception e) {
Joachim Bingel3fa584b2014-12-17 13:35:43 +0000474 System.err.println("ERROR: "+errorListener.generateFullErrorMsg());
Joachim Bingel017915d2014-12-16 13:03:04 +0000475 System.err.println("Parsing exception message: " + e);
Michael Hanldf206ab2014-05-13 10:22:27 +0000476 }
477 if (tree == null) {
Michael Hanlf1fead42014-05-14 15:13:33 +0000478 throw new QueryException("Could not parse query. Make sure it is correct syntax.");
Michael Hanldf206ab2014-05-13 10:22:27 +0000479 }
480 // Return the generated tree
481 return tree;
482 }
Joachim Bingel761d1c12014-12-17 14:02:40 +0000483}