Finalized API glue for korap-core
diff --git a/CHANGES b/CHANGES
index 1145a25..893561a 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,7 @@
+0.23 2013-12-05
+        - Error handling corrected in KorapResult.
+	- Json deserializer finished for queries.
+
 0.22 2013-12-04
         - Introduced KorapSearch.
 	- Json deserializer (started).
diff --git a/pom.xml b/pom.xml
index d5e783f..be2f0a2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
 
   <groupId>KorAP-modules</groupId>
   <artifactId>KorAP-lucene-index</artifactId>
-  <version>0.22</version>
+  <version>0.23</version>
   <packaging>jar</packaging>
 
   <name>KorAP-lucene-index</name>
diff --git a/src/main/java/de/ids_mannheim/korap/KorapQuery.java b/src/main/java/de/ids_mannheim/korap/KorapQuery.java
index 2ee0623..b76b7b8 100644
--- a/src/main/java/de/ids_mannheim/korap/KorapQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/KorapQuery.java
@@ -25,6 +25,8 @@
     private String field;
     private ObjectMapper json;
 
+    private String defaultFoundry = "mate/";
+
     // Logger
     private final static Logger log = LoggerFactory.getLogger(KorapQuery.class);
 
@@ -161,7 +163,7 @@
 	    case "=":
 		String value = json.get("@value").asText();
 
