blob: 70034257452f27e784be1177fe8bd41cfd9962dd [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 Bingelaa4ab2f2015-01-16 14:26:51 +00006import de.ids_mannheim.korap.query.serialize.util.KoralObjectGenerator;
Joachim Bingel3fa584b2014-12-17 13:35:43 +00007import de.ids_mannheim.korap.query.serialize.util.StatusCodes;
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +00008
Michael Hanldf206ab2014-05-13 10:22:27 +00009import org.antlr.v4.runtime.*;
Joachim Bingel787836a2014-08-07 14:50:18 +000010import org.antlr.v4.runtime.tree.*;
Joachim Bingel017915d2014-12-16 13:03:04 +000011import org.slf4j.Logger;
12import org.slf4j.LoggerFactory;
Joachim Bingeldbbde772014-05-12 15:26:10 +000013
14import java.lang.reflect.Method;
15import java.util.*;
Joachim Bingel3e7e2c72014-12-16 16:36:50 +000016import java.util.regex.Matcher;
Joachim Bingel017915d2014-12-16 13:03:04 +000017import java.util.regex.Pattern;
Joachim Bingeldbbde772014-05-12 15:26:10 +000018
Joachim Bingeldbbde772014-05-12 15:26:10 +000019/**
Joachim Bingela6954de2015-03-20 16:37:37 +010020 * This class processes queries that define virtual collections and
21 * create
22 * an KoralQuery representation of these in the <tt>collection</tt>
23 * attribute
24 * of the KoralQuery tree. See the official documentation for VC query
25 * syntax
Joachim Bingela145c982015-02-18 18:31:57 +010026 * and functionality.
27 *
28 * @author Michael Hanl (hanl@ids-mannheim.de)
29 * @author Joachim Bingel (bingel@ids-mannheim.de)
Joachim Bingel7cb346e2015-03-09 10:56:20 +010030 * @version 0.3.0
Joachim Bingela145c982015-02-18 18:31:57 +010031 * @since 0.1.0
Joachim Bingeldbbde772014-05-12 15:26:10 +000032 */
Joachim Bingel1faf8a52015-01-09 13:17:34 +000033public class CollectionQueryProcessor extends Antlr4AbstractQueryProcessor {
Joachim Bingeldbbde772014-05-12 15:26:10 +000034
Joachim Bingela6954de2015-03-20 16:37:37 +010035 private static Logger log = LoggerFactory
36 .getLogger(CollectionQueryProcessor.class);
Michael Hanl2bd00c62014-11-04 16:26:42 +000037
Joachim Bingelf8a578b2014-10-08 08:41:00 +000038
Joachim Bingela6954de2015-03-20 16:37:37 +010039 public CollectionQueryProcessor () {
40 KoralObjectGenerator.setQueryProcessor(this);
41 }
42
43
44 public CollectionQueryProcessor (boolean verbose) {
45 KoralObjectGenerator.setQueryProcessor(this);
46 CollectionQueryProcessor.verbose = verbose;
47 }
48
49
50 public CollectionQueryProcessor (String query) {
51 KoralObjectGenerator.setQueryProcessor(this);
52 process(query);
53 }
54
55
56 @Override
57 public void process (String query) {
Michael Hanldf206ab2014-05-13 10:22:27 +000058 ParseTree tree = parseCollectionQuery(query);
59 if (this.parser != null) {
60 super.parser = this.parser;
Michael Hanldf206ab2014-05-13 10:22:27 +000061 }
Joachim Bingela6954de2015-03-20 16:37:37 +010062 else {
63 throw new NullPointerException("Parser has not been instantiated!");
64 }
65 log.info("Processing virtual collection query: " + query);
66 if (verbose)
67 System.out.println(tree.toStringTree(parser));
Joachim Bingel3d5b69b2015-01-14 10:46:44 +000068 if (tree != null) {
Joachim Bingela6954de2015-03-20 16:37:37 +010069 log.debug("ANTLR parse tree: " + tree.toStringTree(parser));
70 processNode(tree);
71 }
72 else {
73 addError(StatusCodes.MALFORMED_QUERY, "Could not parse query >>> "
74 + query + " <<<.");
75 }
Michael Hanldf206ab2014-05-13 10:22:27 +000076 }
77
Joachim Bingela6954de2015-03-20 16:37:37 +010078
79 private void processNode (ParseTree node) {
Michael Hanldf206ab2014-05-13 10:22:27 +000080 // Top-down processing
81 String nodeCat = getNodeCat(node);
82 openNodeCats.push(nodeCat);
83
84 stackedObjects = 0;
Michael Hanldf206ab2014-05-13 10:22:27 +000085 if (verbose) {
86 System.err.println(" " + objectStack);
87 System.out.println(openNodeCats);
88 }
Joachim Bingeldbbde772014-05-12 15:26:10 +000089
Joachim Bingela6954de2015-03-20 16:37:37 +010090 /*
Michael Hanldf206ab2014-05-13 10:22:27 +000091 ****************************************************************
Joachim Bingela6954de2015-03-20 16:37:37 +010092 ****************************************************************
93 * Processing individual node categories *
94 ****************************************************************
95 ****************************************************************
96 */
Michael Hanldf206ab2014-05-13 10:22:27 +000097
Joachim Bingela3f51f72014-07-22 14:45:31 +000098 if (nodeCat.equals("relation")) {
Joachim Bingela6954de2015-03-20 16:37:37 +010099 String operator = getNodeCat(node.getChild(1).getChild(0)).equals(
100 "&") ? "and" : "or";
101 LinkedHashMap<String, Object> relationGroup = KoralObjectGenerator
102 .makeDocGroup(operator);
Joachim Bingela3f51f72014-07-22 14:45:31 +0000103 putIntoSuperObject(relationGroup);
104 objectStack.push(relationGroup);
Michael Hanldf206ab2014-05-13 10:22:27 +0000105 stackedObjects++;
Michael Hanldf206ab2014-05-13 10:22:27 +0000106 }
107
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000108 if (nodeCat.equals("constraint")) {
Michael Hanldf206ab2014-05-13 10:22:27 +0000109 ParseTree fieldNode = getFirstChildWithCat(node, "field");
110 String field = fieldNode.getChild(0).toStringTree(parser);
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000111 ParseTree operatorNode = getFirstChildWithCat(node, "operator");
112 ParseTree valueNode = getFirstChildWithCat(node, "value");
Joachim Bingela6954de2015-03-20 16:37:37 +0100113 LinkedHashMap<String, Object> term = KoralObjectGenerator.makeDoc();
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000114 term.put("key", field);
115 term.putAll(parseValue(valueNode));
116 String match = operatorNode.getText();
117 term.put("match", "match:" + interpretMatchOperator(match));
118 if (checkOperatorValueConformance(term) == false) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100119 requestMap = new LinkedHashMap<String, Object>();
120 return;
Michael Hanldf206ab2014-05-13 10:22:27 +0000121 }
Joachim Bingel017915d2014-12-16 13:03:04 +0000122 if (checkDateValidity(valueNode)) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100123 addWarning("The collection query contains a value that looks"
124 + " like a date ('" + valueNode.getText() + "') and an"
125 + " operator that is only defined for strings" + " ('"
126 + match + "'). The value is interpreted as"
127 + " a string. Use a date operator to ensure the value"
128 + " is treated as a date");
Joachim Bingel017915d2014-12-16 13:03:04 +0000129 }
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000130 putIntoSuperObject(term);
131 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100132
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000133 if (nodeCat.equals("dateconstraint")) {
134 ParseTree fieldNode = getFirstChildWithCat(node, "field");
135 String field = fieldNode.getChild(0).toStringTree(parser);
136 ParseTree dateOpNode = getFirstChildWithCat(node, "dateOp");
137 ParseTree dateNode = getFirstChildWithCat(node, "date");
Michael Hanldf206ab2014-05-13 10:22:27 +0000138
Joachim Bingela6954de2015-03-20 16:37:37 +0100139 LinkedHashMap<String, Object> term = KoralObjectGenerator.makeDoc();
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000140 term.put("key", field);
141 term.putAll(parseValue(dateNode));
142 String match = dateOpNode.getText();
143 term.put("match", "match:" + interpretMatchOperator(match));
144 if (checkOperatorValueConformance(term) == false) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100145 requestMap = new LinkedHashMap<String, Object>();
146 return;
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000147 }
148 putIntoSuperObject(term);
Michael Hanldf206ab2014-05-13 10:22:27 +0000149 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100150
Joachim Bingel787836a2014-08-07 14:50:18 +0000151 if (nodeCat.equals("token")) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100152 LinkedHashMap<String, Object> token = KoralObjectGenerator
153 .makeToken();
154 // handle negation
155 List<ParseTree> negations = getChildrenWithCat(node, "!");
156 boolean negated = false;
157 boolean isRegex = false;
158 if (negations.size() % 2 == 1)
159 negated = true;
160 if (getNodeCat(node.getChild(0)).equals("key")) {
161 // no 'term' child, but direct key specification: process here
162 LinkedHashMap<String, Object> term = KoralObjectGenerator
163 .makeTerm();
164 String key = node.getChild(0).getText();
165 if (getNodeCat(node.getChild(0).getChild(0)).equals("regex")) {
166 isRegex = true;
167 term.put("type", "type:regex");
168 key = key.substring(1, key.length() - 1);
169 }
170 term.put("layer", "orth");
171 term.put("key", key);
172 String matches = negated ? "ne" : "eq";
173 term.put("match", "match:" + matches);
174 ParseTree flagNode = getFirstChildWithCat(node, "flag");
175 if (flagNode != null) {
176 // substring removes leading slash '/'
177 String flag = getNodeCat(flagNode.getChild(0)).substring(1);
178 if (flag.contains("i"))
179 term.put("caseInsensitive", true);
180 else if (flag.contains("I"))
181 term.put("caseInsensitive", false);
182 if (flag.contains("x")) {
183 term.put("type", "type:regex");
184 if (!isRegex) {
185 key = QueryUtils.escapeRegexSpecialChars(key);
186 }
187 term.put("key", ".*?" + key + ".*?"); // overwrite key
188 }
189 }
190 token.put("wrap", term);
191 }
192 else {
193 // child is 'term' or 'termGroup' -> process in extra method
194 LinkedHashMap<String, Object> termOrTermGroup = parseTermOrTermGroup(
195 node.getChild(1), negated);
196 token.put("wrap", termOrTermGroup);
197 }
198 putIntoSuperObject(token);
199 visited.add(node.getChild(0));
200 visited.add(node.getChild(2));
201 }
Michael Hanldf206ab2014-05-13 10:22:27 +0000202 objectsToPop.push(stackedObjects);
Michael Hanlf1fead42014-05-14 15:13:33 +0000203
Joachim Bingela6954de2015-03-20 16:37:37 +0100204 /*
Michael Hanlf1fead42014-05-14 15:13:33 +0000205 ****************************************************************
Joachim Bingela6954de2015-03-20 16:37:37 +0100206 ****************************************************************
207 * recursion until 'request' node (root of tree) is processed *
208 ****************************************************************
209 ****************************************************************
210 */
Michael Hanldf206ab2014-05-13 10:22:27 +0000211 for (int i = 0; i < node.getChildCount(); i++) {
212 ParseTree child = node.getChild(i);
213 processNode(child);
214 }
Joachim Bingeldbbde772014-05-12 15:26:10 +0000215
Joachim Bingela6954de2015-03-20 16:37:37 +0100216 /*
Michael Hanlf1fead42014-05-14 15:13:33 +0000217 **************************************************************
Joachim Bingela6954de2015-03-20 16:37:37 +0100218 * Stuff that happens after processing the children of a node *
219 **************************************************************
220 */
Michael Hanldf206ab2014-05-13 10:22:27 +0000221 if (!objectsToPop.isEmpty()) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100222 int toPop = objectsToPop.pop();
Joachim Bingela3f51f72014-07-22 14:45:31 +0000223 for (int i = 0; i < toPop; i++) {
Michael Hanldf206ab2014-05-13 10:22:27 +0000224 objectStack.pop();
225 }
226 }
227 openNodeCats.pop();
Joachim Bingeldbbde772014-05-12 15:26:10 +0000228
Joachim Bingeldbbde772014-05-12 15:26:10 +0000229
Joachim Bingeldbbde772014-05-12 15:26:10 +0000230 }
231
Joachim Bingela6954de2015-03-20 16:37:37 +0100232
233 /**
234 * Checks whether the combination of operator and value is legal
235 * (inequation operators <,>,<=,>= may only be used with dates).
236 */
237 private boolean checkOperatorValueConformance (
Joachim Bingela145c982015-02-18 18:31:57 +0100238 LinkedHashMap<String, Object> term) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100239 String match = (String) term.get("match");
240 String type = (String) term.get("type");
241 if (type == null || type.equals("type:regex")) {
242 if (!(match.equals("match:eq") || match.equals("match:ne")
243 || match.equals("match:contains") || match
244 .equals("match:containsnot"))) {
245 addError(StatusCodes.INCOMPATIBLE_OPERATOR_AND_OPERAND,
246 "You used an inequation operator with a string value.");
247 return false;
248 }
249 }
250 return true;
251 }
Joachim Bingel49da7ca2014-10-07 12:26:14 +0000252
Joachim Bingela3f51f72014-07-22 14:45:31 +0000253
Joachim Bingela6954de2015-03-20 16:37:37 +0100254 private LinkedHashMap<String, Object> parseValue (ParseTree valueNode) {
255 LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
256 if (getNodeCat(valueNode).equals("date")) {
257 map.put("type", "type:date");
258 checkDateValidity(valueNode);
259 }
260 if (getNodeCat(valueNode.getChild(0)).equals("regex")) {
261 String regex = valueNode.getChild(0).getChild(0)
262 .toStringTree(parser);
263 map.put("value", regex.substring(1, regex.length() - 1));
264 map.put("type", "type:regex");
265 }
266 else if (getNodeCat(valueNode.getChild(0)).equals("multiword")) {
267 String mw = ""; // multiword
268 for (int i = 1; i < valueNode.getChild(0).getChildCount() - 1; i++) {
269 mw += valueNode.getChild(0).getChild(i).getText() + " ";
270 }
271 map.put("value", mw.substring(0, mw.length() - 1));
272 }
273 else {
274 map.put("value", valueNode.getChild(0).toStringTree(parser));
275 }
276 return map;
277 }
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000278
Joachim Bingela6954de2015-03-20 16:37:37 +0100279
280 /**
281 * Checks if a date
282 *
283 * @param valueNode
284 * @return
285 */
286 private boolean checkDateValidity (ParseTree valueNode) {
287 Pattern p = Pattern.compile("[0-9]{4}(-([0-9]{2})(-([0-9]{2}))?)?");
288 Matcher m = p.matcher(valueNode.getText());
289
290 if (!m.find())
291 return false;
292 String month = m.group(2);
293 String day = m.group(4);
294 if (month != null) {
295 if (Integer.parseInt(month) > 12) {
296 return false;
297 }
298 else if (day != null) {
299 if (Integer.parseInt(day) > 31) {
300 return false;
301 }
302 }
303 }
304 return true;
305 }
306
307
308 private String interpretMatchOperator (String match) {
Michael Hanldf206ab2014-05-13 10:22:27 +0000309 String out = null;
310 switch (match) {
311 case "<":
312 out = "lt";
313 break;
314 case ">":
315 out = "gt";
316 break;
317 case "<=":
318 out = "leq";
319 break;
320 case ">=":
321 out = "geq";
322 break;
323 case "=":
324 out = "eq";
325 break;
326 case "!=":
327 out = "ne";
328 break;
Joachim Bingela499e922014-10-08 13:32:50 +0000329 case "~":
330 out = "contains";
Joachim Bingela6954de2015-03-20 16:37:37 +0100331 break;
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000332 case "!~":
Joachim Bingel017915d2014-12-16 13:03:04 +0000333 out = "containsnot";
Joachim Bingela6954de2015-03-20 16:37:37 +0100334 break;
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000335 case "in":
336 out = "eq";
337 break;
338 case "on":
339 out = "eq";
340 break;
341 case "until":
342 out = "leq";
Joachim Bingela6954de2015-03-20 16:37:37 +0100343 break;
Joachim Bingel1f4c5ad2014-12-16 10:40:42 +0000344 case "since":
345 out = "geq";
346 break;
347 default:
Joachim Bingela6954de2015-03-20 16:37:37 +0100348 out = match;
349 addError(StatusCodes.UNKNOWN_QUERY_ELEMENT,
350 "Unknown operator '" + match + "'.");
351 break;
Michael Hanldf206ab2014-05-13 10:22:27 +0000352 }
353 return out;
354 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100355
356
357 @Deprecated
358 private String invertInequation (String op) {
Michael Hanldf206ab2014-05-13 10:22:27 +0000359 String inv = null;
360 switch (op) {
361 case "lt":
362 inv = "gt";
363 break;
364 case "leq":
365 inv = "geq";
366 break;
367 case "gt":
368 inv = "lt";
369 break;
370 case "geq":
371 inv = "leq";
372 break;
373 }
374 return inv;
375 }
376
Joachim Bingela6954de2015-03-20 16:37:37 +0100377
378 private void putIntoSuperObject (LinkedHashMap<String, Object> object) {
Michael Hanldf206ab2014-05-13 10:22:27 +0000379 putIntoSuperObject(object, 0);
380 }
381
Joachim Bingela6954de2015-03-20 16:37:37 +0100382
383 @SuppressWarnings({ "unchecked" })
384 private void putIntoSuperObject (LinkedHashMap<String, Object> object,
Joachim Bingela145c982015-02-18 18:31:57 +0100385 int objStackPosition) {
Michael Hanldf206ab2014-05-13 10:22:27 +0000386 if (objectStack.size() > objStackPosition) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100387 ArrayList<Object> topObjectOperands = (ArrayList<Object>) objectStack
388 .get(objStackPosition).get("operands");
Joachim Bingela3f51f72014-07-22 14:45:31 +0000389 topObjectOperands.add(object);
Joachim Bingela6954de2015-03-20 16:37:37 +0100390 }
391 else {
392 // requestMap = object;
393 requestMap.put("collection", object);
Michael Hanldf206ab2014-05-13 10:22:27 +0000394 }
395 }
396
Joachim Bingela6954de2015-03-20 16:37:37 +0100397
398 private LinkedHashMap<String, Object> parseTermOrTermGroup (ParseTree node,
399 boolean negated) {
400 return parseTermOrTermGroup(node, negated, "token");
401 }
402
403
Joachim Bingel787836a2014-08-07 14:50:18 +0000404 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100405 * Parses a (term) or (termGroup) node
406 *
407 * @param node
408 * @param negatedGlobal
409 * Indicates whether the term/termGroup is globally
410 * negated, e.g. through a negation operator preceding
411 * the related token
412 * like "![base=foo]". Global negation affects the
413 * "match" parameter.
414 * @return A term or termGroup object, depending on input
415 */
416 @SuppressWarnings("unchecked")
417 private LinkedHashMap<String, Object> parseTermOrTermGroup (ParseTree node,
418 boolean negatedGlobal, String mode) {
419 if (getNodeCat(node).equals("term")) {
420 String key = null;
421 LinkedHashMap<String, Object> term = KoralObjectGenerator
422 .makeTerm();
423 // handle negation
424 boolean negated = negatedGlobal;
425 boolean isRegex = false;
426 List<ParseTree> negations = getChildrenWithCat(node, "!");
427 if (negations.size() % 2 == 1)
428 negated = !negated;
429 // retrieve possible nodes
430 ParseTree keyNode = getFirstChildWithCat(node, "key");
431 ParseTree valueNode = getFirstChildWithCat(node, "value");
432 ParseTree layerNode = getFirstChildWithCat(node, "layer");
433 ParseTree foundryNode = getFirstChildWithCat(node, "foundry");
434 ParseTree termOpNode = getFirstChildWithCat(node, "termOp");
435 ParseTree flagNode = getFirstChildWithCat(node, "flag");
436 // process foundry
437 if (foundryNode != null)
438 term.put("foundry", foundryNode.getText());
439 // process layer: map "base" -> "lemma"
440 if (layerNode != null) {
441 String layer = layerNode.getText();
442 if (layer.equals("base"))
443 layer = "lemma";
444 if (mode.equals("span"))
445 term.put("key", layer);
446 else
447 term.put("layer", layer);
448 }
449 // process key: 'normal' or regex?
450 key = keyNode.getText();
451 if (getNodeCat(keyNode.getChild(0)).equals("regex")) {
452 isRegex = true;
453 term.put("type", "type:regex");
454 // remove leading and trailing slashes
455 key = key.substring(1, key.length() - 1);
456 }
457 if (mode.equals("span"))
458 term.put("value", key);
459 else
460 term.put("key", key);
461 // process value
462 if (valueNode != null)
463 term.put("value", valueNode.getText());
464 // process operator ("match" property)
465 if (termOpNode != null) {
466 String termOp = termOpNode.getText();
467 negated = termOp.contains("!") ? !negated : negated;
468 if (!negated)
469 term.put("match", "match:eq");
470 else
471 term.put("match", "match:ne");
472 }
473 // process possible flags
474 if (flagNode != null) {
475 // substring removes leading slash '/'
476 String flag = getNodeCat(flagNode.getChild(0)).substring(1);
477 if (flag.contains("i"))
478 term.put("caseInsensitive", true);
479 else if (flag.contains("I"))
480 term.put("caseInsensitive", false);
481 if (flag.contains("x")) {
482 if (!isRegex) {
483 key = QueryUtils.escapeRegexSpecialChars(key);
484 }
485 // flag 'x' allows submatches: append .*? to key
486 term.put("key", ".*?" + key + ".*?");
487 term.put("type", "type:regex");
488 }
489 }
490 return term;
491 }
492 else {
493 // For termGroups, establish a boolean relation between operands
494 // and recursively call this function with the term or termGroup
495 // operands.
496 LinkedHashMap<String, Object> termGroup = null;
497 ParseTree leftOp = null;
498 ParseTree rightOp = null;
499 // check for leading/trailing parantheses
500 if (!getNodeCat(node.getChild(0)).equals("("))
501 leftOp = node.getChild(0);
502 else
503 leftOp = node.getChild(1);
504 if (!getNodeCat(node.getChild(node.getChildCount() - 1))
505 .equals(")"))
506 rightOp = node.getChild(node.getChildCount() - 1);
507 else
508 rightOp = node.getChild(node.getChildCount() - 2);
509 // establish boolean relation
510 ParseTree boolOp = getFirstChildWithCat(node, "booleanOp");
511 String operator = boolOp.getText().equals("&") ? "and" : "or";
512 termGroup = KoralObjectGenerator.makeTermGroup(operator);
513 ArrayList<Object> operands = (ArrayList<Object>) termGroup
514 .get("operands");
515 // recursion with left/right operands
516 operands.add(parseTermOrTermGroup(leftOp, negatedGlobal, mode));
517 operands.add(parseTermOrTermGroup(rightOp, negatedGlobal, mode));
518 return termGroup;
519 }
520 }
521
522
523 private ParserRuleContext parseCollectionQuery (String query) {
Joachim Bingel3fa584b2014-12-17 13:35:43 +0000524 Lexer lexer = new CollectionQueryLexer((CharStream) null);
Michael Hanldf206ab2014-05-13 10:22:27 +0000525 ParserRuleContext tree = null;
Joachim Bingela6954de2015-03-20 16:37:37 +0100526 Antlr4DescriptiveErrorListener errorListener = new Antlr4DescriptiveErrorListener(
527 query);
Michael Hanldf206ab2014-05-13 10:22:27 +0000528 // Like p. 111
529 try {
530
531 // Tokenize input data
Joachim Bingel3fa584b2014-12-17 13:35:43 +0000532 ANTLRInputStream input = new ANTLRInputStream(query);
533 lexer.setInputStream(input);
534 CommonTokenStream tokens = new CommonTokenStream(lexer);
Michael Hanldf206ab2014-05-13 10:22:27 +0000535 parser = new CollectionQueryParser(tokens);
536
537 // Don't throw out erroneous stuff
538 parser.setErrorHandler(new BailErrorStrategy());
Joachim Bingel3fa584b2014-12-17 13:35:43 +0000539 lexer.removeErrorListeners();
540 lexer.addErrorListener(errorListener);
Michael Hanldf206ab2014-05-13 10:22:27 +0000541 parser.removeErrorListeners();
Joachim Bingel3fa584b2014-12-17 13:35:43 +0000542 parser.addErrorListener(errorListener);
Michael Hanldf206ab2014-05-13 10:22:27 +0000543 // Get starting rule from parser
544 Method startRule = CollectionQueryParser.class.getMethod("start");
Joachim Bingela6954de2015-03-20 16:37:37 +0100545 tree = (ParserRuleContext) startRule
546 .invoke(parser, (Object[]) null);
Michael Hanldf206ab2014-05-13 10:22:27 +0000547 }
548 // Some things went wrong ...
549 catch (Exception e) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100550 System.err
551 .println("ERROR: " + errorListener.generateFullErrorMsg());
Joachim Bingel017915d2014-12-16 13:03:04 +0000552 System.err.println("Parsing exception message: " + e);
Michael Hanldf206ab2014-05-13 10:22:27 +0000553 }
Michael Hanldf206ab2014-05-13 10:22:27 +0000554 // Return the generated tree
555 return tree;
556 }
Joachim Bingel761d1c12014-12-17 14:02:40 +0000557}