- merge meta queries from collection query and PQ 'meta'
- code cleanup
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 2bd7273..07afcd1 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
@@ -24,6 +24,7 @@
 import de.ids_mannheim.korap.query.parse.annis.AqlParser;
 import de.ids_mannheim.korap.query.serialize.util.Antlr4DescriptiveErrorListener;
 import de.ids_mannheim.korap.query.serialize.util.KoralObjectGenerator;
+import de.ids_mannheim.korap.query.serialize.util.StatusCodes;
 
 /**
  * Map representation of ANNIS QL syntax tree as returned by ANTLR
@@ -87,11 +88,6 @@
      */
     private LinkedHashMap<String, Integer> nodeReferencesProcessed = new LinkedHashMap<String, Integer>();
     /**
-     * Keeps track of the operand references in the previously processed relation. Needed for the decision whether to postpone
-     * the processing of a relation (in case it does not share any operands with the previous relation).
-     */
-    private ArrayList<String> previousRelationOperandRefs = new ArrayList<String>();
-    /**
      * Keeps track of queued relations. Relations sometimes cannot be processed directly, namely in case it does not share 
      * any operands with the previous relation. Then wait until a relation with a shared operand has been processed.
      */
@@ -116,9 +112,15 @@
             processNode(tree);
             // Last check to see if all relations have left the queue
             if (!queuedRelations.isEmpty()) {
-                ParseTree queued = queuedRelations.getFirst();
+                ParseTree queued = queuedRelations.pop();
                 if (verbose) System.out.println("Taking off queue (last rel): "+ queued.getText());
-                processNode(queuedRelations.pop());   
+                if (checkOperandsProcessedPreviously(queued)) {
+                    processNode(queued);
+                } else {
+                    addError(StatusCodes.UNBOUND_ANNIS_RELATION, "The relation "+queued.getText()
+                            +" is not bound to any other relations.");
+                    requestMap.put("query", new LinkedHashMap<String, Object>());
+                }
             }
         }
     }