-		value = value.replaceFirst("base:", "l:").replaceFirst("orth:", "s:");
+		value = value.replaceFirst("base:", defaultFoundry +"l:").replaceFirst("orth:", "s:");
 	
 		if (json.has("@subtype") && json.get("@subtype").asText().equals("korap:regex")) {
 		    if (value.charAt(0) == '\'' || value.charAt(0) == '"') {
diff --git a/src/main/java/de/ids_mannheim/korap/KorapResult.java b/src/main/java/de/ids_mannheim/korap/KorapResult.java
index 22a66bb..2f8def8 100644
--- a/src/main/java/de/ids_mannheim/korap/KorapResult.java
+++ b/src/main/java/de/ids_mannheim/korap/KorapResult.java
@@ -34,6 +34,10 @@
     // This is KorapMatch instead of KorapResult!
     private final static Logger log = LoggerFactory.getLogger(KorapMatch.class);
 
+    // Empty result
+    public KorapResult () {
+    };
+
     public KorapResult (String query,
 			int startIndex,
 			short itemsPerPage,
diff --git a/src/main/java/de/ids_mannheim/korap/KorapSearch.java b/src/main/java/de/ids_mannheim/korap/KorapSearch.java
index d0be889..9d614ff 100644
--- a/src/main/java/de/ids_mannheim/korap/KorapSearch.java
+++ b/src/main/java/de/ids_mannheim/korap/KorapSearch.java
@@ -28,6 +28,7 @@
     public KorapSearchContext leftContext, rightContext;
     private KorapCollection collection;
     private KorapIndex index;
+    private String error;
 
     {
 	leftContext = new KorapSearchContext();
@@ -76,18 +77,64 @@
 	public KorapSearchContext setLength (int value) {
 	    return this.setLength((short) value);
 	};
+
+	public void fromJSON (JsonNode json) {
+	    String type = json.get(0).asText();
+	    if (type.equals("token")) {
+		this.setToken(true);
+	    }
+	    else if (type.equals("char")) {
+		this.setCharacter(true);
+	    };
+	    this.setLength(json.get(1).asInt());
+	};
     };
 
-    public KorapSearch (String json) {
+
+    public KorapSearch (String jsonString) {
 	ObjectMapper mapper = new ObjectMapper();
 	try {
-	    JsonNode rootNode = mapper.readValue(json, JsonNode.class);
+	    JsonNode json = mapper.readValue(jsonString, JsonNode.class);
 
-	    this.query = new KorapQuery("tokens").fromJSON(rootNode.get("query")).toQuery();
+	    if (json.has("query")) {
+		try {
+		    this.query = new KorapQuery("tokens").fromJSON(json.get("query")).toQuery();
+		}
+		catch (QueryException q) {
+		    this.error = q.getMessage();
+		};
+	    }
+	    else {
+		this.error = "No query defined";
+	    };
+
+	    if (this.error == null) {
+
+		// Defined count
+		if (json.has("count"))
+		    this.setCount(json.get("count").asInt());
+
+		// Defined startIndex
+		if (json.has("startIndex"))
+		    this.setStartIndex(json.get("startIndex").asInt());
+
+		// Defined startPage
+		if (json.has("startPage"))
+		    this.setStartPage(json.get("startPage").asInt());
+
+		// Defined contexts
+		if (json.has("context")) {
+		    JsonNode context = json.get("context");
+		    if (context.has("left"))
+			this.leftContext.fromJSON(context.get("left"));
+		    if (context.has("right"))
+			this.rightContext.fromJSON(context.get("right"));
+
+		};
+	    };
 	}
 	catch (IOException e) {
-	}
-	catch (QueryException e) {
+	    this.error = e.getMessage();
 	};
     };
 
@@ -120,6 +167,17 @@
 	return this;
     };
 
+    public KorapSearch setStartPage (int value) {
+	if (value >= 0) {
+	    this.startIndex = (value * this.getCount()) - this.getCount();
+	}
+	else {
+	    this.startIndex = 0;
+	};
+
+	return this;
+    };
+
     public short getCount () {
 	return this.count;
     };
@@ -129,6 +187,7 @@
     };
 
     public KorapSearch setCount (int value) {
+	// Todo: Maybe update startIndex with known startPage!
 	this.setCount((short) value);
 	return this;
     };
@@ -158,6 +217,12 @@
     };
 
     public KorapResult run (KorapIndex ki) {
+	if (this.error != null) {
+	    KorapResult kr = new KorapResult();
+	    kr.setError(this.error);
+	    return kr;
+	};
+
 	this.getCollection().setIndex(ki);
 	return ki.search(this.getCollection(), this);
     };
diff --git a/src/test/java/de/ids_mannheim/korap/query/TestKorapQueryJSON.java b/src/test/java/de/ids_mannheim/korap/query/TestKorapQueryJSON.java
index f6b85c7..c995068 100644
--- a/src/test/java/de/ids_mannheim/korap/query/TestKorapQueryJSON.java
+++ b/src/test/java/de/ids_mannheim/korap/query/TestKorapQueryJSON.java
@@ -16,6 +16,8 @@
 @RunWith(JUnit4.class)
 public class TestKorapQueryJSON {
 
+    private String defaultFoundry = "mate/";
+
     @Ignore
     public void queryJSONBsp1 () {
 	SpanQueryWrapperInterface sqwi = jsonQuery(getClass().getResource("/queries/bsp1.json").getFile());
@@ -30,7 +32,7 @@
 	SpanQueryWrapperInterface sqwi = jsonQuery(getClass().getResource("/queries/bsp1b.json").getFile());
 
 	// [base=foo]|([base=foo][base=bar]) meta author=Goethe&year=1815
-	assertEquals(sqwi.toQuery().toString(), "spanOr([tokens:l:foo, spanNext(tokens:l:foo, tokens:l:bar)])");
+	assertEquals(sqwi.toQuery().toString(), "spanOr([tokens:"+defaultFoundry+"l:foo, spanNext(tokens:"+defaultFoundry+"l:foo, tokens:"+defaultFoundry+"l:bar)])");
     };
 
 
@@ -39,7 +41,7 @@
 	SpanQueryWrapperInterface sqwi = jsonQuery(getClass().getResource("/queries/bsp2.json").getFile());
 
 	// ([base=foo]|[base=bar])[base=foobar]
-	assertEquals(sqwi.toQuery().toString(), "spanNext(spanOr([tokens:l:foo, tokens:l:bar]), tokens:l:foobar)");
+	assertEquals(sqwi.toQuery().toString(), "spanNext(spanOr([tokens:"+defaultFoundry+"l:foo, tokens:"+defaultFoundry+"l:bar]), tokens:"+defaultFoundry+"l:foobar)");
     };
 
     @Test
@@ -47,7 +49,7 @@
 	SpanQueryWrapperInterface sqwi = jsonQuery(getClass().getResource("/queries/bsp3.json").getFile());
 
 	// shrink({[base=Mann]})
-	assertEquals(sqwi.toQuery().toString(), "shrink(0: {0: tokens:l:Mann})");
+	assertEquals(sqwi.toQuery().toString(), "shrink(0: {0: tokens:"+defaultFoundry+"l:Mann})");
     };
 
     @Test
@@ -55,7 +57,7 @@
 	SpanQueryWrapperInterface sqwi = jsonQuery(getClass().getResource("/queries/bsp4.json").getFile());
 
 	// shrink({[base=foo]}[orth=bar])
-	assertEquals(sqwi.toQuery().toString(), "shrink(0: spanNext({0: tokens:l:foo}, tokens:s:bar))");
+	assertEquals(sqwi.toQuery().toString(), "shrink(0: spanNext({0: tokens:"+defaultFoundry+"l:foo}, tokens:s:bar))");
     };
 
     @Test
@@ -63,7 +65,7 @@
 	SpanQueryWrapperInterface sqwi = jsonQuery(getClass().getResource("/queries/bsp5.json").getFile());
 
 	// shrink(1:[base=Der]{1:[base=Mann]}) 
-	assertEquals(sqwi.toQuery().toString(), "shrink(1: spanNext(tokens:l:Der, {1: tokens:l:Mann}))");
+	assertEquals(sqwi.toQuery().toString(), "shrink(1: spanNext(tokens:"+defaultFoundry+"l:Der, {1: tokens:"+defaultFoundry+"l:Mann}))");
     };
 
     @Test
