Fixed OPOV

Change-Id: Ia630648357210cbac9c32a29ec9513cc21bda214
diff --git a/Changes b/Changes
index b4bc7bd..be9fd4b 100644
--- a/Changes
+++ b/Changes
@@ -1,9 +1,10 @@
-0.33 2019-02-15
+0.33 2019-02-20
     - Added debug flags (margaretha)
     - [bugfix] Fixed poliqarp layer bug (#52; margaretha)
     - Replaced log4j with log4j2 (margaretha)
     - Fixed issue #43 - C2 OPIN (margaretha)
     - [bugfix] Flatten serialization of query errors (diewald, margaretha) 
+    - Fixed OPOV and added range in OPOV grammar (margaretha)
     
 0.32 2018-12-13
     - [bugfix] Support verbatim string queries (#57; diewald).
diff --git a/pom.xml b/pom.xml
index 7417cea..1364510 100644
--- a/pom.xml
+++ b/pom.xml
@@ -182,7 +182,10 @@
 				<artifactId>maven-surefire-plugin</artifactId>
 				<version>2.22.1</version>
 				<configuration>
-          <useSystemClassLoader>false</useSystemClassLoader>
+                    <useSystemClassLoader>false</useSystemClassLoader>
+					<excludes>
+						<exclude>**/OPINTest.java</exclude>
+					</excludes>
 				</configuration>
 			</plugin>
 			<plugin>
@@ -196,6 +199,7 @@
 					<compilerVersion>2.0</compilerVersion>
 					<source>${java.version}</source>
 					<target>${java.version}</target>
+					<argLine>-Xms512m</argLine>
 				</configuration>
 			</plugin>
 			<!-- <plugin>
diff --git a/src/main/antlr/cosmas/c2ps_opOV.g b/src/main/antlr/cosmas/c2ps_opOV.g
index a80db9b..6297d5a 100644
--- a/src/main/antlr/cosmas/c2ps_opOV.g
+++ b/src/main/antlr/cosmas/c2ps_opOV.g
@@ -15,6 +15,7 @@
 
 options {output=AST;}
 tokens  {OPOV;
+     RANGE; ALL; HIT; 
 	 POS; 
 	 EXCL; YES;
 	 GROUP; MIN; MAX; }
@@ -42,7 +43,11 @@
 
 opts	:	opt (',' opt)*   -> opt*;
 
-opt	:	(optPos | optExcl | optGrp);
+opt	:	(optRange |optPos | optExcl | optGrp);
+
+// Bereich:
+optRange:	('ALL' | 'all') -> ^(RANGE ALL) 
+	| 	('HIT' | 'hit')	-> ^(RANGE HIT); 
 
 // Position:
 optPos	:	POSTYP 
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/Cosmas2QueryProcessor.java b/src/main/java/de/ids_mannheim/korap/query/serialize/Cosmas2QueryProcessor.java
index 3e8c575..5f19798 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/Cosmas2QueryProcessor.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/Cosmas2QueryProcessor.java
@@ -511,6 +511,22 @@
         ((ArrayList<Object>) topGroup.get("operands")).add(group);
         return topGroup;
     }
+    
+    private Map<String, Object> addClassRefOp (
+            ClassRefOp operation, Map<String, Object> group,
+            int classCounter) {
+
+        Integer[] classIn = new Integer[2];
+        classIn[0] = (classCounter + 128 - 1);
+        classIn[1] = (classCounter + 128);
+
+        int classOut = classCounter + 128 + 1;
+        // wrap position in a classRefCheck
+        Map<String, Object> topGroup = KoralObjectGenerator
+                .makeClassRefOp(operation, classIn, classOut);
+        ((ArrayList<Object>) topGroup.get("operands")).add(group);
+        return topGroup;
+    }
 
 
     private Map<String, Object> addClassFocus (boolean isMatchAll,
@@ -540,17 +556,18 @@
 
         int focusClassCounter = classCounter;
         Map<String, Object> posGroup;
+        
+        if (!isComplexQuery(node, "ARG1")){
+            wrapOperandInClass(node, 1, 128 + classCounter++);
+            focusClassCounter = classCounter;
+        }
+        
         if (isExclusion) {
             posGroup = KoralObjectGenerator.makeGroup(KoralOperation.EXCLUSION);
         }
         else {
-            if (!isComplexQuery(node, "ARG1")){
-                wrapOperandInClass(node, 1, 128 + classCounter++);
-                focusClassCounter = classCounter;
-            }
             posGroup = KoralObjectGenerator.makeGroup(KoralOperation.POSITION);
             if (DEBUG) log.debug(posGroup.toString());
-            
         }
 
         Map<String, Object> positionOptions;
@@ -579,10 +596,16 @@
                     addClassRefCheck((ArrayList<ClassRefCheck>) positionOptions
                             .get("classRefCheck"), posGroup, 2);
         }
-        // add focus
-        posGroup = addClassFocus((boolean) positionOptions.get("matchall"),
-                posGroup, focusClassCounter);
-
+        
+        if (nodeCat.equals("OPIN")) {
+            // add focus
+            posGroup = addClassFocus((boolean) positionOptions.get("matchall"),
+                    posGroup, focusClassCounter);
+        }
+        else{
+            posGroup = addClassRefOp(ClassRefOp.UNION, posGroup, 2);
+        }
+        
         // wrap in 'merge' operation if grouping option is set
         if (!isExclusion && positionOptions.containsKey("grouping")
                 && (boolean) positionOptions.get("grouping")) {
@@ -1201,14 +1224,14 @@
             if (!posOption.equals("FI") && !posOption.equals("FE")) {
                 classRefCheck.add(ClassRefCheck.INCLUDES);
             }
-            checkRange(classRefCheck, node, posOption);
+            checkRange(node);
         }
         else if (!isExclusion) {
             if (!classRefCheck.isEmpty()) {
                 wrapOperandInClass(node, 2, 128 + classCounter++);
             }
             else{
-                checkRange(classRefCheck, node, posOption);
+                checkRange(node);
             }
         }
         
@@ -1240,8 +1263,7 @@
         return posOptions;
     }
 
