support for multiple equal type meta queries and test classes
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/CollectionQuery.java b/src/main/java/de/ids_mannheim/korap/query/serialize/CollectionQuery.java
index 5cdd28c..c17bdc2 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/CollectionQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/CollectionQuery.java
@@ -18,18 +18,19 @@
  */
 public class CollectionQuery {
 
+
+    private static ObjectMapper serialzer = new ObjectMapper();
     private JsonFactory factory;
     private CollectionTypes types;
-    private ObjectMapper serialzer;
     private List<Map> rq;
-    private List<Map> mfil;
-    private List<Map> mext;
+    private Multimap<String, String> mfilter;
+    private Multimap<String, String> mextension;
+
 
     public CollectionQuery() {
-        this.serialzer = new ObjectMapper();
         this.rq = new ArrayList<>();
-        this.mfil = new ArrayList<>();
-        this.mext = new ArrayList<>();
+        this.mfilter = ArrayListMultimap.create();
+        this.mextension = ArrayListMultimap.create();
         this.factory = serialzer.getFactory();
         this.types = new CollectionTypes();
     }
@@ -53,6 +54,84 @@
         return this;
     }
 
+
+    public CollectionQuery addMetaFilter(String key, String value) {
+        this.mfilter.put(key, value);
+        return this;
+    }
+
+    public CollectionQuery addMetaFilter(String queries) {
+        this.mfilter.putAll(resEq(queries));
+        return this;
+    }
+
+    public CollectionQuery addMetaExtend(String attr, String val) {
+        this.mextension.put(attr, val);
+        return this;
+    }
+
+    public CollectionQuery addMetaExtend(String queries) {
+        this.mextension.putAll(resEq(queries));
+        return this;
+    }
+
+
+    private List<Map> createFilter() {
+        List<Map> mfil = new ArrayList();
+        boolean multypes = this.mfilter.keys().size() > 1;
+        String def_key = null;
+
+        if (!multypes) {
+            Multiset<String> keys = this.mfilter.keys();
+            def_key = keys.toArray(new String[keys.size()])[0];
+        }
+
+        List value = this.createValue(this.mfilter);
+        if (mfilter.values().size() == 1)
+            Collections.addAll(mfil, types.createMetaFilter((Map) value.get(0)));
+        else {
+            Map group;
+            if (!multypes)
+                group = types.createGroup("and", def_key, value);
+            else
+                group = types.createGroup("and", null, value);
+            Collections.addAll(mfil, types.createMetaFilter(group));
+        }
+        return mfil;
+    }
+
+    private List<Map> createExtender() {
+        List<Map> mex = new ArrayList();
+        boolean multypes = this.mextension.keys().size() > 1;
+        String def_key = null;
+
+        if (!multypes)
+            def_key = this.mextension.keys().toArray(new String[0])[0];
+
+        List value = this.createValue(this.mextension);
+        // todo: missing: - takes only one resource, but resources can be chained!
+        if (this.mextension.values().size() == 1)
+            Collections.addAll(mex, types.createMetaExtend((Map) value.get(0)));
+        else {
+            Map group;
+            if (!multypes)
+                group = types.createGroup("and", def_key, value);
+            else
+                group = types.createGroup("and", null, value);
+            Collections.addAll(mex, types.createMetaExtend(group));
+        }
+        return mex;
+    }
+
+    private List<Map> join() {
+        List<Map> cursor = new ArrayList<>(this.rq);
+        if (!this.mfilter.isEmpty())
+            cursor.addAll(this.createFilter());
+        if (!this.mextension.isEmpty())
+            cursor.addAll(this.createExtender());
+        return cursor;
+    }
+
     private List createValue(Multimap<String, String> map) {
         List value = new ArrayList<>();
         String[] dates = new String[3];
@@ -101,65 +180,6 @@
         return value;
     }
 
