blob: 528d799abec4bbc4d655c8322cb057aa09ac34c9 [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.LinkedHashMap;
8import java.util.LinkedList;
9import java.util.List;
10import java.util.Map;
Joachim Bingelfb9d5fd2014-06-25 09:32:43 +000011import java.util.NoSuchElementException;
Joachim Bingeldc03c002014-04-17 13:40:40 +000012
13import org.antlr.v4.runtime.ANTLRInputStream;
14import org.antlr.v4.runtime.BailErrorStrategy;
15import org.antlr.v4.runtime.CharStream;
16import org.antlr.v4.runtime.CommonTokenStream;
17import org.antlr.v4.runtime.Lexer;
Joachim Bingeldc03c002014-04-17 13:40:40 +000018import org.antlr.v4.runtime.ParserRuleContext;
Joachim Bingeldc03c002014-04-17 13:40:40 +000019import org.antlr.v4.runtime.tree.ParseTree;
Joachim Bingel75038792014-05-19 15:12:23 +000020import org.slf4j.LoggerFactory;
Joachim Bingelc63f7812014-07-30 09:12:25 +000021import org.slf4j.Logger;
Joachim Bingeldc03c002014-04-17 13:40:40 +000022
Joachim Bingel6003b852014-12-18 14:20:55 +000023import de.ids_mannheim.korap.query.parse.annis.AqlLexer;
24import de.ids_mannheim.korap.query.parse.annis.AqlParser;
Joachim Bingel3fa584b2014-12-17 13:35:43 +000025import de.ids_mannheim.korap.query.serialize.util.Antlr4DescriptiveErrorListener;
Joachim Bingelaa4ab2f2015-01-16 14:26:51 +000026import de.ids_mannheim.korap.query.serialize.util.KoralObjectGenerator;
Joachim Bingel1f8f3782015-01-19 17:58:41 +000027import de.ids_mannheim.korap.query.serialize.util.StatusCodes;
Joachim Bingeldc03c002014-04-17 13:40:40 +000028
29/**
Joachim Bingela6954de2015-03-20 16:37:37 +010030 * Processor class for ANNIS QL queries. This class uses an ANTLR v4
31 * grammar
32 * for query parsing, it therefore extends
33 * {@link Antlr4AbstractQueryProcessor}.
34 * The parser object is inherited from the parent class and
35 * instantiated in {@link #parseAnnisQuery(String)} as an
36 * {@link AqlParser}.
Joachim Bingela145c982015-02-18 18:31:57 +010037 *
38 * @see http://annis-tools.org/aql.html
39 *
Joachim Bingelc273a442015-01-26 14:03:51 +000040 * @author Joachim Bingel (bingel@ids-mannheim.de)
Joachim Bingel7cb346e2015-03-09 10:56:20 +010041 * @version 0.3.0
Joachim Bingela145c982015-02-18 18:31:57 +010042 * @since 0.1.0
Joachim Bingeldc03c002014-04-17 13:40:40 +000043 */
Joachim Bingel1faf8a52015-01-09 13:17:34 +000044public class AnnisQueryProcessor extends Antlr4AbstractQueryProcessor {
Joachim Bingela6954de2015-03-20 16:37:37 +010045 private static Logger log = LoggerFactory
46 .getLogger(AnnisQueryProcessor.class);
Joachim Bingelc9551b32015-01-19 14:26:58 +000047 /**
Joachim Bingela6954de2015-03-20 16:37:37 +010048 * Flag that indicates whether token fields or meta fields are
49 * currently
Joachim Bingela145c982015-02-18 18:31:57 +010050 * being processed
Joachim Bingelc9551b32015-01-19 14:26:58 +000051 */
52 boolean inMeta = false;
53 /**
Joachim Bingela6954de2015-03-20 16:37:37 +010054 * Keeps track of operands that are to be integrated into yet
55 * uncreated
Joachim Bingela145c982015-02-18 18:31:57 +010056 * objects.
Joachim Bingelc9551b32015-01-19 14:26:58 +000057 */
Joachim Bingela6954de2015-03-20 16:37:37 +010058 LinkedList<LinkedHashMap<String, Object>> operandStack = new LinkedList<LinkedHashMap<String, Object>>();
Joachim Bingelc9551b32015-01-19 14:26:58 +000059 /**
Joachim Bingela6954de2015-03-20 16:37:37 +010060 * Keeps track of explicitly (by #-var definition) or implicitly
61 * (number
62 * as reference) introduced entities (for later reference by
63 * #-operator)
Joachim Bingelc9551b32015-01-19 14:26:58 +000064 */
Joachim Bingela6954de2015-03-20 16:37:37 +010065 Map<String, LinkedHashMap<String, Object>> nodeVariables = new LinkedHashMap<String, LinkedHashMap<String, Object>>();
Joachim Bingel0fae2202015-01-28 15:53:55 +000066 /**
Joachim Bingela6954de2015-03-20 16:37:37 +010067 * Keeps track of explicitly (by #-var definition) or implicitly
68 * (number
69 * as reference) introduced entities (for later reference by
70 * #-operator)
Joachim Bingel0fae2202015-01-28 15:53:55 +000071 */
Joachim Bingela6954de2015-03-20 16:37:37 +010072 Map<ParseTree, String> nodes2refs = new LinkedHashMap<ParseTree, String>();
Joachim Bingelc9551b32015-01-19 14:26:58 +000073 /**
74 * Counter for variable definitions.
75 */
Joachim Bingelc273a442015-01-26 14:03:51 +000076 Integer variableCount = 1;
Joachim Bingelc9551b32015-01-19 14:26:58 +000077 /**
Joachim Bingela6954de2015-03-20 16:37:37 +010078 * Marks the currently active token in order to know where to add
79 * flags
Joachim Bingela145c982015-02-18 18:31:57 +010080 * (might already have been taken away from token stack).
Joachim Bingelc9551b32015-01-19 14:26:58 +000081 */
Joachim Bingela6954de2015-03-20 16:37:37 +010082 LinkedHashMap<String, Object> curToken = new LinkedHashMap<String, Object>();
Joachim Bingelc9551b32015-01-19 14:26:58 +000083 /**
Joachim Bingela6954de2015-03-20 16:37:37 +010084 * Keeps track of operands lists that are to be serialised in an
85 * inverted
86 * order (e.g. the IN() operator) compared to their AST
87 * representation.
Joachim Bingelc9551b32015-01-19 14:26:58 +000088 */
Joachim Bingela6954de2015-03-20 16:37:37 +010089 private LinkedList<ArrayList<Object>> invertedOperandsLists = new LinkedList<ArrayList<Object>>();
Joachim Bingelc9551b32015-01-19 14:26:58 +000090 /**
91 * Keeps track of operation:class numbers.
92 */
Joachim Bingel5fd09322015-01-29 14:01:30 +000093 int classCounter = 1;
Joachim Bingelc9551b32015-01-19 14:26:58 +000094 /**
Joachim Bingela6954de2015-03-20 16:37:37 +010095 * Keeps track of numers of relations processed (important when
96 * dealing
Joachim Bingela145c982015-02-18 18:31:57 +010097 * with multiple predications).
Joachim Bingelc9551b32015-01-19 14:26:58 +000098 */
99 int relationCounter = 0;
100 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100101 * Keeps track of references to nodes that are operands of groups
102 * (e.g.
103 * tree relations). Those nodes appear on the top level of the
104 * parse tree
105 * but are to be integrated into the AqlTree at a later point
106 * (namely as
107 * operands of the respective group). Therefore, store references
108 * to these
109 * nodes here and exclude the operands from being written into the
110 * query
111 * map individually.
Joachim Bingelc9551b32015-01-19 14:26:58 +0000112 */
Joachim Bingelc273a442015-01-26 14:03:51 +0000113 private int totalRelationCount = 0;
Joachim Bingelc9551b32015-01-19 14:26:58 +0000114 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100115 * Keeps a record of reference-class-mapping, i.e. which 'class'
116 * has been
117 * assigned to which #n reference. This is important when
118 * introducing
119 * koral:reference spans to refer back to previously established
120 * classes for
Joachim Bingela145c982015-02-18 18:31:57 +0100121 * entities.
Joachim Bingelc9551b32015-01-19 14:26:58 +0000122 */
Joachim Bingela6954de2015-03-20 16:37:37 +0100123 private LinkedHashMap<String, Integer> refClassMapping = new LinkedHashMap<String, Integer>();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000124 /**
Joachim Bingel0fae2202015-01-28 15:53:55 +0000125 * Keeps a record of unary relations on spans/tokens.
126 */
Joachim Bingela6954de2015-03-20 16:37:37 +0100127 private LinkedHashMap<String, ArrayList<ParseTree>> unaryRelations = new LinkedHashMap<String, ArrayList<ParseTree>>();
Joachim Bingel0fae2202015-01-28 15:53:55 +0000128 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100129 * Keeps track of the number of references to a node/token by
130 * means of #n.
131 * E.g. in the query <tt>tok="x" & tok="y" & tok="z" & #1 . #2 &
132 * #2 . #3</tt>,
Joachim Bingela145c982015-02-18 18:31:57 +0100133 * the 2nd token ("y") is referenced twice, the others once.
Joachim Bingelc9551b32015-01-19 14:26:58 +0000134 */
Joachim Bingela6954de2015-03-20 16:37:37 +0100135 private LinkedHashMap<String, Integer> nodeReferencesTotal = new LinkedHashMap<String, Integer>();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000136 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100137 * Keeps track of the number of references to a node/token that
138 * have
Joachim Bingela145c982015-02-18 18:31:57 +0100139 * already been processed.
Joachim Bingelc9551b32015-01-19 14:26:58 +0000140 */
Joachim Bingela6954de2015-03-20 16:37:37 +0100141 private LinkedHashMap<String, Integer> nodeReferencesProcessed = new LinkedHashMap<String, Integer>();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000142 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100143 * Keeps track of queued relations. Relations sometimes cannot be
144 * processed
145 * directly, namely in case it does not share any operands with
146 * the
147 * previous relation. Then wait until a relation with a shared
148 * operand has
Joachim Bingela145c982015-02-18 18:31:57 +0100149 * been processed.
Joachim Bingelc9551b32015-01-19 14:26:58 +0000150 */
151 private LinkedList<ParseTree> queuedRelations = new LinkedList<ParseTree>();
Joachim Bingelc273a442015-01-26 14:03:51 +0000152 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100153 * For some objects, it may be decided in the initial scan
154 * ({@link #processAndTopExpr(ParseTree)} that they need to be
155 * wrapped in a
156 * class operation when retrieved later. This map stores this
157 * information.
158 * More precisely, it stores for every node in the tree which
159 * class ID its
Joachim Bingela145c982015-02-18 18:31:57 +0100160 * derived KoralQuery object will receive.
Joachim Bingelc273a442015-01-26 14:03:51 +0000161 */
Joachim Bingela6954de2015-03-20 16:37:37 +0100162 private LinkedHashMap<ParseTree, Integer> objectsToWrapInClass = new LinkedHashMap<ParseTree, Integer>();
Joachim Bingel1846c8c2014-07-08 14:13:31 +0000163
Joachim Bingela6954de2015-03-20 16:37:37 +0100164
165 public AnnisQueryProcessor (String query) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000166 KoralObjectGenerator.setQueryProcessor(this);
167 process(query);
168 }
Joachim Bingeldc03c002014-04-17 13:40:40 +0000169
Joachim Bingela6954de2015-03-20 16:37:37 +0100170
Joachim Bingelc9551b32015-01-19 14:26:58 +0000171 @Override
Joachim Bingela6954de2015-03-20 16:37:37 +0100172 public void process (String query) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000173 ParseTree tree = parseAnnisQuery(query);
174 if (this.parser != null) {
175 super.parser = this.parser;
Joachim Bingelc9551b32015-01-19 14:26:58 +0000176 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100177 else {
178 throw new NullPointerException("Parser has not been instantiated!");
179 }
180 log.info("Processing Annis query: " + query);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000181 if (tree != null) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100182 log.debug("ANTLR parse tree: " + tree.toStringTree(parser));
Joachim Bingelc9551b32015-01-19 14:26:58 +0000183 processNode(tree);
184 // Last check to see if all relations have left the queue
185 if (!queuedRelations.isEmpty()) {
Joachim Bingel1f8f3782015-01-19 17:58:41 +0000186 ParseTree queued = queuedRelations.pop();
Joachim Bingela6954de2015-03-20 16:37:37 +0100187 if (verbose)
188 System.out.println("Taking off queue (last rel): "
189 + queued.getText());
Joachim Bingel1f8f3782015-01-19 17:58:41 +0000190 if (checkOperandsProcessedPreviously(queued)) {
191 processNode(queued);
Joachim Bingela6954de2015-03-20 16:37:37 +0100192 }
193 else {
194 addError(StatusCodes.UNBOUND_ANNIS_RELATION,
195 "The relation " + queued.getText()
196 + " is not bound to any other relations.");
197 requestMap
198 .put("query", new LinkedHashMap<String, Object>());
Joachim Bingel1f8f3782015-01-19 17:58:41 +0000199 }
Joachim Bingelc9551b32015-01-19 14:26:58 +0000200 }
201 }
202 }
Joachim Bingel1846c8c2014-07-08 14:13:31 +0000203
Joachim Bingela6954de2015-03-20 16:37:37 +0100204
Joachim Bingela145c982015-02-18 18:31:57 +0100205 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100206 * Traverses the parse tree by recursively calling itself,
207 * starting with
208 * the root node of the tree and calling itself with the children
209 * of its
210 * current node in a depth-first, left-to-right fashion. In each
211 * call,
212 * depending on the category of the current node, special
213 * processor
214 * methods for the respective node category are called to process
215 * the node.
216 *
217 * @param node
218 * The node currently visited in the parse tree
219 * traversal.
Joachim Bingela145c982015-02-18 18:31:57 +0100220 */
Joachim Bingela6954de2015-03-20 16:37:37 +0100221 private void processNode (ParseTree node) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000222 String nodeCat = getNodeCat(node);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000223 // Top-down processing
Joachim Bingela6954de2015-03-20 16:37:37 +0100224 if (visited.contains(node))
225 return;
Joachim Bingelc9551b32015-01-19 14:26:58 +0000226 openNodeCats.push(nodeCat);
227 stackedObjects = 0;
Joachim Bingelc9551b32015-01-19 14:26:58 +0000228 // Before doing anything else, check if any relations are queued
229 // and need to be processed first
230 if (nodeCat.equals("n_ary_linguistic_term")) {
231 if (!queuedRelations.isEmpty()) {
232 ParseTree queued = queuedRelations.getFirst();
233 if (checkOperandsProcessedPreviously(queued)) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100234 if (verbose)
235 System.out.println("Taking off queue: "
236 + queued.getText());
Joachim Bingelc9551b32015-01-19 14:26:58 +0000237 queuedRelations.removeFirst();
238 processNode(queued);
239 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100240 }
Joachim Bingelc9551b32015-01-19 14:26:58 +0000241 }
Joachim Bingelc9551b32015-01-19 14:26:58 +0000242 if (verbose) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100243 System.err.println(" " + objectStack);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000244 System.out.println(openNodeCats);
245 }
Joachim Bingeldc03c002014-04-17 13:40:40 +0000246
Joachim Bingelc9551b32015-01-19 14:26:58 +0000247 /*
248 ****************************************************************
249 ****************************************************************
250 * Processing individual node categories *
251 ****************************************************************
252 ****************************************************************
253 */
254 if (nodeCat.equals("exprTop")) {
255 processExprTop(node);
256 }
Joachim Bingel1846c8c2014-07-08 14:13:31 +0000257
Joachim Bingelc9551b32015-01-19 14:26:58 +0000258 if (nodeCat.equals("andTopExpr")) {
259 processAndTopExpr(node);
260 }
Joachim Bingel1846c8c2014-07-08 14:13:31 +0000261
Joachim Bingelc9551b32015-01-19 14:26:58 +0000262 if (nodeCat.equals("n_ary_linguistic_term")) {
263 processN_ary_linguistic_term(node);
264 }
Joachim Bingeldc03c002014-04-17 13:40:40 +0000265
Joachim Bingelc9551b32015-01-19 14:26:58 +0000266 objectsToPop.push(stackedObjects);
Joachim Bingel1846c8c2014-07-08 14:13:31 +0000267
Joachim Bingelc9551b32015-01-19 14:26:58 +0000268 /*
269 ****************************************************************
270 ****************************************************************
271 * recursion until 'request' node (root of tree) is processed *
272 ****************************************************************
273 ****************************************************************
274 */
Joachim Bingela6954de2015-03-20 16:37:37 +0100275 for (int i = 0; i < node.getChildCount(); i++) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000276 ParseTree child = node.getChild(i);
277 processNode(child);
278 }
Joachim Bingel019ba5c2014-04-28 14:59:04 +0000279
Joachim Bingelc9551b32015-01-19 14:26:58 +0000280 /*
281 **************************************************************
282 * Stuff that happens after processing the children of a node *
283 **************************************************************
284 */
285 if (!objectsToPop.isEmpty()) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100286 for (int i = 0; i < objectsToPop.pop(); i++) {
287 objectStack.pop();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000288 }
289 }
290 openNodeCats.pop();
291 }
Joachim Bingel9c3ddb92014-06-23 13:49:58 +0000292
Joachim Bingela6954de2015-03-20 16:37:37 +0100293
Joachim Bingela145c982015-02-18 18:31:57 +0100294 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100295 * Processes an <tt>andTopExpr</tt> node. This is a child of the
296 * root
297 * and contains a set of expressions connected by logical
298 * conjunction.
Joachim Bingela145c982015-02-18 18:31:57 +0100299 * Several of these nodes are possibly connected via disjunction.
Joachim Bingela6954de2015-03-20 16:37:37 +0100300 *
301 * @param node
302 * The current parse tree node (must be of category
303 * <tt>andTopExpr</tt>).
Joachim Bingela145c982015-02-18 18:31:57 +0100304 */
Joachim Bingela6954de2015-03-20 16:37:37 +0100305 private void processAndTopExpr (ParseTree node) {
Joachim Bingela145c982015-02-18 18:31:57 +0100306 // Before processing any child expr node, check if it has one or more
307 // "*ary_linguistic_term" nodes.
Joachim Bingelc9551b32015-01-19 14:26:58 +0000308 // Those nodes may use references to earlier established operand nodes.
Joachim Bingela145c982015-02-18 18:31:57 +0100309 // Those operand nodes are not to be included into the query map
310 // individually but naturally as operands of the relations/groups
311 // introduced by the node. For that purpose, this section mines all
312 // used references and stores them in a list for later reference.
Joachim Bingela6954de2015-03-20 16:37:37 +0100313 for (ParseTree unaryTermNode : getDescendantsWithCat(node,
314 "unary_linguistic_term")) {
Joachim Bingel0fae2202015-01-28 15:53:55 +0000315 String ref = getNodeCat(unaryTermNode.getChild(0)).substring(1);
Joachim Bingelef0b5b02015-01-30 09:37:43 +0000316 ArrayList<ParseTree> unaryTermsForRef = unaryRelations.get(ref);
Joachim Bingela6954de2015-03-20 16:37:37 +0100317 if (unaryTermsForRef == null)
318 unaryTermsForRef = new ArrayList<ParseTree>();
Joachim Bingelef0b5b02015-01-30 09:37:43 +0000319 unaryTermsForRef.add(unaryTermNode);
320 unaryRelations.put(ref, unaryTermsForRef);
Joachim Bingel0fae2202015-01-28 15:53:55 +0000321 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100322 for (ParseTree lingTermNode : getDescendantsWithCat(node,
323 "n_ary_linguistic_term")) {
324 for (ParseTree refOrNode : getChildrenWithCat(lingTermNode,
325 "refOrNode")) {
326 String refOrNodeString = refOrNode.getChild(0).toStringTree(
327 parser);
Joachim Bingelc273a442015-01-26 14:03:51 +0000328 if (refOrNodeString.startsWith("#")) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100329 String ref = refOrNode.getChild(0).toStringTree(parser)
330 .substring(1);
Joachim Bingelc273a442015-01-26 14:03:51 +0000331 if (nodeReferencesTotal.containsKey(ref)) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100332 nodeReferencesTotal.put(ref,
333 nodeReferencesTotal.get(ref) + 1);
334 }
335 else {
Joachim Bingelc273a442015-01-26 14:03:51 +0000336 nodeReferencesTotal.put(ref, 1);
Joachim Bingela6954de2015-03-20 16:37:37 +0100337 nodeReferencesProcessed.put(ref, 0);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000338 }
339 }
340 }
Joachim Bingelc273a442015-01-26 14:03:51 +0000341 totalRelationCount++;
Joachim Bingelc9551b32015-01-19 14:26:58 +0000342 }
Joachim Bingelc273a442015-01-26 14:03:51 +0000343 // Then, mine all object definitions.
Joachim Bingela6954de2015-03-20 16:37:37 +0100344 for (ParseTree variableExprNode : getDescendantsWithCat(node,
345 "variableExpr")) {
Joachim Bingel0fae2202015-01-28 15:53:55 +0000346 String ref;
347 // might be a ref label rather than a counting number
Joachim Bingela6954de2015-03-20 16:37:37 +0100348 ParseTree varDef = getFirstChildWithCat(
349 variableExprNode.getParent(), "varDef");
Joachim Bingel0fae2202015-01-28 15:53:55 +0000350 if (varDef != null) {
Joachim Bingela145c982015-02-18 18:31:57 +0100351 // remove trailing #
Joachim Bingela6954de2015-03-20 16:37:37 +0100352 ref = varDef.getText().replaceFirst("#", "");
353 }
354 else {
Joachim Bingel0fae2202015-01-28 15:53:55 +0000355 ref = variableCount.toString();
356 }
357 nodes2refs.put(variableExprNode, ref);
Joachim Bingela6954de2015-03-20 16:37:37 +0100358 LinkedHashMap<String, Object> object = processVariableExpr(variableExprNode);
359 nodeVariables.put(ref, object);
Joachim Bingel0fae2202015-01-28 15:53:55 +0000360 variableCount++;
Joachim Bingela145c982015-02-18 18:31:57 +0100361 // Check if this object definition is part of a "direct declaration
362 // relation", i.e. a relation which declares its operands directly
363 // rather than using references to earlier declared objects. These
364 // objects must still be available for later reference, handle this
365 // here. Direct declaration relation is present when grandparent is
366 // n_ary_linguistic_term node.
Joachim Bingela6954de2015-03-20 16:37:37 +0100367 if (getNodeCat(variableExprNode.getParent().getParent()).equals(
368 "n_ary_linguistic_term")) {
Joachim Bingelc273a442015-01-26 14:03:51 +0000369 if (nodeReferencesTotal.containsKey(ref)) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100370 nodeReferencesTotal.put(ref,
371 nodeReferencesTotal.get(ref) + 1);
372 }
373 else {
Joachim Bingelc273a442015-01-26 14:03:51 +0000374 nodeReferencesTotal.put(ref, 1);
375 }
Joachim Bingela145c982015-02-18 18:31:57 +0100376 // This is important for later relations wrapping the present
377 // relation. If the object isn't registered as processed, it
378 // won't be available for referencing.
Joachim Bingela6954de2015-03-20 16:37:37 +0100379 nodeReferencesProcessed.put(ref, 1);
Joachim Bingelc273a442015-01-26 14:03:51 +0000380 // Register this node for latter wrapping in class.
381 if (nodeReferencesTotal.get(ref) > 1) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100382 refClassMapping.put(ref, classCounter + 128);
383 objectsToWrapInClass.put(variableExprNode,
384 128 + classCounter++);
Joachim Bingelc273a442015-01-26 14:03:51 +0000385 }
386 }
Joachim Bingelc273a442015-01-26 14:03:51 +0000387 }
Joachim Bingelc9551b32015-01-19 14:26:58 +0000388 }
Joachim Bingeld4ae5fd2014-04-29 15:00:16 +0000389
Joachim Bingela6954de2015-03-20 16:37:37 +0100390
391 private void processExprTop (ParseTree node) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000392 List<ParseTree> andTopExprs = getChildrenWithCat(node, "andTopExpr");
393 if (andTopExprs.size() > 1) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100394 LinkedHashMap<String, Object> topOr = KoralObjectGenerator
395 .makeGroup("disjunction");
Joachim Bingelc9551b32015-01-19 14:26:58 +0000396 requestMap.put("query", topOr);
397 objectStack.push(topOr);
398 }
399 }
Joachim Bingeleee549e2014-04-29 11:15:37 +0000400
Joachim Bingela6954de2015-03-20 16:37:37 +0100401
Joachim Bingelef0b5b02015-01-30 09:37:43 +0000402 @SuppressWarnings("unchecked")
Joachim Bingela6954de2015-03-20 16:37:37 +0100403 private LinkedHashMap<String, Object> processVariableExpr (ParseTree node) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000404 // simplex word or complex assignment (like qname = textSpec)?
405 String firstChildNodeCat = getNodeCat(node.getChild(0));
406 LinkedHashMap<String, Object> object = null;
407 if (firstChildNodeCat.equals("node")) {
408 object = KoralObjectGenerator.makeSpan();
Joachim Bingela6954de2015-03-20 16:37:37 +0100409 }
410 else if (firstChildNodeCat.equals("tok")) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000411 object = KoralObjectGenerator.makeToken();
412 if (node.getChildCount() > 1) { // empty tokens do not wrap a term
Joachim Bingela6954de2015-03-20 16:37:37 +0100413 LinkedHashMap<String, Object> term = KoralObjectGenerator
414 .makeTerm();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000415 term.put("layer", "orth");
416 object.put("wrap", term);
417 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100418 }
419 else if (firstChildNodeCat.equals("qName")) {
Joachim Bingela145c982015-02-18 18:31:57 +0100420 // Only (foundry/)?layer specified.
421 // May be token or span, depending on indicated layer!
422 // (e.g. cnx/cat=NP vs mate/pos=NN)
423 // TODO generalize the list below -> look up layers associated with
424 // tokens rather than spans somewhere
Joachim Bingela6954de2015-03-20 16:37:37 +0100425 HashMap<String, Object> qNameParse = parseQNameNode(node
426 .getChild(0));
427 if (Arrays.asList(new String[] { "p", "lemma", "m", "orth" })
428 .contains(qNameParse.get("layer"))) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000429 object = KoralObjectGenerator.makeToken();
Joachim Bingela6954de2015-03-20 16:37:37 +0100430 LinkedHashMap<String, Object> term = KoralObjectGenerator
431 .makeTerm();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000432 object.put("wrap", term);
433 term.putAll(qNameParse);
Joachim Bingela6954de2015-03-20 16:37:37 +0100434 }
435 else {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000436 object = KoralObjectGenerator.makeSpan();
437 object.putAll(qNameParse);
438 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100439 }
440 else if (firstChildNodeCat.equals("textSpec")) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000441 object = KoralObjectGenerator.makeToken();
Joachim Bingela6954de2015-03-20 16:37:37 +0100442 LinkedHashMap<String, Object> term = KoralObjectGenerator
443 .makeTerm();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000444 object.put("wrap", term);
445 term.put("layer", "orth");
446 term.putAll(parseTextSpec(node.getChild(0)));
447 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100448 if (node.getChildCount() == 3) {
Joachim Bingela145c982015-02-18 18:31:57 +0100449 // (foundry/)?layer=key specification
Joachim Bingelb9814e32015-02-24 16:18:10 +0100450 if (object.get("@type").equals("koral:token")) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100451 HashMap<String, Object> term = (HashMap<String, Object>) object
452 .get("wrap");
Joachim Bingelc9551b32015-01-19 14:26:58 +0000453 term.putAll(parseTextSpec(node.getChild(2)));
Joachim Bingela6954de2015-03-20 16:37:37 +0100454 term.put(
455 "match",
456 parseMatchOperator(getFirstChildWithCat(node,
457 "eqOperator")));
458 }
459 else {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000460 object.putAll(parseTextSpec(node.getChild(2)));
Joachim Bingela6954de2015-03-20 16:37:37 +0100461 object.put(
462 "match",
463 parseMatchOperator(getFirstChildWithCat(node,
464 "eqOperator")));
Joachim Bingelc9551b32015-01-19 14:26:58 +0000465 }
466 }
Joachim Bingel1846c8c2014-07-08 14:13:31 +0000467
Joachim Bingel0fae2202015-01-28 15:53:55 +0000468 // Check if there's a unary relation defined for this node
469 // If yes, parse and retrieve it and put it in the object.
470 String ref = nodes2refs.get(node);
Joachim Bingel0fae2202015-01-28 15:53:55 +0000471 if (unaryRelations.containsKey(ref)) {
Joachim Bingelef0b5b02015-01-30 09:37:43 +0000472 ArrayList<ParseTree> unaryTermsForRef = unaryRelations.get(ref);
473 if (unaryTermsForRef.size() == 1) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100474 object.put("attr", parseUnaryOperator(unaryTermsForRef.get(0)));
475 }
476 else {
477 LinkedHashMap<String, Object> termGroup = KoralObjectGenerator
478 .makeTermGroup("and");
479 ArrayList<Object> operands = (ArrayList<Object>) termGroup
480 .get("operands");
Joachim Bingelef0b5b02015-01-30 09:37:43 +0000481 for (ParseTree unaryTerm : unaryTermsForRef) {
482 operands.add(parseUnaryOperator(unaryTerm));
483 }
484 object.put("attr", termGroup);
485 }
Joachim Bingel0fae2202015-01-28 15:53:55 +0000486 }
Joachim Bingelc9551b32015-01-19 14:26:58 +0000487 if (object != null) {
Joachim Bingel1f8f3782015-01-19 17:58:41 +0000488 // query: object only, no relation
Joachim Bingel0fae2202015-01-28 15:53:55 +0000489 if (totalRelationCount == 0) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000490 putIntoSuperObject(object);
491 }
492 ParseTree parentsFirstChild = node.getParent().getChild(0);
493 if (getNodeCat(parentsFirstChild).endsWith("#")) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100494 nodeVariables.put(
495 getNodeCat(parentsFirstChild).replaceAll("#", ""),
496 object);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000497 }
Joachim Bingelc273a442015-01-26 14:03:51 +0000498 if (objectsToWrapInClass.containsKey(node)) {
499 int classId = objectsToWrapInClass.get(node);
500 object = KoralObjectGenerator.wrapInClass(object, classId);
501 }
Joachim Bingelc9551b32015-01-19 14:26:58 +0000502 }
503 return object;
504 }
Joachim Bingela07b8e72014-05-09 15:06:07 +0000505
Joachim Bingela6954de2015-03-20 16:37:37 +0100506
Joachim Bingelc9551b32015-01-19 14:26:58 +0000507 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100508 * Processes an operand node, creating a map for the operand
509 * containing
510 * all its information given in the node definition (referenced
511 * via '#').
512 * If this node has been referred to and used earlier, a reference
513 * is
514 * created in its place. The operand will be wrapped in a class
515 * group if
Joachim Bingela145c982015-02-18 18:31:57 +0100516 * necessary.
Joachim Bingela6954de2015-03-20 16:37:37 +0100517 *
518 * @param operandNode
519 * The operand node of a relation, e.g. '#1'
520 * @return A map object with the appropriate KoralQuery
521 * representation
522 * of the operand
Joachim Bingelc9551b32015-01-19 14:26:58 +0000523 */
Joachim Bingela6954de2015-03-20 16:37:37 +0100524 private LinkedHashMap<String, Object> retrieveOperand (ParseTree operandNode) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000525 LinkedHashMap<String, Object> operand = null;
526 if (!getNodeCat(operandNode.getChild(0)).equals("variableExpr")) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100527 String ref = operandNode.getChild(0).toStringTree(parser)
528 .substring(1);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000529 operand = nodeVariables.get(ref);
530 if (nodeReferencesTotal.get(ref) > 1) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100531 if (nodeReferencesProcessed.get(ref) == 0) {
532 refClassMapping.put(ref, classCounter + 128);
533 operand = KoralObjectGenerator.wrapInClass(operand,
534 128 + classCounter++);
535 }
536 else if (nodeReferencesProcessed.get(ref) > 0
537 && nodeReferencesTotal.get(ref) > 1) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000538 try {
Joachim Bingela145c982015-02-18 18:31:57 +0100539 operand = KoralObjectGenerator.wrapInReference(
540 operandStack.pop(), refClassMapping.get(ref));
Joachim Bingela6954de2015-03-20 16:37:37 +0100541 }
542 catch (NoSuchElementException e) {
543 operand = KoralObjectGenerator
544 .makeReference(refClassMapping.get(ref));
Joachim Bingelc9551b32015-01-19 14:26:58 +0000545 }
546 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100547 nodeReferencesProcessed.put(ref,
548 nodeReferencesProcessed.get(ref) + 1);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000549 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100550 }
551 else {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000552 operand = processVariableExpr(operandNode.getChild(0));
553 }
554 return operand;
555 }
Joachim Bingel1846c8c2014-07-08 14:13:31 +0000556
Joachim Bingela6954de2015-03-20 16:37:37 +0100557
Joachim Bingelc9551b32015-01-19 14:26:58 +0000558 /**
559 * @param node
560 * @return
561 */
Joachim Bingela6954de2015-03-20 16:37:37 +0100562 private boolean checkOperandsProcessedPreviously (ParseTree node) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000563 // We can assume two operands.
564 ParseTree operand1 = node.getChild(0);
565 ParseTree operand2 = node.getChild(2);
Joachim Bingela6954de2015-03-20 16:37:37 +0100566 if (checkOperandProcessedPreviously(operand1)
567 || checkOperandProcessedPreviously(operand2)) {
Joachim Bingel4acf2462015-01-27 11:49:57 +0000568 return true;
569 }
570 return false;
571 }
Joachim Bingel019ba5c2014-04-28 14:59:04 +0000572
Joachim Bingela6954de2015-03-20 16:37:37 +0100573
Joachim Bingel4acf2462015-01-27 11:49:57 +0000574 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100575 * @param operand
Joachim Bingel4acf2462015-01-27 11:49:57 +0000576 * @return
577 */
Joachim Bingela6954de2015-03-20 16:37:37 +0100578 private boolean checkOperandProcessedPreviously (ParseTree operand) {
Joachim Bingel4acf2462015-01-27 11:49:57 +0000579 String operandRef = operand.getText();
580 if (operandRef.startsWith("#")) {
581 operandRef = operandRef.substring(1, operandRef.length());
Joachim Bingel4acf2462015-01-27 11:49:57 +0000582 if (nodeReferencesProcessed.get(operandRef) > 0) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000583 return true;
584 }
585 }
586 return false;
587 }
Joachim Bingelc8a28e42014-04-24 15:06:42 +0000588
Joachim Bingela6954de2015-03-20 16:37:37 +0100589
Joachim Bingelc9551b32015-01-19 14:26:58 +0000590 @SuppressWarnings("unchecked")
Joachim Bingela6954de2015-03-20 16:37:37 +0100591 private void processN_ary_linguistic_term (ParseTree node) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000592 relationCounter++;
Joachim Bingela145c982015-02-18 18:31:57 +0100593 // Get operator and determine type of group (sequence/treeRelation/
594 // relation/...). It's possible in Annis QL to concatenate operatiors,
595 // so there may be several operators under one n_ary_linguistic_term
596 // node. Counter 'i' will iteratively point to all operator nodes
597 // (odd-numbered children) under this node.
Joachim Bingela6954de2015-03-20 16:37:37 +0100598 for (int i = 1; i < node.getChildCount(); i = i + 2) {
599 ParseTree operandTree1 = node.getChild(i - 1);
600 ParseTree operandTree2 = node.getChild(i + 1);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000601 String reltype = getNodeCat(node.getChild(i).getChild(0));
Joachim Bingelc8a28e42014-04-24 15:06:42 +0000602
Joachim Bingela6954de2015-03-20 16:37:37 +0100603 LinkedHashMap<String, Object> group = null;
Joachim Bingelc9551b32015-01-19 14:26:58 +0000604 ArrayList<Object> operands = null;
605 // make sure one of the operands has already been put into a
Joachim Bingel1f8f3782015-01-19 17:58:41 +0000606 // relation (if this is not the 1st relation). If none of the
607 // operands has been ingested at a lower level (and is therefore
608 // unavailable for refrencing), queue this relation for later
609 // processing.
Joachim Bingelc9551b32015-01-19 14:26:58 +0000610 if (relationCounter != 1) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100611 if (!checkOperandsProcessedPreviously(node)) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000612 queuedRelations.add(node);
613 relationCounter--;
Joachim Bingel4acf2462015-01-27 11:49:57 +0000614 if (verbose) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100615 System.out
616 .println("Adding to queue: " + node.getText());
Joachim Bingel4acf2462015-01-27 11:49:57 +0000617 }
Joachim Bingelc9551b32015-01-19 14:26:58 +0000618 objectsToPop.push(stackedObjects);
619 return;
620 }
621 }
622 // Retrieve operands.
Joachim Bingela6954de2015-03-20 16:37:37 +0100623 LinkedHashMap<String, Object> operand1 = retrieveOperand(operandTree1);
624 LinkedHashMap<String, Object> operand2 = retrieveOperand(operandTree2);
Joachim Bingela145c982015-02-18 18:31:57 +0100625 // 'Proper' n_ary_linguistic_operators receive a considerably
626 // different serialisation than 'commonparent' and 'commonancestor'
627 // For the latter cases, a dummy span is introduced and declared as
628 // a span class that has a dominance relation towards the two
629 // operands, one after the other, thus resulting in two nested
630 // relations! A Poliqarp+ equivalent for A $ B would be
Joachim Bingelc9551b32015-01-19 14:26:58 +0000631 // contains(focus(1:contains({1:<>},A)), B).
632 // This is modeled here...
Joachim Bingela6954de2015-03-20 16:37:37 +0100633 if (reltype.equals("commonparent")
634 || reltype.equals("commonancestor")) {
Joachim Bingela145c982015-02-18 18:31:57 +0100635 // make an (outer) group and an inner group containing the dummy
636 // node or previous relations
Joachim Bingelc9551b32015-01-19 14:26:58 +0000637 group = KoralObjectGenerator.makeGroup("relation");
Joachim Bingela6954de2015-03-20 16:37:37 +0100638 LinkedHashMap<String, Object> innerGroup = KoralObjectGenerator
639 .makeGroup("relation");
640 LinkedHashMap<String, Object> relation = KoralObjectGenerator
641 .makeRelation();
642 LinkedHashMap<String, Object> term = KoralObjectGenerator
643 .makeTerm();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000644 term.put("layer", "c");
645 relation.put("wrap", term);
646 // commonancestor is an indirect commonparent relation
Joachim Bingela6954de2015-03-20 16:37:37 +0100647 if (reltype.equals("commonancestor"))
648 relation.put("boundary",
649 KoralObjectGenerator.makeBoundary(1, null));
Joachim Bingelc9551b32015-01-19 14:26:58 +0000650 group.put("relation", relation);
651 innerGroup.put("relation", relation);
Joachim Bingela145c982015-02-18 18:31:57 +0100652 // Get operands list before possible re-assignment of 'group'
653 // (see following 'if')
Joachim Bingela6954de2015-03-20 16:37:37 +0100654 ArrayList<Object> outerOperands = (ArrayList<Object>) group
655 .get("operands");
656 ArrayList<Object> innerOperands = (ArrayList<Object>) innerGroup
657 .get("operands");
Joachim Bingela145c982015-02-18 18:31:57 +0100658 // for lowest level, add the underspecified node as first
659 // operand and wrap it in a class group
Joachim Bingelc9551b32015-01-19 14:26:58 +0000660 if (i == 1) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100661 innerOperands
662 .add(KoralObjectGenerator.wrapInClass(
663 KoralObjectGenerator.makeSpan(),
664 classCounter + 128));
Joachim Bingela145c982015-02-18 18:31:57 +0100665 // add the first operand and wrap the whole group in a
666 // focusing reference
Joachim Bingelc9551b32015-01-19 14:26:58 +0000667 innerOperands.add(operand1);
Joachim Bingela6954de2015-03-20 16:37:37 +0100668 innerGroup = KoralObjectGenerator.wrapInReference(
669 innerGroup, classCounter + 128);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000670 outerOperands.add(innerGroup);
Joachim Bingela6954de2015-03-20 16:37:37 +0100671 }
672 else {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000673 outerOperands.add(operandStack.pop());
674 }
Joachim Bingela145c982015-02-18 18:31:57 +0100675 // Lookahead: if next operator is not commonparent or
676 // commonancestor, wrap in class for accessibility
Joachim Bingela6954de2015-03-20 16:37:37 +0100677 if (i < node.getChildCount() - 2
678 && !getNodeCat(node.getChild(i + 2).getChild(0))
679 .startsWith("common")) {
680 operand2 = KoralObjectGenerator.wrapInClass(operand2,
681 ++classCounter + 128);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000682 }
683 outerOperands.add(operand2);
Joachim Bingela145c982015-02-18 18:31:57 +0100684 // Wrap in another reference object in case other relations
685 // are following
Joachim Bingela6954de2015-03-20 16:37:37 +0100686 if (i < node.getChildCount() - 2) {
687 group = KoralObjectGenerator.wrapInReference(group,
688 classCounter + 128);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000689 }
Joachim Bingela145c982015-02-18 18:31:57 +0100690 // All other n-ary linguistic relations have special 'relation'
691 // attributes defined in KoralQ. and can be handled more easily
Joachim Bingela6954de2015-03-20 16:37:37 +0100692 }
693 else {
694 LinkedHashMap<String, Object> operatorGroup = parseOperatorNode(node
695 .getChild(i).getChild(0));
Joachim Bingelc9551b32015-01-19 14:26:58 +0000696 String groupType;
697 try {
698 groupType = (String) operatorGroup.get("groupType");
Joachim Bingela6954de2015-03-20 16:37:37 +0100699 }
700 catch (ClassCastException | NullPointerException n) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000701 groupType = "relation";
702 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100703 if (groupType.equals("relation")
704 || groupType.equals("treeRelation")) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000705 group = KoralObjectGenerator.makeGroup(groupType);
Joachim Bingela6954de2015-03-20 16:37:37 +0100706 LinkedHashMap<String, Object> relation = new LinkedHashMap<String, Object>();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000707 putAllButGroupType(relation, operatorGroup);
708 group.put("relation", relation);
Joachim Bingela6954de2015-03-20 16:37:37 +0100709 }
710 else if (groupType.equals("sequence")) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000711 group = KoralObjectGenerator.makeGroup(groupType);
712 putAllButGroupType(group, operatorGroup);
Joachim Bingela6954de2015-03-20 16:37:37 +0100713 }
714 else if (groupType.equals("position")) {
715 group = new LinkedHashMap<String, Object>();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000716 putAllButGroupType(group, operatorGroup);
717 }
Joachim Bingel9c3ddb92014-06-23 13:49:58 +0000718
Joachim Bingela145c982015-02-18 18:31:57 +0100719 // Get operands list before possible re-assignment of 'group'
720 // (see following 'if')
Joachim Bingela6954de2015-03-20 16:37:37 +0100721 operands = (ArrayList<Object>) group.get("operands");
Joachim Bingel1846c8c2014-07-08 14:13:31 +0000722
Joachim Bingela6954de2015-03-20 16:37:37 +0100723 ParseTree leftChildSpec = getFirstChildWithCat(node.getChild(i)
724 .getChild(0), "@l");
725 ParseTree rightChildSpec = getFirstChildWithCat(node
726 .getChild(i).getChild(0), "@r");
Joachim Bingelc9551b32015-01-19 14:26:58 +0000727 if (leftChildSpec != null || rightChildSpec != null) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100728 String frame = (leftChildSpec != null) ? "frames:startsWith"
729 : "frames:endsWith";
730 LinkedHashMap<String, Object> positionGroup = KoralObjectGenerator
731 .makePosition(new String[] { frame });
732 operand2 = KoralObjectGenerator.wrapInClass(operand2,
733 ++classCounter + 128);
734 ((ArrayList<Object>) positionGroup.get("operands"))
735 .add(group);
736 ((ArrayList<Object>) positionGroup.get("operands"))
737 .add(KoralObjectGenerator
738 .makeReference(classCounter + 128));
Joachim Bingelc9551b32015-01-19 14:26:58 +0000739 group = positionGroup;
740 }
741
Joachim Bingela145c982015-02-18 18:31:57 +0100742 // Wrap in reference object in case other relations follow
Joachim Bingela6954de2015-03-20 16:37:37 +0100743 if (i < node.getChildCount() - 2) {
744 group = KoralObjectGenerator.wrapInReference(group,
745 classCounter + 128);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000746 }
747
748 // Inject operands.
749 // -> Case distinction:
Joachim Bingela6954de2015-03-20 16:37:37 +0100750 if (node.getChildCount() == 3) {
Joachim Bingela145c982015-02-18 18:31:57 +0100751 // Things are easy when there's just one operator
752 // (thus 3 children incl. operands)...
Joachim Bingela6954de2015-03-20 16:37:37 +0100753 if (operand1 != null)
754 operands.add(operand1);
755 if (operand2 != null)
756 operands.add(operand2);
757 }
758 else {
Joachim Bingela145c982015-02-18 18:31:57 +0100759 // ... but things get a little more complicated here. The
760 // AST is of this form: (operand1 operator1 operand2
761 // operator2 operand3 operator3 ...), but we'll have
762 // to serialize it in a nested, binary way: (((operand1
763 // operator1 operand2) operator2 operand3) operator3 ...).
764 // The following code will do just that:
Joachim Bingelc9551b32015-01-19 14:26:58 +0000765 if (i == 1) {
766 // for the first operator, include both operands
Joachim Bingela6954de2015-03-20 16:37:37 +0100767 if (operand1 != null)
768 operands.add(operand1);
769 if (operand2 != null)
770 operands.add(KoralObjectGenerator.wrapInClass(
771 operand2, 128 + classCounter++));
Joachim Bingela145c982015-02-18 18:31:57 +0100772 // Don't put this into the super object directly but
773 // store on operandStack (because this group will have
774 // to be an operand of a subsequent operator)
Joachim Bingelc9551b32015-01-19 14:26:58 +0000775 operandStack.push(group);
Joachim Bingela145c982015-02-18 18:31:57 +0100776 // for all subsequent operators, only take 2nd operand
777 // (1st was already added by previous operator)
Joachim Bingela6954de2015-03-20 16:37:37 +0100778 }
779 else if (i < node.getChildCount() - 2) {
Joachim Bingela145c982015-02-18 18:31:57 +0100780 // for all intermediate operators, include other
781 // previous groups and 2nd operand. Store this on the
782 // operandStack, too.
Joachim Bingela6954de2015-03-20 16:37:37 +0100783 if (operand2 != null)
784 operands.add(KoralObjectGenerator.wrapInClass(
785 operand2, 128 + classCounter++));
Joachim Bingelc9551b32015-01-19 14:26:58 +0000786 operands.add(0, operandStack.pop());
787 operandStack.push(group);
Joachim Bingela6954de2015-03-20 16:37:37 +0100788 }
789 else if (i == node.getChildCount() - 2) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000790 // This is the last operator. Include 2nd operand only
Joachim Bingela6954de2015-03-20 16:37:37 +0100791 if (operand2 != null)
792 operands.add(operand2);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000793 }
794 }
795 }
Joachim Bingela145c982015-02-18 18:31:57 +0100796 // Final step: decide what to do with the 'group' object, depending
797 // on whether all relations have been processed
Joachim Bingela6954de2015-03-20 16:37:37 +0100798 if (i == node.getChildCount() - 2
799 && relationCounter == totalRelationCount) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000800 putIntoSuperObject(group);
801 if (!operandStack.isEmpty()) {
802 operands.add(0, operandStack.pop());
803 }
804 objectStack.push(group);
805 stackedObjects++;
Joachim Bingela6954de2015-03-20 16:37:37 +0100806 }
807 else {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000808 operandStack.push(group);
809 }
810 }
811 }
812
Joachim Bingela6954de2015-03-20 16:37:37 +0100813
Joachim Bingelc9551b32015-01-19 14:26:58 +0000814 /**
Joachim Bingela6954de2015-03-20 16:37:37 +0100815 * Parses a unary_linguistic_operator node. Possible operators
816 * are:
817 * root, arity, tokenarity. Operators are embedded into a
818 * koral:term,
Joachim Bingelb9814e32015-02-24 16:18:10 +0100819 * in turn wrapped by an 'attr' property in a koral:span.
Joachim Bingela6954de2015-03-20 16:37:37 +0100820 *
821 * @param node
822 * The unary_linguistic_operator node
823 * @return A map containing the attr key, to be inserted into
824 * koral:span
Joachim Bingelc9551b32015-01-19 14:26:58 +0000825 */
Joachim Bingela6954de2015-03-20 16:37:37 +0100826 private LinkedHashMap<String, Object> parseUnaryOperator (ParseTree node) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000827 LinkedHashMap<String, Object> term = KoralObjectGenerator.makeTerm();
828 String op = node.getChild(1).toStringTree(parser).substring(1);
829 if (op.equals("arity") || op.equals("tokenarity")) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100830 LinkedHashMap<String, Object> boundary = boundaryFromRangeSpec(
831 node.getChild(3), false);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000832 term.put(op, boundary);
Joachim Bingela6954de2015-03-20 16:37:37 +0100833 }
834 else {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000835 term.put(op, true);
836 }
Joachim Bingel0fae2202015-01-28 15:53:55 +0000837 return term;
Joachim Bingelc9551b32015-01-19 14:26:58 +0000838 }
839
Joachim Bingela6954de2015-03-20 16:37:37 +0100840
Joachim Bingelc9551b32015-01-19 14:26:58 +0000841 @SuppressWarnings("unchecked")
Joachim Bingela6954de2015-03-20 16:37:37 +0100842 private LinkedHashMap<String, Object> parseOperatorNode (
Joachim Bingela145c982015-02-18 18:31:57 +0100843 ParseTree operatorNode) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000844 LinkedHashMap<String, Object> relation = null;
845 String operator = getNodeCat(operatorNode);
846 // DOMINANCE
847 if (operator.equals("dominance")) {
848 relation = KoralObjectGenerator.makeRelation();
849 relation.put("groupType", "relation");
Joachim Bingela6954de2015-03-20 16:37:37 +0100850 ParseTree qName = getFirstChildWithCat(operatorNode, "qName");
851 ParseTree edgeSpecNode = getFirstChildWithCat(operatorNode,
852 "edgeSpec");
Joachim Bingelc9551b32015-01-19 14:26:58 +0000853 ParseTree star = getFirstChildWithCat(operatorNode, "*");
Joachim Bingela6954de2015-03-20 16:37:37 +0100854 ParseTree rangeSpec = getFirstChildWithCat(operatorNode,
855 "rangeSpec");
856 LinkedHashMap<String, Object> term = KoralObjectGenerator
857 .makeTerm();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000858 term.put("layer", "c");
Joachim Bingela6954de2015-03-20 16:37:37 +0100859 if (qName != null)
860 term = parseQNameNode(qName);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000861 if (edgeSpecNode != null) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100862 LinkedHashMap<String, Object> edgeSpec = parseEdgeSpec(edgeSpecNode);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000863 String edgeSpecType = (String) edgeSpec.get("@type");
Joachim Bingelb9814e32015-02-24 16:18:10 +0100864 if (edgeSpecType.equals("koral:termGroup")) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000865 ((ArrayList<Object>) edgeSpec.get("operands")).add(term);
866 term = edgeSpec;
Joachim Bingela6954de2015-03-20 16:37:37 +0100867 }
868 else {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000869 term = KoralObjectGenerator.makeTermGroup("and");
Joachim Bingela6954de2015-03-20 16:37:37 +0100870 ArrayList<Object> termGroupOperands = (ArrayList<Object>) term
871 .get("operands");
Joachim Bingelc9551b32015-01-19 14:26:58 +0000872 termGroupOperands.add(edgeSpec);
Joachim Bingela6954de2015-03-20 16:37:37 +0100873 LinkedHashMap<String, Object> constTerm = KoralObjectGenerator
874 .makeTerm();
Joachim Bingelc9551b32015-01-19 14:26:58 +0000875 constTerm.put("layer", "c");
876 termGroupOperands.add(constTerm);
877 }
878 }
Joachim Bingela6954de2015-03-20 16:37:37 +0100879 if (star != null)
880 relation.put("boundary",
881 KoralObjectGenerator.makeBoundary(0, null));
882 if (rangeSpec != null)
883 relation.put("boundary", boundaryFromRangeSpec(rangeSpec));
Joachim Bingelc9551b32015-01-19 14:26:58 +0000884 relation.put("wrap", term);
885 }
886 else if (operator.equals("pointing")) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000887 relation = KoralObjectGenerator.makeRelation();
888 relation.put("groupType", "relation");
889 ParseTree qName = getFirstChildWithCat(operatorNode, "qName");
Joachim Bingela6954de2015-03-20 16:37:37 +0100890 ParseTree edgeSpec = getFirstChildWithCat(operatorNode, "edgeSpec");
Joachim Bingelc9551b32015-01-19 14:26:58 +0000891 ParseTree star = getFirstChildWithCat(operatorNode, "*");
Joachim Bingela6954de2015-03-20 16:37:37 +0100892 ParseTree rangeSpec = getFirstChildWithCat(operatorNode,
893 "rangeSpec");
894 LinkedHashMap<String, Object> term = KoralObjectGenerator
895 .makeTerm();
896 if (qName != null)
897 term.putAll(parseQNameNode(qName));
898 if (edgeSpec != null)
899 term.putAll(parseEdgeSpec(edgeSpec));
900 if (star != null)
901 relation.put("boundary",
902 KoralObjectGenerator.makeBoundary(0, null));
903 if (rangeSpec != null)
904 relation.put("boundary", boundaryFromRangeSpec(rangeSpec));
Joachim Bingelc9551b32015-01-19 14:26:58 +0000905 relation.put("wrap", term);
906 }
907 else if (operator.equals("precedence")) {
908 relation = new LinkedHashMap<String, Object>();
909 relation.put("groupType", "sequence");
Joachim Bingela6954de2015-03-20 16:37:37 +0100910 ParseTree rangeSpec = getFirstChildWithCat(operatorNode,
911 "rangeSpec");
Joachim Bingelc9551b32015-01-19 14:26:58 +0000912 ParseTree star = getFirstChildWithCat(operatorNode, "*");
913 ArrayList<Object> distances = new ArrayList<Object>();
914 if (star != null) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100915 distances.add(KoralObjectGenerator.makeDistance("w", 0, null));
Joachim Bingelc9551b32015-01-19 14:26:58 +0000916 relation.put("distances", distances);
917 }
918 if (rangeSpec != null) {
919 distances.add(parseDistance(rangeSpec));
920 relation.put("distances", distances);
921 }
922 relation.put("inOrder", true);
923 }
924 else if (operator.equals("spanrelation")) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000925 String reltype = operatorNode.getChild(0).toStringTree(parser);
Joachim Bingela6954de2015-03-20 16:37:37 +0100926 String[] frames = new String[] {};
Joachim Bingelc9551b32015-01-19 14:26:58 +0000927 switch (reltype) {
928 case "_=_":
Joachim Bingela6954de2015-03-20 16:37:37 +0100929 frames = new String[] { "frames:matches" };
Joachim Bingelc9551b32015-01-19 14:26:58 +0000930 break;
931 case "_l_":
Joachim Bingela6954de2015-03-20 16:37:37 +0100932 frames = new String[] { "frames:startsWith",
933 "frames:matches" };
Joachim Bingelc9551b32015-01-19 14:26:58 +0000934 break;
935 case "_r_":
Joachim Bingela6954de2015-03-20 16:37:37 +0100936 frames = new String[] { "frames:endsWith", "frames:matches" };
Joachim Bingelc9551b32015-01-19 14:26:58 +0000937 break;
938 case "_i_":
Joachim Bingela6954de2015-03-20 16:37:37 +0100939 frames = new String[] { "frames:isAround" };
Joachim Bingel1d791042015-02-03 10:19:47 +0000940 break;
Joachim Bingelc9551b32015-01-19 14:26:58 +0000941 case "_o_":
Joachim Bingela6954de2015-03-20 16:37:37 +0100942 frames = new String[] { "frames:overlapsLeft",
943 "frames:overlapsRight" };
Joachim Bingelc9551b32015-01-19 14:26:58 +0000944 break;
945 case "_ol_":
Joachim Bingela6954de2015-03-20 16:37:37 +0100946 frames = new String[] { "frames:overlapsLeft" };
Joachim Bingelc9551b32015-01-19 14:26:58 +0000947 break;
948 case "_or_":
Joachim Bingela6954de2015-03-20 16:37:37 +0100949 frames = new String[] { "frames:overlapsRight" };
Joachim Bingelc9551b32015-01-19 14:26:58 +0000950 break;
951 }
Joachim Bingel1d791042015-02-03 10:19:47 +0000952 relation = KoralObjectGenerator.makePosition(frames);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000953 relation.put("groupType", "position");
Joachim Bingela6954de2015-03-20 16:37:37 +0100954 }
Joachim Bingel5fd09322015-01-29 14:01:30 +0000955 else if (operator.equals("near")) {
956 relation = new LinkedHashMap<String, Object>();
957 relation.put("groupType", "sequence");
Joachim Bingela6954de2015-03-20 16:37:37 +0100958 ParseTree rangeSpec = getFirstChildWithCat(operatorNode,
959 "rangeSpec");
Joachim Bingel5fd09322015-01-29 14:01:30 +0000960 ParseTree star = getFirstChildWithCat(operatorNode, "*");
961 ArrayList<Object> distances = new ArrayList<Object>();
962 if (star != null) {
Joachim Bingela6954de2015-03-20 16:37:37 +0100963 distances.add(KoralObjectGenerator.makeDistance("w", 0, null));
Joachim Bingel5fd09322015-01-29 14:01:30 +0000964 relation.put("distances", distances);
965 }
966 if (rangeSpec != null) {
967 distances.add(parseDistance(rangeSpec));
968 relation.put("distances", distances);
969 }
970 relation.put("inOrder", false);
Joachim Bingelc9551b32015-01-19 14:26:58 +0000971 }
972 else if (operator.equals("identity")) {
973 //TODO since ANNIS v. 3.1.6
974 }
975 else if (operator.equals("equalvalue")) {
976 //TODO since ANNIS v. 3.1.6
977 }
978 else if (operator.equals("notequalvalue")) {
979 //TODO since ANNIS v. 3.1.6
980 }
981 return relation;
982 }
983
Joachim Bingela6954de2015-03-20 16:37:37 +0100984
Joachim Bingelc9551b32015-01-19 14:26:58 +0000985 @SuppressWarnings("unchecked")
Joachim Bingela6954de2015-03-20 16:37:37 +0100986 private LinkedHashMap<String, Object> parseEdgeSpec (ParseTree edgeSpec) {
Joachim Bingelc9551b32015-01-19 14:26:58 +0000987 List<ParseTree> annos = getChildrenWithCat(edgeSpec, "edgeAnno");
Joachim Bingela6954de2015-03-20 16:37:37 +0100988 if (annos.size() == 1)
989 return parseEdgeAnno(annos.get(0));
Joachim Bingelc9551b32015-01-19 14:26:58 +0000990 else {
Joachim Bingela6954de2015-03-20 16:37:37 +0100991 LinkedHashMap<String, Object> termGroup = KoralObjectGenerator
992 .makeTermGroup("and");
993 ArrayList<Object> operands = (ArrayList<Object>) termGroup
994 .get("operands");
Joachim Bingelc9551b32015-01-19 14:26:58 +0000995 for (ParseTree anno : annos) {
996 operands.add(parseEdgeAnno(anno));
997 }
998 return termGroup;
999 }
1000 }
1001
Joachim Bingela6954de2015-03-20 16:37:37 +01001002
1003 private LinkedHashMap<String, Object> parseEdgeAnno (ParseTree edgeAnnoSpec) {
1004 LinkedHashMap<String, Object> edgeAnno = KoralObjectGenerator
1005 .makeTerm();
1006 ParseTree textSpecNode = getFirstChildWithCat(edgeAnnoSpec, "textSpec");
Joachim Bingelc9551b32015-01-19 14:26:58 +00001007 ParseTree layerNode = getFirstChildWithCat(edgeAnnoSpec, "layer");
1008 ParseTree foundryNode = getFirstChildWithCat(edgeAnnoSpec, "foundry");
Joachim Bingela6954de2015-03-20 16:37:37 +01001009 ParseTree matchOperatorNode = getFirstChildWithCat(edgeAnnoSpec,
1010 "eqOperator");
1011 if (foundryNode != null)
1012 edgeAnno.put("foundry", foundryNode.getChild(0)
1013 .toStringTree(parser));
1014 if (layerNode != null)
1015 edgeAnno.put("layer", layerNode.getChild(0).toStringTree(parser));
Joachim Bingelc9551b32015-01-19 14:26:58 +00001016 edgeAnno.putAll(parseTextSpec(textSpecNode));
1017 edgeAnno.put("match", parseMatchOperator(matchOperatorNode));
1018 return edgeAnno;
1019 }
1020
Joachim Bingela6954de2015-03-20 16:37:37 +01001021
1022 private LinkedHashMap<String, Object> boundaryFromRangeSpec (
Joachim Bingela145c982015-02-18 18:31:57 +01001023 ParseTree rangeSpec) {
Joachim Bingela6954de2015-03-20 16:37:37 +01001024 return boundaryFromRangeSpec(rangeSpec, true);
Joachim Bingelc9551b32015-01-19 14:26:58 +00001025 }
1026
Joachim Bingela6954de2015-03-20 16:37:37 +01001027
1028 private LinkedHashMap<String, Object> boundaryFromRangeSpec (
Joachim Bingela145c982015-02-18 18:31:57 +01001029 ParseTree rangeSpec, boolean expandToMax) {
Joachim Bingela6954de2015-03-20 16:37:37 +01001030 Integer min = Integer.parseInt(rangeSpec.getChild(0).toStringTree(
1031 parser));
Joachim Bingelc9551b32015-01-19 14:26:58 +00001032 Integer max = min;
Joachim Bingela6954de2015-03-20 16:37:37 +01001033 if (expandToMax)
1034 max = null;
1035 if (rangeSpec.getChildCount() == 3)
1036 max = Integer.parseInt(rangeSpec.getChild(2).toStringTree(parser));
Joachim Bingelc9551b32015-01-19 14:26:58 +00001037 return KoralObjectGenerator.makeBoundary(min, max);
1038 }
1039
Joachim Bingela6954de2015-03-20 16:37:37 +01001040
1041 private LinkedHashMap<String, Object> parseDistance (ParseTree rangeSpec) {
Joachim Bingel019190b2015-03-20 10:41:24 +01001042 String minString = rangeSpec.getChild(0).toStringTree(parser);
1043 String maxString = null; // not always given, prevent NPE
1044 if (minString.equals("0")) {
1045 addError(StatusCodes.MALFORMED_QUERY, "Distance may not be 0!");
1046 return KoralObjectGenerator.makeDistance("w", 0, 0);
1047 }
1048 // decrease by 1 to account for disparity between ANNIS distance and
1049 // koral:distance (ANNIS "x .1,3 y" means distance range 0,2 in KoralQ)
Joachim Bingela6954de2015-03-20 16:37:37 +01001050 Integer min = Integer.parseInt(minString) - 1;
Joachim Bingelc9551b32015-01-19 14:26:58 +00001051 Integer max = null;
Joachim Bingel019190b2015-03-20 10:41:24 +01001052 if (rangeSpec.getChildCount() == 3) {
1053 maxString = rangeSpec.getChild(2).toStringTree(parser);
Joachim Bingela6954de2015-03-20 16:37:37 +01001054 max = Integer.parseInt(maxString) - 1;
Joachim Bingel019190b2015-03-20 10:41:24 +01001055 }
Joachim Bingelc9551b32015-01-19 14:26:58 +00001056 return KoralObjectGenerator.makeDistance("w", min, max);
1057 }
1058
Joachim Bingela6954de2015-03-20 16:37:37 +01001059
1060 private LinkedHashMap<String, Object> parseTextSpec (ParseTree node) {
Joachim Bingelc9551b32015-01-19 14:26:58 +00001061 LinkedHashMap<String, Object> term = new LinkedHashMap<String, Object>();
1062 if (hasChild(node, "regex")) {
1063 term.put("type", "type:regex");
Joachim Bingela6954de2015-03-20 16:37:37 +01001064 term.put("key", node.getChild(0).getChild(0).toStringTree(parser)
1065 .replaceAll("/", ""));
1066 }
1067 else {
Joachim Bingelc9551b32015-01-19 14:26:58 +00001068 term.put("key", node.getChild(1).toStringTree(parser));
1069 }
1070 term.put("match", "match:eq");
1071 return term;
1072 }
1073
Joachim Bingela6954de2015-03-20 16:37:37 +01001074
Joachim Bingelc9551b32015-01-19 14:26:58 +00001075 /**
1076 * Parses the match operator (= or !=)
Joachim Bingela6954de2015-03-20 16:37:37 +01001077 *
Joachim Bingelc9551b32015-01-19 14:26:58 +00001078 * @param node
1079 * @return
1080 */
Joachim Bingela6954de2015-03-20 16:37:37 +01001081 private String parseMatchOperator (ParseTree node) {
1082 if (node.getChildCount() > 0) {
1083 return node.getChild(0).getText().equals("=") ? "match:eq"
1084 : "match:ne";
Joachim Bingelc9551b32015-01-19 14:26:58 +00001085 }
Joachim Bingel6e4e9f32015-01-30 18:59:31 +00001086 return "match:eq";
Joachim Bingelc9551b32015-01-19 14:26:58 +00001087 }
1088
Joachim Bingela6954de2015-03-20 16:37:37 +01001089
1090 private LinkedHashMap<String, Object> parseQNameNode (ParseTree node) {
1091 LinkedHashMap<String, Object> fields = new LinkedHashMap<String, Object>();
Joachim Bingelc9551b32015-01-19 14:26:58 +00001092 ParseTree layerNode = getFirstChildWithCat(node, "layer");
1093 ParseTree foundryNode = getFirstChildWithCat(node, "foundry");
Joachim Bingela6954de2015-03-20 16:37:37 +01001094 if (foundryNode != null)
1095 fields.put("foundry", foundryNode.getChild(0).toStringTree(parser));
Joachim Bingelc9551b32015-01-19 14:26:58 +00001096 String layer = layerNode.getChild(0).toStringTree(parser);
Joachim Bingela6954de2015-03-20 16:37:37 +01001097 if (layer.equals("pos"))
1098 layer = "p";
1099 if (layer.equals("cat"))
1100 layer = "c";
Joachim Bingelc9551b32015-01-19 14:26:58 +00001101 fields.put("layer", layer);
1102 return fields;
1103 }
1104
Joachim Bingela6954de2015-03-20 16:37:37 +01001105
1106 private void putIntoSuperObject (LinkedHashMap<String, Object> object) {
Joachim Bingelc9551b32015-01-19 14:26:58 +00001107 putIntoSuperObject(object, 0);
1108 }
1109
Joachim Bingela6954de2015-03-20 16:37:37 +01001110
Joachim Bingelc9551b32015-01-19 14:26:58 +00001111 @SuppressWarnings({ "unchecked" })
Joachim Bingela6954de2015-03-20 16:37:37 +01001112 private void putIntoSuperObject (LinkedHashMap<String, Object> object,
1113 int objStackPosition) {
1114 if (objectStack.size() > objStackPosition) {
1115 ArrayList<Object> topObjectOperands = (ArrayList<Object>) objectStack
1116 .get(objStackPosition).get("operands");
Joachim Bingelc9551b32015-01-19 14:26:58 +00001117 if (!invertedOperandsLists.contains(topObjectOperands)) {
1118 topObjectOperands.add(object);
Joachim Bingela6954de2015-03-20 16:37:37 +01001119 }
1120 else {
Joachim Bingelc9551b32015-01-19 14:26:58 +00001121 topObjectOperands.add(0, object);
1122 }
Joachim Bingela6954de2015-03-20 16:37:37 +01001123 }
1124 else {
Joachim Bingelc9551b32015-01-19 14:26:58 +00001125 requestMap.put("query", object);
1126 }
1127 }
1128
Joachim Bingela6954de2015-03-20 16:37:37 +01001129
1130 private void putAllButGroupType (Map<String, Object> container,
1131 Map<String, Object> input) {
Joachim Bingelc9551b32015-01-19 14:26:58 +00001132 for (String key : input.keySet()) {
1133 if (!key.equals("groupType")) {
1134 container.put(key, input.get(key));
1135 }
1136 }
1137 }
1138
Joachim Bingela6954de2015-03-20 16:37:37 +01001139
Joachim Bingelc9551b32015-01-19 14:26:58 +00001140 private ParserRuleContext parseAnnisQuery (String query) {
Joachim Bingela6954de2015-03-20 16:37:37 +01001141 Lexer lexer = new AqlLexer((CharStream) null);
Joachim Bingelc9551b32015-01-19 14:26:58 +00001142 ParserRuleContext tree = null;
Joachim Bingela6954de2015-03-20 16:37:37 +01001143 Antlr4DescriptiveErrorListener errorListener = new Antlr4DescriptiveErrorListener(
1144 query);
Joachim Bingelc9551b32015-01-19 14:26:58 +00001145 // Like p. 111
1146 try {
1147 // Tokenize input data
1148 ANTLRInputStream input = new ANTLRInputStream(query);
1149 lexer.setInputStream(input);
1150 CommonTokenStream tokens = new CommonTokenStream(lexer);
1151 parser = new AqlParser(tokens);
1152 // Don't throw out erroneous stuff
1153 parser.setErrorHandler(new BailErrorStrategy());
1154 lexer.removeErrorListeners();
Joachim Bingel3fa584b2014-12-17 13:35:43 +00001155 lexer.addErrorListener(errorListener);
1156 parser.removeErrorListeners();
1157 parser.addErrorListener(errorListener);
Joachim Bingelc9551b32015-01-19 14:26:58 +00001158 // Get starting rule from parser
Joachim Bingela6954de2015-03-20 16:37:37 +01001159 Method startRule = AqlParser.class.getMethod("start");
1160 tree = (ParserRuleContext) startRule
1161 .invoke(parser, (Object[]) null);
Joachim Bingelc9551b32015-01-19 14:26:58 +00001162 }
1163 // Some things went wrong ...
1164 catch (Exception e) {
Joachim Bingela145c982015-02-18 18:31:57 +01001165 log.error("Could not parse query. "
1166 + "Please make sure it is well-formed.");
Joachim Bingelc9551b32015-01-19 14:26:58 +00001167 log.error(errorListener.generateFullErrorMsg().toString());
1168 addError(errorListener.generateFullErrorMsg());
1169 }
1170 return tree;
1171 }
Joachim Bingel761d1c12014-12-17 14:02:40 +00001172}