Changed C2 wildcards serialization to type:regex.

Change-Id: If706a4893fd0172b6b89013428260abd58fc65f6
diff --git a/Changes b/Changes
index 097242c..006c566 100644
--- a/Changes
+++ b/Changes
@@ -1,3 +1,7 @@
+0.27 2017-09-12
+	- Changed Cosmas2 wildcards serialization as regex (margaretha).  
+	- Updated C2 OP OV frames (margaretha).
+	
 0.26 2017-06-29
 	- Updated collection query grammar (margaretha)
 	- Updated maven dependency phase (margaretha)
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 0155d71..fd6c3c4 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
@@ -118,7 +118,11 @@
      */
     private LinkedList<ArrayList<Object>> invertedOperandsLists = new LinkedList<ArrayList<Object>>();
 
-
+    public static Pattern wildcardStarPattern = Pattern.compile("([*])");
+    public static Pattern wildcardPlusPattern = Pattern.compile("([+])");
+    public static Pattern wildcardQuestionPattern = Pattern.compile("([?])");
+    
+    
     /**
      * @param tree
      *            The syntax tree as returned by ANTLR
@@ -470,21 +474,33 @@
 
     private boolean isExclusion (Tree node) {
         Tree exclnode = getFirstChildWithCat(node, "EXCL");
-        if (exclnode != null
-                && exclnode.getChild(0).toStringTree().equals("YES")) {
-            return true;
-        }
+        if (exclnode != null && exclnode.getChild(0).toStringTree()
+                .equals("YES")) { return true; }
         return false;
     }
 
 
     private Map<String, Object> addClassRefCheck (
             ArrayList<ClassRefCheck> check, Map<String, Object> group) {
-        Integer[] classIn = new Integer[] { classCounter + 128 - 2,
-                classCounter + 128 - 1 };
+
+        int classOut = classCounter + 128;
+        ArrayList<Integer> classIn = new ArrayList<Integer>();
+        if (check.contains(ClassRefCheck.INCLUDES)) {
+            classIn.add(classCounter + 128);
+            classOut = classCounter + 128 + 1;
+        }
+        if (check.contains(ClassRefCheck.EQUALS)
+                || check.contains(ClassRefCheck.UNEQUALS)) {
+            classIn.add(classCounter + 128 - 2);
+            classIn.add(classCounter + 128 - 1);
+        }
+        else if (check.contains(ClassRefCheck.INTERSECTS)) {
+            classIn.add(classCounter + 128 - 2);
+            classIn.add(classCounter + 128 - 1);
+        }
         // wrap position in a classRefCheck
         Map<String, Object> topGroup = KoralObjectGenerator
-                .makeClassRefCheck(check, classIn, classCounter + 128);
+                .makeClassRefCheck(check, classIn, classOut);
         ((ArrayList<Object>) topGroup.get("operands")).add(group);
         return topGroup;
     }
@@ -541,27 +557,14 @@
                 .get("classRefCheck");
 
         // Step II: wrap in classRefCheck and/or focus and decide where to put
-        if (isExclusion) {
-            if (!checkList.isEmpty()) {
-                wrapOperandInClass(node, 1, 128 + classCounter++);
-                wrapOperandInClass(node, 2, 128 + classCounter++);
-
-                posGroup = addClassRefCheck(
-                        (ArrayList<ClassRefCheck>) positionOptions
-                                .get("classRefCheck"),
-                        posGroup);
-            }
+        if (!checkList.isEmpty()) {
+            posGroup = addClassRefCheck(
+                    (ArrayList<ClassRefCheck>) positionOptions
+                            .get("classRefCheck"),
+                    posGroup);
         }
-        else {
-            if (!checkList.isEmpty()) {
-                posGroup = addClassRefCheck(
-                        (ArrayList<ClassRefCheck>) positionOptions
-                                .get("classRefCheck"),
-                        posGroup);
-            }
-//            posGroup = addClassFocus((boolean) positionOptions.get("matchall"),
-//                    posGroup);
-        }
+        //            posGroup = addClassFocus((boolean) positionOptions.get("matchall"),
+        //                    posGroup);
 
         // wrap in 'merge' operation if grouping option is set
         if (!isExclusion && positionOptions.containsKey("grouping")
@@ -737,8 +740,7 @@
         Map<String, Object> zerodistance = KoralObjectGenerator
                 .makeDistance("t", 0, 0);
         zerodistance.put("@type", "cosmas:distance"); // overwrite @type: cosmas:distance! 
-        if (nodeCat.equals("OPNOT"))
-            zerodistance.put("exclude", true);
+        if (nodeCat.equals("OPNOT")) zerodistance.put("exclude", true);
         distances.add(zerodistance);
         distgroup.put("distances", distances);
         distgroup.put("operands", new ArrayList<Object>());
@@ -786,8 +788,7 @@
                 Map<String, Object> fm = termToFieldMap(
                         elnameNode.getChild(0).toStringTree());
 
-                if (fm == null)
-                    return;
+                if (fm == null) return;
 
                 // Workaround for things like #ELEM(S) to become #ELEM(s)
                 if (fm.get("foundry") == null && fm.get("layer") == null
@@ -867,8 +868,7 @@
                             termGroupOperands.add(subTermGroup);
                         }
                     }
-                    if (getNodeCat(attrNode).equals("NOTEQ"))
-                        negate = true;
+                    if (getNodeCat(attrNode).equals("NOTEQ")) negate = true;
                 }
                 // possibly only one term was present throughout all
                 // nodes: extract it from the group
@@ -901,9 +901,7 @@
         for (String morphterm : morphterms) {
 
             fieldMap = termToFieldMap(morphterm);
-            if (fieldMap == null) {
-                return;
-            };
+            if (fieldMap == null) { return; };
 
             terms.add(fieldMap);
         }
@@ -942,11 +940,31 @@
         String attr = nodeCat.equals("OPWF") ? "orth" : "lemma";
         String value = node.getChild(0).toStringTree().replaceAll("\"", "");
         // check for wildcard string
-        Pattern p = Pattern.compile("[+*?]");
-        Matcher m = p.matcher(value);
-        if (m.find())
-            fieldMap.put("type", "type:wildcard");
 
+        // http://www.ids-mannheim.de/cosmas2/web-app/hilfe/suchanfrage/eingabe-zeile/syntax/platzhalter.html
+        boolean isFound = false;
+        Matcher m = wildcardStarPattern.matcher(value);
+        if (m.find()){
+            isFound = true;
+            value = m.replaceAll(".$1");
+        }
+        m.reset();
+        m = wildcardQuestionPattern.matcher(value);
+        if (m.find()){
+            isFound = true;
+            value = m.replaceAll(".");
+        }
+        m.reset();
+        m = wildcardPlusPattern.matcher(value);
+        if (m.find()){
+            isFound = true;
+            value = m.replaceAll(".?");
+        }
+        
+        if (isFound){
+            fieldMap.put("type", "type:regex");
+        }
+        
         if (value.startsWith("$")) {
             value = value.substring(1);
             ArrayList<String> flags = new ArrayList<String>();
@@ -1104,8 +1122,7 @@
         frames.add(position);
         Map<String, Object> positionGroup = KoralObjectGenerator
                 .makePosition(frames);
-        if (negated)
-            positionGroup.put("exclude", true);
+        if (negated) positionGroup.put("exclude", true);
         ArrayList<Object> posOperands = new ArrayList<Object>();
         Map<String, Object> classGroup = KoralObjectGenerator
                 .makeSpanClass(128 + classCounter++);
@@ -1151,26 +1168,38 @@
         }
 
         if (isExclusion) {
-            checkINWithExclusionOptions(posOption, positions,
-                    classRefCheck);
+            checkINWithExclusionOptions(posOption, positions, classRefCheck);
         }
         else {
             checkINOptions(posOption, positions, classRefCheck);
         }
 
         posOptions.put("frames", Converter.enumListToStringList(positions));
-        posOptions.put("classRefCheck", classRefCheck);
+
         if (!isExclusion && rangenode != null) {
             String range = rangenode.getChild(0).toStringTree().toLowerCase();
-            // ALL is default in KorAP
             // if (range.equals("all")) {
-                
-                // posOptions.put("matchall", true);
-                // Map<String,Object> ref =
-                // makeResetReference(); // reset all defined classes
-                // wrapOperand(node,2,ref);
+
+            // posOptions.put("matchall", true);
+            // Map<String,Object> ref =
+            // makeResetReference(); // reset all defined classes
+            // wrapOperand(node,2,ref);
             //}
             // HIT is default in C2
+            if (!range.equals("all")) {
+                // EM: check in the arg2 is a complex query
+                Tree arg2Operator = getFirstChildWithCat(node, "ARG2")
+                        .getChild(0);
+                if (getFirstChildWithCat(arg2Operator, "ARG2") != null) {
+                    classRefCheck.add(ClassRefCheck.INCLUDES);
+                }
+            }
+        }
+        posOptions.put("classRefCheck", classRefCheck);
+        if (classRefCheck.contains(ClassRefCheck.EQUALS)
+                || classRefCheck.contains(ClassRefCheck.UNEQUALS)) {
+            wrapOperandInClass(node, 1, 128 + classCounter++);
+            wrapOperandInClass(node, 2, 128 + classCounter++);
         }
 
         Boolean grouping = false;
@@ -1259,13 +1288,13 @@
             posOption = posnode.getChild(0).toStringTree();
             switch (posOption) {
                 case "L":
-                    positions.add(KoralFrame.STARTS_WITH);
+                    positions.add(KoralFrame.ALIGNS_LEFT);
                     positions.add(KoralFrame.OVERLAPS_LEFT);
                     positions.add(KoralFrame.MATCHES);
                     classRefCheck.add(ClassRefCheck.INTERSECTS);
                     break;
                 case "R":
-                    positions.add(KoralFrame.ENDS_WITH);
+                    positions.add(KoralFrame.ALIGNS_RIGHT);
                     positions.add(KoralFrame.OVERLAPS_RIGHT);
                     positions.add(KoralFrame.MATCHES);
                     classRefCheck.add(ClassRefCheck.INTERSECTS);
@@ -1283,7 +1312,7 @@
                     classRefCheck.add(ClassRefCheck.UNEQUALS);
                     break;
                 case "X":
-                    positions.add(KoralFrame.IS_AROUND);
+                    positions.add(KoralFrame.IS_WITHIN);
                     classRefCheck.add(ClassRefCheck.INTERSECTS);
                     break;
             }
@@ -1294,6 +1323,10 @@
 
         posOptions.put("frames", Converter.enumListToStringList(positions));
         posOptions.put("classRefCheck", classRefCheck);
+        if (!classRefCheck.isEmpty()){
+            wrapOperandInClass(node, 1, 128 + classCounter++);
+            wrapOperandInClass(node, 2, 128 + classCounter++);
+        }
         if (exclnode != null) {
             if (exclnode.getChild(0).toStringTree().equals("YES")) {
                 negatePosition = !negatePosition;
@@ -1411,13 +1444,10 @@
         Map<String, Object> fieldMap = null;
         fieldMap = KoralObjectGenerator.makeTerm();
 
-        if (m.group(2) != null)
-            fieldMap.put("foundry", m.group(2));
-        if (m.group(4) != null)
-            fieldMap.put("layer", m.group(4));
+        if (m.group(2) != null) fieldMap.put("foundry", m.group(2));
+        if (m.group(4) != null) fieldMap.put("layer", m.group(4));
         if (m.group(5) != null) {
-            if ("!=".equals(m.group(5)))
-                negate = !negate;
+            if ("!=".equals(m.group(5))) negate = !negate;
         }
         if (m.group(6) != null) {
             String key = m.group(6);
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/util/KoralObjectGenerator.java b/src/main/java/de/ids_mannheim/korap/query/serialize/util/KoralObjectGenerator.java
index 0222864..ee592d0 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/util/KoralObjectGenerator.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/util/KoralObjectGenerator.java
@@ -142,12 +142,12 @@
 
 
     public static Map<String, Object> makeClassRefCheck (
-            ArrayList<ClassRefCheck> checks, Integer[] classIn, int classOut) {
+            ArrayList<ClassRefCheck> checks, ArrayList<Integer> classIn, int classOut) {
         Map<String, Object> group = new HashMap<String, Object>();
         group.put("@type", KoralType.GROUP.toString());
         group.put("operation", KoralOperation.CLASS.toString());
         group.put("classRefCheck", Converter.enumListToStringList(checks));
-        group.put("classIn", Arrays.asList(classIn));
+        group.put("classIn", classIn);
         group.put("classOut", classOut);
         group.put("operands", new ArrayList<Object>());
         return group;
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 b703ca3..3f47722 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
@@ -72,15 +72,25 @@
         qs.setQuery(query, "cosmas2");
         res = mapper.readTree(qs.toJSON());
         assertEquals("koral:term", res.at("/query/wrap/@type").asText());
-        assertEquals("type:wildcard", res.at("/query/wrap/type").asText());
-        assertEquals("*der", res.at("/query/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/wrap/type").asText());
+        assertEquals(".*der", res.at("/query/wrap/key").asText());
         assertEquals("orth", res.at("/query/wrap/layer").asText());
         assertEquals("match:eq", res.at("/query/wrap/match").asText());
 
-        query = "*de*?r";
+        query = "*de?r";
         qs.setQuery(query, "cosmas2");
         res = mapper.readTree(qs.toJSON());
-        assertEquals("*de*?r", res.at("/query/wrap/key").asText());
+        assertEquals(".*de.r", res.at("/query/wrap/key").asText());
+        
+        query = "*de+r";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals(".*de.?r", res.at("/query/wrap/key").asText());
+
+        query = "*de+?r";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals(".*de.?.r", res.at("/query/wrap/key").asText());
     }
 
 
@@ -698,77 +708,74 @@
         query = "wegen #OV <s>";
         qs.setQuery(query, "cosmas2");
         res = mapper.readTree(qs.toJSON());
-        assertEquals("koral:reference", res.at("/query/@type").asText());
-        assertEquals("operation:focus", res.at("/query/operation").asText());
-        assertEquals(130, res.at("/query/classRef/0").asInt());
-        assertEquals("koral:group", res.at("/query/operands/0/@type").asText());
-        assertEquals("operation:class", res.at("/query/operands/0/operation")
+        assertEquals("koral:group", res.at("/query/@type").asText());
+        assertEquals("operation:class", res.at("/query/operation")
                 .asText());
         assertEquals("classRefCheck:intersects",
-                res.at("/query/operands/0/classRefCheck/0").asText());
+                res.at("/query/classRefCheck/0").asText());
         //		assertEquals("classRefOp:merge",            res.at("/query/operands/0/classRefOp").asText());
-        assertEquals(131, res.at("/query/operands/0/classOut").asInt());
-        assertEquals(129, res.at("/query/operands/0/classIn/0").asInt());
-        assertEquals(130, res.at("/query/operands/0/classIn/1").asInt());
+        assertEquals(131, res.at("/query/classOut").asInt());
+        assertEquals(129, res.at("/query/classIn/0").asInt());
+        assertEquals(130, res.at("/query/classIn/1").asInt());
         assertEquals("koral:group", res
-                .at("/query/operands/0/operands/0/@type").asText());
+                .at("/query/operands/0/@type").asText());
         assertEquals("operation:position",
-                res.at("/query/operands/0/operands/0/operation").asText());
-        assertEquals(true, res.at("/query/operands/0/operands/0/frames/0")
+                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/operands/0/@type").asText());
+                .at("/query/operands/0/@type").asText());
         assertEquals("operation:class",
-                res.at("/query/operands/0/operands/0/operands/0/operation")
+                res.at("/query/operands/0/operands/0/operation")
                         .asText());
         assertEquals(129,
-                res.at("/query/operands/0/operands/0/operands/0/classOut")
-                        .asInt());
-        assertEquals(
-                "koral:span",
-                res.at("/query/operands/0/operands/0/operands/0/operands/0/@type")
-                        .asText());
-        assertEquals(
-                "s",
-                res.at("/query/operands/0/operands/0/operands/0/operands/0/wrap/key")
-                        .asText());
-        assertEquals("koral:group",
-                res.at("/query/operands/0/operands/0/operands/1/@type")
-                        .asText());
-        assertEquals("operation:class",
-                res.at("/query/operands/0/operands/0/operands/1/operation")
-                        .asText());
-        assertEquals(130,
-                res.at("/query/operands/0/operands/0/operands/1/classOut")
+                res.at("/query/operands/0/operands/0/classOut")
                         .asInt());
         assertEquals(
                 "koral:token",
-                res.at("/query/operands/0/operands/0/operands/1/operands/0/@type")
+                res.at("/query/operands/0/operands/0/operands/0/@type")
                         .asText());
         assertEquals(
                 "wegen",
-                res.at("/query/operands/0/operands/0/operands/1/operands/0/wrap/key")
+                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(130,
+                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/operands/0/classRefCheck/0").asText());
-        assertEquals("frames:startsWith",
-                res.at("/query/operands/0/operands/0/frames/0").asText());
+                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/operands/0/frames/1").asText());
+                res.at("/query/operands/0/frames/1").asText());
         assertEquals("frames:matches",
-                res.at("/query/operands/0/operands/0/frames/2").asText());
+                res.at("/query/operands/0/frames/2").asText());
 
         query = "wegen #OV(F) <s>";
         qs.setQuery(query, "cosmas2");
         res = mapper.readTree(qs.toJSON());
         assertEquals("classRefCheck:intersects",
-                res.at("/query/operands/0/classRefCheck/0").asText());
+                res.at("/query/classRefCheck/0").asText());
         assertEquals("frames:matches",
-                res.at("/query/operands/0/operands/0/frames/0").asText());
+                res.at("/query/operands/0/frames/0").asText());
         assertEquals(true, res.at("/query/operands/0/operands/0/frames/1")
                 .isMissingNode());
 
@@ -776,17 +783,17 @@
         qs.setQuery(query, "cosmas2");
         res = mapper.readTree(qs.toJSON());
         assertEquals("classRefCheck:unequals",
-                res.at("/query/operands/0/classRefCheck/0").asText());
+                res.at("/query/classRefCheck/0").asText());
         assertEquals("frames:matches",
-                res.at("/query/operands/0/operands/0/frames/0").asText());
+                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/operands/0/classRefCheck/0").asText());
+                res.at("/query/classRefCheck/0").asText());
         assertEquals("frames:matches",
-                res.at("/query/operands/0/operands/0/frames/0").asText());
+                res.at("/query/operands/0/frames/0").asText());
     }
 
 
diff --git a/src/test/java/de/ids_mannheim/korap/test/cosmas2/OPINTest.java b/src/test/java/de/ids_mannheim/korap/test/cosmas2/OPINTest.java
index d58c223..35f18c5 100644
--- a/src/test/java/de/ids_mannheim/korap/test/cosmas2/OPINTest.java
+++ b/src/test/java/de/ids_mannheim/korap/test/cosmas2/OPINTest.java
@@ -31,8 +31,7 @@
         res = mapper.readTree(qs.toJSON());
 
         assertEquals("koral:group", res.at("/query/@type").asText());
-        assertEquals("operation:position",
-                res.at("/query/operation").asText());
+        assertEquals("operation:position", res.at("/query/operation").asText());
         assertEquals(4, res.at("/query/frames").size());
         assertEquals("frames:alignsLeft", res.at("/query/frames/0").asText());
         assertEquals("frames:alignsRight", res.at("/query/frames/1").asText());
@@ -50,58 +49,46 @@
         qs.setQuery(query, "cosmas2");
         res = mapper.readTree(qs.toJSON());
         assertEquals("koral:group", res.at("/query/@type").asText());
-        assertEquals("operation:position",
-                res.at("/query/operation").asText());
-        assertEquals("frames:isWithin",
-                res.at("/query/frames/0").asText());
-        assertEquals("wegen",
-                res.at("/query/operands/0/wrap/key").asText());
-        assertEquals("s",
-                res.at("/query/operands/1/wrap/key").asText());
+        assertEquals("operation:position", res.at("/query/operation").asText());
+        assertEquals("frames:isWithin", res.at("/query/frames/0").asText());
+        assertEquals("wegen", res.at("/query/operands/0/wrap/key").asText());
+        assertEquals("s", res.at("/query/operands/1/wrap/key").asText());
     }
-    
-    
+
+
     @Test
     public void testOPINWithOptionL ()
             throws JsonProcessingException, IOException {
         query = "wegen #IN(L) <s>";
         qs.setQuery(query, "cosmas2");
         res = mapper.readTree(qs.toJSON());
-        
-        assertEquals("operation:position",
-                res.at("/query/operation").asText());
-        assertEquals(2, res.at("/query/frames").size());
-        assertEquals("frames:alignsLeft",
-                res.at("/query/frames/0").asText());
-        assertEquals("frames:matches",
-                res.at("/query/frames/1").asText());
 
-        assertEquals("wegen",
-                res.at("/query/operands/0/wrap/key").asText());
-        assertEquals("s",
-                res.at("/query/operands/1/wrap/key").asText());
+        assertEquals("operation:position", res.at("/query/operation").asText());
+        assertEquals(2, res.at("/query/frames").size());
+        assertEquals("frames:alignsLeft", res.at("/query/frames/0").asText());
+        assertEquals("frames:matches", res.at("/query/frames/1").asText());
+
+        assertEquals("wegen", res.at("/query/operands/0/wrap/key").asText());
+        assertEquals("s", res.at("/query/operands/1/wrap/key").asText());
     }
-    
+
+
     @Test
     public void testOPINWithOptionR ()
             throws JsonProcessingException, IOException {
         query = "wegen #IN(R) <s>";
         qs.setQuery(query, "cosmas2");
         res = mapper.readTree(qs.toJSON());
-        
-        assertEquals("operation:position",
-                res.at("/query/operation").asText());
-        assertEquals("frames:alignsRight",
-                res.at("/query/frames/0").asText());
-        assertEquals("frames:matches",
-                res.at("/query/frames/1").asText());
+
+        assertEquals("operation:position", res.at("/query/operation").asText());
+        assertEquals("frames:alignsRight", res.at("/query/frames/0").asText());
+        assertEquals("frames:matches", res.at("/query/frames/1").asText());
         assertEquals(2, res.at("/query/frames").size());
-        assertEquals("wegen",
-                res.at("/query/operands/0/wrap/key").asText());
-        assertEquals("s",
-                res.at("/query/operands/1/wrap/key").asText());
+        assertEquals("wegen", res.at("/query/operands/0/wrap/key").asText());
+        assertEquals("s", res.at("/query/operands/1/wrap/key").asText());
     }
 
+
     @Test
     public void testOPINwithOptionF ()
             throws JsonProcessingException, IOException {
@@ -110,10 +97,8 @@
         res = mapper.readTree(qs.toJSON());
         assertEquals(true,
                 res.at("/query/operands/0/classRefCheck").isMissingNode());
-        assertEquals("frames:matches",
-                res.at("/query/frames/0").asText());
-        assertEquals(true,
-                res.at("/query/frames/1").isMissingNode());
+        assertEquals("frames:matches", res.at("/query/frames/0").asText());
+        assertEquals(true, res.at("/query/frames/1").isMissingNode());
     }
 
 
@@ -127,9 +112,9 @@
                 res.at("/query/classRefCheck/0").asText());
         assertEquals("frames:matches",
                 res.at("/query/operands/0/frames/0").asText());
-        assertEquals(true, res.at("/query/operands/0/frames/1")
-                .isMissingNode());
-        
+        assertEquals(true,
+                res.at("/query/operands/0/frames/1").isMissingNode());
+
     }
 
 
@@ -143,10 +128,11 @@
                 res.at("/query/classRefCheck/0").asText());
         assertEquals("frames:matches",
                 res.at("/query/operands/0/frames/0").asText());
-        assertEquals(true, res.at("/query/operands/0/frames/1")
-                .isMissingNode());
+        assertEquals(true,
+                res.at("/query/operands/0/frames/1").isMissingNode());
     }
 
+
     @Test
     public void testOPINWithOptionN_ALL ()
             throws JsonProcessingException, IOException {
@@ -155,41 +141,130 @@
         qs.setQuery(query, "cosmas2");
         res = mapper.readTree(qs.toJSON());
         assertEquals("koral:group", res.at("/query/@type").asText());
-        assertEquals("operation:position",
-                res.at("/query/operation").asText());
-        assertEquals("frames:isWithin",
-                res.at("/query/frames/0").asText());
-        assertEquals("frames:matches",
-                res.at("/query/frames/1").asText());
-        assertEquals("sich",
-                res.at("/query/operands/0/wrap/key").asText());
+        assertEquals("operation:position", res.at("/query/operation").asText());
+        assertEquals("frames:isWithin", res.at("/query/frames/0").asText());
+        assertEquals("frames:matches", res.at("/query/frames/1").asText());
+        assertEquals("sich", res.at("/query/operands/0/wrap/key").asText());
         assertEquals("gelten",
-                res.at("/query/operands/1/operands/0/operands/0/wrap/key").asText());
+                res.at("/query/operands/1/operands/0/operands/0/wrap/key")
+                        .asText());
         assertEquals("zurecht",
-                res.at("/query/operands/1/operands/1/operands/0/wrap/key").asText());
+                res.at("/query/operands/1/operands/1/operands/0/wrap/key")
+                        .asText());
     }
-    
+
+
+    // Default
+
     @Test
     public void testOPINWithOptionN_HIT ()
             throws JsonProcessingException, IOException {
-        // EM: KorAP does not support matching in multiple hits?   
         query = "gilt #IN(N,HIT) (&gelten /w5:10 zurecht)";
         qs.setQuery(query, "cosmas2");
         res = mapper.readTree(qs.toJSON());
+        
         assertEquals("koral:group", res.at("/query/@type").asText());
+        assertEquals("operation:class", res.at("/query/operation").asText());
+        assertEquals("classRefCheck:includes",
+                res.at("/query/classRefCheck/0").asText());
+        assertEquals(1, res.at("/query/classRefCheck").size());
+        assertEquals(129, res.at("/query/classIn/0").asInt());
+        assertEquals(130, res.at("/query/classOut").asInt());
+
         assertEquals("operation:position",
-                res.at("/query/operation").asText());
+                res.at("/query/operands/0/operation").asText());
         assertEquals("frames:isWithin",
-                res.at("/query/frames/0").asText());
+                res.at("/query/operands/0/frames/0").asText());
         assertEquals("frames:matches",
-                res.at("/query/frames/1").asText());
-        assertEquals("wegen",
-                res.at("/query/operands/0/wrap/key").asText());
-        assertEquals("s",
-                res.at("/query/operands/1/wrap/key").asText());
+                res.at("/query/operands/0/frames/1").asText());
+        assertEquals("gilt",
+                res.at("/query/operands/0/operands/0/wrap/key").asText());
+        assertEquals("operation:sequence",
+                res.at("/query/operands/0/operands/1/operation").asText());
+
+        // sequence operands
+        res = res.at("/query/operands/0/operands/1/operands");
+        assertEquals("gelten", res.at("/0/operands/0/wrap/key").asText());
+        assertEquals(129, res.at("/0/classOut").asInt());
+        assertEquals("zurecht", res.at("/1/operands/0/wrap/key").asText());
+        assertEquals(129, res.at("/1/classOut").asInt());
     }
     
     @Test
+    public void testOPINWithOptionFE_HIT ()
+            throws JsonProcessingException, IOException {
+        query = "gilt #IN(FE,HIT) (&gelten /w5:10 zurecht)";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+        
+        assertEquals("koral:group", res.at("/query/@type").asText());
+        assertEquals("operation:class", res.at("/query/operation").asText());
+        assertEquals("classRefCheck:equals",
+                res.at("/query/classRefCheck/0").asText());
+        assertEquals("classRefCheck:includes",
+                res.at("/query/classRefCheck/1").asText());
+        assertEquals(2, res.at("/query/classRefCheck").size());
+        assertEquals(3, res.at("/query/classIn").size());
+        assertEquals(132, res.at("/query/classOut").asInt());
+
+        assertEquals("operation:position",
+                res.at("/query/operands/0/operation").asText());
+        assertEquals("frames:matches",
+                res.at("/query/operands/0/frames/0").asText());
+        // positions operands
+        res = res.at("/query/operands/0/operands");
+        assertEquals("gilt",
+                res.at("/0/operands/0/wrap/key").asText());
+        assertEquals(129, res.at("/0/classOut").asInt());
+        assertEquals(130, res.at("/1/classOut").asInt());
+        
+        // sequence operands
+        res = res.at("/1/operands/0/operands");        
+        assertEquals("gelten", res.at("/0/operands/0/wrap/key").asText());
+        assertEquals(131, res.at("/0/classOut").asInt());
+        assertEquals("zurecht", res.at("/1/operands/0/wrap/key").asText());
+        assertEquals(131, res.at("/1/classOut").asInt());
+    }
+    
+    @Test
+    public void testOPINWithOptionFI_HIT ()
+            throws JsonProcessingException, IOException {
+        query = "gilt #IN(FI,HIT) (&gelten /w5:10 zurecht)";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+        
+        assertEquals("koral:group", res.at("/query/@type").asText());
+        assertEquals("operation:class", res.at("/query/operation").asText());
+        assertEquals("classRefCheck:unequals",
+                res.at("/query/classRefCheck/0").asText());
+        assertEquals("classRefCheck:includes",
+                res.at("/query/classRefCheck/1").asText());
+        assertEquals(2, res.at("/query/classRefCheck").size());
+        assertEquals(3, res.at("/query/classIn").size());
+        assertEquals(132, res.at("/query/classOut").asInt());
+
+        assertEquals("operation:position",
+                res.at("/query/operands/0/operation").asText());
+        assertEquals("frames:matches",
+                res.at("/query/operands/0/frames/0").asText());
+        // positions operands
+        res = res.at("/query/operands/0/operands");
+        assertEquals("gilt",
+                res.at("/0/operands/0/wrap/key").asText());
+        assertEquals(129, res.at("/0/classOut").asInt());
+        assertEquals(130, res.at("/1/classOut").asInt());
+        
+        // sequence operands
+        res = res.at("/1/operands/0/operands");        
+        assertEquals("gelten", res.at("/0/operands/0/wrap/key").asText());
+        assertEquals(131, res.at("/0/classOut").asInt());
+        assertEquals("zurecht", res.at("/1/operands/0/wrap/key").asText());
+        assertEquals(131, res.at("/1/classOut").asInt());
+    }
+
+
+    @Test
+    @Ignore
     public void testOPINWithOptionN_MAX ()
             throws JsonProcessingException, IOException {
         // EM: Fix operation:merge