-    private void checkRange (ArrayList<ClassRefCheck> classRefCheck,
-            Tree node, String posOption) {
+    private void checkRange (Tree node) {
         // if (range.equals("all")) {
 
         // posOptions.put("matchall", true);
@@ -1342,52 +1364,42 @@
 
     private Map<String, Object> parseOPOVOptions (Tree node, boolean isExclusion) {
         Tree posnode = getFirstChildWithCat(node, "POS");
-        Tree rangenode = getFirstChildWithCat(node, "RANGE");
         Tree exclnode = getFirstChildWithCat(node, "EXCL");
         Tree groupnode = getFirstChildWithCat(node, "GROUP");
         Map<String, Object> posOptions = new HashMap<String, Object>();
         ArrayList<KoralFrame> positions = new ArrayList<KoralFrame>();
         ArrayList<ClassRefCheck> classRefCheck = new ArrayList<ClassRefCheck>();
         posOptions.put("matchall", false);
-        String posOption = null;
+        String posOption = "";
         if (posnode != null) {
             posOption = posnode.getChild(0).toStringTree();
-            if (isExclusion){
-                checkOVExclusionOptions(posOption, positions, classRefCheck);
-            }
-            else{
-                checkOVOptions(posOption, positions, classRefCheck);
-            }
         }
-        else if (isExclusion){
+        
+        if (isExclusion){
             classRefCheck.add(ClassRefCheck.DISJOINT);
+            checkOVExclusionOptions(posOption, positions, classRefCheck);
         }
         else{
-            classRefCheck.add(ClassRefCheck.INTERSECTS);
+            checkOVOptions(posOption, positions, classRefCheck);
         }
-
+        
         posOptions.put("frames", Converter.enumListToStringList(positions));
         posOptions.put("classRefCheck", classRefCheck);
-        if (!classRefCheck.isEmpty()) {
-            wrapOperandInClass(node, 1, 128 + classCounter++);
+        if (isComplexQuery(node, "ARG2")) {
+            checkRange(node);
+        }
+        else if (!classRefCheck.isEmpty()){
             wrapOperandInClass(node, 2, 128 + classCounter++);
         }
+        else{
+            checkRange(node);
+        }
 //        if (exclnode != null) {
 //            if (exclnode.getChild(0).toStringTree().equals("YES")) {
 //                negatePosition = !negatePosition;
 //            }
 //        }
 
-        if (rangenode != null) {
-            String range = rangenode.getChild(0).toStringTree().toLowerCase();
-            if (range.equals("all")) {
-                posOptions.put("matchall", true);
-                // Map<String,Object> ref =
-                // makeResetReference(); // reset all defined classes
-                // wrapOperand(node,2,ref);
-            }
-        }
-
 //        if (negatePosition) {
 //            posOptions.put("exclude", "true");
 //        }
@@ -1446,11 +1458,11 @@
         classRefCheck.add(ClassRefCheck.INTERSECTS);
         switch (posOption) {
             case "L":
-                positions.add(KoralFrame.ALIGNS_LEFT);
+                positions.add(KoralFrame.STARTS_WITH);
                 positions.add(KoralFrame.OVERLAPS_LEFT);
                 break;
             case "R":
-                positions.add(KoralFrame.ALIGNS_RIGHT);
+                positions.add(KoralFrame.ENDS_WITH);
                 positions.add(KoralFrame.OVERLAPS_RIGHT);
                 break;
             case "F":
@@ -1466,8 +1478,13 @@
                 classRefCheck.add(ClassRefCheck.DIFFERS);
                 break;
             case "X":
-                positions.add(KoralFrame.IS_WITHIN);
-                positions.remove(KoralFrame.MATCHES);
+                positions.add(KoralFrame.IS_AROUND);
+                break;
+            default:
+                positions.add(KoralFrame.MATCHES);
+                positions.add(KoralFrame.STARTS_WITH);
+                positions.add(KoralFrame.ENDS_WITH);
+                positions.add(KoralFrame.IS_AROUND);
                 break;
         }
         
diff --git a/src/test/java/de/ids_mannheim/korap/test/cosmas2/Cosmas2QueryProcessorTest.java b/src/test/java/de/ids_mannheim/korap/test/cosmas2/Cosmas2QueryProcessorTest.java
index eab7700..dedc9f3 100644
--- a/src/test/java/de/ids_mannheim/korap/test/cosmas2/Cosmas2QueryProcessorTest.java
+++ b/src/test/java/de/ids_mannheim/korap/test/cosmas2/Cosmas2QueryProcessorTest.java
@@ -6,7 +6,6 @@
 import java.io.IOException;
 import java.util.ArrayList;
 
-import org.junit.Ignore;
 import org.junit.Test;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
@@ -706,104 +705,6 @@
 
 
     @Test
-    @Ignore
-    public void testOPOV () throws JsonProcessingException, IOException {
-        query = "wegen #OV <s>";
-        qs.setQuery(query, "cosmas2");
-        res = mapper.readTree(qs.toJSON());
-        // System.out.println(res);
-        assertEquals("koral:group", res.at("/query/@type").asText());
-        assertEquals("operation:class", res.at("/query/operation")
-                .asText());
-        assertEquals("classRefCheck:intersects",
-                res.at("/query/classRefCheck/0").asText());
-        //		assertEquals("classRefOp:merge",            res.at("/query/operands/0/classRefOp").asText());
-//        assertEquals(132, res.at("/query/classOut").asInt());
-//        assertEquals(131, res.at("/query/classIn/0").asInt());
-//        assertEquals(130, res.at("/query/classIn/1").asInt());
-        assertEquals("koral:group", res
-                .at("/query/operands/0/@type").asText());
-        assertEquals("operation:position",
-                res.at("/query/operands/0/operation").asText());
-        assertEquals(true, res.at("/query/operands/0/frames/0")
-                .isMissingNode());
-        assertEquals("koral:group", res
-                .at("/query/operands/0/@type").asText());
-        assertEquals("operation:class",
-                res.at("/query/operands/0/operands/0/operation")
-                        .asText());
-        assertEquals(130,
-                res.at("/query/operands/0/operands/0/classOut")
-                        .asInt());
-        assertEquals(
-                "koral:token",
-                res.at("/query/operands/0/operands/0/operands/0/@type")
-                        .asText());
-        assertEquals(
-                "wegen",
-                res.at("/query/operands/0/operands/0/operands/0/wrap/key")
-                        .asText());
-        assertEquals("koral:group",
-                res.at("/query/operands/0/operands/1/@type")
-                        .asText());
-        assertEquals("operation:class",
-                res.at("/query/operands/0/operands/1/operation")
-                        .asText());
-        assertEquals(131,
-                res.at("/query/operands/0/operands/1/classOut")
-                        .asInt());
-        assertEquals(
-                "koral:span",
-                res.at("/query/operands/0/operands/1/operands/0/@type")
-                        .asText());
-        assertEquals(
-                "s",
-                res.at("/query/operands/0/operands/1/operands/0/wrap/key")
-                        .asText());
-
-        query = "wegen #OV(L) <s>";
-        qs.setQuery(query, "cosmas2");
-        res = mapper.readTree(qs.toJSON());
-        assertEquals("classRefCheck:intersects",
-                res.at("/query/classRefCheck/0").asText());
-        assertEquals("frames:alignsLeft",
-                res.at("/query/operands/0/frames/0").asText());
-        assertEquals("frames:overlapsLeft",
-                res.at("/query/operands/0/frames/1").asText());
-//        assertEquals("frames:matches",
-//                res.at("/query/operands/0/frames/0").asText());
-
-        query = "wegen #OV(F) <s>";
-        qs.setQuery(query, "cosmas2");
-        res = mapper.readTree(qs.toJSON());
-        assertEquals("classRefCheck:intersects",
-                res.at("/query/classRefCheck/0").asText());
-        assertEquals("frames:matches",
-                res.at("/query/operands/0/frames/0").asText());
-        assertEquals(true, res.at("/query/operands/0/operands/0/frames/1")
-                .isMissingNode());
-
-        query = "wegen #OV(FI) <s>";
-        qs.setQuery(query, "cosmas2");
-        res = mapper.readTree(qs.toJSON());
-        assertEquals("classRefCheck:intersects",
-                res.at("/query/classRefCheck/0").asText());
-        assertEquals("classRefCheck:differs",
-                res.at("/query/classRefCheck/1").asText());
-        assertEquals("frames:matches",
-                res.at("/query/operands/0/frames/0").asText());
-
-        query = "wegen #OV(FE) <s>";
-        qs.setQuery(query, "cosmas2");
-        res = mapper.readTree(qs.toJSON());
-        assertEquals("classRefCheck:equals",
-                res.at("/query/classRefCheck/0").asText());
-        assertEquals("frames:matches",
-                res.at("/query/operands/0/frames/0").asText());
-    }
-
-
-    @Test
     public void testBEG_END () throws JsonProcessingException, IOException {
         query = "#BEG(der /w3:5 Mann)";
         qs.setQuery(query, "cosmas2");
diff --git a/src/test/java/de/ids_mannheim/korap/test/cosmas2/OPOVTest.java b/src/test/java/de/ids_mannheim/korap/test/cosmas2/OPOVTest.java
new file mode 100644
index 0000000..d16ea80
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/test/cosmas2/OPOVTest.java
@@ -0,0 +1,242 @@
+package de.ids_mannheim.korap.test.cosmas2;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import de.ids_mannheim.korap.query.object.ClassRefCheck;
+import de.ids_mannheim.korap.query.object.ClassRefOp;
+import de.ids_mannheim.korap.query.object.KoralOperation;
+import de.ids_mannheim.korap.query.serialize.QuerySerializer;
+
+public class OPOVTest {
+
+    String query;
+    ArrayList<JsonNode> operands;
+
+    QuerySerializer qs = new QuerySerializer();
+    ObjectMapper mapper = new ObjectMapper();
+    JsonNode res;
+
+    private void checkClassOp (JsonNode node, ClassRefOp operation) {
+        assertEquals("koral:group", node.at("/@type").asText());
+        assertEquals("operation:class", node.at("/operation").asText());
+        assertEquals(operation.toString(), node.at("/classRefOp").asText());
+        assertEquals(2, node.at("/classIn").size());
+        assertEquals(129, node.at("/classIn/0").asInt());
+        assertEquals(130, node.at("/classIn/1").asInt());
+        assertEquals(131, node.at("/classOut").asInt());
+    }
+
+    private void checkClassRef (JsonNode node, ClassRefCheck ref) {
+        assertEquals("koral:group", node.at("/@type").asText());
+        assertEquals("operation:class", node.at("/operation").asText());
+        assertEquals(ref.toString(), node.at("/classRefCheck/0").asText());
+        assertTrue(node.at("/classOut").isMissingNode());
+        assertEquals(1, node.at("/classRefCheck").size());
+        assertEquals(2, node.at("/classIn").size());
+        assertEquals(129, node.at("/classIn/0").asInt());
+        assertEquals(130, node.at("/classIn/1").asInt());
+    }
+
+    @Test
+    public void testOPOV () throws JsonProcessingException, IOException {
+        query = "wegen #OV <s>";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+
+        checkClassOp(res.at("/query"), ClassRefOp.UNION);
+        res = res.at("/query/operands/0");
+
+        checkClassRef(res, ClassRefCheck.INTERSECTS);
+        // frames
+        assertEquals("koral:group", res.at("/operands/0/@type").asText());
+        assertEquals("operation:position",
+                res.at("/operands/0/operation").asText());
+        assertEquals(4, res.at("/operands/0/frames").size());
+
+        // operands
+        assertEquals("koral:group", res.at("/operands/0/@type").asText());
+        assertEquals("operation:class",
+                res.at("/operands/0/operands/0/operation").asText());
+        assertEquals(129, res.at("/operands/0/operands/0/classOut").asInt());
+        assertEquals("koral:token",
+                res.at("/operands/0/operands/0/operands/0/@type").asText());
+        assertEquals("wegen",
+                res.at("/operands/0/operands/0/operands/0/wrap/key").asText());
+        assertEquals("koral:group",
+                res.at("/operands/0/operands/1/@type").asText());
+        assertEquals("operation:class",
+                res.at("/operands/0/operands/1/operation").asText());
+
+        assertEquals(130, res.at("/operands/0/operands/1/classOut").asInt());
+        assertEquals("koral:span",
+                res.at("/operands/0/operands/1/operands/0/@type").asText());
+        assertEquals("s",
+                res.at("/operands/0/operands/1/operands/0/wrap/key").asText());
+    }
+
+    @Test
+    public void testOPOV_X () throws JsonProcessingException, IOException {
+        query = "wegen #OV(X) <s>";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+
+        checkClassOp(res.at("/query"), ClassRefOp.UNION);
+        res = res.at("/query/operands/0");
+        checkClassRef(res, ClassRefCheck.INTERSECTS);
+
+        assertEquals(1, res.at("/operands/0/frames").size());
+        assertEquals("frames:isAround",
+                res.at("/operands/0/frames/0").asText());
+    }
+
+    @Test
+    public void testOPOV_L () throws JsonProcessingException, IOException {
+        query = "wegen #OV(L) <s>";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+
+        checkClassOp(res.at("/query"), ClassRefOp.UNION);
+        res = res.at("/query/operands/0");
+        checkClassRef(res, ClassRefCheck.INTERSECTS);
+
+        assertEquals(2, res.at("/operands/0/frames").size());
+        assertEquals("frames:startsWith",
+                res.at("/operands/0/frames/0").asText());
+        assertEquals("frames:overlapsLeft",
+                res.at("/operands/0/frames/1").asText());
+        // assertEquals("frames:matches",
+        // res.at("/operands/0/frames/0").asText());
+    }
+
+    @Test
+    public void testOPOV_R () throws JsonProcessingException, IOException {
+        query = "wegen #OV(R) <s>";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+
+        checkClassOp(res.at("/query"), ClassRefOp.UNION);
+        res = res.at("/query/operands/0");
+        checkClassRef(res, ClassRefCheck.INTERSECTS);
+
+        assertEquals(2, res.at("/operands/0/frames").size());
+        assertEquals("frames:endsWith",
+                res.at("/operands/0/frames/0").asText());
+        assertEquals("frames:overlapsRight",
+                res.at("/operands/0/frames/1").asText());
+        // assertEquals("frames:matches",
+        // res.at("/operands/0/frames/0").asText());
+    }
+
+    @Test
+    public void testOPOV_F () throws JsonProcessingException, IOException {
+        query = "wegen #OV(F) <s>";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+
+        checkClassOp(res.at("/query"), ClassRefOp.UNION);
+        res = res.at("/query/operands/0");
+        checkClassRef(res, ClassRefCheck.INTERSECTS);
+
+        assertEquals(1, res.at("/operands/0/frames").size());
+        assertEquals("frames:matches", res.at("/operands/0/frames/0").asText());
+    }
+
+    @Test
+    public void testOPOV_FI () throws JsonProcessingException, IOException {
+        query = "wegen #OV(FI) <s>";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+        // System.out.println(res);
+        checkClassOp(res.at("/query"), ClassRefOp.UNION);
+        JsonNode node = res.at("/query/operands/0");
+
+        assertEquals("koral:group", node.at("/@type").asText());
+        assertEquals("operation:class", node.at("/operation").asText());
+        assertEquals(ClassRefCheck.INTERSECTS.toString(),
+                node.at("/classRefCheck/0").asText());
+        assertEquals(ClassRefCheck.DIFFERS.toString(),
+                node.at("/classRefCheck/1").asText());
+
+        assertTrue(node.at("/classOut").isMissingNode());
+        assertEquals(2, node.at("/classRefCheck").size());
+        assertEquals(2, node.at("/classIn").size());
+        assertEquals(129, node.at("/classIn/0").asInt());
+        assertEquals(130, node.at("/classIn/1").asInt());
+
+        assertEquals(1, node.at("/operands/0/frames").size());
+        assertEquals("frames:matches",
+                node.at("/operands/0/frames/0").asText());
+    }
+
+    @Test
+    public void testOPOV_FE () throws JsonProcessingException, IOException {
+        query = "wegen #OV(FE) <s>";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+
+        checkClassOp(res.at("/query"), ClassRefOp.UNION);
+        res = res.at("/query/operands/0");
+        checkClassRef(res, ClassRefCheck.EQUALS);
+
+        assertEquals(1, res.at("/operands/0/frames").size());
+        assertEquals("frames:matches", res.at("/operands/0/frames/0").asText());
+    }
+
+    
+    @Test
+    public void testOPOV_L_ALL () throws JsonProcessingException, IOException {
+        query = "der #OV(L,ALL) (der /+w2:2 ist)";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+
+        checkClassOp(res.at("/query"), ClassRefOp.UNION);
+        res = res.at("/query/operands/0");
+        checkClassRef(res, ClassRefCheck.INTERSECTS);
+
+        assertEquals(2, res.at("/operands/0/frames").size());
+        assertEquals("frames:startsWith",
+                res.at("/operands/0/frames/0").asText());
+        assertEquals("frames:overlapsLeft",
+                res.at("/operands/0/frames/1").asText());
+
+        // class
+        res = res.at("/operands/0/operands/1");
+        assertEquals(KoralOperation.CLASS.toString(),
+                res.at("/operation").asText());
+        assertEquals(130, res.at("/classOut").asInt());
+    }
+    
+    @Test
+    public void testOPOV_L_HIT () throws JsonProcessingException, IOException {
+        query = "der #OV(L) (der /+w2:2 ist)";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+        checkClassOp(res.at("/query"), ClassRefOp.UNION);
+        res = res.at("/query/operands/0");
+        checkClassRef(res, ClassRefCheck.INTERSECTS);
+
+        assertEquals(2, res.at("/operands/0/frames").size());
+        assertEquals("frames:startsWith",
+                res.at("/operands/0/frames/0").asText());
+        assertEquals("frames:overlapsLeft",
+                res.at("/operands/0/frames/1").asText());
+
+        // sequence
+        res = res.at("/operands/0/operands/1");
+        assertEquals(KoralOperation.SEQUENCE.toString(),
+                res.at("/operation").asText());
+        // class
+        assertEquals(130, res.at("/operands/0/classOut").asInt());
+        assertEquals(130, res.at("/operands/1/classOut").asInt());
+    }
+}