@@ -309,9 +311,9 @@
 
         if (object != null) {
             ParseTree grandparent = node.getParent().getParent();
+            // query: object only, no relation
             if (getNodeCat(grandparent).equals("andTopExpr") && 
                     grandparent.getChildCount()==1) { 
-                // query: object only, no relation
                 putIntoSuperObject(object);
             }
             ParseTree parentsFirstChild = node.getParent().getChild(0);
@@ -393,8 +395,10 @@
             LinkedHashMap<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 it is,
-            // queue this relation for later processing.
+            // relation (if this is not the 1st relation). If none of the
+            // operands has been ingested at a lower level (and is therefore
+            // unavailable for refrencing), queue this relation for later 
+            // processing.
             if (relationCounter != 1) {
                 if (! checkOperandsProcessedPreviously(node)) {
                     queuedRelations.add(node);
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 63ba0e5..d3a9392 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
@@ -786,13 +786,14 @@
         LinkedHashMap<String, Object> token = KoralObjectGenerator.makeToken();
         ArrayList<Object> terms = new ArrayList<Object>();
         LinkedHashMap<String, Object> fieldMap = null;
+        // regex group #2 is foundry, #4 layer, #5 operator,
+        // #6 key, #8 value
+        String wordOrRegex = "\\w+|\".*?\"";
+        Pattern p = Pattern
+                .compile("((\\w+)/)?((\\w*)(!?=))?("+wordOrRegex+")(:("+wordOrRegex+"))?");
+        Matcher m;
         for (String morphterm : morphterms) {
-            // regex group #2 is foundry, #4 layer, #5 operator,
-            // #6 key, #8 value
-            String wordOrRegex = "\\w+|\".*?\"";
-            Pattern p = Pattern
-                    .compile("((\\w+)/)?((\\w*)(!?=))?("+wordOrRegex+")(:("+wordOrRegex+"))?");
-            Matcher m = p.matcher(morphterm);
+            m = p.matcher(morphterm);
             if (!m.matches()) {
                 addError(StatusCodes.UNKNOWN_QUERY_ERROR,
                         "Something went wrong parsing the argument in MORPH().");
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/PoliqarpPlusQueryProcessor.java b/src/main/java/de/ids_mannheim/korap/query/serialize/PoliqarpPlusQueryProcessor.java
index 8ff5474..b325a3f 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/PoliqarpPlusQueryProcessor.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/PoliqarpPlusQueryProcessor.java
@@ -158,6 +158,10 @@
         if (nodeCat.equals("meta")) {
             processMeta(node);
         }
+        
+//        if (nodeCat.equals("term") || nodeCat.equals("termGroup")) {
+//            if (inMeta ) putIntoSuperObject(parseTermOrTermGroup(node, false));
+//        }
 
         if (nodeCat.equals("within")
                 && !getNodeCat(node.getParent()).equals("position")) {
@@ -550,11 +554,14 @@
         addWarning("You used the 'meta' keyword in a PoliqarpPlus query. This"
                 + " feature is currently not supported. Please use virtual "
                 + "collections to restrict documents by metadata.");
+        CollectionQueryProcessor cq = new CollectionQueryProcessor(node.getChild(1).getText());
+        requestMap.put("collection", cq.getRequestMap().get("collection"));
         for (ParseTree child : getChildren(node)) {
             visited.add(child);
         }
     }
 
+    @SuppressWarnings("unchecked")
     private void processWithin(ParseTree node) {
         ParseTree domainNode = node.getChild(1);
         String domain = getNodeCat(domainNode);
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/QuerySerializer.java b/src/main/java/de/ids_mannheim/korap/query/serialize/QuerySerializer.java
index c076b79..5f3bf1e 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/QuerySerializer.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/QuerySerializer.java
@@ -3,6 +3,7 @@
 import com.fasterxml.jackson.core.JsonGenerationException;
 import com.fasterxml.jackson.databind.JsonMappingException;
 
+import de.ids_mannheim.korap.query.serialize.util.KoralObjectGenerator;
 import de.ids_mannheim.korap.query.serialize.util.StatusCodes;
 import de.ids_mannheim.korap.utils.JsonUtils;
 import de.ids_mannheim.korap.utils.KorAPLogger;
@@ -11,7 +12,9 @@
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -20,10 +23,6 @@
  */
 public class QuerySerializer {
 
-    public enum QueryLanguage {
-        POLIQARPPLUS, ANNIS, COSMAS2, CQL, CQP
-    }
-
     static HashMap<String, Class<? extends AbstractQueryProcessor>> qlProcessorAssignment;
 
     static {
@@ -39,11 +38,11 @@
     public static String queryLanguageVersion;
 
     private AbstractQueryProcessor ast;
-    private Object collection;
-    private Map meta;
-    private List errors;
-    private List warnings;
-    private List messages;
+    private Map<String, Object> collection;
+    private Map<String, Object> meta;
+    private List<Object> errors;
+    private List<Object> warnings;
+    private List<Object> messages;
     private org.slf4j.Logger log = LoggerFactory
             .getLogger(QuerySerializer.class);
 
@@ -59,7 +58,10 @@
         int i = 0;
         String[] queries;
         if (args.length == 0) {
-            queries = new String[] {};
+            queries = new String[] {
+                    "tok=\"corpus\" & tok=\"query\" & tok=\"language\"& #2 . #3 & #1 . #2 "
+                    
+            };
         }
         else
             queries = new String[] { args[0] };
@@ -70,8 +72,7 @@
                 System.out.println(q);
                 String ql = "cosmas2";
                 jg.setCollection("pubDate=2014");
-                jg.run(q, ql, System.getProperty("user.home") + "/" + ql + "_"
-                        + i + ".jsonld");
+                jg.run(q, ql);
                 System.out.println();
             }
             catch (NullPointerException npe) {
@@ -106,7 +107,7 @@
      * @throws IOException
      * @throws QueryException
      */
-    public void run(String query, String queryLanguage, String outFile)
+    public void run(String query, String queryLanguage)
             throws IOException {
         if (queryLanguage.equalsIgnoreCase("poliqarp")) {
             ast = new PoliqarpPlusQueryProcessor(query);
@@ -186,11 +187,12 @@
         if (ast != null) {
             Map<String, Object> requestMap = ast.getRequestMap();
             Map meta = (Map) requestMap.get("meta");
+            Map collection = (Map) requestMap.get("collection");
             List errors = (List) requestMap.get("errors");
             List warnings = (List) requestMap.get("warnings");
             List messages = (List) requestMap.get("messages");
-            if (collection != null)
-                requestMap.put("collection", collection);
+            this.collection = mergeCollection(collection, this.collection);
+            requestMap.put("collection", this.collection);
             if (this.meta != null) {
                 meta.putAll(this.meta);
                 requestMap.put("meta", meta);
@@ -213,6 +215,20 @@
         return new HashMap<>();
     }
 
+    private Map<String, Object> mergeCollection(Map<String, Object> collection1, Map<String, Object> collection2) {
+        LinkedHashMap<String, Object> docGroup = KoralObjectGenerator.makeDocGroup("and");
+        ArrayList<Object> operands = (ArrayList<Object>) docGroup.get("operands");
+        if (collection1 == null || collection1.isEmpty()) {
+            return collection2;
+        } else if (collection2 == null || collection2.isEmpty()) {
+            return collection1;
+        } else {
+            operands.add(collection1);
+            operands.add(collection2);
+            return docGroup;   
+        }
+    }
+    
     public QuerySerializer addMeta(String cli, String cri, int cls, int crs,
             int num, int pageIndex) {
         MetaQueryBuilder meta = new MetaQueryBuilder();
@@ -241,15 +257,15 @@
     public QuerySerializer setCollectionSimple(String collection) {
         CollectionQueryBuilder qobj = new CollectionQueryBuilder();
         qobj.addResource(collection);
-        this.collection = qobj.raw();
+        this.collection = (Map<String, Object>) qobj.raw();
         return this;
     }
 
     public QuerySerializer setCollection(String collection) {
         CollectionQueryProcessor tree = new CollectionQueryProcessor();
-        Map collectionRequest = tree.getRequestMap();
+        Map<String, Object> collectionRequest = tree.getRequestMap();
         tree.process(collection);
-        this.collection = collectionRequest.get("collection");
+        this.collection = (Map<String, Object>) collectionRequest.get("collection");
         this.errors = (List) collectionRequest.get("errors");
         this.warnings = (List) collectionRequest.get("warnings");
         this.messages = (List) collectionRequest.get("messages");
@@ -257,13 +273,13 @@
     }
 
     public QuerySerializer setCollection(CollectionQueryBuilder2 collections) {
-        this.collection = collections.raw();
+        this.collection = (Map<String, Object>) collections.raw();
         return this;
     }
 
     public QuerySerializer setDeprCollection
             (CollectionQueryBuilder collections) {
-        this.collection = collections.raw();
+        this.collection = (Map<String, Object>) collections.raw();
         return this;
     }
 }