-    // fixme: map can only have one key/value pair. thus,
-    // fixme: text class can only be added once. Multiple types are not possible!
-    public CollectionQuery addMetaFilter(String queries) {
-        Multimap<String, String> m = resEq(queries);
-        boolean multypes = m.keys().size() > 1;
-        String def_key = null;
-
-
-        if (!multypes){
-            Multiset<String> keys = m.keys();
-            def_key = keys.toArray(new String[keys.size()])[0];
-        }
-
-        List value = this.createValue(m);
-        // todo: missing: - takes only one resource, but resources can be chained!
-        if (m.values().size() == 1)
-            Collections.addAll(this.mfil, types.createMetaFilter((Map) value.get(0)));
-        else {
-            Map group;
-            if (!multypes)
-                group = types.createGroup("and", def_key, value);
-            else
-                group = types.createGroup("and", null, value);
-            Collections.addAll(this.mfil, types.createMetaFilter(group));
-        }
-        return this;
-    }
-
-    public CollectionQuery addMetaExtend(String queries) {
-        Multimap<String, String> m = resEq(queries);
-        boolean multypes = m.keys().size() > 1;
-        String def_key = null;
-
-        if (!multypes)
-            def_key = m.keys().toArray(new String[0])[0];
-
-        List value = this.createValue(m);
-        // todo: missing: - takes only one resource, but resources can be chained!
-        if (m.values().size() == 1)
-            Collections.addAll(this.mext, types.createMetaExtend((Map) value.get(0)));
-        else {
-            Map group;
-            if (!multypes)
-                group = types.createGroup("and", def_key, value);
-            else
-                group = types.createGroup("and", null, value);
-            Collections.addAll(this.mext, types.createMetaExtend(group));
-        }
-        return this;
-    }
-
-    public CollectionQuery addMetaFilter(String attr, String val) {
-        return addMetaFilter(attr + ":" + val);
-    }
-
-    public CollectionQuery addMetaExtend(String attr, String val) {
-        return addMetaExtend(attr + ":" + val);
-    }
-
     private String[] processDates(List<String> dates) {
         if (dates.isEmpty())
             return new String[3];
@@ -182,38 +202,17 @@
         return el;
     }
 
