layer mapping for query serializations!
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/CosmasTree.java b/src/main/java/de/ids_mannheim/korap/query/serialize/CosmasTree.java
index 29ae40b..3aa9322 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/CosmasTree.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/CosmasTree.java
@@ -1,14 +1,10 @@
 package de.ids_mannheim.korap.query.serialize;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
+import de.ids_mannheim.korap.query.cosmas2.c2psLexer;
+import de.ids_mannheim.korap.query.cosmas2.c2psParser;
+import de.ids_mannheim.korap.query.serialize.util.CosmasCondition;
+import de.ids_mannheim.korap.query.serialize.util.ResourceMapper;
+import de.ids_mannheim.korap.util.QueryException;
 import org.antlr.runtime.ANTLRStringStream;
 import org.antlr.runtime.RecognitionException;
 import org.antlr.runtime.tree.Tree;
@@ -16,250 +12,251 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import de.ids_mannheim.korap.query.cosmas2.c2psLexer;
-import de.ids_mannheim.korap.query.cosmas2.c2psParser;
-import de.ids_mannheim.korap.query.serialize.util.CosmasCondition;
-import de.ids_mannheim.korap.util.QueryException;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Map representation of CosmasII syntax tree as returned by ANTLR
- * @author joachim
  *
+ * @author joachim
  */
 public class CosmasTree extends Antlr3AbstractSyntaxTree {
-	
-	private static Logger log = LoggerFactory.getLogger(CosmasTree.class);
-	
-	/*
-	 * Following collections have the following functions:
-	 * - the request is a map with two keys (meta/query):			{meta=[], query=[]}
-	 * - the query is a list of token group maps: 					{meta=[], query=[tg1=[], tg2=[]]}
-	 * - each token group is a list of tokens: 						{meta=[], query=[tg1=[t1_1, t1_2], tg2=[t2_1, t2_2, t2_3]]}
-	 * - each token corresponds to a single 'fields' linked list	{meta=[], query=[tg1=[t1_1=[], t1_2=[]], ... ]}
-	 * - each fields list contains a logical operator and 'field maps' defining attributes and values
-	 * 																{meta=[], query=[tg1=[t1_1=[[disj, {base=foo}, {base=bar}]], t1_2=[]], ... ]}
-	 */
-	String query;
-	LinkedHashMap<String,Object> requestMap = new LinkedHashMap<String,Object>();
-	/**
-	 * Keeps track of active object.
-	 */
-	LinkedList<LinkedHashMap<String,Object>> objectStack = new LinkedList<LinkedHashMap<String,Object>>();
-	/**
-	 * Makes it possible to store several distantTokenGroups
-	 */
-	LinkedList<ArrayList<List<Object>>> distantTokensStack = new LinkedList<ArrayList<List<Object>>>();
-	/**
-	 * Field for repetition query (Kleene + or * operations, or min/max queries: {2,4}
-	 */
-	String repetition = "";
-	/**
-	 * Keeps track of open node categories
-	 */
-	LinkedList<String> openNodeCats = new LinkedList<String>();
-	/**
-	 * Global control structure for fieldGroups, keeps track of open fieldGroups.
-	 */
-	LinkedList<ArrayList<Object>> openFieldGroups = new LinkedList<ArrayList<Object>>();
-	/**
-	 * Keeps track of how many objects there are to pop after every recursion of {@link #processNode(ParseTree)}
-	 */
-	LinkedList<Integer> objectsToPop = new LinkedList<Integer>();
-	/**
-	 * Flag that indicates whether token fields or meta fields are currently being processed
-	 */
-	boolean inMeta = false;
-	/**
-	 * 
-	 */
-	int classRefCounter = 1;
-	boolean negate = false;
-	
-	Tree cosmasTree;
-	
-	LinkedHashMap<String,Object> treeMap = new LinkedHashMap<String,Object>();
-	/**
-	 * Keeps track of all visited nodes in a tree
-	 */
-	List<Tree> visited = new ArrayList<Tree>();
 
-	Integer stackedObjects = 0;
-	
-	private static boolean debug = false;
-	/**
-	 * A list of node categories that can be sequenced (i.e. which can be in a sequence with any number of other nodes in this list)
-	 */
-	private final List<String> sequentiableCats = Arrays.asList(new String[] {"OPWF", "OPLEM", "OPMORPH", "OPBEG", "OPEND", "OPIN", "OPBED"});
-	/**
-	 * Keeps track of sequenced nodes, i.e. nodes that implicitly govern  a sequence, as in (C2PQ (OPWF der) (OPWF Mann)).
-	 * This is necessary in order to know when to take the sequence off the object stack, as the sequence is introduced by the
-	 * first child but cannot be closed after this first child in order not to lose its siblings
-	 */
-	private LinkedList<Tree> sequencedNodes = new LinkedList<Tree>();
+    private static Logger log = LoggerFactory.getLogger(CosmasTree.class);
 
-	private boolean hasSequentiableSiblings;
+    /*
+     * Following collections have the following functions:
+     * - the request is a map with two keys (meta/query):			{meta=[], query=[]}
+     * - the query is a list of token group maps: 					{meta=[], query=[tg1=[], tg2=[]]}
+     * - each token group is a list of tokens: 						{meta=[], query=[tg1=[t1_1, t1_2], tg2=[t2_1, t2_2, t2_3]]}
+     * - each token corresponds to a single 'fields' linked list	{meta=[], query=[tg1=[t1_1=[], t1_2=[]], ... ]}
+     * - each fields list contains a logical operator and 'field maps' defining attributes and values
+     * 																{meta=[], query=[tg1=[t1_1=[[disj, {base=foo}, {base=bar}]], t1_2=[]], ... ]}
+     */
+    String query;
+    LinkedHashMap<String, Object> requestMap = new LinkedHashMap<String, Object>();
+    /**
+     * Keeps track of active object.
+     */
+    LinkedList<LinkedHashMap<String, Object>> objectStack = new LinkedList<LinkedHashMap<String, Object>>();
+    /**
+     * Makes it possible to store several distantTokenGroups
+     */
+    LinkedList<ArrayList<List<Object>>> distantTokensStack = new LinkedList<ArrayList<List<Object>>>();
+    /**
+     * Field for repetition query (Kleene + or * operations, or min/max queries: {2,4}
+     */
+    String repetition = "";
+    /**
+     * Keeps track of open node categories
+     */
+    LinkedList<String> openNodeCats = new LinkedList<String>();
+    /**
+     * Global control structure for fieldGroups, keeps track of open fieldGroups.
+     */
+    LinkedList<ArrayList<Object>> openFieldGroups = new LinkedList<ArrayList<Object>>();
+    /**
+     * Keeps track of how many objects there are to pop after every recursion of {@link #processNode(ParseTree)}
+     */
+    LinkedList<Integer> objectsToPop = new LinkedList<Integer>();
+    /**
+     * Flag that indicates whether token fields or meta fields are currently being processed
+     */
+    boolean inMeta = false;
+    /**
+     *
+     */
+    int classRefCounter = 1;
+    boolean negate = false;
 
-	/**
-	 * Keeps track of operands lists that are to be serialised in an inverted
-	 * order (e.g. the IN() operator) compared to their AST representation. 
-	 */
-	private LinkedList<ArrayList<Object>> invertedOperandsLists = new LinkedList<ArrayList<Object>>();
-	
-	private LinkedList<ArrayList<ArrayList<Object>>> distributedOperandsLists = new LinkedList<ArrayList<ArrayList<Object>>>();
-	/**
-	 * 
-	 * @param tree The syntax tree as returned by ANTLR
-	 * @param parser The ANTLR parser instance that generated the parse tree
-	 * @throws QueryException 
-	 */
-	public CosmasTree(String query) throws QueryException {
-		this.query = query;
-		process(query);
-		System.out.println("\n"+requestMap.get("query"));
-		log.info(">>> " + requestMap.get("query") + " <<<");
-	}
-	
-	@Override
-	public Map<String, Object> getRequestMap() {
-		return this.requestMap;
-	}
-	
-	
-	
-	@Override
-	public void process(String query) throws QueryException {
-		Tree tree = null;
-		try {
-			tree = parseCosmasQuery(query);
-		} catch (RecognitionException e) {
-			throw new QueryException("Your query could not be processed. Please make sure it is well-formed.");
-		} catch (NullPointerException e) {
-			throw new QueryException("Your query could not be processed. Please make sure it is well-formed.");
-		}
-		log.info("Processing CosmasII query");
-		System.out.println("Processing Cosmas");
-		requestMap.put("@context", "http://ids-mannheim.de/ns/KorAP/json-ld/v0.1/context.jsonld");
+    Tree cosmasTree;
+
+    LinkedHashMap<String, Object> treeMap = new LinkedHashMap<String, Object>();
+    /**
+     * Keeps track of all visited nodes in a tree
+     */
+    List<Tree> visited = new ArrayList<Tree>();
+
+    Integer stackedObjects = 0;
+
+    private static boolean debug = false;
+    /**
+     * A list of node categories that can be sequenced (i.e. which can be in a sequence with any number of other nodes in this list)
+     */
+    private final List<String> sequentiableCats = Arrays.asList(new String[]{"OPWF", "OPLEM", "OPMORPH", "OPBEG", "OPEND", "OPIN", "OPBED"});
+    /**
+     * Keeps track of sequenced nodes, i.e. nodes that implicitly govern  a sequence, as in (C2PQ (OPWF der) (OPWF Mann)).
+     * This is necessary in order to know when to take the sequence off the object stack, as the sequence is introduced by the
+     * first child but cannot be closed after this first child in order not to lose its siblings
+     */
+    private LinkedList<Tree> sequencedNodes = new LinkedList<Tree>();
+
+    private boolean hasSequentiableSiblings;
+
+    /**
+     * Keeps track of operands lists that are to be serialised in an inverted
+     * order (e.g. the IN() operator) compared to their AST representation.
+     */
+    private LinkedList<ArrayList<Object>> invertedOperandsLists = new LinkedList<ArrayList<Object>>();
+
+    private LinkedList<ArrayList<ArrayList<Object>>> distributedOperandsLists = new LinkedList<ArrayList<ArrayList<Object>>>();
+
+    /**
+     * @param tree   The syntax tree as returned by ANTLR
+     * @param parser The ANTLR parser instance that generated the parse tree
+     * @throws QueryException
+     */
+    public CosmasTree(String query) throws QueryException {
+        this.query = query;
+        process(query);
+        System.out.println("\n" + requestMap.get("query"));
+        log.info(">>> " + requestMap.get("query") + " <<<");
+    }
+
+    @Override
+    public Map<String, Object> getRequestMap() {
+        return this.requestMap;
+    }
+
+
+    @Override
+    public void process(String query) throws QueryException {
+        Tree tree = null;
+        try {
+            tree = parseCosmasQuery(query);
+        } catch (RecognitionException e) {
+            throw new QueryException("Your query could not be processed. Please make sure it is well-formed.");
+        } catch (NullPointerException e) {
+            throw new QueryException("Your query could not be processed. Please make sure it is well-formed.");
+        }
+        log.info("Processing CosmasII query");
+        System.out.println("Processing Cosmas");
+        requestMap.put("@context", "http://ids-mannheim.de/ns/KorAP/json-ld/v0.1/context.jsonld");
 //		prepareContext(requestMap);
-		processNode(tree);
-		log.info(requestMap.toString());
-	}
-	
-	@SuppressWarnings("unchecked")
-	private void processNode(Tree node) {
-		
-		// Top-down processing
-		if (visited.contains(node)) return;
-		else visited.add(node);
-		
-		
-		String nodeCat = getNodeCat(node);
-		openNodeCats.push(nodeCat);
-		
-		stackedObjects = 0;
-		
-		if (debug) {
-			System.err.println(" "+objectStack);
-			System.out.println(openNodeCats);
-		}
-		
+        processNode(tree);
+        log.info(requestMap.toString());
+    }
+
+    @SuppressWarnings("unchecked")
+    private void processNode(Tree node) {
+
+        // Top-down processing
+        if (visited.contains(node)) return;
+        else visited.add(node);
+
+
+        String nodeCat = getNodeCat(node);
+        openNodeCats.push(nodeCat);
+
+        stackedObjects = 0;
+
+        if (debug) {
+            System.err.println(" " + objectStack);
+            System.out.println(openNodeCats);
+        }
+
 		
 		/* ***************************************
-		 * Processing individual node categories *
+         * Processing individual node categories *
 		 *****************************************/
 
-		
-		// Check for potential implicit sequences as in (C2PQ (OPWF der) (OPWF Mann)). The sequence is introduced
-		// by the first child if it (and its siblings) is sequentiable.
-		if (sequentiableCats.contains(nodeCat)) {
-			// for each node, check if parent has more than one child (-> could be implicit sequence)
-			Tree parent = node.getParent();
-			if (parent.getChildCount()>1) {
-				// if node is first child of parent...
-				if (node == parent.getChild(0)) {
-					hasSequentiableSiblings = false;
-					for (int i=1; i<parent.getChildCount() ;i++) {
-						if (sequentiableCats.contains(getNodeCat(parent.getChild(i)))) {
-							hasSequentiableSiblings = true;
-							continue;
-						}
-					}
-					if (hasSequentiableSiblings) {
-						// Step I: create sequence
-						LinkedHashMap<String, Object> sequence = new LinkedHashMap<String, Object>();
-						sequence.put("@type", "korap:group");
-						sequence.put("operation", "operation:sequence");
-						sequence.put("operands", new ArrayList<Object>());
-						// push sequence on object stack but don't increment stackedObjects counter since
-						// we've got to wait until the parent node is processed - therefore, add the parent
-						// to the sequencedNodes list and remove the sequence from the stack when the parent
-						// has been processed
-						objectStack.push(sequence);
-						sequencedNodes.push(parent);
-						// Step II: decide where to put sequence
-						putIntoSuperObject(sequence, 1);
-					}
-				}
-			}
-		}
-		
-		// Nodes introducing tokens. Process all in the same manner, except for the fieldMap entry
-		if (nodeCat.equals("OPWF") || nodeCat.equals("OPLEM")) {
-			
-			//Step I: get info
-			LinkedHashMap<String, Object> token = new LinkedHashMap<String, Object>();
-			token.put("@type", "korap:token");
-			objectStack.push(token);
-			stackedObjects++;
-			LinkedHashMap<String, Object> fieldMap = new LinkedHashMap<String, Object>();
-			token.put("wrap", fieldMap);
-			
-			fieldMap.put("@type", "korap:term");			
-			// make category-specific fieldMap entry
-			String attr = nodeCat.equals("OPWF") ? "orth" : "lemma";
-			String value = node.getChild(0).toStringTree().replaceAll("\"", "");
-			if (value.startsWith("$")) {
-				value = value.substring(1);
-				fieldMap.put("caseInsensitive", true);
-			}
-			fieldMap.put("key", value);
-			fieldMap.put("layer", attr);
-			
-			// negate field (see above)
-			if (negate) {
-				fieldMap.put("match", "match:ne");
-			} else {
-				fieldMap.put("match", "match:eq");
-			}
-			//Step II: decide where to put
-			if (! hasChild(node, "TPOS")) {
-				putIntoSuperObject(token, 1);
-			} else {
-				
-			}
-			
-		}
-		
-		if (nodeCat.equals("OPMORPH")) {
-			//Step I: get info
-			LinkedHashMap<String, Object> token = new LinkedHashMap<String, Object>();
-			token.put("@type", "korap:token");
-			LinkedHashMap<String, Object> fieldMap = new LinkedHashMap<String, Object>();
-			token.put("wrap", fieldMap);
-			
-			fieldMap.put("@type", "korap:term");
+
+        // Check for potential implicit sequences as in (C2PQ (OPWF der) (OPWF Mann)). The sequence is introduced
+        // by the first child if it (and its siblings) is sequentiable.
+        if (sequentiableCats.contains(nodeCat)) {
+            // for each node, check if parent has more than one child (-> could be implicit sequence)
+            Tree parent = node.getParent();
+            if (parent.getChildCount() > 1) {
+                // if node is first child of parent...
+                if (node == parent.getChild(0)) {
+                    hasSequentiableSiblings = false;
+                    for (int i = 1; i < parent.getChildCount(); i++) {
+                        if (sequentiableCats.contains(getNodeCat(parent.getChild(i)))) {
+                            hasSequentiableSiblings = true;
+                            continue;
+                        }
+                    }
+                    if (hasSequentiableSiblings) {
+                        // Step I: create sequence
+                        LinkedHashMap<String, Object> sequence = new LinkedHashMap<String, Object>();
+                        sequence.put("@type", "korap:group");
+                        sequence.put("operation", "operation:sequence");
+                        sequence.put("operands", new ArrayList<Object>());
+                        // push sequence on object stack but don't increment stackedObjects counter since
+                        // we've got to wait until the parent node is processed - therefore, add the parent
+                        // to the sequencedNodes list and remove the sequence from the stack when the parent
+                        // has been processed
+                        objectStack.push(sequence);
+                        sequencedNodes.push(parent);
+                        // Step II: decide where to put sequence
+                        putIntoSuperObject(sequence, 1);
+                    }
+                }
+            }
+        }
+
+        // Nodes introducing tokens. Process all in the same manner, except for the fieldMap entry
+        if (nodeCat.equals("OPWF") || nodeCat.equals("OPLEM")) {
+
+            //Step I: get info
+            LinkedHashMap<String, Object> token = new LinkedHashMap<String, Object>();
+            token.put("@type", "korap:token");
+            objectStack.push(token);
+            stackedObjects++;
+            LinkedHashMap<String, Object> fieldMap = new LinkedHashMap<String, Object>();
+            token.put("wrap", fieldMap);
+
+            fieldMap.put("@type", "korap:term");
+            // make category-specific fieldMap entry
+            String attr = nodeCat.equals("OPWF") ? "orth" : "lemma";
+            String value = node.getChild(0).toStringTree().replaceAll("\"", "");
+            if (value.startsWith("$")) {
+                value = value.substring(1);
+                fieldMap.put("caseInsensitive", true);
+            }
+
+            attr = ResourceMapper.descriptor2policy(attr);
+            fieldMap.put("key", value);
+            fieldMap.put("layer", attr);
+
+            // negate field (see above)
+            if (negate) {
+                fieldMap.put("match", "match:ne");
+            } else {
+                fieldMap.put("match", "match:eq");
+            }
+            //Step II: decide where to put
+            if (!hasChild(node, "TPOS")) {
+                putIntoSuperObject(token, 1);
+            } else {
+
+            }
+
+        }
+
+        if (nodeCat.equals("OPMORPH")) {
+            //Step I: get info
+            LinkedHashMap<String, Object> token = new LinkedHashMap<String, Object>();
+            token.put("@type", "korap:token");
+            LinkedHashMap<String, Object> fieldMap = new LinkedHashMap<String, Object>();
+            token.put("wrap", fieldMap);
+
+            fieldMap.put("@type", "korap:term");
 //			fieldMap.put("key", "morph:"+node.getChild(0).toString().replace(" ", "_"));
-			String[] morphValues = node.getChild(0).toString().split(" ");
-			String pos = morphValues[0];
-			
-			fieldMap.put("key", pos);
-			fieldMap.put("layer", "pos");
-			// make category-specific fieldMap entry
-			// negate field (see above)
-			if (negate) {
-				fieldMap.put("match", "match:ne");
-			} else {
-				fieldMap.put("match", "match:eq");
-			}
+            String[] morphValues = node.getChild(0).toString().split(" ");
+            String pos = morphValues[0];
+
+            fieldMap.put("key", pos);
+
+            fieldMap.put("layer", ResourceMapper.descriptor2policy("pos"));
+            // make category-specific fieldMap entry
+            // negate field (see above)
+            if (negate) {
+                fieldMap.put("match", "match:ne");
+            } else {
+                fieldMap.put("match", "match:eq");
+            }
 //			List<String> morphValues = parseMorph(node.getChild(0).toStringTree());
 //			System.err.println(morphValues);
 //			if (morphValues.size() == 1) {
@@ -297,27 +294,27 @@
 //					}
 //				}
 //			}
-			
-			
-			//Step II: decide where to put
-			putIntoSuperObject(token, 0);
-		}
-		
-		if (nodeCat.equals("OPELEM")) {
-			// Step I: create element
-			LinkedHashMap<String, Object> elem = new LinkedHashMap<String, Object>();
-			elem.put("@type", "korap:span");
-			if (node.getChild(0).toStringTree().equals("EMPTY")) {
-				
-			} else {
-				int elname = 0;
-				Tree elnameNode = getFirstChildWithCat(node, "ELNAME");
-				if (elnameNode != null) {
-					elem.put("key", elnameNode.getChild(0).toStringTree().toLowerCase());
-					elname = 1;
-				}
-				if (node.getChildCount() > elname) {
-					/*
+
+
+            //Step II: decide where to put
+            putIntoSuperObject(token, 0);
+        }
+
+        if (nodeCat.equals("OPELEM")) {
+            // Step I: create element
+            LinkedHashMap<String, Object> elem = new LinkedHashMap<String, Object>();
+            elem.put("@type", "korap:span");
+            if (node.getChild(0).toStringTree().equals("EMPTY")) {
+
+            } else {
+                int elname = 0;
+                Tree elnameNode = getFirstChildWithCat(node, "ELNAME");
+                if (elnameNode != null) {
+                    elem.put("key", elnameNode.getChild(0).toStringTree().toLowerCase());
+                    elname = 1;
+                }
+                if (node.getChildCount() > elname) {
+                    /*
 					 * Attributes can carry several values, like #ELEM(W ANA != 'N V'), 
 					 * denoting a word whose POS is neither N nor V.
 					 * When seeing this, create a sub-termGroup and put it into the top-level
@@ -326,368 +323,370 @@
 					 * top-level group (in order to avoid a top-level group that only
 					 * contains a sub-group).
 					 */
-					LinkedHashMap<String, Object> termGroup = makeTermGroup("and");
-					ArrayList<Object> termGroupOperands = (ArrayList<Object>) termGroup.get("operands"); 
-					for (int i=elname; i<node.getChildCount(); i++) {
-						Tree attrNode = node.getChild(i);
-						if (attrNode.getChildCount()==2) {
-							LinkedHashMap<String, Object> term = makeTerm();
-							termGroupOperands.add(term);
-							String layer = attrNode.getChild(0).toStringTree();
-							String[] splitted = layer.split("/");
-							if (splitted.length > 1) {
-								term.put("foundry", splitted[0]);
-								layer = splitted[1];
-							}
-							term.put("layer", translateMorph(layer));
-							term.put("key", attrNode.getChild(1).toStringTree());
-							String match = getNodeCat(attrNode).equals("EQ") ? "eq" : "ne";
-							term.put("match", "match:"+match);
-						} else {
-							LinkedHashMap<String, Object> subTermGroup = makeTermGroup("and");
-							ArrayList<Object> subTermGroupOperands = (ArrayList<Object>) subTermGroup.get("operands"); 
-							int j;
-							for (j=1; j<attrNode.getChildCount(); j++) {
-								LinkedHashMap<String, Object> term = makeTerm();
-								String layer = attrNode.getChild(0).toStringTree();
-								String[] splitted = layer.split("/");
-								if (splitted.length > 1) {
-									term.put("foundry", splitted[0]);
-									layer = splitted[1];
-								}
-								term.put("layer", translateMorph(layer));
-								term.put("key", attrNode.getChild(j).toStringTree());
-								String match = getNodeCat(attrNode).equals("EQ") ? "eq" : "ne";
-								term.put("match", "match:"+match);
-								if (node.getChildCount()==elname+1) {
-									termGroupOperands.add(term);
-									System.err.println("taga");
-								} else {
-									subTermGroupOperands.add(term);
-									System.err.println(layer);
-								}
-							}
-							if (node.getChildCount()>elname+1) {
-								System.err.println(termGroupOperands);
-								termGroupOperands.add(subTermGroup);
-								System.err.println(termGroupOperands);
-							}
-						}
-						if (getNodeCat(attrNode).equals("NOTEQ")) negate=true;
-					}
-					elem.put("attr", termGroup);
-				}
-			}
-			
-			//Step II: decide where to put
-			putIntoSuperObject(elem);
-		}		
-		
-		if (nodeCat.equals("OPLABEL")) {
-			// Step I: create element
-			LinkedHashMap<String, Object> elem = new LinkedHashMap<String, Object>();
-			elem.put("@type", "korap:span");
-			elem.put("key", node.getChild(0).toStringTree().replaceAll("<|>", ""));
-			//Step II: decide where to put
-			putIntoSuperObject(elem);
-		}
-		
-		if (nodeCat.equals("OPAND") || nodeCat.equals("OPNOT")) {
-			// Step I: create group
-			LinkedHashMap<String, Object> distgroup = new LinkedHashMap<String, Object>();
-			distgroup.put("@type", "korap:group");
-			distgroup.put("operation", "operation:sequence");
-			ArrayList<Object> distances = new ArrayList<Object>(); 
-			LinkedHashMap<String, Object> zerodistance = new LinkedHashMap<String, Object>();
-			zerodistance.put("@type", "korap:distance");
-			zerodistance.put("key", "t");
-			zerodistance.put("min", 0);
-			zerodistance.put("max", 0);
-			if (nodeCat.equals("OPNOT")) zerodistance.put("exclude", true);
-			distances.add(zerodistance);
-			distgroup.put("distances", distances);
-			distgroup.put("operands", new ArrayList<Object>());
-			objectStack.push(distgroup);
-			stackedObjects++;
-			// Step II: decide where to put
-			putIntoSuperObject(distgroup, 1);
-		}
-		
-		if (nodeCat.equals("OPOR")) {
-			// Step I: create group
-			LinkedHashMap<String, Object> disjunction = new LinkedHashMap<String, Object>();
-			disjunction.put("@type", "korap:group");
-			disjunction.put("operation", "operation:or");
-			disjunction.put("operands", new ArrayList<Object>());
-			objectStack.push(disjunction);
-			stackedObjects++;
-			// Step II: decide where to put
-			putIntoSuperObject(disjunction, 1);
-		}
-		
-		if (nodeCat.equals("OPPROX")) {
-			//TODO direction "both": wrap in "or" group with operands once flipped, once not
-			// collect info
-			Tree prox_opts = node.getChild(0);
-			Tree typ = prox_opts.getChild(0);
-			Tree dist_list = prox_opts.getChild(1);
-			// Step I: create group
-			LinkedHashMap<String, Object> proxSequence = new LinkedHashMap<String, Object>();
-			proxSequence.put("@type", "korap:group");
-			proxSequence.put("operation", "operation:"+ "sequence");
-			objectStack.push(proxSequence);
-			stackedObjects++;
-			ArrayList<Object> constraints = new ArrayList<Object>();
-			boolean exclusion = ! typ.getChild(0).toStringTree().equals("PROX"); 
-			
-			boolean inOrder = false;
-			proxSequence.put("inOrder", inOrder);
-			proxSequence.put("distances", constraints);
-			
-			ArrayList<Object> operands = new ArrayList<Object>(); 
-			proxSequence.put("operands", operands);
-		
-			// possibly several distance constraints
-			for (int i=0; i<dist_list.getChildCount(); i++) {
-				String direction = dist_list.getChild(i).getChild(0).getChild(0).toStringTree().toLowerCase();
-				String min = dist_list.getChild(i).getChild(1).getChild(0).toStringTree();
-				String max = dist_list.getChild(i).getChild(1).getChild(1).toStringTree();
-				String meas = dist_list.getChild(i).getChild(2).getChild(0).toStringTree();
-				if (min.equals("VAL0")) {
-					min="0";
-				}
-				LinkedHashMap<String, Object> distance = new LinkedHashMap<String, Object>();
-				distance.put("@type", "korap:distance");
-				distance.put("key", meas);
-				distance.put("min", Integer.parseInt(min));
-				distance.put("max", Integer.parseInt(max));
-				if (exclusion) {
-					distance.put("exclude", exclusion);
-				}
-				constraints.add(distance);
-				if (direction.equals("plus")) {
-					inOrder=true;
-				} else if (direction.equals("minus")) {
-					inOrder=true;
-					invertedOperandsLists.add(operands);
-				}
-			}
-			proxSequence.put("inOrder", inOrder);
-			// Step II: decide where to put
-			putIntoSuperObject(proxSequence, 1);
-		}
-		
-		// inlcusion or overlap
-		if (nodeCat.equals("OPIN") || nodeCat.equals("OPOV")) {
-			// Step I: create group
-			LinkedHashMap<String, Object> submatchgroup = new LinkedHashMap<String, Object>();
-			submatchgroup.put("@type", "korap:group");
-			submatchgroup.put("operation", "operation:"+ "submatch");
-			ArrayList<Integer> classRef = new ArrayList<Integer>();
-			classRef.add(classRefCounter);
-			submatchgroup.put("classRef", classRef);
-			
-			ArrayList<Object> submatchoperands = new ArrayList<Object>(); 
-			LinkedHashMap<String, Object> posgroup = new LinkedHashMap<String, Object>();
-			submatchgroup.put("operands", submatchoperands);
-			submatchoperands.add(posgroup);
-			posgroup.put("@type", "korap:group");
-//			String relation = nodeCat.equals("OPIN") ? "position" : "overlaps";
-			posgroup.put("operation", "operation:"+ "position");
-			if (nodeCat.equals("OPIN")) {
-				parseOPINOptions(node, posgroup);
-			} else {
-				parseOPOVOptions(node, posgroup);
-			}
-			ArrayList<Object> posoperands = new ArrayList<Object>();
-			posgroup.put("operands", posoperands);
-			objectStack.push(posgroup);
-			// mark this an inverted list
-			invertedOperandsLists.push(posoperands);
-			stackedObjects++;
-			// Step II: decide where to put
-			putIntoSuperObject(submatchgroup, 1);
-		}
-		
-		
-		// Wrap the first argument of an #IN operator in a class group
-		if (nodeCat.equals("ARG1") && (openNodeCats.get(1).equals("OPIN") || openNodeCats.get(1).equals("OPOV") || openNodeCats.get(2).equals("OPNHIT"))) {
-			// Step I: create group
-			LinkedHashMap<String, Object> classGroup = new LinkedHashMap<String, Object>();
-			classGroup.put("@type", "korap:group");
-			classGroup.put("operation", "operation:"+ "class");
-			classGroup.put("class", classRefCounter);
-			classRefCounter++;
-			classGroup.put("operands", new ArrayList<Object>());
-			objectStack.push(classGroup);
-			stackedObjects++;
-			// Step II: decide where to put
-			putIntoSuperObject(classGroup, 1);
-		}
-		
-		// Wrap the 2nd argument of an #IN operator embedded in NHIT in a class group
-		if (nodeCat.equals("ARG2") && openNodeCats.get(2).equals("OPNHIT")) {
-			// Step I: create group
-			LinkedHashMap<String, Object> classGroup = new LinkedHashMap<String, Object>();
-			classGroup.put("@type", "korap:group");
-			classGroup.put("operation", "operation:"+ "class");
-			classGroup.put("class", classRefCounter);
-			classRefCounter++;
-			classGroup.put("operands", new ArrayList<Object>());
-			objectStack.push(classGroup);
-			stackedObjects++;
-			// Step II: decide where to put
-			putIntoSuperObject(classGroup, 1);
-		}
-		
-		
-		if (nodeCat.equals("OPNHIT")) {
-			LinkedHashMap<String, Object> exclGroup = new LinkedHashMap<String, Object>();
-			exclGroup.put("@type", "korap:group");
-			exclGroup.put("operation", "operation:"+ "submatch");
-			ArrayList<Integer> classRef = new ArrayList<Integer>();
-			
-			classRef.add(classRefCounter);
-//			classRefCounter++;
-			 // yes, do this twice!
-			classRef.add(classRefCounter+1);
-//			classRefCounter++;
-			exclGroup.put("classRef", classRef);
-			exclGroup.put("classRefOp", "classRefOp:"+"intersection");
-			ArrayList<Object> operands = new ArrayList<Object>();
-			exclGroup.put("operands", operands);
-			objectStack.push(exclGroup);
-			stackedObjects++;
-			putIntoSuperObject(exclGroup, 1);
-		}
-		
-		if (nodeCat.equals("OPEND") || nodeCat.equals("OPBEG")) {
-			// Step I: create group
-			LinkedHashMap<String, Object> beggroup = new LinkedHashMap<String, Object>();
-			beggroup.put("@type", "korap:group");
-			beggroup.put("operation", "operation:"+ "submatch");
-			ArrayList<Integer> spanRef = new ArrayList<Integer>();
-			if (nodeCat.equals("OPBEG")) {
-				spanRef.add(0); spanRef.add(1);
-			} else {
-				spanRef.add(-1); spanRef.add(1);
-			}
-			beggroup.put("spanRef", spanRef);
-			beggroup.put("operands", new ArrayList<Object>());
-			objectStack.push(beggroup);
-			stackedObjects++;
-			
-			// Step II: decide where to put
-			putIntoSuperObject(beggroup, 1);
-		}
-		
-		if (nodeCat.equals("OPBED")) {
-			// Step I: create group
-			int optsChild = node.getChildCount()-1;
-			Tree conditions = node.getChild(optsChild).getChild(0);
-			
-			// create a containing group expressing the submatch constraint on the first argument
-			LinkedHashMap<String, Object> submatchgroup = new LinkedHashMap<String, Object>();
-			submatchgroup.put("@type", "korap:group");
-			submatchgroup.put("operation", "operation:"+ "submatch");
-			ArrayList<Integer> spanRef = new ArrayList<Integer>();
-			spanRef.add(1);
-			submatchgroup.put("classRef", spanRef);
-			ArrayList<Object> submatchoperands = new ArrayList<Object>();
-			submatchgroup.put("operands", submatchoperands);
-			putIntoSuperObject(submatchgroup, 0);
-			
-			// Distinguish two cases. Normal case: query has just one condition, like #BED(X, sa) ...
-			if (conditions.getChildCount()==1) {
-				CosmasCondition c = new CosmasCondition(conditions.getChild(0));
-				
-				// create the group expressing the position constraint
-				LinkedHashMap<String, Object> posgroup = new LinkedHashMap<String, Object>();
-				posgroup.put("@type", "korap:group");
-				posgroup.put("operation", "operation:"+ "position");
-				
-				posgroup.put("frame", "frame:"+c.position);
-				if (c.negated) posgroup.put("exclude", true);
-				ArrayList<Object> operands = new ArrayList<Object>();
-				posgroup.put("operands", operands);
+                    LinkedHashMap<String, Object> termGroup = makeTermGroup("and");
+                    ArrayList<Object> termGroupOperands = (ArrayList<Object>) termGroup.get("operands");
+                    for (int i = elname; i < node.getChildCount(); i++) {
+                        Tree attrNode = node.getChild(i);
+                        if (attrNode.getChildCount() == 2) {
+                            LinkedHashMap<String, Object> term = makeTerm();
+                            termGroupOperands.add(term);
+                            String layer = attrNode.getChild(0).toStringTree();
+                            String[] splitted = layer.split("/");
+                            if (splitted.length > 1) {
+                                term.put("foundry", splitted[0]);
+                                layer = splitted[1];
+                            }
+                            term.put("layer", translateMorph(layer));
+                            term.put("key", attrNode.getChild(1).toStringTree());
+                            String match = getNodeCat(attrNode).equals("EQ") ? "eq" : "ne";
+                            term.put("match", "match:" + match);
+                        } else {
+                            LinkedHashMap<String, Object> subTermGroup = makeTermGroup("and");
+                            ArrayList<Object> subTermGroupOperands = (ArrayList<Object>) subTermGroup.get("operands");
+                            int j;
+                            for (j = 1; j < attrNode.getChildCount(); j++) {
+                                LinkedHashMap<String, Object> term = makeTerm();
+                                String layer = attrNode.getChild(0).toStringTree();
+                                String[] splitted = layer.split("/");
+                                if (splitted.length > 1) {
+                                    term.put("foundry", splitted[0]);
+                                    layer = splitted[1];
+                                }
+                                term.put("layer", translateMorph(layer));
+                                term.put("key", attrNode.getChild(j).toStringTree());
+                                String match = getNodeCat(attrNode).equals("EQ") ? "eq" : "ne";
+                                term.put("match", "match:" + match);
+                                if (node.getChildCount() == elname + 1) {
+                                    termGroupOperands.add(term);
+                                    System.err.println("taga");
+                                } else {
+                                    subTermGroupOperands.add(term);
+                                    System.err.println(layer);
+                                }
+                            }
+                            if (node.getChildCount() > elname + 1) {
+                                System.err.println(termGroupOperands);
+                                termGroupOperands.add(subTermGroup);
+                                System.err.println(termGroupOperands);
+                            }
+                        }
+                        if (getNodeCat(attrNode).equals("NOTEQ")) negate = true;
+                    }
+                    elem.put("attr", termGroup);
+                }
+            }
 
-				// create span representing the element expressed in the condition
-				LinkedHashMap<String, Object> bedElem = new LinkedHashMap<String, Object>();
-				bedElem.put("@type", "korap:span");
-				bedElem.put("key", c.elem);
-				
-				// create a class group containing the argument, in order to submatch the arg.
-				LinkedHashMap<String, Object> classGroup = new LinkedHashMap<String, Object>();
-				classGroup.put("@type", "korap:group");
-				classGroup.put("operation", "operation:class");
-				classGroup.put("class", classRefCounter);
-				classRefCounter++;
-				classGroup.put("operands", new ArrayList<Object>());
-				objectStack.push(classGroup);
-				stackedObjects++;
-				operands.add(bedElem);
-				operands.add(classGroup);
-				// Step II: decide where to put
-				submatchoperands.add(posgroup);
-				
-			// ... or the query has several conditions specified, like #BED(XY, sa,-pa). In that case,
-			//     create an 'and' group and embed the position groups in its operands
-			} else {
-				// node has several conditions (like 'sa, -pa')
-				// -> create zero-distance sequence group and embed all position groups there
-				LinkedHashMap<String, Object> conjunct = new LinkedHashMap<String, Object>();
-				conjunct.put("@type", "korap:group");
-				conjunct.put("operation", "operation:"+ "sequence");
-				ArrayList<Object> distances = new ArrayList<Object>();
-				conjunct.put("distances", distances);
-				LinkedHashMap<String, Object> zerodistance = new LinkedHashMap<String, Object>();
-				zerodistance.put("@type", "korap:distance");
-				zerodistance.put("key", "w");
-				zerodistance.put("min", 0);
-				zerodistance.put("max", 0);
-				distances.add(zerodistance);
-				ArrayList<Object> operands = new ArrayList<Object>();
-				conjunct.put("operands", operands);
-				ArrayList<ArrayList<Object>> distributedOperands = new ArrayList<ArrayList<Object>>();
-				
-				for (int i=0; i<conditions.getChildCount(); i++) {
-					// for each condition, create a position group containing a class group. problem: how to get argument into every operands list?
-					// -> use distributedOperandsLists
-					LinkedHashMap<String, Object> posGroup = new LinkedHashMap<String, Object>();
-					operands.add(posGroup);
-					
-					// make position group
-					CosmasCondition c = new CosmasCondition(conditions.getChild(i));
-					posGroup.put("@type", "korap:group");
-					posGroup.put("operation", "operation:"+ "position");
-					posGroup.put("frame", "frame:"+c.position);
-					if (c.negated) posGroup.put("exclude", "true");
-					ArrayList<Object> posOperands = new ArrayList<Object>();
-					
-					// make class group 
-					LinkedHashMap<String, Object> classGroup = new LinkedHashMap<String, Object>();
-					classGroup.put("@type", "korap:group");
-					classGroup.put("operation", "operation:class");
-					classGroup.put("class", classRefCounter);
-					classRefCounter++;
-					ArrayList<Object> classOperands = new ArrayList<Object>(); 
-					classGroup.put("operands", classOperands);
-					distributedOperands.add(classOperands);  // subtree to be put into every class group -> distribute
-					
-					// put the span and the class group into the position group
-					posGroup.put("operands", posOperands);
-					LinkedHashMap<String, Object> span = new LinkedHashMap<String, Object>();
-					posOperands.add(span);
-					posOperands.add(classGroup);
-					span.put("@type", "korap:span");
-					span.put("key", c.elem);
-				}
-				submatchoperands.add(conjunct);
-				distributedOperandsLists.push(distributedOperands);
-			}
-			
-		}
-		objectsToPop.push(stackedObjects);
+            //Step II: decide where to put
+            putIntoSuperObject(elem);
+        }
+
+        if (nodeCat.equals("OPLABEL")) {
+            // Step I: create element
+            LinkedHashMap<String, Object> elem = new LinkedHashMap<String, Object>();
+            elem.put("@type", "korap:span");
+            elem.put("key", node.getChild(0).toStringTree().replaceAll("<|>", ""));
+            //Step II: decide where to put
+            putIntoSuperObject(elem);
+        }
+
+        if (nodeCat.equals("OPAND") || nodeCat.equals("OPNOT")) {
+            // Step I: create group
+            LinkedHashMap<String, Object> distgroup = new LinkedHashMap<String, Object>();
+            distgroup.put("@type", "korap:group");
+            distgroup.put("operation", "operation:sequence");
+            ArrayList<Object> distances = new ArrayList<Object>();
+            LinkedHashMap<String, Object> zerodistance = new LinkedHashMap<String, Object>();
+            zerodistance.put("@type", "korap:distance");
+            zerodistance.put("key", "t");
+            zerodistance.put("min", 0);
+            zerodistance.put("max", 0);
+            if (nodeCat.equals("OPNOT")) zerodistance.put("exclude", true);
+            distances.add(zerodistance);
+            distgroup.put("distances", distances);
+            distgroup.put("operands", new ArrayList<Object>());
+            objectStack.push(distgroup);
+            stackedObjects++;
+            // Step II: decide where to put
+            putIntoSuperObject(distgroup, 1);
+        }
+
+        if (nodeCat.equals("OPOR")) {
+            // Step I: create group
+            LinkedHashMap<String, Object> disjunction = new LinkedHashMap<String, Object>();
+            disjunction.put("@type", "korap:group");
+            disjunction.put("operation", "operation:or");
+            disjunction.put("operands", new ArrayList<Object>());
+            objectStack.push(disjunction);
+            stackedObjects++;
+            // Step II: decide where to put
+            putIntoSuperObject(disjunction, 1);
+        }
+
+        if (nodeCat.equals("OPPROX")) {
+            //TODO direction "both": wrap in "or" group with operands once flipped, once not
+            // collect info
+            Tree prox_opts = node.getChild(0);
+            Tree typ = prox_opts.getChild(0);
+            Tree dist_list = prox_opts.getChild(1);
+            // Step I: create group
+            LinkedHashMap<String, Object> proxSequence = new LinkedHashMap<String, Object>();
+            proxSequence.put("@type", "korap:group");
+            proxSequence.put("operation", "operation:" + "sequence");
+            objectStack.push(proxSequence);
+            stackedObjects++;
+            ArrayList<Object> constraints = new ArrayList<Object>();
+            boolean exclusion = !typ.getChild(0).toStringTree().equals("PROX");
+
+            boolean inOrder = false;
+            proxSequence.put("inOrder", inOrder);
+            proxSequence.put("distances", constraints);
+
+            ArrayList<Object> operands = new ArrayList<Object>();
+            proxSequence.put("operands", operands);
+
+            // possibly several distance constraints
+            for (int i = 0; i < dist_list.getChildCount(); i++) {
+                String direction = dist_list.getChild(i).getChild(0).getChild(0).toStringTree().toLowerCase();
+                String min = dist_list.getChild(i).getChild(1).getChild(0).toStringTree();
+                String max = dist_list.getChild(i).getChild(1).getChild(1).toStringTree();
+                String meas = dist_list.getChild(i).getChild(2).getChild(0).toStringTree();
+                if (min.equals("VAL0")) {
+                    min = "0";
+                }
+                LinkedHashMap<String, Object> distance = new LinkedHashMap<String, Object>();
+                distance.put("@type", "korap:distance");
+                distance.put("key", meas);
+                distance.put("min", Integer.parseInt(min));
+                distance.put("max", Integer.parseInt(max));
+                if (exclusion) {
+                    distance.put("exclude", exclusion);
+                }
+                constraints.add(distance);
+                if (direction.equals("plus")) {
+                    inOrder = true;
+                } else if (direction.equals("minus")) {
+                    inOrder = true;
+                    invertedOperandsLists.add(operands);
+                }
+            }
+            proxSequence.put("inOrder", inOrder);
+            // Step II: decide where to put
+            putIntoSuperObject(proxSequence, 1);
+        }
+
+        // inlcusion or overlap
+        if (nodeCat.equals("OPIN") || nodeCat.equals("OPOV")) {
+            // Step I: create group
+            LinkedHashMap<String, Object> submatchgroup = new LinkedHashMap<String, Object>();
+            submatchgroup.put("@type", "korap:group");
+            submatchgroup.put("operation", "operation:" + "submatch");
+            ArrayList<Integer> classRef = new ArrayList<Integer>();
+            classRef.add(classRefCounter);
+            submatchgroup.put("classRef", classRef);
+
+            ArrayList<Object> submatchoperands = new ArrayList<Object>();
+            LinkedHashMap<String, Object> posgroup = new LinkedHashMap<String, Object>();
+            submatchgroup.put("operands", submatchoperands);
+            submatchoperands.add(posgroup);
+            posgroup.put("@type", "korap:group");
+//			String relation = nodeCat.equals("OPIN") ? "position" : "overlaps";
+            posgroup.put("operation", "operation:" + "position");
+            if (nodeCat.equals("OPIN")) {
+                parseOPINOptions(node, posgroup);
+            } else {
+                parseOPOVOptions(node, posgroup);
+            }
+            ArrayList<Object> posoperands = new ArrayList<Object>();
+            posgroup.put("operands", posoperands);
+            objectStack.push(posgroup);
+            // mark this an inverted list
+            invertedOperandsLists.push(posoperands);
+            stackedObjects++;
+            // Step II: decide where to put
+            putIntoSuperObject(submatchgroup, 1);
+        }
+
+
+        // Wrap the first argument of an #IN operator in a class group
+        if (nodeCat.equals("ARG1") && (openNodeCats.get(1).equals("OPIN") || openNodeCats.get(1).equals("OPOV") || openNodeCats.get(2).equals("OPNHIT"))) {
+            // Step I: create group
+            LinkedHashMap<String, Object> classGroup = new LinkedHashMap<String, Object>();
+            classGroup.put("@type", "korap:group");
+            classGroup.put("operation", "operation:" + "class");
+            classGroup.put("class", classRefCounter);
+            classRefCounter++;
+            classGroup.put("operands", new ArrayList<Object>());
+            objectStack.push(classGroup);
+            stackedObjects++;
+            // Step II: decide where to put
+            putIntoSuperObject(classGroup, 1);
+        }
+
+        // Wrap the 2nd argument of an #IN operator embedded in NHIT in a class group
+        if (nodeCat.equals("ARG2") && openNodeCats.get(2).equals("OPNHIT")) {
+            // Step I: create group
+            LinkedHashMap<String, Object> classGroup = new LinkedHashMap<String, Object>();
+            classGroup.put("@type", "korap:group");
+            classGroup.put("operation", "operation:" + "class");
+            classGroup.put("class", classRefCounter);
+            classRefCounter++;
+            classGroup.put("operands", new ArrayList<Object>());
+            objectStack.push(classGroup);
+            stackedObjects++;
+            // Step II: decide where to put
+            putIntoSuperObject(classGroup, 1);
+        }
+
+
+        if (nodeCat.equals("OPNHIT")) {
+            LinkedHashMap<String, Object> exclGroup = new LinkedHashMap<String, Object>();
+            exclGroup.put("@type", "korap:group");
+            exclGroup.put("operation", "operation:" + "submatch");
+            ArrayList<Integer> classRef = new ArrayList<Integer>();
+
+            classRef.add(classRefCounter);
+//			classRefCounter++;
+            // yes, do this twice!
+            classRef.add(classRefCounter + 1);
+//			classRefCounter++;
+            exclGroup.put("classRef", classRef);
+            exclGroup.put("classRefOp", "classRefOp:" + "intersection");
+            ArrayList<Object> operands = new ArrayList<Object>();
+            exclGroup.put("operands", operands);
+            objectStack.push(exclGroup);
+            stackedObjects++;
+            putIntoSuperObject(exclGroup, 1);
+        }
+
+        if (nodeCat.equals("OPEND") || nodeCat.equals("OPBEG")) {
+            // Step I: create group
+            LinkedHashMap<String, Object> beggroup = new LinkedHashMap<String, Object>();
+            beggroup.put("@type", "korap:group");
+            beggroup.put("operation", "operation:" + "submatch");
+            ArrayList<Integer> spanRef = new ArrayList<Integer>();
+            if (nodeCat.equals("OPBEG")) {
+                spanRef.add(0);
+                spanRef.add(1);
+            } else {
+                spanRef.add(-1);
+                spanRef.add(1);
+            }
+            beggroup.put("spanRef", spanRef);
+            beggroup.put("operands", new ArrayList<Object>());
+            objectStack.push(beggroup);
+            stackedObjects++;
+
+            // Step II: decide where to put
+            putIntoSuperObject(beggroup, 1);
+        }
+
+        if (nodeCat.equals("OPBED")) {
+            // Step I: create group
+            int optsChild = node.getChildCount() - 1;
+            Tree conditions = node.getChild(optsChild).getChild(0);
+
+            // create a containing group expressing the submatch constraint on the first argument
+            LinkedHashMap<String, Object> submatchgroup = new LinkedHashMap<String, Object>();
+            submatchgroup.put("@type", "korap:group");
+            submatchgroup.put("operation", "operation:" + "submatch");
+            ArrayList<Integer> spanRef = new ArrayList<Integer>();
+            spanRef.add(1);
+            submatchgroup.put("classRef", spanRef);
+            ArrayList<Object> submatchoperands = new ArrayList<Object>();
+            submatchgroup.put("operands", submatchoperands);
+            putIntoSuperObject(submatchgroup, 0);
+
+            // Distinguish two cases. Normal case: query has just one condition, like #BED(X, sa) ...
+            if (conditions.getChildCount() == 1) {
+                CosmasCondition c = new CosmasCondition(conditions.getChild(0));
+
+                // create the group expressing the position constraint
+                LinkedHashMap<String, Object> posgroup = new LinkedHashMap<String, Object>();
+                posgroup.put("@type", "korap:group");
+                posgroup.put("operation", "operation:" + "position");
+
+                posgroup.put("frame", "frame:" + c.position);
+                if (c.negated) posgroup.put("exclude", true);
+                ArrayList<Object> operands = new ArrayList<Object>();
+                posgroup.put("operands", operands);
+
+                // create span representing the element expressed in the condition
+                LinkedHashMap<String, Object> bedElem = new LinkedHashMap<String, Object>();
+                bedElem.put("@type", "korap:span");
+                bedElem.put("key", c.elem);
+
+                // create a class group containing the argument, in order to submatch the arg.
+                LinkedHashMap<String, Object> classGroup = new LinkedHashMap<String, Object>();
+                classGroup.put("@type", "korap:group");
+                classGroup.put("operation", "operation:class");
+                classGroup.put("class", classRefCounter);
+                classRefCounter++;
+                classGroup.put("operands", new ArrayList<Object>());
+                objectStack.push(classGroup);
+                stackedObjects++;
+                operands.add(bedElem);
+                operands.add(classGroup);
+                // Step II: decide where to put
+                submatchoperands.add(posgroup);
+
+                // ... or the query has several conditions specified, like #BED(XY, sa,-pa). In that case,
+                //     create an 'and' group and embed the position groups in its operands
+            } else {
+                // node has several conditions (like 'sa, -pa')
+                // -> create zero-distance sequence group and embed all position groups there
+                LinkedHashMap<String, Object> conjunct = new LinkedHashMap<String, Object>();
+                conjunct.put("@type", "korap:group");
+                conjunct.put("operation", "operation:" + "sequence");
+                ArrayList<Object> distances = new ArrayList<Object>();
+                conjunct.put("distances", distances);
+                LinkedHashMap<String, Object> zerodistance = new LinkedHashMap<String, Object>();
+                zerodistance.put("@type", "korap:distance");
+                zerodistance.put("key", "w");
+                zerodistance.put("min", 0);
+                zerodistance.put("max", 0);
+                distances.add(zerodistance);
+                ArrayList<Object> operands = new ArrayList<Object>();
+                conjunct.put("operands", operands);
+                ArrayList<ArrayList<Object>> distributedOperands = new ArrayList<ArrayList<Object>>();
+
+                for (int i = 0; i < conditions.getChildCount(); i++) {
+                    // for each condition, create a position group containing a class group. problem: how to get argument into every operands list?
+                    // -> use distributedOperandsLists
+                    LinkedHashMap<String, Object> posGroup = new LinkedHashMap<String, Object>();
+                    operands.add(posGroup);
+
+                    // make position group
+                    CosmasCondition c = new CosmasCondition(conditions.getChild(i));
+                    posGroup.put("@type", "korap:group");
+                    posGroup.put("operation", "operation:" + "position");
+                    posGroup.put("frame", "frame:" + c.position);
+                    if (c.negated) posGroup.put("exclude", "true");
+                    ArrayList<Object> posOperands = new ArrayList<Object>();
+
+                    // make class group
+                    LinkedHashMap<String, Object> classGroup = new LinkedHashMap<String, Object>();
+                    classGroup.put("@type", "korap:group");
+                    classGroup.put("operation", "operation:class");
+                    classGroup.put("class", classRefCounter);
+                    classRefCounter++;
+                    ArrayList<Object> classOperands = new ArrayList<Object>();
+                    classGroup.put("operands", classOperands);
+                    distributedOperands.add(classOperands);  // subtree to be put into every class group -> distribute
+
+                    // put the span and the class group into the position group
+                    posGroup.put("operands", posOperands);
+                    LinkedHashMap<String, Object> span = new LinkedHashMap<String, Object>();
+                    posOperands.add(span);
+                    posOperands.add(classGroup);
+                    span.put("@type", "korap:span");
+                    span.put("key", c.elem);
+                }
+                submatchoperands.add(conjunct);
+                distributedOperandsLists.push(distributedOperands);
+            }
+
+        }
+        objectsToPop.push(stackedObjects);
 		
 		/*
 		 ****************************************************************
@@ -696,223 +695,225 @@
 		 ****************************************************************
 		 ****************************************************************
 		 */
-		for (int i=0; i<node.getChildCount(); i++) {
-			Tree child = node.getChild(i);
-			processNode(child);
-		}
+        for (int i = 0; i < node.getChildCount(); i++) {
+            Tree child = node.getChild(i);
+            processNode(child);
+        }
 		
 		/*
 		 **************************************************************
 		 * Stuff that happens after processing the children of a node *
 		 **************************************************************
 		 */
-		
-		// remove sequence from object stack if node is implicitly sequenced
-		if (sequencedNodes.size()>0) {
-			if (node == sequencedNodes.getFirst()) {
-				objectStack.pop();
-				sequencedNodes.pop();
-			}
-		}
-		
-		for (int i=0; i<objectsToPop.get(0); i++) {
-			objectStack.pop();
-		}
-		objectsToPop.pop();
-		
-		if (nodeCat.equals("ARG2") && openNodeCats.get(1).equals("OPNOT")) {
-			negate = false;
-		}
 
-		openNodeCats.pop();
-		
-	}
+        // remove sequence from object stack if node is implicitly sequenced
+        if (sequencedNodes.size() > 0) {
+            if (node == sequencedNodes.getFirst()) {
+                objectStack.pop();
+                sequencedNodes.pop();
+            }
+        }
 
