Implemented exclusion for the Cosmas 2 IN operation.

Change-Id: Ibb964482b584d8f08452116a834b7be03f51ace7
diff --git a/src/main/java/de/ids_mannheim/korap/query/object/CosmasPosition.java b/src/main/java/de/ids_mannheim/korap/query/object/CosmasPosition.java
new file mode 100644
index 0000000..1b81929
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/object/CosmasPosition.java
@@ -0,0 +1,6 @@
+package de.ids_mannheim.korap.query.object;
+
+public enum CosmasPosition {
+
+    N, L, R, F, FE, FI;
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/object/KoralOperation.java b/src/main/java/de/ids_mannheim/korap/query/object/KoralOperation.java
index 98dae3e..675b627 100644
--- a/src/main/java/de/ids_mannheim/korap/query/object/KoralOperation.java
+++ b/src/main/java/de/ids_mannheim/korap/query/object/KoralOperation.java
@@ -5,7 +5,7 @@
  * 
  */
 public enum KoralOperation {
-    SEQUENCE, POSITION, DISJUNCTION, REPETITION, CLASS, MERGE, RELATION, FOCUS;
+    SEQUENCE, POSITION, DISJUNCTION, REPETITION, CLASS, MERGE, RELATION, FOCUS, EXCLUSION;
 
     @Override
     public String toString() {
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/AbstractQueryProcessor.java b/src/main/java/de/ids_mannheim/korap/query/serialize/AbstractQueryProcessor.java
index 60c2a51..7b101a9 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/AbstractQueryProcessor.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/AbstractQueryProcessor.java
@@ -46,7 +46,7 @@
      * objects
      * into last created objects.
      */
-    LinkedList<LinkedHashMap<String, Object>> objectStack = new LinkedList<LinkedHashMap<String, Object>>();
+    LinkedList<Map<String, Object>> objectStack = new LinkedList<Map<String, Object>>();
     /**
      * Keeps track of how many objects there are to pop after every
      * recursion
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/AnnisQueryProcessor.java b/src/main/java/de/ids_mannheim/korap/query/serialize/AnnisQueryProcessor.java
index 5a1c0b0..2611ab9 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/AnnisQueryProcessor.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/AnnisQueryProcessor.java
@@ -4,7 +4,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.util.LinkedHashMap;
+import java.util.Map;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -60,21 +60,21 @@
      * uncreated
      * objects.
      */
-    LinkedList<LinkedHashMap<String, Object>> operandStack = new LinkedList<LinkedHashMap<String, Object>>();
+    LinkedList<Map<String, Object>> operandStack = new LinkedList<Map<String, Object>>();
     /**
      * Keeps track of explicitly (by #-var definition) or implicitly
      * (number
      * as reference) introduced entities (for later reference by
      * #-operator)
      */
-    Map<String, LinkedHashMap<String, Object>> nodeVariables = new LinkedHashMap<String, LinkedHashMap<String, Object>>();
+    Map<String, Map<String, Object>> nodeVariables = new HashMap<String, Map<String, Object>>();
     /**
      * Keeps track of explicitly (by #-var definition) or implicitly
      * (number
      * as reference) introduced entities (for later reference by
-     * #-operator)
+     * #-operator)s
      */
-    Map<ParseTree, String> nodes2refs = new LinkedHashMap<ParseTree, String>();
+    Map<ParseTree, String> nodes2refs = new HashMap<ParseTree, String>();
     /**
      * Counter for variable definitions.
      */
@@ -84,7 +84,7 @@
      * flags
      * (might already have been taken away from token stack).
      */
-    LinkedHashMap<String, Object> curToken = new LinkedHashMap<String, Object>();
+    Map<String, Object> curToken = new HashMap<String, Object>();
     /**
      * Keeps track of operands lists that are to be serialised in an
      * inverted
@@ -125,11 +125,11 @@
      * classes for
      * entities.
      */
-    private LinkedHashMap<String, Integer> refClassMapping = new LinkedHashMap<String, Integer>();
+    private Map<String, Integer> refClassMapping = new HashMap<String, Integer>();
     /**
      * Keeps a record of unary relations on spans/tokens.
      */
-    private LinkedHashMap<String, ArrayList<ParseTree>> unaryRelations = new LinkedHashMap<String, ArrayList<ParseTree>>();
+    private Map<String, ArrayList<ParseTree>> unaryRelations = new HashMap<String, ArrayList<ParseTree>>();
     /**
      * Keeps track of the number of references to a node/token by
      * means of #n.
@@ -137,13 +137,13 @@
      * #2 . #3</tt>,
      * the 2nd token ("y") is referenced twice, the others once.
      */
-    private LinkedHashMap<String, Integer> nodeReferencesTotal = new LinkedHashMap<String, Integer>();
+    private Map<String, Integer> nodeReferencesTotal = new HashMap<String, Integer>();
     /**
      * Keeps track of the number of references to a node/token that
      * have
      * already been processed.
      */
-    private LinkedHashMap<String, Integer> nodeReferencesProcessed = new LinkedHashMap<String, Integer>();
+    private Map<String, Integer> nodeReferencesProcessed = new HashMap<String, Integer>();
     /**
      * Keeps track of queued relations. Relations sometimes cannot be
      * processed
@@ -164,7 +164,7 @@
      * class ID its
      * derived KoralQuery object will receive.
      */
-    private LinkedHashMap<ParseTree, Integer> objectsToWrapInClass = new LinkedHashMap<ParseTree, Integer>();
+    private Map<ParseTree, Integer> objectsToWrapInClass = new HashMap<ParseTree, Integer>();
 
 
     public AnnisQueryProcessor (String query) {
@@ -200,7 +200,7 @@
                             "The relation " + queued.getText()
                                     + " is not bound to any other relations.");
                     requestMap
-                            .put("query", new LinkedHashMap<String, Object>());
+                            .put("query", new HashMap<String, Object>());
                 }
             }
         }
@@ -360,7 +360,7 @@
                 ref = variableCount.toString();
             }
             nodes2refs.put(variableExprNode, ref);
-            LinkedHashMap<String, Object> object = processVariableExpr(variableExprNode);
+            Map<String, Object> object = processVariableExpr(variableExprNode);
             nodeVariables.put(ref, object);
             variableCount++;
             // Check if this object definition is part of a "direct declaration
