multiple operators in n_ary_linguistic_relation now correctly serialized with submatch (to become focus)
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/AbstractSyntaxTree.java b/src/main/java/de/ids_mannheim/korap/query/serialize/AbstractSyntaxTree.java
index e6d94ad..4943874 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/AbstractSyntaxTree.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/AbstractSyntaxTree.java
@@ -55,6 +55,15 @@
 		return group;
 	}
 	
+	protected LinkedHashMap<String, Object> makeClass(int classCount) {
+		LinkedHashMap<String, Object> group = new LinkedHashMap<String, Object>();
+		group.put("@type", "korap:group");
+		group.put("operation", "operation:class");
+		group.put("class", classCount);
+		group.put("operands", new ArrayList<Object>());
+		return group;
+	}
+	
 	protected LinkedHashMap<String, Object> makeTreeRelation(String reltype) {
 		LinkedHashMap<String, Object> group = new LinkedHashMap<String, Object>();
 		group.put("@type", "korap:treeRelation");
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/AqlTree.java b/src/main/java/de/ids_mannheim/korap/query/serialize/AqlTree.java
index cebb22f..c64e702 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/AqlTree.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/AqlTree.java
@@ -82,6 +82,10 @@
 	LinkedList<Integer> objectsToPop = new LinkedList<Integer>();
 	Integer stackedObjects = 0;
 	/**
+	 * Keeps track of operation:class numbers.
+	 */
+	int classCounter = 0;
+	/**
 	 * Keeps track of references to nodes that are operands of groups (e.g. tree relations). Those nodes appear on the top level of the parse tree
 	 * but are to be integrated into the AqlTree at a later point (namely as operands of the respective group). Therefore, store references to these
 	 * nodes here and exclude the operands from being written into the query map individually.   
@@ -201,8 +205,14 @@
 			// and stores them in a list for later reference.
 			List<ParseTree> globalLingTermNodes = new ArrayList<ParseTree>();
 			for (ParseTree exprNode : getChildrenWithCat(node,"expr")) {
+				// Pre-process any 'variableExpr' such that the variableReferences map can be filled
+				List<ParseTree> definitionNodes = new ArrayList<ParseTree>();
+				definitionNodes.addAll(getChildrenWithCat(exprNode, "variableExpr"));
+				for (ParseTree definitionNode : definitionNodes) {
+					processNode(definitionNode);
+				}
+				// Then, mine all relations between nodes
 				List<ParseTree> lingTermNodes = new ArrayList<ParseTree>();
-//				lingTermNodes.addAll(getChildrenWithCat(exprNode, "unary_linguistic_term"));
 				lingTermNodes.addAll(getChildrenWithCat(exprNode, "n_ary_linguistic_term"));
 				globalLingTermNodes.addAll(lingTermNodes);
 				// Traverse refOrNode nodes under *ary_linguistic_term nodes and extract references
@@ -265,7 +275,7 @@
 		if (nodeCat.equals("n_ary_linguistic_term")) {
 			// get operator and determine type of group (sequence/treeRelation/relation/...)
 			// It's possible in Annis QL to concatenate operators, so there may be several operators under one n_ary_linguistic_term node. 
-			// Counter 'i' will point to all operator nodes under this node.
+			// Counter 'i' will point to all operator nodes (odd-numbered) under this node.
 			for (int i=1; i<node.getChildCount(); i = i+2) {
 				ParseTree operand1 = node.getChild(i-1);
 				ParseTree operand2 = node.getChild(i+1);
@@ -276,18 +286,39 @@
 				} catch (ClassCastException | NullPointerException n) {
 					groupType = "relation";
 				}
-				LinkedHashMap<String, Object> group = makeGroup(groupType);
-				if (groupType.equals("relation") || groupType.equals("treeRelation")) {
-					LinkedHashMap<String, Object> relationGroup = new LinkedHashMap<String, Object>();
-					putAllButGroupType(relationGroup, operatorTree);
-					group.put("relation", relationGroup);
-				} else if (groupType.equals("sequence")) {
-					putAllButGroupType(group, operatorTree);
-				} else if (groupType.equals("position")) {
-					putAllButGroupType(group, operatorTree);
+				LinkedHashMap<String, Object> group;
+				List<Object> operands;
+				if (i == node.getChildCount()-2) {
+					group = makeGroup(groupType);
+					if (groupType.equals("relation") || groupType.equals("treeRelation")) {
+						LinkedHashMap<String, Object> relationGroup = new LinkedHashMap<String, Object>();
+						putAllButGroupType(relationGroup, operatorTree);
+						group.put("relation", relationGroup);
+					} else if (groupType.equals("sequence")) {
+						putAllButGroupType(group, operatorTree);
+					} else if (groupType.equals("position")) {
+						putAllButGroupType(group, operatorTree);
+					}
+					// insert referenced nodes into operands list
+					operands = (List<Object>) group.get("operands");
+				} else {
+					group = makeGroup("submatch");
+					ArrayList<Integer> classRef = new ArrayList<Integer>();
+					classRef.add(classCounter);
+					LinkedHashMap<String, Object> actualGroup = makeGroup(groupType);
+					group.put("classRef", classRef);
+					((ArrayList<Object>) group.get("operands")).add(actualGroup);
+					if (groupType.equals("relation") || groupType.equals("treeRelation")) {
+						LinkedHashMap<String, Object> relationGroup = new LinkedHashMap<String, Object>();
+						putAllButGroupType(relationGroup, operatorTree);
+						actualGroup.put("relation", relationGroup);
+					} else if (groupType.equals("sequence")) {
+						putAllButGroupType(actualGroup, operatorTree);
+					} else if (groupType.equals("position")) {
+						putAllButGroupType(actualGroup, operatorTree);
+					}
+					operands = (List<Object>) actualGroup.get("operands");
 				}
-				// insert referenced nodes into operands list
-				List<Object> operands = (List<Object>) group.get("operands");
 				
 				// -> Case distinction:
 				// Things are easy when there's just one operator (thus 3 children incl. operands)...				
@@ -315,7 +346,10 @@
 						}
 						if (!getNodeCat(operand2.getChild(0)).equals("variableExpr")) {
 							String ref2= operand2.getChild(0).toStringTree(parser).substring(1);
-							operands.add(variableReferences.get(ref2));
+							LinkedHashMap<String, Object> classGroup = makeClass(classCounter);
+							((ArrayList<Object>) classGroup.get("operands")).add(variableReferences.get(ref2));
+							operands.add(classGroup);
+							classCounter++;
 						}
 						// Don't put this into the super object directly but store on operandStack 
 						// (because this group will have to be an operand of a subsequent operator)
@@ -338,7 +372,10 @@
 					} else {
 						if (!getNodeCat(operand2.getChild(0)).equals("variableExpr")) {
 							String ref2 = operand2.getChild(0).toStringTree(parser).substring(1);
-							operands.add(variableReferences.get(ref2));
+							LinkedHashMap<String, Object> classGroup = makeClass(classCounter);
+							((ArrayList<Object>) classGroup.get("operands")).add(variableReferences.get(ref2));
+							operands.add(classGroup);
+							classCounter++;
 						}
 						operands.add(0, operandStack.pop());
 						operandStack.push(group);
@@ -735,7 +772,8 @@
 			 "cat=\"NP\" & #1:tokenarity=2",
 			 "node & node & node & #1 . #2 . #3",
 			 "cat=\"CP\" & cat=\"VP\" & cat=\"NP\" & #1 > #2 > #3",
-			 "cat=\"CP\" & cat=\"VP\" & cat=\"NP\" & cat=\"DP\" & #1 > #2 > #3 > #4"
+			 "cat=\"CP\" & cat=\"VP\" & cat=\"NP\" & cat=\"DP\" & #1 > #2 > #3 > #4",
+			 "pos=\"N\" & pos=\"V\" & pos=\"P\" & #1 . #2 & #2 . #3"
 //			 "cnx/cat=\"NP\" > node",
 //			 "node > node",
 //			 "cat=/NP/ > node",
diff --git a/src/test/java/AqlTreeTest.java b/src/test/java/AqlTreeTest.java
index f7af8cc..fc4847f 100644
--- a/src/test/java/AqlTreeTest.java
+++ b/src/test/java/AqlTreeTest.java
@@ -104,6 +104,20 @@
 	}
 	
 	@Test
+	public void testDefPredicationInversion() throws QueryException {
+		query = " #1 > #2 & cnx/cat=\"VP\" & cnx/cat=\"NP\"";
+		String dom1 = 
+				"{@type=korap:group, operation=operation:relation, operands=[" +
+						"{@type=korap:span, foundry=cnx, layer=cat, key=VP, match=match:eq}," +
+						"{@type=korap:span, foundry=cnx, layer=cat, key=NP, match=match:eq}" +
+				"], relation={@type=korap:treeRelation, reltype=dominance}" +
+				"}";
+		aqlt = new AqlTree(query);
+		map = aqlt.getRequestMap().get("query").toString();
+		assertEquals(dom1.replaceAll(" ", ""), map.replaceAll(" ", ""));
+	}
+	
+	@Test
 	public void testSimpleDominance() throws QueryException {
 		query = "node & node & #2 > #1";
 		String dom1 = 
@@ -167,10 +181,14 @@
 		query = "cat=\"CP\" & cat=\"VP\" & cat=\"NP\" & #1 > #2 > #3";
 		String dom1 = 
 				"{@type=korap:group, operation=operation:relation, operands=[" +
-						"{@type=korap:group, operation=operation:relation, operands=[" +
-							"{@type=korap:span, layer=cat, key=CP, match=match:eq}," +
-							"{@type=korap:span, layer=cat, key=VP, match=match:eq}" +
-						"], relation={@type=korap:treeRelation, reltype=dominance}}," +
+						"{@type=korap:group, operation=operation:submatch, operands=[" +
+							"{@type=korap:group, operation=operation:relation, operands=[" +
+								"{@type=korap:span, layer=cat, key=CP, match=match:eq}," +
+								"{@type=korap:group, operation=operation:class, class=0, operands=[" +
+									"{@type=korap:span, layer=cat, key=VP, match=match:eq}" +
+								"]}" +
+							"], relation={@type=korap:treeRelation, reltype=dominance}}" +
+						"], classRef=[0]}," +
 						"{@type=korap:span, layer=cat, key=NP, match=match:eq}" +
 				"], relation={@type=korap:treeRelation, reltype=dominance}" +
 				"}";
@@ -181,13 +199,21 @@
 		query = "cat=\"CP\" & cat=\"VP\" & cat=\"NP\" & cat=\"DP\" & #1 > #2 > #3 > #4";
 		String dom2 = 
 				"{@type=korap:group, operation=operation:relation, operands=[" +
-						"{@type=korap:group, operation=operation:relation, operands=[" +
+						"{@type=korap:group, operation=operation:submatch, operands=[" +
 							"{@type=korap:group, operation=operation:relation, operands=[" +
-								"{@type=korap:span, layer=cat, key=CP, match=match:eq}," +
-								"{@type=korap:span, layer=cat, key=VP, match=match:eq}" +
-							"], relation={@type=korap:treeRelation, reltype=dominance}}," +
-							"{@type=korap:span, layer=cat, key=NP, match=match:eq}" +
-						"], relation={@type=korap:treeRelation, reltype=dominance}}," +
+								"{@type=korap:group, operation=operation:submatch, operands=[" +
+									"{@type=korap:group, operation=operation:relation, operands=[" +
+										"{@type=korap:span, layer=cat, key=CP, match=match:eq}," +
+										"{@type=korap:group, operation=operation:class, class=0, operands=[" +
+											"{@type=korap:span, layer=cat, key=VP, match=match:eq}" +
+										"]}" +
+									"], relation={@type=korap:treeRelation, reltype=dominance}}" +
+								"], classRef=[0]}," +
+								"{@type=korap:group, operation=operation:class, class=1, operands=[" +
+									"{@type=korap:span, layer=cat, key=NP, match=match:eq}" +
+								"]}" +
+							"], relation={@type=korap:treeRelation, reltype=dominance}}" +
+						"], classRef=[1]}," +
 						"{@type=korap:span, layer=cat, key=DP, match=match:eq}" +
 					"], relation={@type=korap:treeRelation, reltype=dominance}" +
 				"}";
@@ -268,18 +294,22 @@
 		query = "tok=\"Sonne\" & tok=\"Mond\" & tok=\"Sterne\" & #1 .0,2 #2 .0,4 #3";
 		String seq4 = 
 				"{@type=korap:group, operation=operation:sequence," +
-						"operands=[" +
+					"operands=[" +
+						"{@type=korap:group, operation=operation:submatch, operands=[" +
 							"{@type=korap:group, operation=operation:sequence, operands=[" +
 								"{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Sonne, match=match:eq}}," +
-								"{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Mond, match=match:eq}}" +
+								"{@type=korap:group, operation=operation:class, class=0, operands=[" +
+									"{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Mond, match=match:eq}}" +
+								"]}" +
 							"], distances=[" +
 								"{@type=korap:distance, key=w, min=0, max=2}" +
-							"], inOrder=true}," +	
-							"{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Sterne, match=match:eq}}" +
-						"],distances=[" +
-							"{@type=korap:distance, key=w, min=0, max=4}" +
-						"], inOrder=true" +
-					"}";
+							"], inOrder=true}" +
+						"], classRef=[0]}," +	
+						"{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Sterne, match=match:eq}}" +
+					"],distances=[" +
+						"{@type=korap:distance, key=w, min=0, max=4}" +
+					"], inOrder=true" +
+				"}";
 		aqlt = new AqlTree(query);
 		map = aqlt.getRequestMap().get("query").toString();
 		assertEquals(seq4.replaceAll(" ", ""), map.replaceAll(" ", ""));
@@ -287,10 +317,14 @@
 		query = "node & node & node & #1 . #2 .1,3 #3";
 		String seq5 = 
 				"{@type=korap:group, operation=operation:sequence, operands=[" +
-						"{@type=korap:group, operation=operation:sequence, operands=[" +
-							"{@type=korap:span}," +
-							"{@type=korap:span}" +
-						"], inOrder=true}," +
+						"{@type=korap:group, operation=operation:submatch, operands=[" +
+							"{@type=korap:group, operation=operation:sequence, operands=[" +
+								"{@type=korap:span}," +
+								"{@type=korap:group, operation=operation:class, class=0, operands=[" +
+									"{@type=korap:span}" +
+								"]} "+
+							"], inOrder=true}" +
+						"], classRef=[0]}," +
 						"{@type=korap:span}" +
 					"], distances=[" +
 							"{@type=korap:distance, key=w, min=1, max=3}" +
@@ -305,12 +339,15 @@
 	public void testMultipleMixedOperators() throws QueryException {
 		query = "tok=\"Sonne\" & tok=\"Mond\" & tok=\"Sterne\" & #1 > #2 .0,4 #3";
 		String seq4 = 
-					"{@type=korap:group, operation=operation:sequence," +
-						"operands=[" +
-							"{@type=korap:group, operation=operation:relation, operands=[" +
-									"{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Sonne, match=match:eq}}," +
-									"{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Mond, match=match:eq}}" +
-								"], relation={@type=korap:treeRelation, reltype=dominance}}," +
+					"{@type=korap:group, operation=operation:sequence, operands=[" +
+							"{@type=korap:group, operation=operation:submatch, operands=[" +
+									"{@type=korap:group, operation=operation:relation, operands=[" +
+										"{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Sonne, match=match:eq}}," +
+										"{@type=korap:group, operation=operation:class, class=0, operands=[" +
+											"{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Mond, match=match:eq}}" +
+										"]}" +
+									"], relation={@type=korap:treeRelation, reltype=dominance}}" +
+								"], classRef=[0]}," +
 								"{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Sterne, match=match:eq}}" +
 						"], distances=[" +
 							"{@type=korap:distance, key=w, min=0, max=4}" +
@@ -319,6 +356,26 @@
 		aqlt = new AqlTree(query);
 		map = aqlt.getRequestMap().get("query").toString();
 		assertEquals(seq4.replaceAll(" ", ""), map.replaceAll(" ", ""));
+		
+		query = "tok=\"Sonne\" & tok=\"Mond\" & #1 > #2 .0,4  tok=\"Sterne\"";
+		String seq5 = 
+					"{@type=korap:group, operation=operation:sequence, operands=[" +
+							"{@type=korap:group, operation=operation:submatch, operands=[" +
+									"{@type=korap:group, operation=operation:relation, operands=[" +
+										"{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Sonne, match=match:eq}}," +
+										"{@type=korap:group, operation=operation:class, class=0, operands=[" +
+											"{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Mond, match=match:eq}}" +
+										"]}" +
+									"], relation={@type=korap:treeRelation, reltype=dominance}}" +
+								"], classRef=[0]}," +
+								"{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Sterne, match=match:eq}}" +
+						"], distances=[" +
+							"{@type=korap:distance, key=w, min=0, max=4}" +
+						"], inOrder=true" +
+					"}";
+		aqlt = new AqlTree(query);
+		map = aqlt.getRequestMap().get("query").toString();
+		assertEquals(seq5.replaceAll(" ", ""), map.replaceAll(" ", ""));
 	}
 	
 	@Test
@@ -384,19 +441,18 @@
 		// a noun before a verb before a noun
 		// XXX Warning incorrect serialisation! Need to ensure that the two Vs are identical! 
 		// Embed in one sequence!
-		query = "pos=\"N\" & pos=\"V\" & pos=\"N\" & #1 . #2 & #2 . #3"; 
+		query = "pos=\"N\" & pos=\"V\" & pos=\"P\" & #1 . #2 & #2 . #3"; 
 		String mult1 = 
 				"{@type=korap:group, operation=operation:sequence, operands=[" +
-					"{@type=korap:group, operation=operation:sequence, operands=[" +
-						"{@type=korap:token, wrap={@type=korap:term, layer=pos, key=N, match=match:eq}}," +
-						"{@type=korap:token, wrap={@type=korap:term, layer=pos, key=V, match=match:eq}}" +
-					"], inOrder=true}," +
-					"{@type=korap:group, operation=operation:sequence, operands=[" +
-						"{@type=korap:token, wrap={@type=korap:term, layer=pos, key=V, match=match:eq}}," +
-						"{@type=korap:token, wrap={@type=korap:term, layer=pos, key=N, match=match:eq}}" +
-					"], inOrder=true}" +
-				"], distances=[" +
-					"{@type=korap:distance, key=t, min=0, max=0}" +
+					"{@type=korap:group, operation=operation:submatch, operands=[" +
+						"{@type=korap:group, operation=operation:sequence, operands=[" +
+							"{@type=korap:token, wrap={@type=korap:term, layer=pos, key=N, match=match:eq}}," +
+							"{@type=korap:group, operation=operation:class, class=0, operands=[" +
+								"{@type=korap:token, wrap={@type=korap:term, layer=pos, key=V, match=match:eq}}" +
+							"]}" +
+						"], inOrder=true}" +
+					"], classRef=[0]}," +
+					"{@type=korap:token, wrap={@type=korap:term, layer=pos, key=P, match=match:eq}}" +
 				"]}";
 		aqlt = new AqlTree(query);
 		map = aqlt.getRequestMap().get("query").toString();