blob: c1933009ac5bff8d375b480cb3c51789cdb9448a [file] [log] [blame]
Joachim Bingeldc03c002014-04-17 13:40:40 +00001package de.ids_mannheim.korap.query.serialize;
2
3import java.lang.reflect.Method;
4import java.util.ArrayList;
Joachim Bingel7ee07862014-04-28 15:22:41 +00005import java.util.Arrays;
Joachim Bingel019ba5c2014-04-28 14:59:04 +00006import java.util.HashMap;
Joachim Bingeldc03c002014-04-17 13:40:40 +00007import java.util.LinkedList;
8import java.util.List;
9import java.util.Map;
Joachim Bingelfb9d5fd2014-06-25 09:32:43 +000010import java.util.NoSuchElementException;
Joachim Bingeldc03c002014-04-17 13:40:40 +000011
12import org.antlr.v4.runtime.ANTLRInputStream;
13import org.antlr.v4.runtime.BailErrorStrategy;
14import org.antlr.v4.runtime.CharStream;
15import org.antlr.v4.runtime.CommonTokenStream;
16import org.antlr.v4.runtime.Lexer;
Joachim Bingeldc03c002014-04-17 13:40:40 +000017import org.antlr.v4.runtime.ParserRuleContext;
Joachim Bingeldc03c002014-04-17 13:40:40 +000018import org.antlr.v4.runtime.tree.ParseTree;
Joachim Bingelc63f7812014-07-30 09:12:25 +000019import org.slf4j.Logger;
margarethad7e75b52017-01-20 13:52:28 +010020import org.slf4j.LoggerFactory;
Joachim Bingeldc03c002014-04-17 13:40:40 +000021
margarethafe7fc452017-01-17 17:19:30 +010022import de.ids_mannheim.korap.query.object.KoralFrame;
23import de.ids_mannheim.korap.query.object.KoralOperation;
24import de.ids_mannheim.korap.query.object.KoralTermGroupRelation;
Joachim Bingel6003b852014-12-18 14:20:55 +000025import de.ids_mannheim.korap.query.parse.annis.AqlLexer;
26import de.ids_mannheim.korap.query.parse.annis.AqlParser;
Joachim Bingel3fa584b2014-12-17 13:35:43 +000027import de.ids_mannheim.korap.query.serialize.util.Antlr4DescriptiveErrorListener;
Joachim Bingelaa4ab2f2015-01-16 14:26:51 +000028import de.ids_mannheim.korap.query.serialize.util.KoralObjectGenerator;
Joachim Bingel1f8f3782015-01-19 17:58:41 +000029import de.ids_mannheim.korap.query.serialize.util.StatusCodes;
Joachim Bingeldc03c002014-04-17 13:40:40 +000030
31/**
Joachim Bingela6954de2015-03-20 16:37:37 +010032 * Processor class for ANNIS QL queries. This class uses an ANTLR v4
33 * grammar
34 * for query parsing, it therefore extends
35 * {@link Antlr4AbstractQueryProcessor}.
36 * The parser object is inherited from the parent class and
37 * instantiated in {@link #parseAnnisQuery(String)} as an
38 * {@link AqlParser}.
Joachim Bingela145c982015-02-18 18:31:57 +010039 *
40 * @see http://annis-tools.org/aql.html
41 *
Joachim Bingelc273a442015-01-26 14:03:51 +000042 * @author Joachim Bingel (bingel@ids-mannheim.de)
margarethafe7fc452017-01-17 17:19:30 +010043 * @author Eliza Margaretha (margaretha@ids-mannheim.de)
Joachim Bingel7cb346e2015-03-09 10:56:20 +010044 * @version 0.3.0
Joachim Bingela145c982015-02-18 18:31:57 +010045 * @since 0.1.0
Joachim Bingeldc03c002014-04-17 13:40:40 +000046 */
Joachim Bingel1faf8a52015-01-09 13:17:34 +000047public class AnnisQueryProcessor extends Antlr4AbstractQueryProcessor {
Joachim Bingela6954de2015-03-20 16:37:37 +010048 private static Logger log = LoggerFactory
49 .getLogger(AnnisQueryProcessor.class);
Joachim Bingelc9551b32015-01-19 14:26:58 +000050 /**
Joachim Bingela6954de2015-03-20 16:37:37 +010051 * Flag that indicates whether token fields or meta fields are
52 * currently
Joachim Bingela145c982015-02-18 18:31:57 +010053 * being processed
Joachim Bingelc9551b32015-01-19 14:26:58 +000054 */
55 boolean inMeta = false;
56 /**
Joachim Bingela6954de2015-03-20 16:37:37 +010057 * Keeps track of operands that are to be integrated into yet
58 * uncreated
Joachim Bingela145c982015-02-18 18:31:57 +010059 * objects.
Joachim Bingelc9551b32015-01-19 14:26:58 +000060 */
margaretha67eec732017-01-18 17:45:16 +010061 LinkedList<Map<String, Object>> operandStack = new LinkedList<Map<String, Object>>();
Joachim Bingelc9551b32015-01-19 14:26:58 +000062 /**
Joachim Bingela6954de2015-03-20 16:37:37 +010063 * Keeps track of explicitly (by #-var definition) or implicitly
64 * (number
65 * as reference) introduced entities (for later reference by
66 * #-operator)
Joachim Bingelc9551b32015-01-19 14:26:58 +000067 */
margaretha67eec732017-01-18 17:45:16 +010068 Map<String, Map<String, Object>> nodeVariables = new HashMap<String, Map<String, Object>>();
Joachim Bingel0fae2202015-01-28 15:53:55 +000069 /**
Joachim Bingela6954de2015-03-20 16:37:37 +010070 * Keeps track of explicitly (by #-var definition) or implicitly
71 * (number
72 * as reference) introduced entities (for later reference by
margaretha67eec732017-01-18 17:45:16 +010073 * #-operator)s
Joachim Bingel0fae2202015-01-28 15:53:55 +000074 */
margaretha67eec732017-01-18 17:45:16 +010075 Map<ParseTree, String> nodes2refs = new HashMap<ParseTree, String>();
Joachim Bingelc9551b32015-01-19 14:26:58 +000076 /**
77 * Counter for variable definitions.
78 */
Joachim Bingelc273a442015-01-26 14:03:51 +000079 Integer variableCount = 1;
Joachim Bingelc9551b32015-01-19 14:26:58 +000080 /**
Joachim Bingela6954de2015-03-20 16:37:37 +010081 * Marks the currently active token in order to know where to add
82 * flags
Joachim Bingela145c982015-02-18 18:31:57 +010083 * (might already have been taken away from token stack).
Joachim Bingelc9551b32015-01-19 14:26:58 +000084 */
margaretha67eec732017-01-18 17:45:16 +010085 Map<String, Object> curToken = new HashMap<String, Object>();
Joachim Bingelc9551b32015-01-19 14:26:58 +000086 /**
Joachim Bingela6954de2015-03-20 16:37:37 +010087 * Keeps track of operands lists that are to be serialised in an
88 * inverted
89 * order (e.g. the IN() operator) compared to their AST
90 * representation.
Joachim Bingelc9551b32015-01-19 14:26:58 +000091 */
Joachim Bingela6954de2015-03-20 16:37:37 +010092 private LinkedList<ArrayList<Object>> invertedOperandsLists = new LinkedList<ArrayList<Object>>();
Joachim Bingelc9551b32015-01-19 14:26:58 +000093 /**
94 * Keeps track of operation:class numbers.
95 */
Joachim Bingel5fd09322015-01-29 14:01:30 +000096 int classCounter = 1;
Joachim Bingelc9551b32015-01-19 14:26:58 +000097 /**
Joachim Bingela6954de2015-03-20 16:37:37 +010098 * Keeps track of numers of relations processed (important when
99 * dealing
Joachim Bingela145c982015-02-18 18:31:57 +0100100 * with multiple predications).
Joachim Bingelc9551b32015-01-19 14:26:58 +0000101 */
102 int relationCounter = 0;
103 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100104 * Keeps track of references to nodes that are operands of groups
105 * (e.g.
106 * tree relations). Those nodes appear on the top level of the
107 * parse tree
108 * but are to be integrated into the AqlTree at a later point
109 * (namely as
110 * operands of the respective group). Therefore, store references
111 * to these
112 * nodes here and exclude the operands from being written into the
113 * query
114 * map individually.
Joachim Bingelc9551b32015-01-19 14:26:58 +0000115 */
Joachim Bingelc273a442015-01-26 14:03:51 +0000116 private int totalRelationCount = 0;
Joachim Bingelc9551b32015-01-19 14:26:58 +0000117 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100118 * Keeps a record of reference-class-mapping, i.e. which 'class'
119 * has been
120 * assigned to which #n reference. This is important when
121 * introducing
122 * koral:reference spans to refer back to previously established
123 * classes for
Joachim Bingela145c982015-02-18 18:31:57 +0100124 * entities.
Joachim Bingelc9551b32015-01-19 14:26:58 +0000125 */
margaretha67eec732017-01-18 17:45:16 +0100126 private Map<String, Integer> refClassMapping = new HashMap<String, Integer>();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000127 /**
Joachim Bingel0fae2202015-01-28 15:53:55 +0000128 * Keeps a record of unary relations on spans/tokens.
129 */
margaretha67eec732017-01-18 17:45:16 +0100130 private Map<String, ArrayList<ParseTree>> unaryRelations = new HashMap<String, ArrayList<ParseTree>>();
Joachim Bingel0fae2202015-01-28 15:53:55 +0000131 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100132 * Keeps track of the number of references to a node/token by
133 * means of #n.
134 * E.g. in the query <tt>tok="x" & tok="y" & tok="z" & #1 . #2 &
135 * #2 . #3</tt>,
Joachim Bingela145c982015-02-18 18:31:57 +0100136 * the 2nd token ("y") is referenced twice, the others once.
Joachim Bingelc9551b32015-01-19 14:26:58 +0000137 */
margaretha67eec732017-01-18 17:45:16 +0100138 private Map<String, Integer> nodeReferencesTotal = new HashMap<String, Integer>();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000139 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100140 * Keeps track of the number of references to a node/token that
141 * have
Joachim Bingela145c982015-02-18 18:31:57 +0100142 * already been processed.
Joachim Bingelc9551b32015-01-19 14:26:58 +0000143 */
margaretha67eec732017-01-18 17:45:16 +0100144 private Map<String, Integer> nodeReferencesProcessed = new HashMap<String, Integer>();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000145 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100146 * Keeps track of queued relations. Relations sometimes cannot be
147 * processed
148 * directly, namely in case it does not share any operands with
149 * the
150 * previous relation. Then wait until a relation with a shared
151 * operand has
Joachim Bingela145c982015-02-18 18:31:57 +0100152 * been processed.
Joachim Bingelc9551b32015-01-19 14:26:58 +0000153 */
154 private LinkedList<ParseTree> queuedRelations = new LinkedList<ParseTree>();
Joachim Bingelc273a442015-01-26 14:03:51 +0000155 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100156 * For some objects, it may be decided in the initial scan
157 * ({@link #processAndTopExpr(ParseTree)} that they need to be
158 * wrapped in a
159 * class operation when retrieved later. This map stores this
160 * information.
161 * More precisely, it stores for every node in the tree which
162 * class ID its
Joachim Bingela145c982015-02-18 18:31:57 +0100163 * derived KoralQuery object will receive.
Joachim Bingelc273a442015-01-26 14:03:51 +0000164 */
margaretha67eec732017-01-18 17:45:16 +0100165 private Map<ParseTree, Integer> objectsToWrapInClass = new HashMap<ParseTree, Integer>();
Joachim Bingel1846c8c2014-07-08 14:13:31 +0000166
Joachim Bingela6954de2015-03-20 16:37:37 +0100167
168 public AnnisQueryProcessor (String query) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000169 KoralObjectGenerator.setQueryProcessor(this);
170 process(query);
171 }
Joachim Bingeldc03c002014-04-17 13:40:40 +0000172
Joachim Bingela6954de2015-03-20 16:37:37 +0100173
Joachim Bingelc9551b32015-01-19 14:26:58 +0000174 @Override
Joachim Bingela6954de2015-03-20 16:37:37 +0100175 public void process (String query) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000176 ParseTree tree = parseAnnisQuery(query);
177 if (this.parser != null) {
178 super.parser = this.parser;
Joachim Bingelc9551b32015-01-19 14:26:58 +0000179 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100180 else {
181 throw new NullPointerException("Parser has not been instantiated!");
182 }
183 log.info("Processing Annis query: " + query);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000184 if (tree != null) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100185 log.debug("ANTLR parse tree: " + tree.toStringTree(parser));
Joachim Bingelc9551b32015-01-19 14:26:58 +0000186 processNode(tree);
187 // Last check to see if all relations have left the queue
188 if (!queuedRelations.isEmpty()) {
Joachim Bingel1f8f3782015-01-19 17:58:41 +0000189 ParseTree queued = queuedRelations.pop();
Joachim Bingela6954de2015-03-20 16:37:37 +0100190 if (verbose)
191 System.out.println("Taking off queue (last rel): "
192 + queued.getText());
Joachim Bingel1f8f3782015-01-19 17:58:41 +0000193 if (checkOperandsProcessedPreviously(queued)) {
194 processNode(queued);
Joachim Bingela6954de2015-03-20 16:37:37 +0100195 }
196 else {
197 addError(StatusCodes.UNBOUND_ANNIS_RELATION,
198 "The relation " + queued.getText()
199 + " is not bound to any other relations.");
200 requestMap
margaretha67eec732017-01-18 17:45:16 +0100201 .put("query", new HashMap<String, Object>());
Joachim Bingel1f8f3782015-01-19 17:58:41 +0000202 }
Joachim Bingelc9551b32015-01-19 14:26:58 +0000203 }
204 }
205 }
Joachim Bingel1846c8c2014-07-08 14:13:31 +0000206
Joachim Bingela6954de2015-03-20 16:37:37 +0100207
Joachim Bingela145c982015-02-18 18:31:57 +0100208 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100209 * Traverses the parse tree by recursively calling itself,
210 * starting with
211 * the root node of the tree and calling itself with the children
212 * of its
213 * current node in a depth-first, left-to-right fashion. In each
214 * call,
215 * depending on the category of the current node, special
216 * processor
217 * methods for the respective node category are called to process
218 * the node.
219 *
220 * @param node
221 * The node currently visited in the parse tree
222 * traversal.
Joachim Bingela145c982015-02-18 18:31:57 +0100223 */
Joachim Bingela6954de2015-03-20 16:37:37 +0100224 private void processNode (ParseTree node) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000225 String nodeCat = getNodeCat(node);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000226 // Top-down processing
Joachim Bingela6954de2015-03-20 16:37:37 +0100227 if (visited.contains(node))
228 return;
Joachim Bingelc9551b32015-01-19 14:26:58 +0000229 openNodeCats.push(nodeCat);
230 stackedObjects = 0;
Joachim Bingelc9551b32015-01-19 14:26:58 +0000231 // Before doing anything else, check if any relations are queued
232 // and need to be processed first
233 if (nodeCat.equals("n_ary_linguistic_term")) {
234 if (!queuedRelations.isEmpty()) {
235 ParseTree queued = queuedRelations.getFirst();
236 if (checkOperandsProcessedPreviously(queued)) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100237 if (verbose)
238 System.out.println("Taking off queue: "
239 + queued.getText());
Joachim Bingelc9551b32015-01-19 14:26:58 +0000240 queuedRelations.removeFirst();
241 processNode(queued);
242 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100243 }
Joachim Bingelc9551b32015-01-19 14:26:58 +0000244 }
Joachim Bingelc9551b32015-01-19 14:26:58 +0000245 if (verbose) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100246 System.err.println(" " + objectStack);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000247 System.out.println(openNodeCats);
248 }
Joachim Bingeldc03c002014-04-17 13:40:40 +0000249
Joachim Bingelc9551b32015-01-19 14:26:58 +0000250 /*
251 ****************************************************************
252 ****************************************************************
253 * Processing individual node categories *
254 ****************************************************************
255 ****************************************************************
256 */
257 if (nodeCat.equals("exprTop")) {
258 processExprTop(node);
259 }
Joachim Bingel1846c8c2014-07-08 14:13:31 +0000260
Joachim Bingelc9551b32015-01-19 14:26:58 +0000261 if (nodeCat.equals("andTopExpr")) {
262 processAndTopExpr(node);
263 }
Joachim Bingel1846c8c2014-07-08 14:13:31 +0000264
Joachim Bingelc9551b32015-01-19 14:26:58 +0000265 if (nodeCat.equals("n_ary_linguistic_term")) {
266 processN_ary_linguistic_term(node);
267 }
Joachim Bingeldc03c002014-04-17 13:40:40 +0000268
Joachim Bingelc9551b32015-01-19 14:26:58 +0000269 objectsToPop.push(stackedObjects);
Joachim Bingel1846c8c2014-07-08 14:13:31 +0000270
Joachim Bingelc9551b32015-01-19 14:26:58 +0000271 /*
272 ****************************************************************
273 ****************************************************************
274 * recursion until 'request' node (root of tree) is processed *
275 ****************************************************************
276 ****************************************************************
277 */
Joachim Bingela6954de2015-03-20 16:37:37 +0100278 for (int i = 0; i < node.getChildCount(); i++) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000279 ParseTree child = node.getChild(i);
280 processNode(child);
281 }
Joachim Bingel019ba5c2014-04-28 14:59:04 +0000282
Joachim Bingelc9551b32015-01-19 14:26:58 +0000283 /*
284 **************************************************************
285 * Stuff that happens after processing the children of a node *
286 **************************************************************
287 */
288 if (!objectsToPop.isEmpty()) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100289 for (int i = 0; i < objectsToPop.pop(); i++) {
290 objectStack.pop();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000291 }
292 }
293 openNodeCats.pop();
294 }
Joachim Bingel9c3ddb92014-06-23 13:49:58 +0000295
Joachim Bingela6954de2015-03-20 16:37:37 +0100296
Joachim Bingela145c982015-02-18 18:31:57 +0100297 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100298 * Processes an <tt>andTopExpr</tt> node. This is a child of the
299 * root
300 * and contains a set of expressions connected by logical
301 * conjunction.
Joachim Bingela145c982015-02-18 18:31:57 +0100302 * Several of these nodes are possibly connected via disjunction.
Joachim Bingela6954de2015-03-20 16:37:37 +0100303 *
304 * @param node
305 * The current parse tree node (must be of category
306 * <tt>andTopExpr</tt>).
Joachim Bingela145c982015-02-18 18:31:57 +0100307 */
Joachim Bingela6954de2015-03-20 16:37:37 +0100308 private void processAndTopExpr (ParseTree node) {
Joachim Bingela145c982015-02-18 18:31:57 +0100309 // Before processing any child expr node, check if it has one or more
310 // "*ary_linguistic_term" nodes.
Joachim Bingelc9551b32015-01-19 14:26:58 +0000311 // Those nodes may use references to earlier established operand nodes.
Joachim Bingela145c982015-02-18 18:31:57 +0100312 // Those operand nodes are not to be included into the query map
313 // individually but naturally as operands of the relations/groups
314 // introduced by the node. For that purpose, this section mines all
315 // used references and stores them in a list for later reference.
Joachim Bingela6954de2015-03-20 16:37:37 +0100316 for (ParseTree unaryTermNode : getDescendantsWithCat(node,
317 "unary_linguistic_term")) {
Joachim Bingel0fae2202015-01-28 15:53:55 +0000318 String ref = getNodeCat(unaryTermNode.getChild(0)).substring(1);
Joachim Bingelef0b5b02015-01-30 09:37:43 +0000319 ArrayList<ParseTree> unaryTermsForRef = unaryRelations.get(ref);
Joachim Bingela6954de2015-03-20 16:37:37 +0100320 if (unaryTermsForRef == null)
321 unaryTermsForRef = new ArrayList<ParseTree>();
Joachim Bingelef0b5b02015-01-30 09:37:43 +0000322 unaryTermsForRef.add(unaryTermNode);
323 unaryRelations.put(ref, unaryTermsForRef);
Joachim Bingel0fae2202015-01-28 15:53:55 +0000324 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100325 for (ParseTree lingTermNode : getDescendantsWithCat(node,
326 "n_ary_linguistic_term")) {
327 for (ParseTree refOrNode : getChildrenWithCat(lingTermNode,
328 "refOrNode")) {
329 String refOrNodeString = refOrNode.getChild(0).toStringTree(
330 parser);
Joachim Bingelc273a442015-01-26 14:03:51 +0000331 if (refOrNodeString.startsWith("#")) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100332 String ref = refOrNode.getChild(0).toStringTree(parser)
333 .substring(1);
Joachim Bingelc273a442015-01-26 14:03:51 +0000334 if (nodeReferencesTotal.containsKey(ref)) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100335 nodeReferencesTotal.put(ref,
336 nodeReferencesTotal.get(ref) + 1);
337 }
338 else {
Joachim Bingelc273a442015-01-26 14:03:51 +0000339 nodeReferencesTotal.put(ref, 1);
Joachim Bingela6954de2015-03-20 16:37:37 +0100340 nodeReferencesProcessed.put(ref, 0);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000341 }
342 }
343 }
Joachim Bingelc273a442015-01-26 14:03:51 +0000344 totalRelationCount++;
Joachim Bingelc9551b32015-01-19 14:26:58 +0000345 }
Joachim Bingelc273a442015-01-26 14:03:51 +0000346 // Then, mine all object definitions.
Joachim Bingela6954de2015-03-20 16:37:37 +0100347 for (ParseTree variableExprNode : getDescendantsWithCat(node,
348 "variableExpr")) {
Joachim Bingel0fae2202015-01-28 15:53:55 +0000349 String ref;
350 // might be a ref label rather than a counting number
Joachim Bingela6954de2015-03-20 16:37:37 +0100351 ParseTree varDef = getFirstChildWithCat(
352 variableExprNode.getParent(), "varDef");
Joachim Bingel0fae2202015-01-28 15:53:55 +0000353 if (varDef != null) {
Joachim Bingela145c982015-02-18 18:31:57 +0100354 // remove trailing #
Joachim Bingela6954de2015-03-20 16:37:37 +0100355 ref = varDef.getText().replaceFirst("#", "");
356 }
357 else {
Joachim Bingel0fae2202015-01-28 15:53:55 +0000358 ref = variableCount.toString();
359 }
360 nodes2refs.put(variableExprNode, ref);
margaretha67eec732017-01-18 17:45:16 +0100361 Map<String, Object> object = processVariableExpr(variableExprNode);
Joachim Bingela6954de2015-03-20 16:37:37 +0100362 nodeVariables.put(ref, object);
Joachim Bingel0fae2202015-01-28 15:53:55 +0000363 variableCount++;
Joachim Bingela145c982015-02-18 18:31:57 +0100364 // Check if this object definition is part of a "direct declaration
365 // relation", i.e. a relation which declares its operands directly
366 // rather than using references to earlier declared objects. These
367 // objects must still be available for later reference, handle this
368 // here. Direct declaration relation is present when grandparent is
369 // n_ary_linguistic_term node.
Joachim Bingela6954de2015-03-20 16:37:37 +0100370 if (getNodeCat(variableExprNode.getParent().getParent()).equals(
371 "n_ary_linguistic_term")) {
Joachim Bingelc273a442015-01-26 14:03:51 +0000372 if (nodeReferencesTotal.containsKey(ref)) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100373 nodeReferencesTotal.put(ref,
374 nodeReferencesTotal.get(ref) + 1);
375 }
376 else {
Joachim Bingelc273a442015-01-26 14:03:51 +0000377 nodeReferencesTotal.put(ref, 1);
378 }
Joachim Bingela145c982015-02-18 18:31:57 +0100379 // This is important for later relations wrapping the present
380 // relation. If the object isn't registered as processed, it
381 // won't be available for referencing.
Joachim Bingela6954de2015-03-20 16:37:37 +0100382 nodeReferencesProcessed.put(ref, 1);
Joachim Bingelc273a442015-01-26 14:03:51 +0000383 // Register this node for latter wrapping in class.
384 if (nodeReferencesTotal.get(ref) > 1) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100385 refClassMapping.put(ref, classCounter + 128);
386 objectsToWrapInClass.put(variableExprNode,
387 128 + classCounter++);
Joachim Bingelc273a442015-01-26 14:03:51 +0000388 }
389 }
Joachim Bingelc273a442015-01-26 14:03:51 +0000390 }
Joachim Bingelc9551b32015-01-19 14:26:58 +0000391 }
Joachim Bingeld4ae5fd2014-04-29 15:00:16 +0000392
Joachim Bingela6954de2015-03-20 16:37:37 +0100393
394 private void processExprTop (ParseTree node) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000395 List<ParseTree> andTopExprs = getChildrenWithCat(node, "andTopExpr");
396 if (andTopExprs.size() > 1) {
margaretha67eec732017-01-18 17:45:16 +0100397 Map<String, Object> topOr = KoralObjectGenerator
margarethafe7fc452017-01-17 17:19:30 +0100398 .makeGroup(KoralOperation.DISJUNCTION);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000399 requestMap.put("query", topOr);
400 objectStack.push(topOr);
401 }
402 }
Joachim Bingeleee549e2014-04-29 11:15:37 +0000403
Joachim Bingela6954de2015-03-20 16:37:37 +0100404
Joachim Bingelef0b5b02015-01-30 09:37:43 +0000405 @SuppressWarnings("unchecked")
margaretha67eec732017-01-18 17:45:16 +0100406 private Map<String, Object> processVariableExpr (ParseTree node) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000407 // simplex word or complex assignment (like qname = textSpec)?
408 String firstChildNodeCat = getNodeCat(node.getChild(0));
margaretha67eec732017-01-18 17:45:16 +0100409 Map<String, Object> object = null;
Joachim Bingelc9551b32015-01-19 14:26:58 +0000410 if (firstChildNodeCat.equals("node")) {
411 object = KoralObjectGenerator.makeSpan();
Joachim Bingela6954de2015-03-20 16:37:37 +0100412 }
413 else if (firstChildNodeCat.equals("tok")) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000414 object = KoralObjectGenerator.makeToken();
415 if (node.getChildCount() > 1) { // empty tokens do not wrap a term
margaretha67eec732017-01-18 17:45:16 +0100416 Map<String, Object> term = KoralObjectGenerator
Joachim Bingela6954de2015-03-20 16:37:37 +0100417 .makeTerm();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000418 term.put("layer", "orth");
419 object.put("wrap", term);
420 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100421 }
422 else if (firstChildNodeCat.equals("qName")) {
Joachim Bingela145c982015-02-18 18:31:57 +0100423 // Only (foundry/)?layer specified.
424 // May be token or span, depending on indicated layer!
425 // (e.g. cnx/cat=NP vs mate/pos=NN)
426 // TODO generalize the list below -> look up layers associated with
427 // tokens rather than spans somewhere
margaretha67eec732017-01-18 17:45:16 +0100428 Map<String, Object> qNameParse = parseQNameNode(node
Joachim Bingela6954de2015-03-20 16:37:37 +0100429 .getChild(0));
430 if (Arrays.asList(new String[] { "p", "lemma", "m", "orth" })
431 .contains(qNameParse.get("layer"))) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000432 object = KoralObjectGenerator.makeToken();
margaretha67eec732017-01-18 17:45:16 +0100433 Map<String, Object> term = KoralObjectGenerator
Joachim Bingela6954de2015-03-20 16:37:37 +0100434 .makeTerm();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000435 object.put("wrap", term);
436 term.putAll(qNameParse);
Joachim Bingela6954de2015-03-20 16:37:37 +0100437 }
438 else {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000439 object = KoralObjectGenerator.makeSpan();
440 object.putAll(qNameParse);
441 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100442 }
443 else if (firstChildNodeCat.equals("textSpec")) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000444 object = KoralObjectGenerator.makeToken();
margaretha67eec732017-01-18 17:45:16 +0100445 Map<String, Object> term = KoralObjectGenerator
Joachim Bingela6954de2015-03-20 16:37:37 +0100446 .makeTerm();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000447 object.put("wrap", term);
448 term.put("layer", "orth");
449 term.putAll(parseTextSpec(node.getChild(0)));
450 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100451 if (node.getChildCount() == 3) {
Joachim Bingela145c982015-02-18 18:31:57 +0100452 // (foundry/)?layer=key specification
Joachim Bingelb9814e32015-02-24 16:18:10 +0100453 if (object.get("@type").equals("koral:token")) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100454 HashMap<String, Object> term = (HashMap<String, Object>) object
455 .get("wrap");
Joachim Bingelc9551b32015-01-19 14:26:58 +0000456 term.putAll(parseTextSpec(node.getChild(2)));
Joachim Bingela6954de2015-03-20 16:37:37 +0100457 term.put(
458 "match",
459 parseMatchOperator(getFirstChildWithCat(node,
460 "eqOperator")));
461 }
462 else {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000463 object.putAll(parseTextSpec(node.getChild(2)));
Joachim Bingela6954de2015-03-20 16:37:37 +0100464 object.put(
465 "match",
466 parseMatchOperator(getFirstChildWithCat(node,
467 "eqOperator")));
Joachim Bingelc9551b32015-01-19 14:26:58 +0000468 }
469 }
Joachim Bingel1846c8c2014-07-08 14:13:31 +0000470
Joachim Bingel0fae2202015-01-28 15:53:55 +0000471 // Check if there's a unary relation defined for this node
472 // If yes, parse and retrieve it and put it in the object.
473 String ref = nodes2refs.get(node);
Joachim Bingel0fae2202015-01-28 15:53:55 +0000474 if (unaryRelations.containsKey(ref)) {
Joachim Bingelef0b5b02015-01-30 09:37:43 +0000475 ArrayList<ParseTree> unaryTermsForRef = unaryRelations.get(ref);
476 if (unaryTermsForRef.size() == 1) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100477 object.put("attr", parseUnaryOperator(unaryTermsForRef.get(0)));
478 }
479 else {
margaretha67eec732017-01-18 17:45:16 +0100480 Map<String, Object> termGroup = KoralObjectGenerator
margarethafe7fc452017-01-17 17:19:30 +0100481 .makeTermGroup(KoralTermGroupRelation.AND);
Joachim Bingela6954de2015-03-20 16:37:37 +0100482 ArrayList<Object> operands = (ArrayList<Object>) termGroup
483 .get("operands");
Joachim Bingelef0b5b02015-01-30 09:37:43 +0000484 for (ParseTree unaryTerm : unaryTermsForRef) {
485 operands.add(parseUnaryOperator(unaryTerm));
486 }
487 object.put("attr", termGroup);
488 }
Joachim Bingel0fae2202015-01-28 15:53:55 +0000489 }
Joachim Bingelc9551b32015-01-19 14:26:58 +0000490 if (object != null) {
Joachim Bingel1f8f3782015-01-19 17:58:41 +0000491 // query: object only, no relation
Joachim Bingel0fae2202015-01-28 15:53:55 +0000492 if (totalRelationCount == 0) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000493 putIntoSuperObject(object);
494 }
495 ParseTree parentsFirstChild = node.getParent().getChild(0);
496 if (getNodeCat(parentsFirstChild).endsWith("#")) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100497 nodeVariables.put(
498 getNodeCat(parentsFirstChild).replaceAll("#", ""),
499 object);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000500 }
Joachim Bingelc273a442015-01-26 14:03:51 +0000501 if (objectsToWrapInClass.containsKey(node)) {
502 int classId = objectsToWrapInClass.get(node);
503 object = KoralObjectGenerator.wrapInClass(object, classId);
504 }
Joachim Bingelc9551b32015-01-19 14:26:58 +0000505 }
506 return object;
507 }
Joachim Bingela07b8e72014-05-09 15:06:07 +0000508
Joachim Bingela6954de2015-03-20 16:37:37 +0100509
Joachim Bingelc9551b32015-01-19 14:26:58 +0000510 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100511 * Processes an operand node, creating a map for the operand
512 * containing
513 * all its information given in the node definition (referenced
514 * via '#').
515 * If this node has been referred to and used earlier, a reference
516 * is
517 * created in its place. The operand will be wrapped in a class
518 * group if
Joachim Bingela145c982015-02-18 18:31:57 +0100519 * necessary.
Joachim Bingela6954de2015-03-20 16:37:37 +0100520 *
521 * @param operandNode
522 * The operand node of a relation, e.g. '#1'
523 * @return A map object with the appropriate KoralQuery
524 * representation
525 * of the operand
Joachim Bingelc9551b32015-01-19 14:26:58 +0000526 */
margaretha67eec732017-01-18 17:45:16 +0100527 private Map<String, Object> retrieveOperand (ParseTree operandNode) {
528 Map<String, Object> operand = null;
Joachim Bingelc9551b32015-01-19 14:26:58 +0000529 if (!getNodeCat(operandNode.getChild(0)).equals("variableExpr")) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100530 String ref = operandNode.getChild(0).toStringTree(parser)
531 .substring(1);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000532 operand = nodeVariables.get(ref);
533 if (nodeReferencesTotal.get(ref) > 1) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100534 if (nodeReferencesProcessed.get(ref) == 0) {
535 refClassMapping.put(ref, classCounter + 128);
536 operand = KoralObjectGenerator.wrapInClass(operand,
537 128 + classCounter++);
538 }
539 else if (nodeReferencesProcessed.get(ref) > 0
540 && nodeReferencesTotal.get(ref) > 1) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000541 try {
Joachim Bingela145c982015-02-18 18:31:57 +0100542 operand = KoralObjectGenerator.wrapInReference(
543 operandStack.pop(), refClassMapping.get(ref));
Joachim Bingela6954de2015-03-20 16:37:37 +0100544 }
545 catch (NoSuchElementException e) {
546 operand = KoralObjectGenerator
547 .makeReference(refClassMapping.get(ref));
Joachim Bingelc9551b32015-01-19 14:26:58 +0000548 }
549 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100550 nodeReferencesProcessed.put(ref,
551 nodeReferencesProcessed.get(ref) + 1);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000552 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100553 }
554 else {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000555 operand = processVariableExpr(operandNode.getChild(0));
556 }
557 return operand;
558 }
Joachim Bingel1846c8c2014-07-08 14:13:31 +0000559
Joachim Bingela6954de2015-03-20 16:37:37 +0100560
Joachim Bingelc9551b32015-01-19 14:26:58 +0000561 /**
562 * @param node
563 * @return
564 */
Joachim Bingela6954de2015-03-20 16:37:37 +0100565 private boolean checkOperandsProcessedPreviously (ParseTree node) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000566 // We can assume two operands.
567 ParseTree operand1 = node.getChild(0);
568 ParseTree operand2 = node.getChild(2);
Joachim Bingela6954de2015-03-20 16:37:37 +0100569 if (checkOperandProcessedPreviously(operand1)
570 || checkOperandProcessedPreviously(operand2)) {
Joachim Bingel4acf2462015-01-27 11:49:57 +0000571 return true;
572 }
573 return false;
574 }
Joachim Bingel019ba5c2014-04-28 14:59:04 +0000575
Joachim Bingela6954de2015-03-20 16:37:37 +0100576
Joachim Bingel4acf2462015-01-27 11:49:57 +0000577 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100578 * @param operand
Joachim Bingel4acf2462015-01-27 11:49:57 +0000579 * @return
580 */
Joachim Bingela6954de2015-03-20 16:37:37 +0100581 private boolean checkOperandProcessedPreviously (ParseTree operand) {
Joachim Bingel4acf2462015-01-27 11:49:57 +0000582 String operandRef = operand.getText();
583 if (operandRef.startsWith("#")) {
584 operandRef = operandRef.substring(1, operandRef.length());
Joachim Bingel4acf2462015-01-27 11:49:57 +0000585 if (nodeReferencesProcessed.get(operandRef) > 0) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000586 return true;
587 }
588 }
589 return false;
590 }
Joachim Bingelc8a28e42014-04-24 15:06:42 +0000591
Joachim Bingela6954de2015-03-20 16:37:37 +0100592
Joachim Bingelc9551b32015-01-19 14:26:58 +0000593 @SuppressWarnings("unchecked")
Joachim Bingela6954de2015-03-20 16:37:37 +0100594 private void processN_ary_linguistic_term (ParseTree node) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000595 relationCounter++;
Joachim Bingela145c982015-02-18 18:31:57 +0100596 // Get operator and determine type of group (sequence/treeRelation/
597 // relation/...). It's possible in Annis QL to concatenate operatiors,
598 // so there may be several operators under one n_ary_linguistic_term
599 // node. Counter 'i' will iteratively point to all operator nodes
600 // (odd-numbered children) under this node.
Joachim Bingela6954de2015-03-20 16:37:37 +0100601 for (int i = 1; i < node.getChildCount(); i = i + 2) {
602 ParseTree operandTree1 = node.getChild(i - 1);
603 ParseTree operandTree2 = node.getChild(i + 1);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000604 String reltype = getNodeCat(node.getChild(i).getChild(0));
Joachim Bingelc8a28e42014-04-24 15:06:42 +0000605
margaretha67eec732017-01-18 17:45:16 +0100606 Map<String, Object> group = null;
Joachim Bingelc9551b32015-01-19 14:26:58 +0000607 ArrayList<Object> operands = null;
608 // make sure one of the operands has already been put into a
Joachim Bingel1f8f3782015-01-19 17:58:41 +0000609 // relation (if this is not the 1st relation). If none of the
610 // operands has been ingested at a lower level (and is therefore
611 // unavailable for refrencing), queue this relation for later
612 // processing.
Joachim Bingelc9551b32015-01-19 14:26:58 +0000613 if (relationCounter != 1) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100614 if (!checkOperandsProcessedPreviously(node)) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000615 queuedRelations.add(node);
616 relationCounter--;
Joachim Bingel4acf2462015-01-27 11:49:57 +0000617 if (verbose) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100618 System.out
619 .println("Adding to queue: " + node.getText());
Joachim Bingel4acf2462015-01-27 11:49:57 +0000620 }
Joachim Bingelc9551b32015-01-19 14:26:58 +0000621 objectsToPop.push(stackedObjects);
622 return;
623 }
624 }
625 // Retrieve operands.
margaretha67eec732017-01-18 17:45:16 +0100626 Map<String, Object> operand1 = retrieveOperand(operandTree1);
627 Map<String, Object> operand2 = retrieveOperand(operandTree2);
Joachim Bingela145c982015-02-18 18:31:57 +0100628 // 'Proper' n_ary_linguistic_operators receive a considerably
629 // different serialisation than 'commonparent' and 'commonancestor'
630 // For the latter cases, a dummy span is introduced and declared as
631 // a span class that has a dominance relation towards the two
632 // operands, one after the other, thus resulting in two nested
633 // relations! A Poliqarp+ equivalent for A $ B would be
Joachim Bingelc9551b32015-01-19 14:26:58 +0000634 // contains(focus(1:contains({1:<>},A)), B).
635 // This is modeled here...
Joachim Bingela6954de2015-03-20 16:37:37 +0100636 if (reltype.equals("commonparent")
637 || reltype.equals("commonancestor")) {
Joachim Bingela145c982015-02-18 18:31:57 +0100638 // make an (outer) group and an inner group containing the dummy
639 // node or previous relations
margarethafe7fc452017-01-17 17:19:30 +0100640 group = KoralObjectGenerator.makeGroup(KoralOperation.RELATION);
margaretha67eec732017-01-18 17:45:16 +0100641 Map<String, Object> innerGroup = KoralObjectGenerator
margarethafe7fc452017-01-17 17:19:30 +0100642 .makeGroup(KoralOperation.RELATION);
margaretha67eec732017-01-18 17:45:16 +0100643 Map<String, Object> relation = KoralObjectGenerator
Joachim Bingela6954de2015-03-20 16:37:37 +0100644 .makeRelation();
margaretha67eec732017-01-18 17:45:16 +0100645 Map<String, Object> term = KoralObjectGenerator
Joachim Bingela6954de2015-03-20 16:37:37 +0100646 .makeTerm();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000647 term.put("layer", "c");
648 relation.put("wrap", term);
649 // commonancestor is an indirect commonparent relation
Joachim Bingela6954de2015-03-20 16:37:37 +0100650 if (reltype.equals("commonancestor"))
651 relation.put("boundary",
652 KoralObjectGenerator.makeBoundary(1, null));
Joachim Bingelc9551b32015-01-19 14:26:58 +0000653 group.put("relation", relation);
654 innerGroup.put("relation", relation);
Joachim Bingela145c982015-02-18 18:31:57 +0100655 // Get operands list before possible re-assignment of 'group'
656 // (see following 'if')
Joachim Bingela6954de2015-03-20 16:37:37 +0100657 ArrayList<Object> outerOperands = (ArrayList<Object>) group
658 .get("operands");
659 ArrayList<Object> innerOperands = (ArrayList<Object>) innerGroup
660 .get("operands");
Joachim Bingela145c982015-02-18 18:31:57 +0100661 // for lowest level, add the underspecified node as first
662 // operand and wrap it in a class group
Joachim Bingelc9551b32015-01-19 14:26:58 +0000663 if (i == 1) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100664 innerOperands
665 .add(KoralObjectGenerator.wrapInClass(
666 KoralObjectGenerator.makeSpan(),
667 classCounter + 128));
Joachim Bingela145c982015-02-18 18:31:57 +0100668 // add the first operand and wrap the whole group in a
669 // focusing reference
Joachim Bingelc9551b32015-01-19 14:26:58 +0000670 innerOperands.add(operand1);
Joachim Bingela6954de2015-03-20 16:37:37 +0100671 innerGroup = KoralObjectGenerator.wrapInReference(
672 innerGroup, classCounter + 128);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000673 outerOperands.add(innerGroup);
Joachim Bingela6954de2015-03-20 16:37:37 +0100674 }
675 else {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000676 outerOperands.add(operandStack.pop());
677 }
Joachim Bingela145c982015-02-18 18:31:57 +0100678 // Lookahead: if next operator is not commonparent or
679 // commonancestor, wrap in class for accessibility
Joachim Bingela6954de2015-03-20 16:37:37 +0100680 if (i < node.getChildCount() - 2
681 && !getNodeCat(node.getChild(i + 2).getChild(0))
682 .startsWith("common")) {
683 operand2 = KoralObjectGenerator.wrapInClass(operand2,
684 ++classCounter + 128);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000685 }
686 outerOperands.add(operand2);
Joachim Bingela145c982015-02-18 18:31:57 +0100687 // Wrap in another reference object in case other relations
688 // are following
Joachim Bingela6954de2015-03-20 16:37:37 +0100689 if (i < node.getChildCount() - 2) {
690 group = KoralObjectGenerator.wrapInReference(group,
691 classCounter + 128);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000692 }
Joachim Bingela145c982015-02-18 18:31:57 +0100693 // All other n-ary linguistic relations have special 'relation'
694 // attributes defined in KoralQ. and can be handled more easily
Joachim Bingela6954de2015-03-20 16:37:37 +0100695 }
696 else {
margaretha67eec732017-01-18 17:45:16 +0100697 Map<String, Object> operatorGroup = parseOperatorNode(node
Joachim Bingela6954de2015-03-20 16:37:37 +0100698 .getChild(i).getChild(0));
Joachim Bingelc9551b32015-01-19 14:26:58 +0000699 String groupType;
700 try {
701 groupType = (String) operatorGroup.get("groupType");
Joachim Bingela6954de2015-03-20 16:37:37 +0100702 }
703 catch (ClassCastException | NullPointerException n) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000704 groupType = "relation";
705 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100706 if (groupType.equals("relation")
margarethafe7fc452017-01-17 17:19:30 +0100707// || groupType.equals("treeRelation")
708 ) {
709 group = KoralObjectGenerator.makeGroup(KoralOperation.RELATION);
margaretha67eec732017-01-18 17:45:16 +0100710 Map<String, Object> relation = new HashMap<String, Object>();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000711 putAllButGroupType(relation, operatorGroup);
margarethac5889632017-04-28 16:04:38 +0200712 group.put("relType", relation);
Joachim Bingela6954de2015-03-20 16:37:37 +0100713 }
714 else if (groupType.equals("sequence")) {
margarethafe7fc452017-01-17 17:19:30 +0100715 group = KoralObjectGenerator.makeGroup(KoralOperation.SEQUENCE);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000716 putAllButGroupType(group, operatorGroup);
Joachim Bingela6954de2015-03-20 16:37:37 +0100717 }
margaretha549fb0a2017-04-27 18:35:54 +0200718 else if (groupType.equals("hierarchy")) {
719 group = KoralObjectGenerator.makeGroup(KoralOperation.HIERARCHY);
720 putAllButGroupType(group, operatorGroup);
721 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100722 else if (groupType.equals("position")) {
margaretha67eec732017-01-18 17:45:16 +0100723 group = new HashMap<String, Object>();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000724 putAllButGroupType(group, operatorGroup);
725 }
Joachim Bingel9c3ddb92014-06-23 13:49:58 +0000726
Joachim Bingela145c982015-02-18 18:31:57 +0100727 // Get operands list before possible re-assignment of 'group'
728 // (see following 'if')
Joachim Bingela6954de2015-03-20 16:37:37 +0100729 operands = (ArrayList<Object>) group.get("operands");
Joachim Bingel1846c8c2014-07-08 14:13:31 +0000730
Joachim Bingela6954de2015-03-20 16:37:37 +0100731 ParseTree leftChildSpec = getFirstChildWithCat(node.getChild(i)
732 .getChild(0), "@l");
733 ParseTree rightChildSpec = getFirstChildWithCat(node
734 .getChild(i).getChild(0), "@r");
Joachim Bingelc9551b32015-01-19 14:26:58 +0000735 if (leftChildSpec != null || rightChildSpec != null) {
margarethafe7fc452017-01-17 17:19:30 +0100736 KoralFrame frame = (leftChildSpec != null) ? KoralFrame.STARTS_WITH
737 : KoralFrame.ENDS_WITH;
738 ArrayList<KoralFrame> frames = new ArrayList<KoralFrame>();
739 frames.add(frame);
margaretha67eec732017-01-18 17:45:16 +0100740 Map<String, Object> positionGroup = KoralObjectGenerator
margarethafe7fc452017-01-17 17:19:30 +0100741 .makePosition(frames);
Joachim Bingela6954de2015-03-20 16:37:37 +0100742 operand2 = KoralObjectGenerator.wrapInClass(operand2,
743 ++classCounter + 128);
744 ((ArrayList<Object>) positionGroup.get("operands"))
745 .add(group);
746 ((ArrayList<Object>) positionGroup.get("operands"))
747 .add(KoralObjectGenerator
748 .makeReference(classCounter + 128));
Joachim Bingelc9551b32015-01-19 14:26:58 +0000749 group = positionGroup;
750 }
751
Joachim Bingela145c982015-02-18 18:31:57 +0100752 // Wrap in reference object in case other relations follow
Joachim Bingela6954de2015-03-20 16:37:37 +0100753 if (i < node.getChildCount() - 2) {
754 group = KoralObjectGenerator.wrapInReference(group,
755 classCounter + 128);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000756 }
757
758 // Inject operands.
759 // -> Case distinction:
Joachim Bingela6954de2015-03-20 16:37:37 +0100760 if (node.getChildCount() == 3) {
Joachim Bingela145c982015-02-18 18:31:57 +0100761 // Things are easy when there's just one operator
762 // (thus 3 children incl. operands)...
Joachim Bingela6954de2015-03-20 16:37:37 +0100763 if (operand1 != null)
764 operands.add(operand1);
765 if (operand2 != null)
766 operands.add(operand2);
767 }
768 else {
Joachim Bingela145c982015-02-18 18:31:57 +0100769 // ... but things get a little more complicated here. The
770 // AST is of this form: (operand1 operator1 operand2
771 // operator2 operand3 operator3 ...), but we'll have
772 // to serialize it in a nested, binary way: (((operand1
773 // operator1 operand2) operator2 operand3) operator3 ...).
774 // The following code will do just that:
Joachim Bingelc9551b32015-01-19 14:26:58 +0000775 if (i == 1) {
776 // for the first operator, include both operands
Joachim Bingela6954de2015-03-20 16:37:37 +0100777 if (operand1 != null)
778 operands.add(operand1);
779 if (operand2 != null)
780 operands.add(KoralObjectGenerator.wrapInClass(
781 operand2, 128 + classCounter++));
Joachim Bingela145c982015-02-18 18:31:57 +0100782 // Don't put this into the super object directly but
783 // store on operandStack (because this group will have
784 // to be an operand of a subsequent operator)
Joachim Bingelc9551b32015-01-19 14:26:58 +0000785 operandStack.push(group);
Joachim Bingela145c982015-02-18 18:31:57 +0100786 // for all subsequent operators, only take 2nd operand
787 // (1st was already added by previous operator)
Joachim Bingela6954de2015-03-20 16:37:37 +0100788 }
789 else if (i < node.getChildCount() - 2) {
Joachim Bingela145c982015-02-18 18:31:57 +0100790 // for all intermediate operators, include other
791 // previous groups and 2nd operand. Store this on the
792 // operandStack, too.
Joachim Bingela6954de2015-03-20 16:37:37 +0100793 if (operand2 != null)
794 operands.add(KoralObjectGenerator.wrapInClass(
795 operand2, 128 + classCounter++));
Joachim Bingelc9551b32015-01-19 14:26:58 +0000796 operands.add(0, operandStack.pop());
797 operandStack.push(group);
Joachim Bingela6954de2015-03-20 16:37:37 +0100798 }
799 else if (i == node.getChildCount() - 2) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000800 // This is the last operator. Include 2nd operand only
Joachim Bingela6954de2015-03-20 16:37:37 +0100801 if (operand2 != null)
802 operands.add(operand2);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000803 }
804 }
805 }
Joachim Bingela145c982015-02-18 18:31:57 +0100806 // Final step: decide what to do with the 'group' object, depending
807 // on whether all relations have been processed
Joachim Bingela6954de2015-03-20 16:37:37 +0100808 if (i == node.getChildCount() - 2
809 && relationCounter == totalRelationCount) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000810 putIntoSuperObject(group);
811 if (!operandStack.isEmpty()) {
812 operands.add(0, operandStack.pop());
813 }
814 objectStack.push(group);
815 stackedObjects++;
Joachim Bingela6954de2015-03-20 16:37:37 +0100816 }
817 else {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000818 operandStack.push(group);
819 }
820 }
821 }
822
Joachim Bingela6954de2015-03-20 16:37:37 +0100823
Joachim Bingelc9551b32015-01-19 14:26:58 +0000824 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100825 * Parses a unary_linguistic_operator node. Possible operators
826 * are:
827 * root, arity, tokenarity. Operators are embedded into a
828 * koral:term,
Joachim Bingelb9814e32015-02-24 16:18:10 +0100829 * in turn wrapped by an 'attr' property in a koral:span.
Joachim Bingela6954de2015-03-20 16:37:37 +0100830 *
831 * @param node
832 * The unary_linguistic_operator node
833 * @return A map containing the attr key, to be inserted into
834 * koral:span
Joachim Bingelc9551b32015-01-19 14:26:58 +0000835 */
margaretha67eec732017-01-18 17:45:16 +0100836 private Map<String, Object> parseUnaryOperator (ParseTree node) {
837 Map<String, Object> term = KoralObjectGenerator.makeTerm();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000838 String op = node.getChild(1).toStringTree(parser).substring(1);
839 if (op.equals("arity") || op.equals("tokenarity")) {
margaretha67eec732017-01-18 17:45:16 +0100840 Map<String, Object> boundary = boundaryFromRangeSpec(
Joachim Bingela6954de2015-03-20 16:37:37 +0100841 node.getChild(3), false);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000842 term.put(op, boundary);
Joachim Bingela6954de2015-03-20 16:37:37 +0100843 }
844 else {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000845 term.put(op, true);
846 }
Joachim Bingel0fae2202015-01-28 15:53:55 +0000847 return term;
Joachim Bingelc9551b32015-01-19 14:26:58 +0000848 }
849
Joachim Bingela6954de2015-03-20 16:37:37 +0100850
Joachim Bingelc9551b32015-01-19 14:26:58 +0000851 @SuppressWarnings("unchecked")
margaretha67eec732017-01-18 17:45:16 +0100852 private Map<String, Object> parseOperatorNode (
Joachim Bingela145c982015-02-18 18:31:57 +0100853 ParseTree operatorNode) {
margaretha67eec732017-01-18 17:45:16 +0100854 Map<String, Object> relation = null;
Joachim Bingelc9551b32015-01-19 14:26:58 +0000855 String operator = getNodeCat(operatorNode);
856 // DOMINANCE
857 if (operator.equals("dominance")) {
margaretha549fb0a2017-04-27 18:35:54 +0200858// relation = KoralObjectGenerator.makeRelation();
859 relation = new HashMap<String, Object>();
860 relation.put("groupType", "hierarchy");
861// ParseTree qName = getFirstChildWithCat(operatorNode, "qName");
Joachim Bingela6954de2015-03-20 16:37:37 +0100862 ParseTree edgeSpecNode = getFirstChildWithCat(operatorNode,
863 "edgeSpec");
Joachim Bingelc9551b32015-01-19 14:26:58 +0000864 ParseTree star = getFirstChildWithCat(operatorNode, "*");
Joachim Bingela6954de2015-03-20 16:37:37 +0100865 ParseTree rangeSpec = getFirstChildWithCat(operatorNode,
866 "rangeSpec");
margaretha549fb0a2017-04-27 18:35:54 +0200867
868// term.put("layer", "c");
869// if (qName != null)
870// term = parseQNameNode(qName);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000871 if (edgeSpecNode != null) {
margaretha549fb0a2017-04-27 18:35:54 +0200872 Map<String, Object> term = KoralObjectGenerator
873 .makeTerm();
margaretha67eec732017-01-18 17:45:16 +0100874 Map<String, Object> edgeSpec = parseEdgeSpec(edgeSpecNode);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000875 String edgeSpecType = (String) edgeSpec.get("@type");
Joachim Bingelb9814e32015-02-24 16:18:10 +0100876 if (edgeSpecType.equals("koral:termGroup")) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000877 ((ArrayList<Object>) edgeSpec.get("operands")).add(term);
margarethac5889632017-04-28 16:04:38 +0200878// term = edgeSpec;
Joachim Bingela6954de2015-03-20 16:37:37 +0100879 }
margarethac5889632017-04-28 16:04:38 +0200880// else {
margaretha549fb0a2017-04-27 18:35:54 +0200881// term = KoralObjectGenerator.makeTermGroup(KoralTermGroupRelation.AND);
882// ArrayList<Object> termGroupOperands = (ArrayList<Object>) term
883// .get("operands");
884// termGroupOperands.add(edgeSpec);
885// Map<String, Object> constTerm = KoralObjectGenerator
886// .makeTerm();
887// constTerm.put("layer", "c");
888// termGroupOperands.add(constTerm);
margarethac5889632017-04-28 16:04:38 +0200889// }
890 term = edgeSpec;
891 Map<String, Object> relType = KoralObjectGenerator.makeRelation();
892 relType.put("wrap", term);
893 relation.put("relType", relType);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000894 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100895 if (star != null)
896 relation.put("boundary",
897 KoralObjectGenerator.makeBoundary(0, null));
898 if (rangeSpec != null)
899 relation.put("boundary", boundaryFromRangeSpec(rangeSpec));
Joachim Bingelc9551b32015-01-19 14:26:58 +0000900 }
901 else if (operator.equals("pointing")) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000902 relation = KoralObjectGenerator.makeRelation();
903 relation.put("groupType", "relation");
904 ParseTree qName = getFirstChildWithCat(operatorNode, "qName");
margarethac5889632017-04-28 16:04:38 +0200905 ParseTree edgeSpec = getFirstChildWithCat(operatorNode, "edgeAnno");
Joachim Bingelc9551b32015-01-19 14:26:58 +0000906 ParseTree star = getFirstChildWithCat(operatorNode, "*");
Joachim Bingela6954de2015-03-20 16:37:37 +0100907 ParseTree rangeSpec = getFirstChildWithCat(operatorNode,
908 "rangeSpec");
margaretha67eec732017-01-18 17:45:16 +0100909 Map<String, Object> term = KoralObjectGenerator
Joachim Bingela6954de2015-03-20 16:37:37 +0100910 .makeTerm();
margarethac5889632017-04-28 16:04:38 +0200911 if (qName != null){
Joachim Bingela6954de2015-03-20 16:37:37 +0100912 term.putAll(parseQNameNode(qName));
margarethac5889632017-04-28 16:04:38 +0200913 relation.put("wrap", term);
914 }
915 if (edgeSpec != null){
Joachim Bingela6954de2015-03-20 16:37:37 +0100916 term.putAll(parseEdgeSpec(edgeSpec));
margarethac5889632017-04-28 16:04:38 +0200917 Map<String, Object> relType = KoralObjectGenerator.makeRelation();
918 relType.put("wrap", term);
919 relation.put("relType", relType);
920 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100921 if (star != null)
922 relation.put("boundary",
923 KoralObjectGenerator.makeBoundary(0, null));
924 if (rangeSpec != null)
925 relation.put("boundary", boundaryFromRangeSpec(rangeSpec));
margarethac5889632017-04-28 16:04:38 +0200926
Joachim Bingelc9551b32015-01-19 14:26:58 +0000927 }
928 else if (operator.equals("precedence")) {
margaretha67eec732017-01-18 17:45:16 +0100929 relation = new HashMap<String, Object>();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000930 relation.put("groupType", "sequence");
Joachim Bingela6954de2015-03-20 16:37:37 +0100931 ParseTree rangeSpec = getFirstChildWithCat(operatorNode,
932 "rangeSpec");
Joachim Bingelc9551b32015-01-19 14:26:58 +0000933 ParseTree star = getFirstChildWithCat(operatorNode, "*");
934 ArrayList<Object> distances = new ArrayList<Object>();
935 if (star != null) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100936 distances.add(KoralObjectGenerator.makeDistance("w", 0, null));
Joachim Bingelc9551b32015-01-19 14:26:58 +0000937 relation.put("distances", distances);
938 }
939 if (rangeSpec != null) {
940 distances.add(parseDistance(rangeSpec));
941 relation.put("distances", distances);
942 }
943 relation.put("inOrder", true);
944 }
945 else if (operator.equals("spanrelation")) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000946 String reltype = operatorNode.getChild(0).toStringTree(parser);
margarethafe7fc452017-01-17 17:19:30 +0100947 ArrayList<KoralFrame> frames = new ArrayList<>();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000948 switch (reltype) {
949 case "_=_":
margarethafe7fc452017-01-17 17:19:30 +0100950 frames.add(KoralFrame.MATCHES);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000951 break;
952 case "_l_":
margarethafe7fc452017-01-17 17:19:30 +0100953 frames.add(KoralFrame.STARTS_WITH);
954 frames.add(KoralFrame.MATCHES);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000955 break;
956 case "_r_":
margarethafe7fc452017-01-17 17:19:30 +0100957 frames.add(KoralFrame.ENDS_WITH);
958 frames.add(KoralFrame.MATCHES);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000959 break;
960 case "_i_":
margarethafe7fc452017-01-17 17:19:30 +0100961 frames.add(KoralFrame.IS_AROUND);
Joachim Bingel1d791042015-02-03 10:19:47 +0000962 break;
Joachim Bingelc9551b32015-01-19 14:26:58 +0000963 case "_o_":
margarethafe7fc452017-01-17 17:19:30 +0100964 frames.add(KoralFrame.OVERLAPS_LEFT);
965 frames.add(KoralFrame.OVERLAPS_RIGHT);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000966 break;
967 case "_ol_":
margarethafe7fc452017-01-17 17:19:30 +0100968 frames.add(KoralFrame.OVERLAPS_LEFT);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000969 break;
970 case "_or_":
margarethafe7fc452017-01-17 17:19:30 +0100971 frames.add(KoralFrame.OVERLAPS_RIGHT);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000972 break;
973 }
Joachim Bingel1d791042015-02-03 10:19:47 +0000974 relation = KoralObjectGenerator.makePosition(frames);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000975 relation.put("groupType", "position");
Joachim Bingela6954de2015-03-20 16:37:37 +0100976 }
Joachim Bingel5fd09322015-01-29 14:01:30 +0000977 else if (operator.equals("near")) {
margaretha67eec732017-01-18 17:45:16 +0100978 relation = new HashMap<String, Object>();
Joachim Bingel5fd09322015-01-29 14:01:30 +0000979 relation.put("groupType", "sequence");
Joachim Bingela6954de2015-03-20 16:37:37 +0100980 ParseTree rangeSpec = getFirstChildWithCat(operatorNode,
981 "rangeSpec");
Joachim Bingel5fd09322015-01-29 14:01:30 +0000982 ParseTree star = getFirstChildWithCat(operatorNode, "*");
983 ArrayList<Object> distances = new ArrayList<Object>();
984 if (star != null) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100985 distances.add(KoralObjectGenerator.makeDistance("w", 0, null));
Joachim Bingel5fd09322015-01-29 14:01:30 +0000986 relation.put("distances", distances);
987 }
988 if (rangeSpec != null) {
989 distances.add(parseDistance(rangeSpec));
990 relation.put("distances", distances);
991 }
992 relation.put("inOrder", false);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000993 }
994 else if (operator.equals("identity")) {
995 //TODO since ANNIS v. 3.1.6
996 }
997 else if (operator.equals("equalvalue")) {
998 //TODO since ANNIS v. 3.1.6
999 }
1000 else if (operator.equals("notequalvalue")) {
1001 //TODO since ANNIS v. 3.1.6
1002 }
1003 return relation;
1004 }
1005
Joachim Bingela6954de2015-03-20 16:37:37 +01001006
Joachim Bingelc9551b32015-01-19 14:26:58 +00001007 @SuppressWarnings("unchecked")
margaretha67eec732017-01-18 17:45:16 +01001008 private Map<String, Object> parseEdgeSpec (ParseTree edgeSpec) {
Joachim Bingelc9551b32015-01-19 14:26:58 +00001009 List<ParseTree> annos = getChildrenWithCat(edgeSpec, "edgeAnno");
Joachim Bingela6954de2015-03-20 16:37:37 +01001010 if (annos.size() == 1)
1011 return parseEdgeAnno(annos.get(0));
Joachim Bingelc9551b32015-01-19 14:26:58 +00001012 else {
margaretha67eec732017-01-18 17:45:16 +01001013 Map<String, Object> termGroup = KoralObjectGenerator
margarethafe7fc452017-01-17 17:19:30 +01001014 .makeTermGroup(KoralTermGroupRelation.AND);
Joachim Bingela6954de2015-03-20 16:37:37 +01001015 ArrayList<Object> operands = (ArrayList<Object>) termGroup
1016 .get("operands");
Joachim Bingelc9551b32015-01-19 14:26:58 +00001017 for (ParseTree anno : annos) {
1018 operands.add(parseEdgeAnno(anno));
1019 }
1020 return termGroup;
1021 }
1022 }
1023
margaretha67eec732017-01-18 17:45:16 +01001024 private Map<String, Object> parseEdgeAnno (ParseTree edgeAnnoSpec) {
1025 Map<String, Object> edgeAnno = KoralObjectGenerator
Joachim Bingela6954de2015-03-20 16:37:37 +01001026 .makeTerm();
1027 ParseTree textSpecNode = getFirstChildWithCat(edgeAnnoSpec, "textSpec");
margarethac5889632017-04-28 16:04:38 +02001028 ParseTree keyNode = getFirstChildWithCat(edgeAnnoSpec, "key");
Joachim Bingelc9551b32015-01-19 14:26:58 +00001029 ParseTree layerNode = getFirstChildWithCat(edgeAnnoSpec, "layer");
1030 ParseTree foundryNode = getFirstChildWithCat(edgeAnnoSpec, "foundry");
Joachim Bingela6954de2015-03-20 16:37:37 +01001031 ParseTree matchOperatorNode = getFirstChildWithCat(edgeAnnoSpec,
1032 "eqOperator");
1033 if (foundryNode != null)
1034 edgeAnno.put("foundry", foundryNode.getChild(0)
1035 .toStringTree(parser));
1036 if (layerNode != null)
1037 edgeAnno.put("layer", layerNode.getChild(0).toStringTree(parser));
margarethac5889632017-04-28 16:04:38 +02001038 if (keyNode != null)
1039 edgeAnno.put("key", keyNode.getChild(0).toStringTree(parser));
1040 edgeAnno.putAll(parseTextSpec(textSpecNode, "value"));
Joachim Bingelc9551b32015-01-19 14:26:58 +00001041 edgeAnno.put("match", parseMatchOperator(matchOperatorNode));
1042 return edgeAnno;
1043 }
1044
Joachim Bingela6954de2015-03-20 16:37:37 +01001045
margaretha67eec732017-01-18 17:45:16 +01001046 private Map<String, Object> boundaryFromRangeSpec (
Joachim Bingela145c982015-02-18 18:31:57 +01001047 ParseTree rangeSpec) {
Joachim Bingela6954de2015-03-20 16:37:37 +01001048 return boundaryFromRangeSpec(rangeSpec, true);
Joachim Bingelc9551b32015-01-19 14:26:58 +00001049 }
1050
Joachim Bingela6954de2015-03-20 16:37:37 +01001051
margaretha67eec732017-01-18 17:45:16 +01001052 private Map<String, Object> boundaryFromRangeSpec (
Joachim Bingela145c982015-02-18 18:31:57 +01001053 ParseTree rangeSpec, boolean expandToMax) {
Joachim Bingela6954de2015-03-20 16:37:37 +01001054 Integer min = Integer.parseInt(rangeSpec.getChild(0).toStringTree(
1055 parser));
Joachim Bingelc9551b32015-01-19 14:26:58 +00001056 Integer max = min;
Joachim Bingela6954de2015-03-20 16:37:37 +01001057 if (expandToMax)
1058 max = null;
1059 if (rangeSpec.getChildCount() == 3)
1060 max = Integer.parseInt(rangeSpec.getChild(2).toStringTree(parser));
Joachim Bingelc9551b32015-01-19 14:26:58 +00001061 return KoralObjectGenerator.makeBoundary(min, max);
1062 }
1063
Joachim Bingela6954de2015-03-20 16:37:37 +01001064
margaretha67eec732017-01-18 17:45:16 +01001065 private Map<String, Object> parseDistance (ParseTree rangeSpec) {
Joachim Bingel019190b2015-03-20 10:41:24 +01001066 String minString = rangeSpec.getChild(0).toStringTree(parser);
1067 String maxString = null; // not always given, prevent NPE
1068 if (minString.equals("0")) {
1069 addError(StatusCodes.MALFORMED_QUERY, "Distance may not be 0!");
1070 return KoralObjectGenerator.makeDistance("w", 0, 0);
1071 }
1072 // decrease by 1 to account for disparity between ANNIS distance and
1073 // koral:distance (ANNIS "x .1,3 y" means distance range 0,2 in KoralQ)
Joachim Bingela6954de2015-03-20 16:37:37 +01001074 Integer min = Integer.parseInt(minString) - 1;
Joachim Bingelc9551b32015-01-19 14:26:58 +00001075 Integer max = null;
Joachim Bingel019190b2015-03-20 10:41:24 +01001076 if (rangeSpec.getChildCount() == 3) {
1077 maxString = rangeSpec.getChild(2).toStringTree(parser);
Joachim Bingela6954de2015-03-20 16:37:37 +01001078 max = Integer.parseInt(maxString) - 1;
Joachim Bingel019190b2015-03-20 10:41:24 +01001079 }
Joachim Bingelc9551b32015-01-19 14:26:58 +00001080 return KoralObjectGenerator.makeDistance("w", min, max);
1081 }
1082
margaretha67eec732017-01-18 17:45:16 +01001083 private Map<String, Object> parseTextSpec (ParseTree node) {
margarethac5889632017-04-28 16:04:38 +02001084 return parseTextSpec(node, "key");
1085 }
1086
1087 private Map<String, Object> parseTextSpec (ParseTree node, String name) {
margaretha67eec732017-01-18 17:45:16 +01001088 Map<String, Object> term = new HashMap<String, Object>();
Joachim Bingelc9551b32015-01-19 14:26:58 +00001089 if (hasChild(node, "regex")) {
1090 term.put("type", "type:regex");
margarethac5889632017-04-28 16:04:38 +02001091 term.put(name, node.getChild(0).getChild(0).toStringTree(parser)
Joachim Bingela6954de2015-03-20 16:37:37 +01001092 .replaceAll("/", ""));
1093 }
1094 else {
margarethac5889632017-04-28 16:04:38 +02001095 term.put(name, node.getChild(1).toStringTree(parser));
Joachim Bingelc9551b32015-01-19 14:26:58 +00001096 }
1097 term.put("match", "match:eq");
1098 return term;
1099 }
1100
Joachim Bingela6954de2015-03-20 16:37:37 +01001101
Joachim Bingelc9551b32015-01-19 14:26:58 +00001102 /**
1103 * Parses the match operator (= or !=)
Joachim Bingela6954de2015-03-20 16:37:37 +01001104 *
Joachim Bingelc9551b32015-01-19 14:26:58 +00001105 * @param node
1106 * @return
1107 */
Joachim Bingela6954de2015-03-20 16:37:37 +01001108 private String parseMatchOperator (ParseTree node) {
1109 if (node.getChildCount() > 0) {
1110 return node.getChild(0).getText().equals("=") ? "match:eq"
1111 : "match:ne";
Joachim Bingelc9551b32015-01-19 14:26:58 +00001112 }
Joachim Bingel6e4e9f32015-01-30 18:59:31 +00001113 return "match:eq";
Joachim Bingelc9551b32015-01-19 14:26:58 +00001114 }
1115
Joachim Bingela6954de2015-03-20 16:37:37 +01001116
margaretha67eec732017-01-18 17:45:16 +01001117 private Map<String, Object> parseQNameNode (ParseTree node) {
1118 Map<String, Object> fields = new HashMap<String, Object>();
Joachim Bingelc9551b32015-01-19 14:26:58 +00001119 ParseTree layerNode = getFirstChildWithCat(node, "layer");
1120 ParseTree foundryNode = getFirstChildWithCat(node, "foundry");
Joachim Bingela6954de2015-03-20 16:37:37 +01001121 if (foundryNode != null)
1122 fields.put("foundry", foundryNode.getChild(0).toStringTree(parser));
Joachim Bingelc9551b32015-01-19 14:26:58 +00001123 String layer = layerNode.getChild(0).toStringTree(parser);
Joachim Bingela6954de2015-03-20 16:37:37 +01001124 if (layer.equals("pos"))
1125 layer = "p";
1126 if (layer.equals("cat"))
1127 layer = "c";
Joachim Bingelc9551b32015-01-19 14:26:58 +00001128 fields.put("layer", layer);
1129 return fields;
1130 }
1131
Joachim Bingela6954de2015-03-20 16:37:37 +01001132
margaretha67eec732017-01-18 17:45:16 +01001133 private void putIntoSuperObject (Map<String, Object> object) {
Joachim Bingelc9551b32015-01-19 14:26:58 +00001134 putIntoSuperObject(object, 0);
1135 }
1136
Joachim Bingela6954de2015-03-20 16:37:37 +01001137
Joachim Bingelc9551b32015-01-19 14:26:58 +00001138 @SuppressWarnings({ "unchecked" })
margaretha67eec732017-01-18 17:45:16 +01001139 private void putIntoSuperObject (Map<String, Object> object,
Joachim Bingela6954de2015-03-20 16:37:37 +01001140 int objStackPosition) {
1141 if (objectStack.size() > objStackPosition) {
1142 ArrayList<Object> topObjectOperands = (ArrayList<Object>) objectStack
1143 .get(objStackPosition).get("operands");
Joachim Bingelc9551b32015-01-19 14:26:58 +00001144 if (!invertedOperandsLists.contains(topObjectOperands)) {
1145 topObjectOperands.add(object);
Joachim Bingela6954de2015-03-20 16:37:37 +01001146 }
1147 else {
Joachim Bingelc9551b32015-01-19 14:26:58 +00001148 topObjectOperands.add(0, object);
1149 }
Joachim Bingela6954de2015-03-20 16:37:37 +01001150 }
1151 else {
Joachim Bingelc9551b32015-01-19 14:26:58 +00001152 requestMap.put("query", object);
1153 }
1154 }
1155
Joachim Bingela6954de2015-03-20 16:37:37 +01001156
1157 private void putAllButGroupType (Map<String, Object> container,
1158 Map<String, Object> input) {
Joachim Bingelc9551b32015-01-19 14:26:58 +00001159 for (String key : input.keySet()) {
1160 if (!key.equals("groupType")) {
1161 container.put(key, input.get(key));
1162 }
1163 }
1164 }
1165
Joachim Bingela6954de2015-03-20 16:37:37 +01001166
Joachim Bingelc9551b32015-01-19 14:26:58 +00001167 private ParserRuleContext parseAnnisQuery (String query) {
Joachim Bingela6954de2015-03-20 16:37:37 +01001168 Lexer lexer = new AqlLexer((CharStream) null);
Joachim Bingelc9551b32015-01-19 14:26:58 +00001169 ParserRuleContext tree = null;
Joachim Bingela6954de2015-03-20 16:37:37 +01001170 Antlr4DescriptiveErrorListener errorListener = new Antlr4DescriptiveErrorListener(
1171 query);
Joachim Bingelc9551b32015-01-19 14:26:58 +00001172 // Like p. 111
1173 try {
1174 // Tokenize input data
1175 ANTLRInputStream input = new ANTLRInputStream(query);
1176 lexer.setInputStream(input);
1177 CommonTokenStream tokens = new CommonTokenStream(lexer);
1178 parser = new AqlParser(tokens);
1179 // Don't throw out erroneous stuff
1180 parser.setErrorHandler(new BailErrorStrategy());
1181 lexer.removeErrorListeners();
Joachim Bingel3fa584b2014-12-17 13:35:43 +00001182 lexer.addErrorListener(errorListener);
1183 parser.removeErrorListeners();
1184 parser.addErrorListener(errorListener);
Joachim Bingelc9551b32015-01-19 14:26:58 +00001185 // Get starting rule from parser
Joachim Bingela6954de2015-03-20 16:37:37 +01001186 Method startRule = AqlParser.class.getMethod("start");
1187 tree = (ParserRuleContext) startRule
1188 .invoke(parser, (Object[]) null);
Joachim Bingelc9551b32015-01-19 14:26:58 +00001189 }
1190 // Some things went wrong ...
1191 catch (Exception e) {
Joachim Bingela145c982015-02-18 18:31:57 +01001192 log.error("Could not parse query. "
1193 + "Please make sure it is well-formed.");
Joachim Bingelc9551b32015-01-19 14:26:58 +00001194 log.error(errorListener.generateFullErrorMsg().toString());
1195 addError(errorListener.generateFullErrorMsg());
1196 }
1197 return tree;
1198 }
Joachim Bingel761d1c12014-12-17 14:02:40 +00001199}