@@ -71,7 +73,7 @@
 	SpanQueryWrapperInterface sqwi = jsonQuery(getClass().getResource("/queries/bsp6.json").getFile());
 
 	// [base=katze]
-	assertEquals(sqwi.toQuery().toString(), "tokens:l:Katze");
+	assertEquals(sqwi.toQuery().toString(), "tokens:"+defaultFoundry+"l:Katze");
     };
 
     @Ignore
@@ -95,7 +97,7 @@
 	SpanQueryWrapperInterface sqwi = jsonQuery(getClass().getResource("/queries/bsp9.json").getFile());
 
 	// [base=Katze&orth=Katzen]
-	assertEquals(sqwi.toQuery().toString(), "spanNear([tokens:l:Katze, tokens:s:Katzen], -1, false)");
+	assertEquals(sqwi.toQuery().toString(), "spanNear([tokens:"+defaultFoundry+"l:Katze, tokens:s:Katzen], -1, false)");
     };
 
     @Test
@@ -103,7 +105,7 @@
 	SpanQueryWrapperInterface sqwi = jsonQuery(getClass().getResource("/queries/bsp10.json").getFile());
 
 	// [base=Katze][orth=und][orth=Hunde]
-	assertEquals(sqwi.toQuery().toString(), "spanNext(spanNext(tokens:l:Katze, tokens:s:und), tokens:s:Hunde)");
+	assertEquals(sqwi.toQuery().toString(), "spanNext(spanNext(tokens:"+defaultFoundry+"l:Katze, tokens:s:und), tokens:s:Hunde)");
     };
 
     @Ignore
@@ -119,7 +121,7 @@
 	SpanQueryWrapperInterface sqwi = jsonQuery(getClass().getResource("/queries/bsp12.json").getFile());
 
 	// contains(<np>,[base=Mann])
-	assertEquals(sqwi.toQuery().toString(), "spanWithin(<tokens:np />, tokens:l:Mann)");
+	assertEquals(sqwi.toQuery().toString(), "spanWithin(<tokens:np />, tokens:"+defaultFoundry+"l:Mann)");
     };
 
     @Ignore
@@ -159,7 +161,7 @@
 	SpanQueryWrapperInterface sqwi = jsonQuery(getClass().getResource("/queries/bsp16.json").getFile());
 
 	// [(base=bar|base=foo)&orth=foobar]
-	assertEquals(sqwi.toQuery().toString(), "spanNear([spanOr([tokens:l:bar, tokens:l:foo]), tokens:s:foobar], -1, false)");
+	assertEquals(sqwi.toQuery().toString(), "spanNear([spanOr([tokens:"+defaultFoundry+"l:bar, tokens:"+defaultFoundry+"l:foo]), tokens:s:foobar], -1, false)");
     };
 
     @Test
@@ -167,7 +169,7 @@
 	SpanQueryWrapperInterface sqwi = jsonQuery(getClass().getResource("/queries/bsp17.json").getFile());
 
 	// within(<np>,[base=Mann])
-	assertEquals(sqwi.toQuery().toString(), "spanWithin(<tokens:np />, tokens:l:Mann)");
+	assertEquals(sqwi.toQuery().toString(), "spanWithin(<tokens:np />, tokens:"+defaultFoundry+"l:Mann)");
     };
 
     public static String getString (String path) {
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 0d62975..763697a 100644
--- a/src/test/java/de/ids_mannheim/korap/search/TestKorapSearch.java
+++ b/src/test/java/de/ids_mannheim/korap/search/TestKorapSearch.java
@@ -90,4 +90,65 @@
 	assertEquals(6, kr.totalResults());
 	assertEquals(kr.getMatch(0).getSnippetBrackets(), "... dem [Buchstaben] A ...");
     };
+
+    @Test
+    public void searchJSON () throws IOException {
+
+	// Construct index
+	KorapIndex ki = new KorapIndex();
+	// Indexing test files
+	for (String i : new String[] {"00001", "00002", "00003", "00004", "00005", "00006", "02439"}) {
+	    ki.addDocFile(
+	      getClass().getResource("/wiki/" + i + ".json.gz").getFile(), true
+            );
+	};
+	ki.commit();
+
+	String json = getString(getClass().getResource("/queries/metaquery.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());
+    };
+
+
+    @Test
+    public void searchJSONFailure () throws IOException {
+
+	// Construct index
+	KorapIndex ki = new KorapIndex();
+	// Indexing test files
+	for (String i : new String[] {"00001", "00002", "00003", "00004", "00005", "00006", "02439"}) {
+	    ki.addDocFile(
+	      getClass().getResource("/wiki/" + i + ".json.gz").getFile(), true
+            );
+	};
+	ki.commit();
+
+	KorapResult kr = new KorapSearch("{ query").run(ki);
+
+	assertEquals(0, kr.getTotalResults());
+	assertNotNull(kr.getError());
+    };
+
+
+
+
+    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();
+    };
 };