Finished serialization of virtual collections
diff --git a/src/main/java/de/ids_mannheim/korap/KorapCollection.java b/src/main/java/de/ids_mannheim/korap/KorapCollection.java
index 2db10b8..cdbb568 100644
--- a/src/main/java/de/ids_mannheim/korap/KorapCollection.java
+++ b/src/main/java/de/ids_mannheim/korap/KorapCollection.java
@@ -22,6 +22,9 @@
 import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.search.DocIdSet;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.JsonNode;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -36,6 +39,7 @@
     private KorapIndex index;
     private KorapDate created;
     private String id;
+    private String error;
     private ArrayList<FilterOperation> filter;
     private int filterCount = 0;
     
@@ -48,10 +52,37 @@
 	this.filter = new ArrayList<FilterOperation>(5);
     };
 
+    public KorapCollection (String jsonString) {
+	this.filter = new ArrayList<FilterOperation>(5);
+	ObjectMapper mapper = new ObjectMapper();
+	try {
+	    JsonNode json = mapper.readValue(jsonString, JsonNode.class);
+	    if (json.has("meta")) {
+		for (JsonNode meta : json.get("meta")) {
+		    this.fromJSON(meta);
+		};
+	    };
+	}
+	catch (Exception e) {
+	    this.error = e.getMessage();
+	};
+    };
+
     public KorapCollection () {
 	this.filter = new ArrayList<FilterOperation>(5);
     };
 
+    public void fromJSON(JsonNode json) {
+	String type = json.get("@type").asText();
+
+	if (type.equals("korap:meta-filter")) {
+	    this.filter(new BooleanFilter(json.get("@value")));
+	}
+	else if (type.equals("korap:meta-extend")) {
+	    this.extend(new BooleanFilter(json.get("@value")));
+	};
+    };
+
     public int getCount() {
 	return this.filterCount;
     };
@@ -60,13 +91,17 @@
 	this.index = ki;
     };
 
+    // The checks asre not necessary
     public KorapCollection filter (BooleanFilter filter) {
-	this.filter.add(
-	    new FilterOperation(
-				(Filter) new QueryWrapperFilter(filter.toQuery()),
-                false
-            )
-        );
+	if (filter == null)
+	    return this;
+	Filter f = (Filter) new QueryWrapperFilter(filter.toQuery());
+	if (filter == null)
+	    return this;
+	FilterOperation fo = new FilterOperation(f,false);
+	if (fo == null)
+	    return this;
+	this.filter.add(fo);
 	this.filterCount++;
 	return this;
     };
@@ -86,6 +121,11 @@
 	return this.filter;
     };
 
+    public FilterOperation getFilter (int i) {
+	return this.filter.get(i);
+    };
+
+
     // DEPRECATED BUT USED IN TEST CASES
     public KorapResult search (SpanQuery query) {
 	return this.index.search(this, query, 0, (short) 20, true, (short) 5, true, (short) 5);
@@ -179,4 +219,4 @@
     };
 
     // implement "till" with rangefilter
-};
\ No newline at end of file
+};
diff --git a/src/main/java/de/ids_mannheim/korap/KorapSearch.java b/src/main/java/de/ids_mannheim/korap/KorapSearch.java
index 9d614ff..09b12a9 100644
--- a/src/main/java/de/ids_mannheim/korap/KorapSearch.java
+++ b/src/main/java/de/ids_mannheim/korap/KorapSearch.java
@@ -108,6 +108,11 @@
 		this.error = "No query defined";
 	    };
 