-    public void clear() {
-        this.rq.clear();
-        this.mfil.clear();
-        this.mext.clear();
+    public List<Map> raw() {
+        return join();
     }
 
-    private List<Map> join() {
-        List<Map> cursor = new ArrayList<>(this.rq);
-        cursor.addAll(this.mfil);
-        cursor.addAll(this.mext);
-        return cursor;
-    }
 
-    private List<Map> getCollectionsOnly() {
-        List<Map> cursor = new ArrayList<>(this.mfil);
-        cursor.addAll(this.mext);
-        return cursor;
-    }
-
-    /**
-     * returns the meta query only and does not contain parent dependencies
-     *
-     * @return
-     */
-    @Deprecated
-    public String stringify() {
-        List collection = getCollectionsOnly();
-        if (collection.isEmpty())
-            return "";
+    public String toCollections() {
+        Map meta = new LinkedHashMap();
+        meta.put("collections", join());
 
         try {
-            return serialzer.writeValueAsString(collection);
+            return serialzer.writeValueAsString(meta);
         } catch (JsonProcessingException e) {
             e.printStackTrace();
             return "";
@@ -237,34 +236,6 @@
 
 
     /**
-     * returns the List<Map> that contains all the meta queries and resource queries
-     * added to the meta query container
-     *
-     * @return
-     */
-    public List<Map> raw() {
-        return join();
-    }
-
-    /**
-     * returns a JSON String representation that contains all information
-     * (meta query and resource meta queries alike) in a root meta JSON node
-     *
-     * @return
-     */
-    public String toCollections() {
-        Map meta = new LinkedHashMap();
-        meta.put("collections", join());
-
-        try {
-            return serialzer.writeValueAsString(meta);
-        } catch (JsonProcessingException e) {
-            e.printStackTrace();
-            return "";
-        }
-    }
-
-    /**
      * resolves all queries as equal (hierarchy) AND relations
      *
      * @param queries
@@ -283,15 +254,9 @@
     }
 
 
-    /**
-     * resolves query string with AND and OR relations alike!
-     *
-     * @param queries
-     * @return
-     */
-    private Multimap<String, String> resDep(String queries) {
-        return null;
+    public void clear() {
+        this.rq.clear();
+        this.mfilter.clear();
+        this.mextension.clear();
     }
-
-
 }
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/CollectionTypes.java b/src/main/java/de/ids_mannheim/korap/query/serialize/CollectionTypes.java
index 8f98259..799acb1 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/CollectionTypes.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/CollectionTypes.java
@@ -90,6 +90,8 @@
         return meta;
     }
 
+
+
     public Map mapify(String s) throws IOException {
         return mapper.readValue(s, Map.class);
     }
diff --git a/src/test/java/MetaQuerySerializationTest.java b/src/test/java/MetaQuerySerializationTest.java
index 03cd549..c5d1fbd 100644
--- a/src/test/java/MetaQuerySerializationTest.java
+++ b/src/test/java/MetaQuerySerializationTest.java
@@ -3,10 +3,8 @@
 import com.fasterxml.jackson.databind.JsonMappingException;
 import de.ids_mannheim.korap.query.serialize.CollectionQuery;
 import de.ids_mannheim.korap.query.serialize.QuerySerializer;
-import de.ids_mannheim.korap.query.serialize.MetaQuery;
 import de.ids_mannheim.korap.util.QueryException;
 
-import org.joda.time.DateTime;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -31,7 +29,10 @@
         b.append("pubPlace:Erfurt");
         b.append(" AND ");
         b.append("textClass:wissenschaft");
-        CollectionQuery qu = new CollectionQuery().addMetaFilter(b.toString());
+//        CollectionQuery qu = new CollectionQuery().addMetaFilter(b.toString());
+        CollectionQuery query = new CollectionQuery().addMetaFilter(b.toString());
+        System.out.println(query.build());
+        System.out.println(query.toCollections());
 //        System.out.println("value reference " + qu.stringify());
 //        System.out.println();
     }
@@ -40,7 +41,7 @@
     public void testSingle() throws IOException {
         CollectionQuery query = new CollectionQuery().addMetaFilter("textClass", "wissenschaft");
 //        System.out.println("------ TEXT SINGLE " + query.stringify());
-        System.out.println();
+        System.out.println(query.build());
     }
 
     @Test
@@ -52,9 +53,9 @@
         b.append(" AND ");
         b.append("author:Goethe");
         CollectionQuery query = new CollectionQuery().addMetaFilter(b.toString());
-        System.out.println("value until/since : " + query.stringify());
+        System.out.println("value until/since : " + query.build());
         System.out.println("meta value until/since " + query.toCollections());
-        Assert.assertEquals("[{\"@type\":\"korap:meta-filter\",\"@value\":{\"@type\":\"korap:group\",\"relation\":\"and\",\"operands\":[{\"@type\":\"korap:term\",\"@field\":\"korap:field#author\",\"@value\":\"Goethe\"},{\"@type\":\"korap:group\",\"@field\":\"korap:field#pubDate\",\"relation\":\"between\",\"operands\":[{\"@type\":\"korap:date\",\"@value\":\"2012-04-01\"},{\"@type\":\"korap:date\",\"@value\":\"2013-04-01\"}]}]}}]", query.stringify());
+        Assert.assertEquals("[{\"@type\":\"korap:meta-filter\",\"@value\":{\"@type\":\"korap:group\",\"relation\":\"and\",\"operands\":[{\"@type\":\"korap:term\",\"@field\":\"korap:field#author\",\"@value\":\"Goethe\"},{\"@type\":\"korap:group\",\"@field\":\"korap:field#pubDate\",\"relation\":\"between\",\"operands\":[{\"@type\":\"korap:date\",\"@value\":\"2012-04-01\"},{\"@type\":\"korap:date\",\"@value\":\"2013-04-01\"}]}]}}]", query.build());
 //        System.out.println();
     }
 
@@ -66,7 +67,7 @@
         b.append("author:Hesse");
         CollectionQuery query = new CollectionQuery().addMetaFilter(b.toString());
         System.out.println("Running date check (until) with additional attribute author");
-        Assert.assertEquals("[{\"@type\":\"korap:meta-filter\",\"@value\":{\"@type\":\"korap:group\",\"relation\":\"and\",\"operands\":[{\"@type\":\"korap:term\",\"@field\":\"korap:field#author\",\"@value\":\"Hesse\"},{\"@type\":\"korap:group\",\"@field\":\"korap:field#pubDate\",\"relation\":\"until\",\"operands\":[{\"@type\":\"korap:date\",\"@value\":\"2013-12-10\"}]}]}}]", query.stringify());
+        Assert.assertEquals("[{\"@type\":\"korap:meta-filter\",\"@value\":{\"@type\":\"korap:group\",\"relation\":\"and\",\"operands\":[{\"@type\":\"korap:term\",\"@field\":\"korap:field#author\",\"@value\":\"Hesse\"},{\"@type\":\"korap:group\",\"@field\":\"korap:field#pubDate\",\"relation\":\"until\",\"operands\":[{\"@type\":\"korap:date\",\"@value\":\"2013-12-10\"}]}]}}]", query.build());
 //        System.out.println("value until : " + query.stringify());
 //        System.out.println();
     }
@@ -78,11 +79,11 @@
         b.append(" AND ");
         b.append("author:Kafka");
         CollectionQuery query = new CollectionQuery().addMetaFilter(b.toString());