-	
-	
+        for (int i = 0; i < objectsToPop.get(0); i++) {
+            objectStack.pop();
+        }
+        objectsToPop.pop();
 
-	private Object translateMorph(String layer) {
-		LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
-		map.put("ANA", "pos");
-		if (map.containsKey(layer))
-			return map.get(layer);
-		else
-			return layer;
-	}
+        if (nodeCat.equals("ARG2") && openNodeCats.get(1).equals("OPNOT")) {
+            negate = false;
+        }
+        openNodeCats.pop();
+    }
 
-	private void parseOPINOptions(Tree node, LinkedHashMap<String, Object> posgroup) {
-		Tree posnode = getFirstChildWithCat(node, "POS");
-		Tree rangenode = getFirstChildWithCat(node, "RANGE");
-		Tree exclnode = getFirstChildWithCat(node, "EXCL");
-		Tree groupnode = getFirstChildWithCat(node, "GROUP");
-		boolean negatePosition = false;
-		
-		String position = "";
-		if (posnode != null) {
-			String value = posnode.getChild(0).toStringTree();
-			position = translateTextAreaArgument(value, "in");
-			if (value.equals("N")) {
-				negatePosition = !negatePosition;
-			}
-		} else {
-			position = "contains";
-		}
-		posgroup.put("frame", "frame:"+position);
-		position = openNodeCats.get(1).equals("OPIN") ? "contains" : "full";
-		
-		if (rangenode != null) {
-			String range = rangenode.getChild(0).toStringTree();
-			posgroup.put("range", range.toLowerCase());
-		}
-		
-		if (exclnode != null) {
-			if (exclnode.getChild(0).toStringTree().equals("YES")) {
-				negatePosition = !negatePosition;
-			}
-		}
-		System.err.println(negatePosition);
-		if (negatePosition) {
-			posgroup.put("exclude", "true");	
+    private Object translateMorph(String layer) {
+        // todo: not very nicely solved! Does this require extension somehow? if not, why not use simple string comparison?!
+//        LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
+//        map.put("ANA", "pos");
+//        if (map.containsKey(layer))
+//            return map.get(layer);
+//        else
+//            return layer;
+        if (layer.equals("ANA"))
+            return ResourceMapper.descriptor2policy(layer);
+        else
+            return layer;
+
+    }
+
+    private void parseOPINOptions(Tree node, LinkedHashMap<String, Object> posgroup) {
+        Tree posnode = getFirstChildWithCat(node, "POS");
+        Tree rangenode = getFirstChildWithCat(node, "RANGE");
+        Tree exclnode = getFirstChildWithCat(node, "EXCL");
+        Tree groupnode = getFirstChildWithCat(node, "GROUP");
+        boolean negatePosition = false;
+
+        String position = "";
+        if (posnode != null) {
+            String value = posnode.getChild(0).toStringTree();
+            position = translateTextAreaArgument(value, "in");
+            if (value.equals("N")) {
+                negatePosition = !negatePosition;
+            }
+        } else {
+            position = "contains";
+        }
+        posgroup.put("frame", "frame:" + position);
+        position = openNodeCats.get(1).equals("OPIN") ? "contains" : "full";
+
+        if (rangenode != null) {
+            String range = rangenode.getChild(0).toStringTree();
+            posgroup.put("range", range.toLowerCase());
+        }
+
+        if (exclnode != null) {
+            if (exclnode.getChild(0).toStringTree().equals("YES")) {
+                negatePosition = !negatePosition;
+            }
+        }
+        System.err.println(negatePosition);
+        if (negatePosition) {
+            posgroup.put("exclude", "true");
 //			negate = !negate;
-		}
-		
-		if (groupnode != null) {
-			String grouping = groupnode.getChild(0).toStringTree().equals("max") ? "true" : "false";
-			posgroup.put("grouping", grouping);
-		}
-	}
-	
-	private void parseOPOVOptions(Tree node, LinkedHashMap<String, Object> posgroup) {
-		Tree posnode = getFirstChildWithCat(node, "POS");
-		Tree exclnode = getFirstChildWithCat(node, "EXCL");
-		Tree groupnode = getFirstChildWithCat(node, "GROUP");
-		
-		String position = "";
-		if (posnode != null) {
-			String value = posnode.getChild(0).toStringTree();
-			position = "-"+translateTextAreaArgument(value, "ov");
-		}
-		posgroup.put("frame", "frame:"+"overlaps"+position);
-		
-		if (exclnode != null) {
-			if (exclnode.getChild(0).toStringTree().equals("YES")) {
-				posgroup.put("match", "match:"+"ne");
-			}
-		}
-		if (groupnode != null) {
-			String grouping = groupnode.getChild(0).toStringTree().equals("@max") ? "true" : "false";
-			posgroup.put("grouping", grouping);
-		}
-		
-	}
+        }
 
-	/**
-	 * Translates the text area specifications (position option arguments) to terms used in serialisation.
-	 * For the allowed argument types and their values for OPIN and OPOV, see
-	 * http://www.ids-mannheim.de/cosmas2/win-app/hilfe/suchanfrage/eingabe-grafisch/syntax/ARGUMENT_I.html or
-	 * http://www.ids-mannheim.de/cosmas2/win-app/hilfe/suchanfrage/eingabe-grafisch/syntax/ARGUMENT_O.html, respectively.
-	 * @param argument
-	 * @param mode 
-	 * @return
-	 */
-	private String translateTextAreaArgument(String argument, String mode) {
-		String position = "";
-		switch (argument) {
-		case "L":
-			position = mode.equals("in") ? "startswith" : "left";
-			break;
-		case "R":
-			position = mode.equals("in") ? "endswith" : "right";
-			break;
-		case "F":
-			position = "leftrightmatch";
-			break;
-		case "FE":
-			position = "matches";
-			break;
-		case "FI":
-			position = "leftrightmatch-noident";
-			break;
-		case "N": // for OPIN only - exclusion constraint formulated in parseOPINOptions 
-			position = "leftrightmatch";
-			break;
-		case "X": // for OPOV only
-			position = "residual";
-			break;
-	}
-		return position;
-	}
-	
-	@SuppressWarnings("unchecked")
-	private void putIntoSuperObject(LinkedHashMap<String, Object> object, int objStackPosition) {
-		if (distributedOperandsLists.size()>0) {
-			ArrayList<ArrayList<Object>> distributedOperands = distributedOperandsLists.pop();
-			for (ArrayList<Object> operands : distributedOperands) {
-				operands.add(object);
-			}
-		} else if (objectStack.size()>objStackPosition) {
-			ArrayList<Object> topObjectOperands = (ArrayList<Object>) objectStack.get(objStackPosition).get("operands");
-			if (!invertedOperandsLists.contains(topObjectOperands)) {
-				topObjectOperands.add(object);
-			} else {
-				topObjectOperands.add(0, object);
-			}
-			
-		} else {
-			requestMap.put("query", object);
-		}
-	}
-	
-	private void putIntoSuperObject(LinkedHashMap<String, Object> object) {
-		putIntoSuperObject(object, 0);
-	}
-	
+        if (groupnode != null) {
+            String grouping = groupnode.getChild(0).toStringTree().equals("max") ? "true" : "false";
+            posgroup.put("grouping", grouping);
+        }
+    }
 
