ticket #201: koral:distance boundaries

Change-Id: I2e20dfbe527b302f4ff3088c7f83a8fd599c1ee6
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 224d088..2e9ac7b 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
@@ -933,11 +933,20 @@
     }
 
     private LinkedHashMap<String, Object> parseDistance(ParseTree rangeSpec) {
-        Integer min = 
-                Integer.parseInt(rangeSpec.getChild(0).toStringTree(parser));
+        String minString = rangeSpec.getChild(0).toStringTree(parser);
+        String maxString = null; // not always given, prevent NPE
+        if (minString.equals("0")) {
+            addError(StatusCodes.MALFORMED_QUERY, "Distance may not be 0!");
+            return KoralObjectGenerator.makeDistance("w", 0, 0);
+        }
+        // decrease by 1 to account for disparity between ANNIS distance and 
+        // koral:distance (ANNIS "x .1,3 y" means distance range 0,2 in KoralQ)
+        Integer min = Integer.parseInt(minString)-1; 
         Integer max = null;
-        if (rangeSpec.getChildCount()==3) 
-            max = Integer.parseInt(rangeSpec.getChild(2).toStringTree(parser));
+        if (rangeSpec.getChildCount() == 3) {
+            maxString = rangeSpec.getChild(2).toStringTree(parser);
+            max = Integer.parseInt(maxString)-1;
+        }
         return KoralObjectGenerator.makeDistance("w", min, max);
     }
 
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 f905beb..b188301 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
@@ -378,7 +378,6 @@
             ParseTree leftChild = node.getChild(i-1);
             ParseTree rightChild = node.getChild(i+1);
             if (leftChild != null) {
-                System.out.println(leftChild.getText());
                 if (! classWrapRegistry.containsKey(leftChild)) {
                     alignmentFirstArg = classCounter++;
                     classWrapRegistry.put(leftChild, alignmentFirstArg);
@@ -387,7 +386,6 @@
                 }
             }
             if (rightChild != null) {
-                System.out.println(rightChild.getText());
                 if (! classWrapRegistry.containsKey(rightChild)) {
                     alignmentSecondArg = classCounter++;
                     classWrapRegistry.put(rightChild, alignmentSecondArg);
@@ -893,10 +891,7 @@
 
     /**
      * Parses the min and max attributes for a boundary object as defined in 
-     * a distance node. Increases the min and max counters by 1 in order to
-     * reflect the disparity between the <i>distance</i> of operands (which is
-     * 1 in the case of directly succeeding tokens) and the Poliqarpish
-     * <i>number of intermediate tokens</i> (which is 0). 
+     * a distance node. 
      * 
      * @param distanceNode A node of category 'distance'
      * @return An array of two fields, where the first is the min value and the
@@ -909,9 +904,9 @@
                 .getChild(emptyTokenSeqIndex));
         Integer min = minmax[0];
         Integer max = minmax[1];
-        min++;
-        if (max != null)
-            max++;
+//        min++;
+//        if (max != null)
+//            max++;
         return new Integer[] { min, max };
     }
 
diff --git a/src/test/java/de/ids_mannheim/korap/query/serialize/AnnisQueryProcessorTest.java b/src/test/java/de/ids_mannheim/korap/query/serialize/AnnisQueryProcessorTest.java
index 80087eb..fc1b74d 100644
--- a/src/test/java/de/ids_mannheim/korap/query/serialize/AnnisQueryProcessorTest.java
+++ b/src/test/java/de/ids_mannheim/korap/query/serialize/AnnisQueryProcessorTest.java
@@ -424,8 +424,22 @@
         assertEquals(true,                  res.at("/query/operands/2").isMissingNode());
         assertEquals("koral:distance",      res.at("/query/distances/0/@type").asText());
         assertEquals("koral:boundary",      res.at("/query/distances/0/boundary/@type").asText());
-        assertEquals(2,                     res.at("/query/distances/0/boundary/min").asInt());
-        assertEquals(3,                     res.at("/query/distances/0/boundary/max").asInt());
+        assertEquals(1,                     res.at("/query/distances/0/boundary/min").asInt());
+        assertEquals(2,                     res.at("/query/distances/0/boundary/max").asInt());
+        
+        query = "tok=\"der\" & tok=\"die\" & #1 .2 #2";
+        qs.setQuery(query, "annis");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals("koral:group",         res.at("/query/@type").asText());
+        assertEquals("operation:sequence",  res.at("/query/operation").asText());
+        assertEquals("der",                 res.at("/query/operands/0/wrap/key").asText());
+        assertEquals("die",                 res.at("/query/operands/1/wrap/key").asText());
+        assertEquals(true,                  res.at("/query/inOrder").asBoolean());
+        assertEquals(true,                  res.at("/query/operands/2").isMissingNode());
+        assertEquals("koral:distance",      res.at("/query/distances/0/@type").asText());
+        assertEquals("koral:boundary",      res.at("/query/distances/0/boundary/@type").asText());
+        assertEquals(1,                     res.at("/query/distances/0/boundary/min").asInt());
+        assertEquals(true,                  res.at("/query/distances/0/boundary/max").isMissingNode());
 
         query = "tok=\"der\" & tok=\"die\" & #1 .* #2";
         qs.setQuery(query, "annis");
@@ -458,8 +472,8 @@
         assertEquals(true,                  res.at("/query/operands/2").isMissingNode());
         assertEquals("koral:distance",      res.at("/query/distances/0/@type").asText());
         assertEquals("koral:boundary",      res.at("/query/distances/0/boundary/@type").asText());
-        assertEquals(2,                     res.at("/query/distances/0/boundary/min").asInt());
-        assertEquals(3,                     res.at("/query/distances/0/boundary/max").asInt());
+        assertEquals(1,                     res.at("/query/distances/0/boundary/min").asInt());
+        assertEquals(2,                     res.at("/query/distances/0/boundary/max").asInt());
 
         query = "tok=\"der\" & tok=\"die\" & #1 ^* #2";
         qs.setQuery(query, "annis");
@@ -596,7 +610,7 @@
 
     @Test
     public void testMultipleMixedOperators() throws Exception {
-        query = "tok=\"Sonne\" & tok=\"Mond\" & tok=\"Sterne\" & #1 > #2 .0,4 #3";
+        query = "tok=\"Sonne\" & tok=\"Mond\" & tok=\"Sterne\" & #1 > #2 .1,4 #3";
         qs.setQuery(query, "annis");
         res = mapper.readTree(qs.toJSON());
         assertEquals("koral:group",         res.at("/query/@type").asText());
@@ -615,9 +629,9 @@
         assertEquals("Sterne",              res.at("/query/operands/1/wrap/key").asText());
         assertEquals("w",                   res.at("/query/distances/0/key").asText());
         assertEquals(0,                     res.at("/query/distances/0/boundary/min").asInt());
-        assertEquals(4,                     res.at("/query/distances/0/boundary/max").asInt());
+        assertEquals(3,                     res.at("/query/distances/0/boundary/max").asInt());
 
-        query = "tok=\"Sonne\" & tok=\"Mond\" & #1 > #2 .0,4  tok=\"Sterne\"";
+        query = "tok=\"Sonne\" & tok=\"Mond\" & #1 > #2 .1,4  tok=\"Sterne\"";
         qs.setQuery(query, "annis");
         res = mapper.readTree(qs.toJSON());
         assertEquals("koral:group",         res.at("/query/@type").asText());
@@ -636,7 +650,7 @@
         assertEquals("Sterne",              res.at("/query/operands/1/wrap/key").asText());
         assertEquals("w",                   res.at("/query/distances/0/key").asText());
         assertEquals(0,                     res.at("/query/distances/0/boundary/min").asInt());
-        assertEquals(4,                     res.at("/query/distances/0/boundary/max").asInt());
+        assertEquals(3,                     res.at("/query/distances/0/boundary/max").asInt());
 
         query = "cat=\"NP\" & cat=\"VP\" & cat=\"PP\" & #1 $ #2 > #3";
         qs.setQuery(query, "annis");
diff --git a/src/test/java/de/ids_mannheim/korap/query/serialize/PoliqarpPlusQueryProcessorTest.java b/src/test/java/de/ids_mannheim/korap/query/serialize/PoliqarpPlusQueryProcessorTest.java
index 7ce445d..b7858af 100644
--- a/src/test/java/de/ids_mannheim/korap/query/serialize/PoliqarpPlusQueryProcessorTest.java
+++ b/src/test/java/de/ids_mannheim/korap/query/serialize/PoliqarpPlusQueryProcessorTest.java
@@ -334,8 +334,8 @@
         assertEquals("koral:distance",		res.at("/query/distances").elements().next().at("/@type").asText());
         assertEquals("w",					res.at("/query/distances").elements().next().at("/key").asText());
         assertEquals("koral:boundary",		res.at("/query/distances").elements().next().at("/boundary/@type").asText());
-        assertEquals(2,						res.at("/query/distances").elements().next().at("/boundary/min").asInt());
-        assertEquals(2,						res.at("/query/distances").elements().next().at("/boundary/max").asInt());
+        assertEquals(1,						res.at("/query/distances").elements().next().at("/boundary/min").asInt());
+        assertEquals(1,						res.at("/query/distances").elements().next().at("/boundary/max").asInt());
         operands = Lists.newArrayList(res.at("/query/operands").elements());
         assertEquals("koral:token",			operands.get(0).at("/@type").asText());
         assertEquals("der",					operands.get(0).at("/wrap/key").asText());
@@ -355,8 +355,8 @@
         assertEquals("koral:distance",		res.at("/query/distances").elements().next().at("/@type").asText());
         assertEquals("w",					res.at("/query/distances").elements().next().at("/key").asText());
         assertEquals("koral:boundary",		res.at("/query/distances").elements().next().at("/boundary/@type").asText());
-        assertEquals(3,						res.at("/query/distances").elements().next().at("/boundary/min").asInt());
-        assertEquals(3,						res.at("/query/distances").elements().next().at("/boundary/max").asInt());
+        assertEquals(2,						res.at("/query/distances").elements().next().at("/boundary/min").asInt());
+        assertEquals(2,						res.at("/query/distances").elements().next().at("/boundary/max").asInt());
         operands = Lists.newArrayList(res.at("/query/operands").elements());
         assertEquals("koral:token",			operands.get(0).at("/@type").asText());
         assertEquals("der",					operands.get(0).at("/wrap/key").asText());
@@ -376,8 +376,8 @@
         assertEquals("koral:distance",		res.at("/query/distances").elements().next().at("/@type").asText());
         assertEquals("w",					res.at("/query/distances").elements().next().at("/key").asText());
         assertEquals("koral:boundary",		res.at("/query/distances").elements().next().at("/boundary/@type").asText());
-        assertEquals(2,						res.at("/query/distances").elements().next().at("/boundary/min").asInt());
-        assertEquals(3,						res.at("/query/distances").elements().next().at("/boundary/max").asInt());
+        assertEquals(1,						res.at("/query/distances").elements().next().at("/boundary/min").asInt());
+        assertEquals(2,						res.at("/query/distances").elements().next().at("/boundary/max").asInt());
         operands = Lists.newArrayList(res.at("/query/operands").elements());
         assertEquals("koral:token",			operands.get(0).at("/@type").asText());
         assertEquals("der",					operands.get(0).at("/wrap/key").asText());
@@ -397,7 +397,7 @@
         assertEquals("koral:distance",		res.at("/query/distances").elements().next().at("/@type").asText());
         assertEquals("w",					res.at("/query/distances").elements().next().at("/key").asText());
         assertEquals("koral:boundary",		res.at("/query/distances").elements().next().at("/boundary/@type").asText());
-        assertEquals(2,						res.at("/query/distances").elements().next().at("/boundary/min").asInt());
+        assertEquals(1,						res.at("/query/distances").elements().next().at("/boundary/min").asInt());
         assertEquals(true,					res.at("/query/distances").elements().next().at("/boundary/max").isMissingNode());
         operands = Lists.newArrayList(res.at("/query/operands").elements());
         assertEquals("koral:token",			operands.get(0).at("/@type").asText());
@@ -418,7 +418,7 @@
         assertEquals("koral:distance",		res.at("/query/distances").elements().next().at("/@type").asText());
         assertEquals("w",					res.at("/query/distances").elements().next().at("/key").asText());
         assertEquals("koral:boundary",		res.at("/query/distances").elements().next().at("/boundary/@type").asText());
-        assertEquals(1,						res.at("/query/distances").elements().next().at("/boundary/min").asInt());
+        assertEquals(0,						res.at("/query/distances").elements().next().at("/boundary/min").asInt());
         assertEquals(true,					res.at("/query/distances").elements().next().at("/boundary/max").isMissingNode());
 
         query = "[base=der][]{2,5}[base=Mann][]?[][base=Frau]";
@@ -430,8 +430,8 @@
         assertEquals("koral:distance",		res.at("/query/distances").elements().next().at("/@type").asText());
         assertEquals("w",					res.at("/query/distances").elements().next().at("/key").asText());
         assertEquals("koral:boundary",		res.at("/query/distances").elements().next().at("/boundary/@type").asText());
-        assertEquals(3,						res.at("/query/distances").elements().next().at("/boundary/min").asInt());
-        assertEquals(6,						res.at("/query/distances").elements().next().at("/boundary/max").asInt());
+        assertEquals(2,						res.at("/query/distances").elements().next().at("/boundary/min").asInt());
+        assertEquals(5,						res.at("/query/distances").elements().next().at("/boundary/max").asInt());
         operands = Lists.newArrayList(res.at("/query/operands").elements());
         assertEquals("koral:token",			operands.get(0).at("/@type").asText());
         assertEquals("der",					operands.get(0).at("/wrap/key").asText());
@@ -442,8 +442,8 @@
         assertEquals("koral:distance",		operands.get(1).get("distances").elements().next().at("/@type").asText());
         assertEquals("w",					operands.get(1).get("distances").elements().next().at("/key").asText());
         assertEquals("koral:boundary",		operands.get(1).get("distances").elements().next().at("/boundary/@type").asText());
-        assertEquals(2,						operands.get(1).get("distances").elements().next().at("/boundary/min").asInt());
-        assertEquals(3,						operands.get(1).get("distances").elements().next().at("/boundary/max").asInt());
+        assertEquals(1,						operands.get(1).get("distances").elements().next().at("/boundary/min").asInt());
+        assertEquals(2,						operands.get(1).get("distances").elements().next().at("/boundary/max").asInt());
         operands = Lists.newArrayList(operands.get(1).get("operands").elements());
         assertEquals("Mann",				operands.get(0).at("/wrap/key").asText());
         assertEquals("lemma",				operands.get(0).at("/wrap/layer").asText());
@@ -469,7 +469,7 @@
         assertEquals("koral:distance",		operands.get(1).get("distances").elements().next().at("/@type").asText());
         assertEquals("w",					operands.get(1).get("distances").elements().next().at("/key").asText());
         assertEquals("koral:boundary",		operands.get(1).get("distances").elements().next().at("/boundary/@type").asText());
-        assertEquals(1,						operands.get(1).get("distances").elements().next().at("/boundary/min").asInt());
+        assertEquals(0,						operands.get(1).get("distances").elements().next().at("/boundary/min").asInt());
         assertEquals(true,					operands.get(1).get("distances").elements().next().at("/boundary/max").isMissingNode());
         operands = Lists.newArrayList(operands.get(1).get("operands").elements());
         assertEquals("der",					operands.get(0).at("/wrap/key").asText());