-        System.out.println("value since : " + query.stringify());
+        System.out.println("value since : " + query.build());
         System.out.println("meta value since " + query.toCollections());
 //        System.out.println();
         System.out.println("Running date check (since) with additional attribute author");
-        Assert.assertEquals("[{\"@type\":\"korap:meta-filter\",\"@value\":{\"@type\":\"korap:group\",\"relation\":\"and\",\"operands\":[{\"@type\":\"korap:term\",\"@field\":\"korap:field#author\",\"@value\":\"Kafka\"},{\"@type\":\"korap:group\",\"@field\":\"korap:field#pubDate\",\"relation\":\"since\",\"operands\":[{\"@type\":\"korap:date\",\"@value\":\"2013-12-10\"}]}]}}]", query.stringify());
+        Assert.assertEquals("[{\"@type\":\"korap:meta-filter\",\"@value\":{\"@type\":\"korap:group\",\"relation\":\"and\",\"operands\":[{\"@type\":\"korap:term\",\"@field\":\"korap:field#author\",\"@value\":\"Kafka\"},{\"@type\":\"korap:group\",\"@field\":\"korap:field#pubDate\",\"relation\":\"since\",\"operands\":[{\"@type\":\"korap:date\",\"@value\":\"2013-12-10\"}]}]}}]", query.build());
     }
 
     @Test
@@ -125,11 +126,11 @@
         b.append("pubDate:<2013-12-10");
         b.append(" AND ");
         b.append("author:Kafka");
-        CollectionQuery query = new CollectionQuery().addMetaFilter(b.toString());
-        query.addMetaExtend("author", "Hesse");
+        CollectionQuery q = new CollectionQuery().addMetaFilter(b.toString());
+        q.addMetaExtend("author", "Hesse");
 
-//        System.out.println("--- ALL " + query.stringify());
-//        System.out.println();
+        System.out.println("--- ALL " + q.build());
+        System.out.println();
 
     }
 
@@ -140,8 +141,8 @@
         b.append(" AND ");
         b.append("author:Kafka");
         CollectionQuery q = new CollectionQuery().addMetaExtend(b.toString());
-//        System.out.println("array repres " + q.toMeta());
-//        System.out.println();
+        System.out.println("array repres " + q.build());
+        System.out.println();
     }
 
     @Test
@@ -149,9 +150,9 @@
         CollectionQuery q = new CollectionQuery().addMetaFilter("corpusID", "A00");
         q.addMetaExtend("corpusID", "A01");
 
-//        System.out.println("results stringified " + q.stringify());
-//        System.out.println("results to meta" + q.toMeta());
-//        System.out.println();
+        System.out.println("results stringified " + q.build());
+        System.out.println("results to meta" + q.toCollections());
+        System.out.println();
     }
 
     /**
@@ -164,17 +165,15 @@
     public void testResources() throws IOException {
         String meta = "[{\"@type\":\"korap:meta-filter\",\"@value\":{\"@type\":\"korap:term\",\"@field\":\"korap:field#corpusID\",\"@value\":\"WPD\"}}]";
         CollectionQuery q = new CollectionQuery().addResource(meta);
-        org.junit.Assert.assertEquals("String should be empty", "", q.stringify());
-//        System.out.println("meta string " + q.toMeta());
         System.out.println("Testing Resource Meta data");
-        org.junit.Assert.assertEquals("{\"meta\":" + meta + "}", q.toCollections());
+        org.junit.Assert.assertEquals("{\"collections\":" + meta + "}", q.toCollections());
     }
 
     @Test
     public void testA00() throws IOException {
         CollectionQuery q = new CollectionQuery().addMetaExtend("corpusID", "A00").addMetaExtend("corpusID", "A01");
-//        System.out.println("A meta: " + q.stringify());
-//        System.out.println();
+        System.out.println("A meta: " + q.build());
+        System.out.println();
     }
 
     @Test
@@ -182,8 +181,8 @@
         String meta = "[{\"@type\":\"korap:meta-filter\",\"@value\":{\"@type\":\"korap:term\",\"@field\":\"korap:field#corpusID\",\"@value\":\"WPD\"}}]";
         CollectionQuery q = new CollectionQuery().addResource(meta);
         q.addMetaFilter("textClass", "wissenschaft");
-//        System.out.println("stringified meta " + q.stringify());
-//        System.out.println("meta string " + q.toMeta());
+        System.out.println("stringified meta " + q.build());
+        System.out.println("meta string " + q.toCollections());
     }
 
 }