-	private Tree parseCosmasQuery(String q) throws RecognitionException {
-		  Pattern p = Pattern.compile("(\\w+):((\\+|-)?(sa|se|pa|pe|ta|te),?)+");
-		  Matcher m = p.matcher(q);
-		  
-		  String rewrittenQuery = q;
-		  while (m.find()) {
-			  String match = m.group();
-			  String conditionsString = match.split(":")[1];
-			  Pattern conditionPattern = Pattern.compile("(\\+|-)?(sa|se|pa|pe|ta|te)");
-			  Matcher conditionMatcher = conditionPattern.matcher(conditionsString);
-			  String replacement = "#BED("+m.group(1)+" , ";
-			  while (conditionMatcher.find()) {
-				  replacement = replacement+conditionMatcher.group()+",";
-			  }
-			  replacement = replacement.substring(0, replacement.length()-1)+")"; //remove trailing comma and close parenthesis
-			  System.out.println(replacement);
-			  rewrittenQuery = rewrittenQuery.replace(match, replacement);
-		  }
-		  q = rewrittenQuery;
-		  Tree tree = null;
-		  ANTLRStringStream	ss = new ANTLRStringStream(q);
-		  c2psLexer	lex = new c2psLexer(ss);
-		  org.antlr.runtime.CommonTokenStream tokens = new org.antlr.runtime.CommonTokenStream(lex);  //v3
-		  parser = new c2psParser(tokens);
-		  c2psParser.c2ps_query_return c2Return = ((c2psParser) parser).c2ps_query();  // statt t().
-		  // AST Tree anzeigen:
-		  tree = (Tree)c2Return.getTree();
-		  
-		  String treestring = tree.toStringTree();
-		  if (treestring.contains("<mismatched token") || treestring.contains("<error") || treestring.contains("<unexpected")) {
-			  log.error("Invalid tree. Could not parse Cosmas query. Make sure it is well-formed.");
-			  throw new RecognitionException();
-		  } 
-		  
-		  return tree;
-	}
-	
-	/**
-	 * @param args
-	 */
-	public static void main(String[] args) {
+    private void parseOPOVOptions(Tree node, LinkedHashMap<String, Object> posgroup) {
+        Tree posnode = getFirstChildWithCat(node, "POS");
+        Tree exclnode = getFirstChildWithCat(node, "EXCL");
+        Tree groupnode = getFirstChildWithCat(node, "GROUP");
+
+        String position = "";
+        if (posnode != null) {
+            String value = posnode.getChild(0).toStringTree();
+            position = "-" + translateTextAreaArgument(value, "ov");
+        }
+        posgroup.put("frame", "frame:" + "overlaps" + position);
+
+        if (exclnode != null) {
+            if (exclnode.getChild(0).toStringTree().equals("YES")) {
+                posgroup.put("match", "match:" + "ne");
+            }
+        }
+        if (groupnode != null) {
+            String grouping = groupnode.getChild(0).toStringTree().equals("@max") ? "true" : "false";
+            posgroup.put("grouping", grouping);
+        }
+
+    }
+
+    /**
+     * Translates the text area specifications (position option arguments) to terms used in serialisation.
+     * For the allowed argument types and their values for OPIN and OPOV, see
+     * http://www.ids-mannheim.de/cosmas2/win-app/hilfe/suchanfrage/eingabe-grafisch/syntax/ARGUMENT_I.html or
+     * http://www.ids-mannheim.de/cosmas2/win-app/hilfe/suchanfrage/eingabe-grafisch/syntax/ARGUMENT_O.html, respectively.
+     *
+     * @param argument
+     * @param mode
+     * @return
+     */
+    private String translateTextAreaArgument(String argument, String mode) {
+        String position = "";
+        switch (argument) {
+            case "L":
+                position = mode.equals("in") ? "startswith" : "left";
+                break;
+            case "R":
+                position = mode.equals("in") ? "endswith" : "right";
+                break;
+            case "F":
+                position = "leftrightmatch";
+                break;
+            case "FE":
+                position = "matches";
+                break;
+            case "FI":
+                position = "leftrightmatch-noident";
+                break;
+            case "N": // for OPIN only - exclusion constraint formulated in parseOPINOptions
+                position = "leftrightmatch";
+                break;
+            case "X": // for OPOV only
+                position = "residual";
+                break;
+        }
+        return position;
+    }
+
+    @SuppressWarnings("unchecked")
+    private void putIntoSuperObject(LinkedHashMap<String, Object> object, int objStackPosition) {
+        if (distributedOperandsLists.size() > 0) {
+            ArrayList<ArrayList<Object>> distributedOperands = distributedOperandsLists.pop();
+            for (ArrayList<Object> operands : distributedOperands) {
+                operands.add(object);
+            }
+        } else if (objectStack.size() > objStackPosition) {
+            ArrayList<Object> topObjectOperands = (ArrayList<Object>) objectStack.get(objStackPosition).get("operands");
+            if (!invertedOperandsLists.contains(topObjectOperands)) {
+                topObjectOperands.add(object);
+            } else {
+                topObjectOperands.add(0, object);
+            }
+
+        } else {
+            requestMap.put("query", object);
+        }
+    }
+
+    private void putIntoSuperObject(LinkedHashMap<String, Object> object) {
+        putIntoSuperObject(object, 0);
+    }
+
+
+    private Tree parseCosmasQuery(String q) throws RecognitionException {
+        Pattern p = Pattern.compile("(\\w+):((\\+|-)?(sa|se|pa|pe|ta|te),?)+");
+        Matcher m = p.matcher(q);
+
+        String rewrittenQuery = q;
+        while (m.find()) {
+            String match = m.group();
+            String conditionsString = match.split(":")[1];
+            Pattern conditionPattern = Pattern.compile("(\\+|-)?(sa|se|pa|pe|ta|te)");
+            Matcher conditionMatcher = conditionPattern.matcher(conditionsString);
+            String replacement = "#BED(" + m.group(1) + " , ";
+            while (conditionMatcher.find()) {
+                replacement = replacement + conditionMatcher.group() + ",";
+            }
+            replacement = replacement.substring(0, replacement.length() - 1) + ")"; //remove trailing comma and close parenthesis
+            System.out.println(replacement);
+            rewrittenQuery = rewrittenQuery.replace(match, replacement);
+        }
+        q = rewrittenQuery;
+        Tree tree = null;
+        ANTLRStringStream ss = new ANTLRStringStream(q);
+        c2psLexer lex = new c2psLexer(ss);
+        org.antlr.runtime.CommonTokenStream tokens = new org.antlr.runtime.CommonTokenStream(lex);  //v3
+        parser = new c2psParser(tokens);
+        c2psParser.c2ps_query_return c2Return = ((c2psParser) parser).c2ps_query();  // statt t().
+        // AST Tree anzeigen:
+        tree = (Tree) c2Return.getTree();
+
+        String treestring = tree.toStringTree();
+        if (treestring.contains("<mismatched token") || treestring.contains("<error") || treestring.contains("<unexpected")) {
+            log.error("Invalid tree. Could not parse Cosmas query. Make sure it is well-formed.");
+            throw new RecognitionException();
+        }
+
+        return tree;
+    }
+
+    /**
+     * @param args
+     */
+    public static void main(String[] args) {
 		/*
 		 * For debugging
 		 */
-		String[] queries = new String[] {
+        String[] queries = new String[]{
 				/* COSMAS 2 */
 //				"MORPH(V)",
 //				"MORPH(V PRES)",
@@ -924,32 +925,32 @@
 //				"#NHIT(gehen /w1:10 voran)",
 //				"#BED(der Mann , sa,-pa)",
 //				"Mann /t0 Frau",
-				"sagt der:sa Bundeskanzler",
+                "sagt der:sa Bundeskanzler",
 //				"Der:sa,-pe,+te ",
-				"#ELEM(W POS!='N V' title=tada)",
-				"#ELEM(W ANA != 'N V')",
-				"#ELEM(W ANA != 'N V' Genre = Sport)",
-				"(&Baum #IN #ELEM(xip/c=np)) #IN(L) #ELEM(s)"
+                "#ELEM(W POS!='N V' title=tada)",
+                "#ELEM(W ANA != 'N V')",
+                "#ELEM(W ANA != 'N V' Genre = Sport)",
+                "(&Baum #IN #ELEM(xip/c=np)) #IN(L) #ELEM(s)"
 //				"(&Baum #IN #ELEM(NP)) #IN(L) #ELEM(S)"
-				};
+        };
 //		CosmasTree.debug=true;
-		for (String q : queries) {
-			try {
-				System.out.println(q);
-				try {
-					CosmasTree act = new CosmasTree(q);
-					System.out.println(act.parseCosmasQuery(q).toStringTree());
-				} catch (QueryException e) {
-					e.printStackTrace();
-				} catch (RecognitionException e) {
-					e.printStackTrace();
-				}
-				System.out.println();
-				
-			} catch (NullPointerException npe) {
-				npe.printStackTrace();
-				System.out.println("null\n");
-			}
-		}
-	}
+        for (String q : queries) {
+            try {
+                System.out.println(q);
+                try {
+                    CosmasTree act = new CosmasTree(q);
+                    System.out.println(act.parseCosmasQuery(q).toStringTree());
+                } catch (QueryException e) {
+                    e.printStackTrace();
+                } catch (RecognitionException e) {
+                    e.printStackTrace();
+                }
+                System.out.println();
+
+            } catch (NullPointerException npe) {
+                npe.printStackTrace();
+                System.out.println("null\n");
+            }
+        }
+    }
 }
\ No newline at end of file