@@ -396,7 +396,7 @@
     private void processExprTop (ParseTree node) {
         List<ParseTree> andTopExprs = getChildrenWithCat(node, "andTopExpr");
         if (andTopExprs.size() > 1) {
-            LinkedHashMap<String, Object> topOr = KoralObjectGenerator
+            Map<String, Object> topOr = KoralObjectGenerator
                     .makeGroup(KoralOperation.DISJUNCTION);
             requestMap.put("query", topOr);
             objectStack.push(topOr);
@@ -405,17 +405,17 @@
 
 
     @SuppressWarnings("unchecked")
-    private LinkedHashMap<String, Object> processVariableExpr (ParseTree node) {
+    private Map<String, Object> processVariableExpr (ParseTree node) {
         // simplex word or complex assignment (like qname = textSpec)?
         String firstChildNodeCat = getNodeCat(node.getChild(0));
-        LinkedHashMap<String, Object> object = null;
+        Map<String, Object> object = null;
         if (firstChildNodeCat.equals("node")) {
             object = KoralObjectGenerator.makeSpan();
         }
         else if (firstChildNodeCat.equals("tok")) {
             object = KoralObjectGenerator.makeToken();
             if (node.getChildCount() > 1) { // empty tokens do not wrap a term
-                LinkedHashMap<String, Object> term = KoralObjectGenerator
+                Map<String, Object> term = KoralObjectGenerator
                         .makeTerm();
                 term.put("layer", "orth");
                 object.put("wrap", term);
@@ -427,12 +427,12 @@
             // (e.g. cnx/cat=NP vs mate/pos=NN)
             // TODO generalize the list below -> look up layers associated with
             // tokens rather than spans somewhere
-            HashMap<String, Object> qNameParse = parseQNameNode(node
+            Map<String, Object> qNameParse = parseQNameNode(node
                     .getChild(0));
             if (Arrays.asList(new String[] { "p", "lemma", "m", "orth" })
                     .contains(qNameParse.get("layer"))) {
                 object = KoralObjectGenerator.makeToken();
-                LinkedHashMap<String, Object> term = KoralObjectGenerator
+                Map<String, Object> term = KoralObjectGenerator
                         .makeTerm();
                 object.put("wrap", term);
                 term.putAll(qNameParse);
@@ -444,7 +444,7 @@
         }
         else if (firstChildNodeCat.equals("textSpec")) {
             object = KoralObjectGenerator.makeToken();
-            LinkedHashMap<String, Object> term = KoralObjectGenerator
+            Map<String, Object> term = KoralObjectGenerator
                     .makeTerm();
             object.put("wrap", term);
             term.put("layer", "orth");
@@ -479,7 +479,7 @@
                 object.put("attr", parseUnaryOperator(unaryTermsForRef.get(0)));
             }
             else {
-                LinkedHashMap<String, Object> termGroup = KoralObjectGenerator
+                Map<String, Object> termGroup = KoralObjectGenerator
                         .makeTermGroup(KoralTermGroupRelation.AND);
                 ArrayList<Object> operands = (ArrayList<Object>) termGroup
                         .get("operands");
@@ -526,8 +526,8 @@
      *         representation
      *         of the operand
      */
-    private LinkedHashMap<String, Object> retrieveOperand (ParseTree operandNode) {
-        LinkedHashMap<String, Object> operand = null;
+    private Map<String, Object> retrieveOperand (ParseTree operandNode) {
+        Map<String, Object> operand = null;
         if (!getNodeCat(operandNode.getChild(0)).equals("variableExpr")) {
             String ref = operandNode.getChild(0).toStringTree(parser)
                     .substring(1);
@@ -605,7 +605,7 @@
             ParseTree operandTree2 = node.getChild(i + 1);
             String reltype = getNodeCat(node.getChild(i).getChild(0));
 
-            LinkedHashMap<String, Object> group = null;
+            Map<String, Object> group = null;
             ArrayList<Object> operands = null;
             // make sure one of the operands has already been put into a 
             // relation (if this is not the 1st relation). If none of the
@@ -625,8 +625,8 @@
                 }
             }
             // Retrieve operands.
-            LinkedHashMap<String, Object> operand1 = retrieveOperand(operandTree1);
-            LinkedHashMap<String, Object> operand2 = retrieveOperand(operandTree2);
+            Map<String, Object> operand1 = retrieveOperand(operandTree1);
+            Map<String, Object> operand2 = retrieveOperand(operandTree2);
             // 'Proper' n_ary_linguistic_operators receive a considerably 
             // different serialisation than 'commonparent' and 'commonancestor'
             // For the latter cases, a dummy span is introduced and declared as
@@ -640,11 +640,11 @@
                 // make an (outer) group and an inner group containing the dummy 
                 // node or previous relations
                 group = KoralObjectGenerator.makeGroup(KoralOperation.RELATION);
-                LinkedHashMap<String, Object> innerGroup = KoralObjectGenerator
+                Map<String, Object> innerGroup = KoralObjectGenerator
                         .makeGroup(KoralOperation.RELATION);
-                LinkedHashMap<String, Object> relation = KoralObjectGenerator
+                Map<String, Object> relation = KoralObjectGenerator
                         .makeRelation();
-                LinkedHashMap<String, Object> term = KoralObjectGenerator
+                Map<String, Object> term = KoralObjectGenerator
                         .makeTerm();
                 term.put("layer", "c");
                 relation.put("wrap", term);
@@ -696,7 +696,7 @@
                 // attributes defined in KoralQ. and can be handled more easily
             }
             else {
-                LinkedHashMap<String, Object> operatorGroup = parseOperatorNode(node
+                Map<String, Object> operatorGroup = parseOperatorNode(node
                         .getChild(i).getChild(0));
                 String groupType;
                 try {
@@ -709,7 +709,7 @@
 //                        || groupType.equals("treeRelation")
                     ) {
                     group = KoralObjectGenerator.makeGroup(KoralOperation.RELATION);
-                    LinkedHashMap<String, Object> relation = new LinkedHashMap<String, Object>();
+                    Map<String, Object> relation = new HashMap<String, Object>();
                     putAllButGroupType(relation, operatorGroup);
                     group.put("relation", relation);
                 }
@@ -718,7 +718,7 @@
                     putAllButGroupType(group, operatorGroup);
                 }
                 else if (groupType.equals("position")) {
-                    group = new LinkedHashMap<String, Object>();
+                    group = new HashMap<String, Object>();
                     putAllButGroupType(group, operatorGroup);
                 }
 
@@ -735,7 +735,7 @@
                             : KoralFrame.ENDS_WITH;
                     ArrayList<KoralFrame> frames = new ArrayList<KoralFrame>();
                     frames.add(frame);
-                    LinkedHashMap<String, Object> positionGroup = KoralObjectGenerator
+                    Map<String, Object> positionGroup = KoralObjectGenerator
                             .makePosition(frames);
                     operand2 = KoralObjectGenerator.wrapInClass(operand2,
                             ++classCounter + 128);
@@ -831,11 +831,11 @@
      * @return A map containing the attr key, to be inserted into
      *         koral:span
      */
-    private LinkedHashMap<String, Object> parseUnaryOperator (ParseTree node) {
-        LinkedHashMap<String, Object> term = KoralObjectGenerator.makeTerm();
+    private Map<String, Object> parseUnaryOperator (ParseTree node) {
+        Map<String, Object> term = KoralObjectGenerator.makeTerm();
         String op = node.getChild(1).toStringTree(parser).substring(1);
         if (op.equals("arity") || op.equals("tokenarity")) {
-            LinkedHashMap<String, Object> boundary = boundaryFromRangeSpec(
+            Map<String, Object> boundary = boundaryFromRangeSpec(
                     node.getChild(3), false);
             term.put(op, boundary);
         }
@@ -847,9 +847,9 @@
 
 
     @SuppressWarnings("unchecked")
-    private LinkedHashMap<String, Object> parseOperatorNode (
+    private Map<String, Object> parseOperatorNode (
             ParseTree operatorNode) {
-        LinkedHashMap<String, Object> relation = null;
+        Map<String, Object> relation = null;
         String operator = getNodeCat(operatorNode);
         // DOMINANCE
         if (operator.equals("dominance")) {
@@ -861,13 +861,13 @@
             ParseTree star = getFirstChildWithCat(operatorNode, "*");
             ParseTree rangeSpec = getFirstChildWithCat(operatorNode,
                     "rangeSpec");
-            LinkedHashMap<String, Object> term = KoralObjectGenerator
+            Map<String, Object> term = KoralObjectGenerator
                     .makeTerm();
             term.put("layer", "c");
             if (qName != null)
                 term = parseQNameNode(qName);
             if (edgeSpecNode != null) {
-                LinkedHashMap<String, Object> edgeSpec = parseEdgeSpec(edgeSpecNode);
+                Map<String, Object> edgeSpec = parseEdgeSpec(edgeSpecNode);
                 String edgeSpecType = (String) edgeSpec.get("@type");
                 if (edgeSpecType.equals("koral:termGroup")) {
                     ((ArrayList<Object>) edgeSpec.get("operands")).add(term);
@@ -878,7 +878,7 @@
                     ArrayList<Object> termGroupOperands = (ArrayList<Object>) term
                             .get("operands");
                     termGroupOperands.add(edgeSpec);
-                    LinkedHashMap<String, Object> constTerm = KoralObjectGenerator
+                    Map<String, Object> constTerm = KoralObjectGenerator
                             .makeTerm();
                     constTerm.put("layer", "c");
                     termGroupOperands.add(constTerm);
@@ -899,7 +899,7 @@
             ParseTree star = getFirstChildWithCat(operatorNode, "*");
             ParseTree rangeSpec = getFirstChildWithCat(operatorNode,
                     "rangeSpec");
-            LinkedHashMap<String, Object> term = KoralObjectGenerator
+            Map<String, Object> term = KoralObjectGenerator
                     .makeTerm();
             if (qName != null)
                 term.putAll(parseQNameNode(qName));
@@ -913,7 +913,7 @@
             relation.put("wrap", term);
         }
         else if (operator.equals("precedence")) {
-            relation = new LinkedHashMap<String, Object>();
+            relation = new HashMap<String, Object>();
             relation.put("groupType", "sequence");
             ParseTree rangeSpec = getFirstChildWithCat(operatorNode,
                     "rangeSpec");
@@ -962,7 +962,7 @@
             relation.put("groupType", "position");
         }
         else if (operator.equals("near")) {
-            relation = new LinkedHashMap<String, Object>();
+            relation = new HashMap<String, Object>();
             relation.put("groupType", "sequence");
             ParseTree rangeSpec = getFirstChildWithCat(operatorNode,
                     "rangeSpec");
@@ -992,12 +992,12 @@
 
 
     @SuppressWarnings("unchecked")
-    private LinkedHashMap<String, Object> parseEdgeSpec (ParseTree edgeSpec) {
+    private Map<String, Object> parseEdgeSpec (ParseTree edgeSpec) {
         List<ParseTree> annos = getChildrenWithCat(edgeSpec, "edgeAnno");
         if (annos.size() == 1)
             return parseEdgeAnno(annos.get(0));
         else {
-            LinkedHashMap<String, Object> termGroup = KoralObjectGenerator
+            Map<String, Object> termGroup = KoralObjectGenerator
                     .makeTermGroup(KoralTermGroupRelation.AND);
             ArrayList<Object> operands = (ArrayList<Object>) termGroup
                     .get("operands");
@@ -1009,8 +1009,8 @@
     }
 
 
-    private LinkedHashMap<String, Object> parseEdgeAnno (ParseTree edgeAnnoSpec) {
-        LinkedHashMap<String, Object> edgeAnno = KoralObjectGenerator
+    private Map<String, Object> parseEdgeAnno (ParseTree edgeAnnoSpec) {
+        Map<String, Object> edgeAnno = KoralObjectGenerator
                 .makeTerm();
         ParseTree textSpecNode = getFirstChildWithCat(edgeAnnoSpec, "textSpec");
         ParseTree layerNode = getFirstChildWithCat(edgeAnnoSpec, "layer");
@@ -1028,13 +1028,13 @@
     }
 
 
-    private LinkedHashMap<String, Object> boundaryFromRangeSpec (
+    private Map<String, Object> boundaryFromRangeSpec (
             ParseTree rangeSpec) {
         return boundaryFromRangeSpec(rangeSpec, true);
     }
 
 
-    private LinkedHashMap<String, Object> boundaryFromRangeSpec (
+    private Map<String, Object> boundaryFromRangeSpec (
             ParseTree rangeSpec, boolean expandToMax) {
         Integer min = Integer.parseInt(rangeSpec.getChild(0).toStringTree(
                 parser));
@@ -1047,7 +1047,7 @@
     }
 
 
-    private LinkedHashMap<String, Object> parseDistance (ParseTree rangeSpec) {
+    private Map<String, Object> parseDistance (ParseTree rangeSpec) {
         String minString = rangeSpec.getChild(0).toStringTree(parser);
         String maxString = null; // not always given, prevent NPE
         if (minString.equals("0")) {
@@ -1066,8 +1066,8 @@
     }
 
 
-    private LinkedHashMap<String, Object> parseTextSpec (ParseTree node) {
-        LinkedHashMap<String, Object> term = new LinkedHashMap<String, Object>();
+    private Map<String, Object> parseTextSpec (ParseTree node) {
+        Map<String, Object> term = new HashMap<String, Object>();
         if (hasChild(node, "regex")) {
             term.put("type", "type:regex");
             term.put("key", node.getChild(0).getChild(0).toStringTree(parser)
@@ -1096,8 +1096,8 @@
     }
 
 
-    private LinkedHashMap<String, Object> parseQNameNode (ParseTree node) {
-        LinkedHashMap<String, Object> fields = new LinkedHashMap<String, Object>();
+    private Map<String, Object> parseQNameNode (ParseTree node) {
+        Map<String, Object> fields = new HashMap<String, Object>();
         ParseTree layerNode = getFirstChildWithCat(node, "layer");
         ParseTree foundryNode = getFirstChildWithCat(node, "foundry");
         if (foundryNode != null)
@@ -1112,13 +1112,13 @@
     }
 
 
-    private void putIntoSuperObject (LinkedHashMap<String, Object> object) {
+    private void putIntoSuperObject (Map<String, Object> object) {
         putIntoSuperObject(object, 0);
     }
 
 
     @SuppressWarnings({ "unchecked" })
-    private void putIntoSuperObject (LinkedHashMap<String, Object> object,
+    private void putIntoSuperObject (Map<String, Object> object,
             int objStackPosition) {
         if (objectStack.size() > objStackPosition) {
             ArrayList<Object> topObjectOperands = (ArrayList<Object>) objectStack
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 c3b31e2..705963c 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
@@ -2,6 +2,7 @@
 
 import de.ids_mannheim.korap.query.object.ClassRefCheck;
 import de.ids_mannheim.korap.query.object.ClassRefOp;
+import de.ids_mannheim.korap.query.object.CosmasPosition;
 import de.ids_mannheim.korap.query.object.KoralFrame;
 import de.ids_mannheim.korap.query.object.KoralMatchOperator;
 import de.ids_mannheim.korap.query.object.KoralOperation;
@@ -469,72 +470,120 @@
     }
 
 
+    private boolean isExclusion (Tree node) {
+        Tree exclnode = getFirstChildWithCat(node, "EXCL");
+        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 };
+        // wrap position in a classRefCheck
+        Map<String, Object> topGroup = KoralObjectGenerator
+                .makeClassRefCheck(check, classIn, classCounter + 128);
+        ((ArrayList<Object>) topGroup.get("operands")).add(group);
+        return topGroup;
+    }
+
+
+    private Map<String, Object> addClassFocus (boolean isMatchAll,
+            Map<String, Object> posGroup) {
+        Map<String, Object> focusGroup = null;
+        if (isMatchAll) {
+            focusGroup = KoralObjectGenerator.makeClassRefOp(ClassRefOp.DELETE,
+                    new Integer[] { 128 + classCounter++ }, 128 + classCounter);
+            ((ArrayList<Object>) focusGroup.get("operands")).add(posGroup);
+        }
+        else { // match only first argument
+            focusGroup = KoralObjectGenerator.wrapInReference(posGroup,
+                    classCounter + 128 - 1);
+            classCounter++;
+        }
+        return focusGroup;
+    }
+
+
     @SuppressWarnings("unchecked")
     private void processOPIN_OPOV (Tree node) {
-        // Step I: create group
         String nodeCat = getNodeCat(node);
-        wrapOperandInClass(node, 2, 128 + classCounter++);
-        wrapOperandInClass(node, 1, 128 + classCounter++);
+
         // LinkedHashMap<String, Object> posgroup =
         // makePosition(null);
-        LinkedHashMap<String, Object> posGroup = KoralObjectGenerator
-                .makeGroup(KoralOperation.POSITION);
-        LinkedHashMap<String, Object> positionOptions;
-        // posgroup
+        boolean isExclusion = isExclusion(node);
+
+        KoralOperation operation;
+        if (isExclusion) {
+            operation = KoralOperation.EXCLUSION;
+        }
+        else {
+            operation = KoralOperation.POSITION;
+            wrapOperandInClass(node, 2, 128 + classCounter++);
+            wrapOperandInClass(node, 1, 128 + classCounter++);
+        }
+
+        Map<String, Object> posGroup = KoralObjectGenerator
+                .makeGroup(operation);
+
+        Map<String, Object> positionOptions;
         if (nodeCat.equals("OPIN")) {
-            positionOptions = parseOPINOptions(node);
+            positionOptions = parseOPINOptions(node, isExclusion);
         }
         else {
             positionOptions = parseOPOVOptions(node);
         }
+
         posGroup.put("frames", positionOptions.get("frames"));
-        posGroup.put("frame", positionOptions.get("frame"));
-        if (positionOptions.containsKey("exclude")) {
-            posGroup.put("exclude", positionOptions.get("exclude"));
-        }
+        // EM: is frame needed?
+        // posGroup.put("frame", positionOptions.get("frame"));
         objectStack.push(posGroup);
-        // mark this an inverted operands object
-        invertedOperandsLists
-                .push((ArrayList<Object>) posGroup.get("operands"));
         stackedObjects++;
-        // Step II: wrap in classRefCheck and/or focus and decide where to put
-        ArrayList<ClassRefCheck> check = (ArrayList<ClassRefCheck>) positionOptions
+
+        ArrayList<ClassRefCheck> checkList = (ArrayList<ClassRefCheck>) positionOptions
                 .get("classRefCheck");
-        Integer[] classIn = new Integer[] { classCounter + 128 - 2,
-                classCounter + 128 - 1 };
-        LinkedHashMap<String, Object> topGroup;
-        if (!check.isEmpty()) {
-            // wrap position in a classRefCheck
-            topGroup = KoralObjectGenerator.makeClassRefCheck(check, classIn,
-                    classCounter + 128);
-            ((ArrayList<Object>) topGroup.get("operands")).add(posGroup);
-        }
-        else {
-            topGroup = posGroup;
-        }
-        LinkedHashMap<String, Object> focusGroup = null;
-        if ((boolean) positionOptions.get("matchall") == true) {
-            focusGroup = KoralObjectGenerator.makeClassRefOp(ClassRefOp.DELETE,
-                    new Integer[] { 128 + classCounter++ }, 128 + classCounter);
-            ((ArrayList<Object>) focusGroup.get("operands")).add(topGroup);
-        }
-        else { // match only first argument
-            focusGroup = KoralObjectGenerator.wrapInReference(topGroup,
-                    classCounter + 128 - 1);
-            classCounter++;
-        }
-        // wrap in 'merge' operation if grouping option is set
-        if (positionOptions.containsKey("grouping")) {
-            if (positionOptions.get("grouping").equals(true)) {
-                LinkedHashMap<String, Object> mergeOperation = KoralObjectGenerator
-                        .makeGroup(KoralOperation.MERGE);
-                ArrayList<Object> mergeOperands = (ArrayList<Object>) mergeOperation
-                        .get("operands");
-                mergeOperands.add(focusGroup);
-                focusGroup = mergeOperation;
+
+        // EM: why bother inverting the operands and creating classes and focus? 
+        // can't we agree on the first operand to be the results, like in operation:exclusion?
+        if (!isExclusion) {
+            // mark this an inverted operands object
+            invertedOperandsLists
+                    .push((ArrayList<Object>) posGroup.get("operands"));
+
+            // Step II: wrap in classRefCheck and/or focus and decide where to put
+            if (!checkList.isEmpty()) {
+                posGroup = addClassRefCheck(checkList, posGroup);
             }
+            posGroup = addClassFocus((boolean) positionOptions.get("matchall"),
+                    posGroup);
+
         }
-        putIntoSuperObject(focusGroup, 1);
+        else if (!checkList.isEmpty()) {
+            wrapOperandInClass(node, 1, 128 + classCounter++);
+            wrapOperandInClass(node, 2, 128 + classCounter++);
+            posGroup = addClassRefCheck(
+                    (ArrayList<ClassRefCheck>) positionOptions
+                            .get("classRefCheck"),
+                    posGroup);
+            posGroup = addClassFocus((boolean) positionOptions.get("matchall"),
+                    posGroup);
+        }
+
+        // wrap in 'merge' operation if grouping option is set
+        if (positionOptions.containsKey("grouping")
+                && (boolean) positionOptions.get("grouping")) {
+            LinkedHashMap<String, Object> mergeOperation = KoralObjectGenerator
+                    .makeGroup(KoralOperation.MERGE);
+            ArrayList<Object> mergeOperands = (ArrayList<Object>) mergeOperation
+                    .get("operands");
+            mergeOperands.add(posGroup);
+            posGroup = mergeOperation;
+        }
+        putIntoSuperObject(posGroup, 1);
     }
 
 
@@ -1099,60 +1148,34 @@
     }
 
 
-    private LinkedHashMap<String, Object> parseOPINOptions (Tree node) {
+    private LinkedHashMap<String, Object> parseOPINOptions (Tree node,
+            boolean isExclusion) {
         Tree posnode = getFirstChildWithCat(node, "POS");
         Tree rangenode = getFirstChildWithCat(node, "RANGE");
-        Tree exclnode = getFirstChildWithCat(node, "EXCL");
         Tree groupnode = getFirstChildWithCat(node, "GROUP");
-        boolean negatePosition = false;
+
         LinkedHashMap<String, Object> posOptions = new LinkedHashMap<String, Object>();
         ArrayList<KoralFrame> positions = new ArrayList<KoralFrame>();
         ArrayList<ClassRefCheck> classRefCheck = new ArrayList<ClassRefCheck>();
         posOptions.put("matchall", false);
+
         String posOption = null;
         if (posnode != null) {
-            posOption = posnode.getChild(0).toStringTree();
-            switch (posOption) {
-                case "L":
-                    positions.add(KoralFrame.STARTS_WITH);
-                    positions.add(KoralFrame.MATCHES);
-                    //                    classRefCheck.add("classRefCheck:includes");
-                    break;
-                case "R":
-                    positions.add(KoralFrame.ENDS_WITH);
-                    positions.add(KoralFrame.MATCHES);
-                    //                    classRefCheck.add("classRefCheck:includes");
-                    break;
-                case "F":
-                    positions.add(KoralFrame.MATCHES);
-                    //                    classRefCheck.add("classRefCheck:includes");
-                    break;
-                case "FE":
-                    positions.add(KoralFrame.MATCHES);
-                    classRefCheck.add(ClassRefCheck.EQUALS);
-                    break;
-                case "FI":
-                    positions.add(KoralFrame.MATCHES);
-                    classRefCheck.add(ClassRefCheck.UNEQUALS);
-                    //                    classRefCheck.add("classRefCheck:includes");
-                    break;
-                case "N":
-                    positions.add(KoralFrame.IS_AROUND);
-                    //                    classRefCheck.add("classRefCheck:includes");
-                    break;
+            posOption = posnode.getChild(0).toStringTree().toUpperCase();
+            if (isExclusion) {
+                checkINWithExclusionOptions(posOption, positions,
+                        classRefCheck);
+            }
+            else {
+                checkINOptions(posOption, positions, classRefCheck);
             }
         }
         else {
             classRefCheck.add(ClassRefCheck.INCLUDES);
         }
+
         posOptions.put("frames", Converter.enumListToStringList(positions));
         posOptions.put("classRefCheck", classRefCheck);
-        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")) {
@@ -1163,15 +1186,10 @@
             }
         }
 
-        if (negatePosition) {
-            posOptions.put("exclude", true);
-        }
-
-        boolean grouping = false;
-        if (groupnode != null) {
-            if (groupnode.getChild(0).toStringTree().equalsIgnoreCase("max")) {
-                grouping = true;
-            }
+        Boolean grouping = false;
+        if (groupnode != null && groupnode.getChild(0).toStringTree()
+                .equalsIgnoreCase("max")) {
+            grouping = true;
         }
         posOptions.put("grouping", grouping);
 
@@ -1179,6 +1197,70 @@
     }
 
 
+    private void checkINOptions (String posOption,
+            ArrayList<KoralFrame> positions,
+            ArrayList<ClassRefCheck> classRefCheck) {
+        switch (posOption) {
+            case "L":
+                positions.add(KoralFrame.STARTS_WITH);
+                positions.add(KoralFrame.MATCHES);
+                //                    classRefCheck.add("classRefCheck:includes");
+                break;
+            case "R":
+                positions.add(KoralFrame.ENDS_WITH);
+                positions.add(KoralFrame.MATCHES);
+                //                    classRefCheck.add("classRefCheck:includes");
+                break;
+            case "F":
+                positions.add(KoralFrame.MATCHES);
+                //                    classRefCheck.add("classRefCheck:includes");
+                break;
+            case "FE":
+                positions.add(KoralFrame.MATCHES);
+                classRefCheck.add(ClassRefCheck.EQUALS);
+                break;
+            case "FI":
+                positions.add(KoralFrame.MATCHES);
+                classRefCheck.add(ClassRefCheck.UNEQUALS);
+                //                    classRefCheck.add("classRefCheck:includes");
+                break;
+            case "N":
+                positions.add(KoralFrame.IS_AROUND);
+                //                    classRefCheck.add("classRefCheck:includes");
+                break;
+        }
+    }
+
+
+    private void checkINWithExclusionOptions (String posOption,
+            ArrayList<KoralFrame> positions,
+            ArrayList<ClassRefCheck> classRefCheck) {
+        if (CosmasPosition.N.name().equals(posOption)) {
+            positions.add(KoralFrame.IS_WITHIN);
+            return;
+        }
+        else if (CosmasPosition.L.name().equals(posOption)) {
+            positions.add(KoralFrame.ALIGNS_LEFT);
+        }
+        else if (CosmasPosition.R.name().equals(posOption)) {
+            positions.add(KoralFrame.ALIGNS_RIGHT);
+        }
+        else if (CosmasPosition.FE.name().equals(posOption)) {
+            classRefCheck.add(ClassRefCheck.UNEQUALS);
+        }
+        else if (CosmasPosition.FI.name().equals(posOption)) {
+            classRefCheck.add(ClassRefCheck.EQUALS);
+        }
+        else if (CosmasPosition.F.name().equals(posOption)) {}
+        else {
+            // throw an error or add an exception;
+            return;
+        }
+
+        positions.add(KoralFrame.MATCHES);
+    }
+
+
     private LinkedHashMap<String, Object> parseOPOVOptions (Tree node) {
         boolean negatePosition = false;
         Tree posnode = getFirstChildWithCat(node, "POS");
@@ -1275,7 +1357,7 @@
 
 
     @SuppressWarnings("unchecked")
-    private void putIntoSuperObject (LinkedHashMap<String, Object> object,
+    private void putIntoSuperObject (Map<String, Object> object,
             int objStackPosition) {
         if (objectStack.size() > objStackPosition) {
             ArrayList<Object> topObjectOperands = (ArrayList<Object>) objectStack
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 8f2b5c6..8fbb7fc 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
@@ -4,6 +4,7 @@
 import java.util.Arrays;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 
 import de.ids_mannheim.korap.query.object.ClassRefCheck;
 import de.ids_mannheim.korap.query.object.ClassRefOp;
@@ -291,8 +292,8 @@
     }
 
 
-    public static LinkedHashMap<String, Object> wrapInReference (
-            LinkedHashMap<String, Object> group, Integer classId) {
+    public static Map<String, Object> wrapInReference (
+            Map<String, Object> group, Integer classId) {
         LinkedHashMap<String, Object> refGroup = makeReference(classId);
         ArrayList<Object> operands = new ArrayList<Object>();
         operands.add(group);
@@ -314,9 +315,9 @@
 
 
     @SuppressWarnings("unchecked")
-    public static LinkedHashMap<String, Object> wrapInClass (
-            LinkedHashMap<String, Object> group, Integer classId) {
-        LinkedHashMap<String, Object> classGroup = makeSpanClass(classId);
+    public static Map<String, Object> wrapInClass (
+            Map<String, Object> group, Integer classId) {
+        Map<String, Object> classGroup = makeSpanClass(classId);
         ((ArrayList<Object>) classGroup.get("operands")).add(group);
         return classGroup;
     }
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 6d6d59c..b703ca3 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
@@ -1,4 +1,4 @@
-package de.ids_mannheim.korap.query.test;
+package de.ids_mannheim.korap.test.cosmas2;
 
 import static org.junit.Assert.*;
 
@@ -694,180 +694,6 @@
 
 
     @Test
-    public void testOPIN () throws JsonProcessingException, IOException {
-        query = "wegen #IN <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")
-                .asText());
-        assertEquals("classRefCheck:includes",
-                res.at("/query/operands/0/classRefCheck/0").asText());
-        assertEquals("koral:group", res
-                .at("/query/operands/0/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")
-                .isMissingNode());
-        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/operands/0/classOut").asInt());
-        assertEquals("koral:group", res
-                .at("/query/operands/0/operands/0/@type").asText());
-        assertEquals("operation:class",
-                res.at("/query/operands/0/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")
-                        .asInt());
-        assertEquals(
-                "koral:token",
-                res.at("/query/operands/0/operands/0/operands/1/operands/0/@type")
-                        .asText());
-        assertEquals(
-                "wegen",
-                res.at("/query/operands/0/operands/0/operands/1/operands/0/wrap/key")
-                        .asText());
-
-        query = "wegen #IN(L) <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:position", res
-                .at("/query/operands/0/operation").asText());
-        assertEquals("frames:startsWith", res.at("/query/operands/0/frames/0")
-                .asText());
-        assertEquals("frames:matches", res.at("/query/operands/0/frames/1")
-                .asText());
-        assertEquals(true, res.at("/query/operands/0/frames/2").isMissingNode());
-        assertEquals("koral:group", res.at("/query/operands/0/@type").asText());
-        assertEquals("operation:class",
-                res.at("/query/operands/0/operands/0/operation").asText());
-        assertEquals(129, res.at("/query/operands/0/operands/0/classOut")
-                .asInt());
-        assertEquals("koral:span",
-                res.at("/query/operands/0/operands/0/operands/0/@type")
-                        .asText());
-        assertEquals("s",
-                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:token",
-                res.at("/query/operands/0/operands/1/operands/0/@type")
-                        .asText());
-        assertEquals("wegen",
-                res.at("/query/operands/0/operands/1/operands/0/wrap/key")
-                        .asText());
-
-        query = "wegen #IN(F) <s>";
-        qs.setQuery(query, "cosmas2");
-        res = mapper.readTree(qs.toJSON());
-        assertEquals(true, res.at("/query/operands/0/classRefCheck")
-                .isMissingNode());
-        assertEquals("frames:matches", res.at("/query/operands/0/frames/0")
-                .asText());
-        assertEquals(true, res.at("/query/operands/0/frames/1").isMissingNode());
-
-        query = "wegen #IN(FI) <s>";
-        qs.setQuery(query, "cosmas2");
-        res = mapper.readTree(qs.toJSON());
-        assertEquals("classRefCheck:unequals",
-                res.at("/query/operands/0/classRefCheck/0").asText());
-        assertEquals("frames:matches",
-                res.at("/query/operands/0/operands/0/frames/0").asText());
-        assertEquals(true, res.at("/query/operands/0/operands/0/frames/1")
-                .isMissingNode());
-
-        query = "wegen #IN(FE) <s>";
-        qs.setQuery(query, "cosmas2");
-        res = mapper.readTree(qs.toJSON());
-        assertEquals("classRefCheck:equals",
-                res.at("/query/operands/0/classRefCheck/0").asText());
-        assertEquals("frames:matches",
-                res.at("/query/operands/0/operands/0/frames/0").asText());
-        assertEquals(true, res.at("/query/operands/0/operands/0/frames/1")
-                .isMissingNode());
-
-        query = "wegen #IN(%, L) <s>";
-        qs.setQuery(query, "cosmas2");
-        res = mapper.readTree(qs.toJSON());
-        assertEquals("frames:startsWith", res.at("/query/operands/0/frames/0")
-                .asText());
-        assertEquals("frames:matches", res.at("/query/operands/0/frames/1")
-                .asText());
-        //        assertEquals(true,                          res.at("/query/operands/0/operands/0/exclude").isMissingNode());
-
-        query = "wegen #IN(FE,%,MIN) <s>";
-        qs.setQuery(query, "cosmas2");
-        res = mapper.readTree(qs.toJSON());
-        assertEquals("classRefCheck:equals",
-                res.at("/query/operands/0/classRefCheck/0").asText());
-        assertEquals("frames:matches",
-                res.at("/query/operands/0/operands/0/frames/0").asText());
-        //        assertEquals(true,							res.at("/query/operands/0/operands/0/exclude").isMissingNode());
-
-        query = "wegen #IN(FE,ALL,%,MIN) <s>";
-        qs.setQuery(query, "cosmas2");
-        res = mapper.readTree(qs.toJSON());
-        assertEquals("operation:class", res.at("/query/operation").asText());
-        assertEquals("classRefOp:delete", res.at("/query/classRefOp").asText());
-        assertEquals(131, res.at("/query/classIn/0").asInt());
-        assertEquals("classRefCheck:equals",
-                res.at("/query/operands/0/classRefCheck/0").asText());
-        assertEquals("frames:matches",
-                res.at("/query/operands/0/operands/0/frames/0").asText());
-        assertEquals(true, res.at("/query/operands/0/operands/0/exclude")
-                .asBoolean());
-
-        query = "wegen #IN(FE,ALL,%,MAX) <s>";
-        qs.setQuery(query, "cosmas2");
-        res = mapper.readTree(qs.toJSON());
-        assertEquals("operation:merge", res.at("/query/operation").asText());
-        assertEquals("operation:class", res.at("/query/operands/0/operation")
-                .asText());
-        assertEquals("classRefOp:delete", res
-                .at("/query/operands/0/classRefOp").asText());
-        assertEquals(131, res.at("/query/operands/0/classIn/0").asInt());
-        assertEquals("classRefCheck:equals",
-                res.at("/query/operands/0/operands/0/classRefCheck/0").asText());
-        assertEquals("frames:matches",
-                res.at("/query/operands/0/operands/0/operands/0/frames/0")
-                        .asText());
-        assertEquals(true,
-                res.at("/query/operands/0/operands/0/operands/0/exclude")
-                        .asBoolean());
-    }
-
-
-    @Test
     public void testOPOV () throws JsonProcessingException, IOException {
         query = "wegen #OV <s>";
         qs.setQuery(query, "cosmas2");
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
new file mode 100644
index 0000000..d5f165e
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/test/cosmas2/OPINTest.java
@@ -0,0 +1,180 @@
+package de.ids_mannheim.korap.test.cosmas2;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+
+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.serialize.QuerySerializer;
+
+public class OPINTest {
+    private String query;
+
+    private QuerySerializer qs = new QuerySerializer();
+    private ObjectMapper mapper = new ObjectMapper();
+    private JsonNode res;
+
+
+    @Test
+    public void testOPIN () throws JsonProcessingException, IOException {
+        query = "wegen #IN <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").asText());
+        assertEquals("classRefCheck:includes",
+                res.at("/query/operands/0/classRefCheck/0").asText());
+        assertEquals("koral:group",
+                res.at("/query/operands/0/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")
+                .isMissingNode());
+        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/operands/0/classOut").asInt());
+        assertEquals("koral:group",
+                res.at("/query/operands/0/operands/0/@type").asText());
+        assertEquals("operation:class",
+                res.at("/query/operands/0/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")
+                        .asInt());
+        assertEquals("koral:token",
+                res.at("/query/operands/0/operands/0/operands/1/operands/0/@type")
+                        .asText());
+        assertEquals("wegen",
+                res.at("/query/operands/0/operands/0/operands/1/operands/0/wrap/key")
+                        .asText());
+    }
+
+
+    @Test
+    public void testOPINWithOptionN ()
+            throws JsonProcessingException, IOException {
+        query = "wegen #IN(N) <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:position",
+                res.at("/query/operands/0/operation").asText());
+        assertEquals("frames:isAround",
+                res.at("/query/operands/0/frames/0").asText());
+    }
+    
+    
+    @Test
+    public void testOPINWithOptionL ()
+            throws JsonProcessingException, IOException {
+        query = "wegen #IN(L) <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:position",
+                res.at("/query/operands/0/operation").asText());
+        assertEquals("frames:startsWith",
+                res.at("/query/operands/0/frames/0").asText());
+        assertEquals("frames:matches",
+                res.at("/query/operands/0/frames/1").asText());
+        assertEquals(true,
+                res.at("/query/operands/0/frames/2").isMissingNode());
+        assertEquals("koral:group", res.at("/query/operands/0/@type").asText());
+        assertEquals("operation:class",
+                res.at("/query/operands/0/operands/0/operation").asText());
+        assertEquals(129,
+                res.at("/query/operands/0/operands/0/classOut").asInt());
+        assertEquals("koral:span", res
+                .at("/query/operands/0/operands/0/operands/0/@type").asText());
+        assertEquals("s",
+                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:token", res
+                .at("/query/operands/0/operands/1/operands/0/@type").asText());
+        assertEquals("wegen",
+                res.at("/query/operands/0/operands/1/operands/0/wrap/key")
+                        .asText());
+    }
+
+    @Test
+    public void testOPINwithOptionF ()
+            throws JsonProcessingException, IOException {
+        query = "wegen #IN(F) <s>";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals(true,
+                res.at("/query/operands/0/classRefCheck").isMissingNode());
+        assertEquals("frames:matches",
+                res.at("/query/operands/0/frames/0").asText());
+        assertEquals(true,
+                res.at("/query/operands/0/frames/1").isMissingNode());
+    }
+
+
+    @Test
+    public void testOPINwithOptionFI ()
+            throws JsonProcessingException, IOException {
+        query = "wegen #IN(FI) <s>";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals("classRefCheck:unequals",
+                res.at("/query/operands/0/classRefCheck/0").asText());
+        assertEquals("frames:matches",
+                res.at("/query/operands/0/operands/0/frames/0").asText());
+        assertEquals(true, res.at("/query/operands/0/operands/0/frames/1")
+                .isMissingNode());
+        
+    }
+
+
+    @Test
+    public void testOPINwithOptionFE ()
+            throws JsonProcessingException, IOException {
+        query = "wegen #IN(FE) <s>";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals("classRefCheck:equals",
+                res.at("/query/operands/0/classRefCheck/0").asText());
+        assertEquals("frames:matches",
+                res.at("/query/operands/0/operands/0/frames/0").asText());
+        assertEquals(true, res.at("/query/operands/0/operands/0/frames/1")
+                .isMissingNode());
+    }
+
+
+}
diff --git a/src/test/java/de/ids_mannheim/korap/test/cosmas2/OPINWithExclusionTest.java b/src/test/java/de/ids_mannheim/korap/test/cosmas2/OPINWithExclusionTest.java
new file mode 100644
index 0000000..25eecf1
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/test/cosmas2/OPINWithExclusionTest.java
@@ -0,0 +1,185 @@
+package de.ids_mannheim.korap.test.cosmas2;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+
+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.serialize.QuerySerializer;
+
+public class OPINWithExclusionTest {
+    private String query;
+
+    private QuerySerializer qs = new QuerySerializer();
+    private ObjectMapper mapper = new ObjectMapper();
+    private JsonNode res;
+
+
+    @Test
+    public void testOPINWithExclusionN ()
+            throws JsonProcessingException, IOException {
+        query = "wegen #IN(%,N) <s>";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+
+        assertEquals("koral:group", res.at("/query/@type").asText());
+        assertEquals("operation:exclusion",
+                res.at("/query/operation").asText());
+        assertEquals(1, res.at("/query/frames").size());
+        assertEquals("frames:isWithin", res.at("/query/frames/0").asText());
+        assertEquals("koral:token", res.at("/query/operands/0/@type").asText());
+        assertEquals("koral:span", res.at("/query/operands/1/@type").asText());
+    }
+
+
+    @Test
+    public void testOPINwithExclusionL ()
+            throws JsonProcessingException, IOException {
+        query = "wegen #IN(%, L) <s>";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+
+        assertEquals("koral:group", res.at("/query/@type").asText());
+        assertEquals("operation:exclusion",
+                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("koral:token", res.at("/query/operands/0/@type").asText());
+        assertEquals("koral:span", res.at("/query/operands/1/@type").asText());
+    }
+
+
+    @Test
+    public void testOPINwithExclusionR ()
+            throws JsonProcessingException, IOException {
+        query = "wegen #IN(%, R) <s>";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+
+        assertEquals("operation:exclusion",
+                res.at("/query/operation").asText());
+        assertEquals(2, res.at("/query/frames").size());
+        assertEquals("frames:alignsRight", res.at("/query/frames/0").asText());
+        assertEquals("frames:matches", res.at("/query/frames/1").asText());
+    }
+
+
+    @Test
+    public void testOPINwithExclusionF ()
+            throws JsonProcessingException, IOException {
+        query = "wegen #IN(%, F) <s>";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+
+        assertEquals("operation:exclusion",
+                res.at("/query/operation").asText());
+        assertEquals(1, res.at("/query/frames").size());
+        assertEquals("frames:matches", res.at("/query/frames/0").asText());
+    }
+
+
+    @Test
+    public void testOPINwithExclusionFE ()
+            throws JsonProcessingException, IOException {
+        query = "wegen #IN(%, FE) <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").asText());
+        
+        assertEquals("operation:class", res.at("/query/operands/0/operation").asText());
+        assertEquals("classRefCheck:unequals",
+                res.at("/query/operands/0/classRefCheck/0").asText());
+
+        JsonNode classRefCheckOperand = res.at("/query/operands/0/operands/0");
+        assertEquals("operation:exclusion",
+                classRefCheckOperand.at("/operation").asText());
+        assertEquals(1, classRefCheckOperand.at("/frames").size());
+        assertEquals("frames:matches",
+                classRefCheckOperand.at("/frames/0").asText());
+    }
+
+
+    @Test
+    public void testOPINwithExclusionFI ()
+            throws JsonProcessingException, IOException {
+        query = "wegen #IN(%, FI) <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").asText());
+        
+        assertEquals("operation:class", res.at("/query/operands/0/operation").asText());
+        assertEquals("classRefCheck:equals",
+                res.at("/query/operands/0/classRefCheck/0").asText());
+
+        JsonNode classRefCheckOperand = res.at("/query/operands/0/operands/0");
+        assertEquals("operation:exclusion",
+                classRefCheckOperand.at("/operation").asText());
+        assertEquals(1, classRefCheckOperand.at("/frames").size());
+        assertEquals("frames:matches",
+                classRefCheckOperand.at("/frames/0").asText());
+    }
+
+
+    @Test
+    public void testOPINwithMultipleExclusion1 ()
+            throws JsonProcessingException, IOException {
+        query = "wegen #IN(FE,%,MIN) <s>";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals("classRefCheck:unequals",
+                res.at("/query/operands/0/classRefCheck/0").asText());
+        assertEquals("frames:matches",
+                res.at("/query/operands/0/operands/0/frames/0").asText());
+        //        assertEquals(true,                            res.at("/query/operands/0/operands/0/exclude").isMissingNode());
+    }
+
+
+    @Test
+    public void testOPINwithMultipleExclusion2 ()
+            throws JsonProcessingException, IOException {
+        query = "wegen #IN(FE,ALL,%,MIN) <s>";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals("operation:class", res.at("/query/operation").asText());
+        assertEquals("classRefOp:delete", res.at("/query/classRefOp").asText());
+        assertEquals(131, res.at("/query/classIn/0").asInt());
+        assertEquals("classRefCheck:unequals",
+                res.at("/query/operands/0/classRefCheck/0").asText());
+        assertEquals("frames:matches",
+                res.at("/query/operands/0/operands/0/frames/0").asText());
+    }
+
+
+    @Test
+    public void testOPINwithMultipleExclusion3 ()
+            throws JsonProcessingException, IOException {
+        query = "wegen #IN(FE,ALL,%,MAX) <s>";
+        qs.setQuery(query, "cosmas2");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals("operation:merge", res.at("/query/operation").asText());
+        assertEquals("operation:class",
+                res.at("/query/operands/0/operation").asText());
+        assertEquals("classRefOp:delete",
+                res.at("/query/operands/0/classRefOp").asText());
+        assertEquals(131, res.at("/query/operands/0/classIn/0").asInt());
+        assertEquals("classRefCheck:unequals", res
+                .at("/query/operands/0/operands/0/classRefCheck/0").asText());
+        assertEquals("frames:matches",
+                res.at("/query/operands/0/operands/0/operands/0/frames/0")
+                        .asText());
+    }
+
+
+}