+	    if (json.has("meta")) {
+		KorapCollection kc = new KorapCollection(jsonString);
+		this.setCollection(kc);
+	    };
+
 	    if (this.error == null) {
 
 		// Defined count
diff --git a/src/main/java/de/ids_mannheim/korap/filter/BooleanFilter.java b/src/main/java/de/ids_mannheim/korap/filter/BooleanFilter.java
index 3e94790..5a22198 100644
--- a/src/main/java/de/ids_mannheim/korap/filter/BooleanFilter.java
+++ b/src/main/java/de/ids_mannheim/korap/filter/BooleanFilter.java
@@ -14,12 +14,18 @@
 import de.ids_mannheim.korap.filter.RegexFilter;
 import de.ids_mannheim.korap.KorapFilter;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.JsonNode;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 
 /*
   Todo: !not
+
+THE JSON STUFF DEFINITIVELY BELONGS INTO KORAPFILTER
+
 */
 
 /**
@@ -41,6 +47,95 @@
 	bool = new BooleanQuery();
     };
 
+    public BooleanFilter (JsonNode json) {
+	bool = new BooleanQuery();
+
+	String type = json.get("@type").asText();
+	String field = _getField(json);
+
+	if (type.equals("korap:term")) {
+	    this.fromJSON(json, field);
+	}
+	else if (type.equals("korap:group")) {
+	    // TODO: relation
+	    for (JsonNode operand : json.get("operands")) {
+		this.fromJSON(operand, field);
+	    };
+	};
+    };
+
+
+    private void fromJSON (JsonNode json, String field) {
+	String type = json.get("@type").asText();
+
+	if (json.has("@field"))
+	    field = _getField(json);
+
+	if (type.equals("korap:term")) {
+	    if (field != null && json.has("@value"))
+		this.and(field, json.get("@value").asText());
+	    return;
+	}
+	else if (type.equals("korap:group")) {
+	    if (!json.has("relation"))
+		return;
+
+	    String date, till;
+
+	    switch (json.get("relation").asText())  {
+	    case "between":
+		date = _getDate(json, 0);
+		till = _getDate(json, 1);
+		if (date != null && till != null)
+		    this.between(date, till);
+		break;
+
+	    case "until":
+		date = _getDate(json, 0);
+		if (date != null)
+		    this.till(date);
+		break;
+
+	    case "since":
+		date = _getDate(json, 0);
+		if (date != null)
+		    this.since(date);
+		break;
+
+	    case "equals":
+		date = _getDate(json, 0);
+		if (date != null)
+		    this.date(date);
+		break;
+	    };
+	}
+    };
+
+    private static String  _getField (JsonNode json)  {
+	if (!json.has("@field"))
+	    return (String) null;
+
+	String field = json.get("@field").asText();
+	return field.replaceFirst("korap:field#", "");
+    };
+
+    private static String _getDate (JsonNode json, int index) {
+	if (!json.has("operands"))
+	    return (String) null;
+
+	if (!json.get("operands").has(index))
+	    return (String) null;
+
+	JsonNode date = json.get("operands").get(index);
+	if (!date.get("@type").asText().equals("korap:date"))
+	    return (String) null;
+
+	if (!date.has("@value"))
+	    return (String) null;
+
+	return date.get("@value").asText();
+    };
+
     public BooleanFilter or (String type, String ... terms) {
 	for (String term : terms) {
 	    bool.add(
diff --git a/src/test/java/de/ids_mannheim/korap/filter/TestKorapCollectionJSON.java b/src/test/java/de/ids_mannheim/korap/filter/TestKorapCollectionJSON.java
new file mode 100644
index 0000000..3dca921
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/filter/TestKorapCollectionJSON.java
@@ -0,0 +1,50 @@
+import java.util.*;
+import java.io.*;
+
+import de.ids_mannheim.korap.KorapCollection;
+
+import static org.junit.Assert.*;
+import org.junit.Test;
+import org.junit.Ignore;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TestKorapCollectionJSON {
+
+    @Test
+    public void metaQuery1 () {
+	String metaQuery = getString(getClass().getResource("/queries/metaquery.json").getFile());
+	KorapCollection kc = new KorapCollection(metaQuery);
+	assertEquals(3,kc.getCount());
+	assertEquals("filter with QueryWrapperFilter(+textClass:wissenschaft)",kc.getFilter(0).toString());
+	assertEquals("filter with QueryWrapperFilter(+pubPlace:Erfurt +author:Hesse)",kc.getFilter(1).toString());
+	assertEquals("extend with QueryWrapperFilter(+pubDate:[20110429 TO 20131231] +textClass:freizeit)",kc.getFilter(2).toString());
+    };
+
+
+    @Test
+    public void metaQuery2 () {
+	String metaQuery = getString(getClass().getResource("/queries/metaquery2.json").getFile());
+	KorapCollection kc = new KorapCollection(metaQuery);
+	assertEquals(1,kc.getCount());
+	assertEquals("filter with QueryWrapperFilter(+author:Hesse +pubDate:[0 TO 20131205])",kc.getFilter(0).toString());
+    };
+
+
+    public static String getString (String path) {
+	StringBuilder contentBuilder = new StringBuilder();
+	try {
+	    BufferedReader in = new BufferedReader(new FileReader(path));
+	    String str;
+	    while ((str = in.readLine()) != null) {
+		contentBuilder.append(str);
+	    };
+	    in.close();
+	} catch (IOException e) {
+	    fail(e.getMessage());
+	}
+	return contentBuilder.toString();
+    };
+
+};
\ No newline at end of file
diff --git a/src/test/java/de/ids_mannheim/korap/search/TestKorapSearch.java b/src/test/java/de/ids_mannheim/korap/search/TestKorapSearch.java
index 763697a..3314648 100644
--- a/src/test/java/de/ids_mannheim/korap/search/TestKorapSearch.java
+++ b/src/test/java/de/ids_mannheim/korap/search/TestKorapSearch.java
@@ -104,14 +104,20 @@
 	};
 	ki.commit();
 
-	String json = getString(getClass().getResource("/queries/metaquery.json").getFile());
-
+	String json = getString(getClass().getResource("/queries/metaquery3.json").getFile());
 
 	KorapResult kr = new KorapSearch(json).run(ki);
 	assertEquals(29, kr.getTotalResults());
 	assertEquals(5, kr.getItemsPerPage());
 	assertEquals(5, kr.getStartIndex());
 	assertEquals("... a: A ist [der klangreichste] der V ...", kr.getMatch(0).getSnippetBrackets());
+
+	json = getString(getClass().getResource("/queries/metaquery4.json").getFile());
+
+	kr = new KorapSearch(json).run(ki);
+	assertEquals(0, kr.getTotalResults());
+	assertEquals(5, kr.getItemsPerPage());
+	assertEquals(5, kr.getStartIndex());
     };
 
 
diff --git a/src/test/resources/queries/metaquery2.json b/src/test/resources/queries/metaquery2.json
index 8dd978f..c80b044 100644
--- a/src/test/resources/queries/metaquery2.json
+++ b/src/test/resources/queries/metaquery2.json
@@ -78,7 +78,7 @@
                             "operands": [
                                 {
                                     "@type": "korap:date",
-                                    "@value": "2013-12-5"
+                                    "@value": "2013-12-05"
                                 }
                             ]
                         }
diff --git a/src/test/resources/queries/metaquery3.json b/src/test/resources/queries/metaquery3.json
new file mode 100644
index 0000000..fd46052
--- /dev/null
+++ b/src/test/resources/queries/metaquery3.json
@@ -0,0 +1,62 @@
+{
+    "@context": {
+        "korap": "http://korap.ids-mannheim.de/ns/query",
+        "@language": "de",
+        "operands": {
+            "@id": "korap:operands",
+            "@container": "@list"
+        },
+        "relation": {
+            "@id": "korap:relation",
+            "@type": "korap:relation#types"
+        },
+        "class": {
+            "@id": "korap:class",
+            "@type": "xsd:integer"
+        },
+        "query": "korap:query",
+        "filter": "korap:filter",
+        "meta": "korap:meta"
+    },
+    "startPage" : 2,
+    "count" : 5,
+    "context" : {
+      "left" : [ "token", 3 ],
+      "right" : [ "char", 6 ]
+    },
+   "query":{
+      "@type":"korap:group",
+      "relation":"or",
+      "operands":[
+         {
+            "@type":"korap:token",
+            "@value":{
+               "@type":"korap:term",
+               "@value":"base:Vokal",
+               "relation":"="
+            }
+         },
+         {
+            "@type":"korap:sequence",
+            "operands":[
+               {
+                  "@type":"korap:token",
+                  "@value":{
+                     "@type":"korap:term",
+                     "@value":"base:der",
+                     "relation":"="
+                  }
+               },
+               {
+                  "@type":"korap:token",
+                  "@value":{
+                     "@type":"korap:term",
+                     "@value":"mate/p:ADJA",
+                     "relation":"="
+                  }
+               }
+            ]
+         }
+      ]
+   }
+}
\ No newline at end of file
diff --git a/src/test/resources/queries/metaquery4.json b/src/test/resources/queries/metaquery4.json
new file mode 100644
index 0000000..6b82550
--- /dev/null
+++ b/src/test/resources/queries/metaquery4.json
@@ -0,0 +1,73 @@
+{
+    "@context": {
+        "korap": "http://korap.ids-mannheim.de/ns/query",
+        "@language": "de",
+        "operands": {
+            "@id": "korap:operands",
+            "@container": "@list"
+        },
+        "relation": {
+            "@id": "korap:relation",
+            "@type": "korap:relation#types"
+        },
+        "class": {
+            "@id": "korap:class",
+            "@type": "xsd:integer"
+        },
+        "query": "korap:query",
+        "filter": "korap:filter",
+        "meta": "korap:meta"
+    },
+    "startPage" : 2,
+    "count" : 5,
+    "context" : {
+      "left" : [ "token", 3 ],
+      "right" : [ "char", 6 ]
+    },
+   "query":{
+      "@type":"korap:group",
+      "relation":"or",
+      "operands":[
+         {
+            "@type":"korap:token",
+            "@value":{
+               "@type":"korap:term",
+               "@value":"base:Vokal",
+               "relation":"="
+            }
+         },
+         {
+            "@type":"korap:sequence",
+            "operands":[
+               {
+                  "@type":"korap:token",
+                  "@value":{
+                     "@type":"korap:term",
+                     "@value":"base:der",
+                     "relation":"="
+                  }
+               },
+               {
+                  "@type":"korap:token",
+                  "@value":{
+                     "@type":"korap:term",
+                     "@value":"mate/p:ADJA",
+                     "relation":"="
+                  }
+               }
+            ]
+         }
+      ]
+   },
+    "meta": [
+        {
+            "@type": "korap:meta-filter",
+            "@id": "korap-filter#id-1223232",
+            "@value": {
+                "@type": "korap:term",
+                "@field": "korap:field#textClass",
+                "@value": "baum"
+            }
+        }
+    ]
+}
\ No newline at end of file