Throw error for optionality in distances

Change-Id: Ib555eef2cf512ce4bf443779096175c3009cc431
diff --git a/src/main/java/de/ids_mannheim/korap/Krill.java b/src/main/java/de/ids_mannheim/korap/Krill.java
index de55528..437e1e9 100644
--- a/src/main/java/de/ids_mannheim/korap/Krill.java
+++ b/src/main/java/de/ids_mannheim/korap/Krill.java
@@ -178,10 +178,10 @@
                 // Throw an error, in case the query matches everywhere
                 if (qw.isEmpty()) {
                     this.addError(780, "This query matches everywhere");
-				}
-				else if (qw.isNull()) {
-					this.addError(783, "This query can't match anywhere");
-				}
+                }
+                else if (qw.isNull()) {
+                    this.addError(783, "This query can't match anywhere");
+                }
 
                 else {
 
diff --git a/src/main/java/de/ids_mannheim/korap/KrillQuery.java b/src/main/java/de/ids_mannheim/korap/KrillQuery.java
index 3185603..3d70505 100644
--- a/src/main/java/de/ids_mannheim/korap/KrillQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/KrillQuery.java
@@ -115,7 +115,7 @@
             // Set max boundary
             this.max = json.has("max") ? json.get("max").asInt(defaultMax)
                     : defaultMax;
-			
+
             if (DEBUG)
                 log.trace("Found koral:boundary with {}:{}", min, max);
         };
@@ -723,12 +723,12 @@
         // Check relation between min and max
         if (min > max)
             max = max;
-		
+
         SpanQueryWrapper sqw = this._fromKoral(operands.get(0));
 
         if (sqw.maybeExtension())
             return sqw.setMin(min).setMax(max);
-		
+
         return new SpanRepetitionQueryWrapper(sqw, min, max);
     };
 
diff --git a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanQueryWrapper.java b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanQueryWrapper.java
index 82349b2..f31006d 100644
--- a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanQueryWrapper.java
+++ b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanQueryWrapper.java
@@ -36,7 +36,7 @@
     protected boolean hasClass = false, isNull = true, isOptional = false,
             isNegative = false, isEmpty = false, isExtended = false,
             isExtendedToTheRight = false, maybeUnsorted = false,
-            retrieveNode = false;
+            retrieveNode = false, isProblematic = false;
 
 
     /**
@@ -47,7 +47,6 @@
      * @throws QueryException
      */
     public SpanQuery toFragmentQuery () throws QueryException {
-        System.err.println("||||||||||||||||||||||||||");
         return (SpanQuery) null;
     };
 
@@ -62,8 +61,9 @@
      */
     public SpanQuery toQuery () throws QueryException {
 
-        if (this.isNull() || this.isEmpty())
+        if (this.isNull() || this.isEmpty()) {
             return null;
+        };
 
         // Wrap the query in a <base/s=t>, if it's extended to the right
         if (this.isExtendedToTheRight()) {
diff --git a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanSequenceQueryWrapper.java b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanSequenceQueryWrapper.java
index 72fadb2..f100853 100644
--- a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanSequenceQueryWrapper.java
+++ b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanSequenceQueryWrapper.java
@@ -612,6 +612,7 @@
 
 
     public boolean isEmpty () {
+
         if (this.segments.size() == 1)
             return this.segments.get(0).isEmpty();
 
@@ -741,6 +742,11 @@
             return (SpanQuery) query;
         };
 
+        if (this.hasConstraints() && this.isProblematic) {
+            throw new QueryException(613,
+                    "Distance constraints not supported with empty, optional or negative operands");
+        };
+
         // DistanceQueries
         if (this.constraints.size() == 1) {
             DistanceConstraint constraint = this.constraints.get(0);
@@ -753,6 +759,11 @@
                     if (this.segments.get(i).isExtended())
                         throw new QueryException(613, limitationError);
 
+                    /* Maybe important
+                    if (this.segments.get(i).isOptional())
+                        throw new QueryException(613, limitationError);
+                    */
+
                     SpanQuery sq = (SpanQuery) this.segments.get(i)
                             .retrieveNode(this.retrieveNode).toFragmentQuery();
                     if (sq == null)
@@ -772,6 +783,11 @@
                     if (this.segments.get(i).isExtended())
                         throw new QueryException(613, limitationError);
 
+                    /* May be necessary
+                    if (this.segments.get(i).isOptional())
+                        throw new QueryException(613, limitationError);
+                    */
+
                     SpanQuery sq = (SpanQuery) this.segments.get(i)
                             .retrieveNode(this.retrieveNode).toFragmentQuery();
                     if (sq == null)
@@ -817,6 +833,8 @@
       - look for an anchor next to it
       - merge the problematic segment with the anchor
       - go on
+
+      - This does not work for distance constraints!
     */
     private boolean _solveProblematicSequence () {
         int size = this.segments.size();
@@ -838,6 +856,18 @@
                 if (DEBUG)
                     log.trace("segment {} is problematic", i);
 
+                // Sequences with distance constraints do not support empty or optional
+                // elemets
+                if (this.hasConstraints()) {
+                    if (DEBUG) {
+                        log.trace("Sequence has constraints, "
+                                + "that do not allow empty or optional elements");
+                    };
+                    this.isSolved = true;
+                    this.isProblematic = true;
+                    return false;
+                };
+
                 // [problem][anchor]
                 if (i < (size - 1) && this.segments.get(i + 1).maybeAnchor()) {
                     if (DEBUG)
@@ -853,6 +883,8 @@
 
                     // An error occurred while solving the problem
                     catch (QueryException e) {
+                        this.isSolved = true;
+                        this.isProblematic = true;
                         return false;
                     };
 
@@ -880,6 +912,8 @@
                                         true));
                     }
                     catch (QueryException e) {
+                        this.isSolved = true;
+                        this.isProblematic = true;
                         return false;
                     };
 
@@ -916,10 +950,12 @@
                 return _solveProblematicSequence();
 
             this.isSolved = true;
-            return true;
+            this.isProblematic = true;
+            return false; // true;
         };
 
         this.isSolved = true;
+        this.isProblematic = false;
         return false;
     };
 
diff --git a/src/test/java/de/ids_mannheim/korap/index/TestMatchIdentifier.java b/src/test/java/de/ids_mannheim/korap/index/TestMatchIdentifier.java
index 8a0ac45..072a633 100644
--- a/src/test/java/de/ids_mannheim/korap/index/TestMatchIdentifier.java
+++ b/src/test/java/de/ids_mannheim/korap/index/TestMatchIdentifier.java
@@ -277,10 +277,10 @@
 
         /*
         km = ki.getMatchInfo(
-			"match-GOE!GOE_AGX.00002-p10-20",
-							 "tokens", true, (String) null, (String) null, true, true, false);	
+        	"match-GOE!GOE_AGX.00002-p10-20",
+        					 "tokens", true, (String) null, (String) null, true, true, false);	
         assertEquals("", km.toJsonString());
-		*/		
+        */
     };
 
 
diff --git a/src/test/java/de/ids_mannheim/korap/index/TestRepetitionIndex.java b/src/test/java/de/ids_mannheim/korap/index/TestRepetitionIndex.java
index cedb868..131ae70 100644
--- a/src/test/java/de/ids_mannheim/korap/index/TestRepetitionIndex.java
+++ b/src/test/java/de/ids_mannheim/korap/index/TestRepetitionIndex.java
@@ -204,6 +204,7 @@
         //		}
     }
 
+
     @Test
     public void testCase5 () throws IOException {
         ki = new KrillIndex();
diff --git a/src/test/java/de/ids_mannheim/korap/query/TestKrillQueryJSON.java b/src/test/java/de/ids_mannheim/korap/query/TestKrillQueryJSON.java
index 31a3612..f11a5da 100644
--- a/src/test/java/de/ids_mannheim/korap/query/TestKrillQueryJSON.java
+++ b/src/test/java/de/ids_mannheim/korap/query/TestKrillQueryJSON.java
@@ -571,15 +571,17 @@
         };
     };
 
-	@Test
+
+    @Test
     public void queryJSONdistancesWithRegexes () throws QueryException {
-		// "der" []{2,3} [opennlp/p="NN"]
+        // "der" []{2,3} [opennlp/p="NN"]
         try {
             String json = getString(getClass().getResource(
                     "/queries/bugs/distances_with_regex_bug.jsonld").getFile());
             KrillQuery kq = new KrillQuery("tokens");
 
-            assertEquals(kq.fromKoral(json).toQuery().toString(),
+            assertEquals(
+                    kq.fromKoral(json).toQuery().toString(),
                     "spanDistance(SpanMultiTermQueryWrapper(tokens:/s:der/), SpanMultiTermQueryWrapper(tokens:/opennlp/p:NN/), [(w[3:4], ordered, notExcluded)])");
         }
         catch (QueryException e) {
@@ -587,6 +589,7 @@
         };
     };
 
+
     public static String getString (String path) {
         StringBuilder contentBuilder = new StringBuilder();
         try {
diff --git a/src/test/java/de/ids_mannheim/korap/query/TestSpanSequenceQueryJSON.java b/src/test/java/de/ids_mannheim/korap/query/TestSpanSequenceQueryJSON.java
index e5030f8..97a9eb2 100644
--- a/src/test/java/de/ids_mannheim/korap/query/TestSpanSequenceQueryJSON.java
+++ b/src/test/java/de/ids_mannheim/korap/query/TestSpanSequenceQueryJSON.java
@@ -283,7 +283,7 @@
         }
         catch (QueryException qe) {
             assertEquals(
-                    "Distance constraints not supported with empty or negative operands",
+                    "Distance constraints not supported with empty, optional or negative operands",
                     qe.getMessage());
         };
     };
@@ -351,27 +351,34 @@
                 sqwi.toQuery().toString());
     };
 
+
     @Test
-    public void queryJSONkoralOptionalityInDistanceBug () throws QueryException {
-		// Sonne [] Mond?
-        SpanQueryWrapper sqwi = jsonQueryFile("distance-with-optionality.jsonld");
-        assertEquals(
-			"focus(254: spanContain(<tokens:base/s:t />, {254: spanOr([spanExpansion(tokens:s:Sonne, []{1, 1}, right), spanNext(spanExpansion(tokens:s:Sonne, []{1, 1}, right), tokens:s:Mond)])}))",
-			sqwi.toQuery().toString());
-		// Could also be a distance at the end ... that's a query planner thing.
+    public void queryJSONkoralOptionalityInDistanceBug () {
+        try {
+            // Sonne [] Mond?
+            SpanQueryWrapper sqwi = jsonQueryFile("distance-with-optionality.jsonld");
+            sqwi.toQuery().toString();
+        }
+        catch (QueryException qe) {
+            assertEquals(
+                    "Distance constraints not supported with empty, optional or negative operands",
+                    qe.getMessage());
+        }
+        // Could also be a distance at the end ... that's a query planner thing.
     };
 
-	@Test
+
+    @Test
     public void queryJSONkoralOptionalityAfterEmptyBug () throws QueryException {
-		// Sonne [] Mond?
+        // Sonne [] Mond?
         SpanQueryWrapper sqwi = jsonQueryFile("empty-followed-by-optionality.jsonld");
         assertEquals(
-			"focus(254: spanContain(<tokens:base/s:t />, {254: spanOr([spanExpansion(tokens:s:Sonne, []{1, 1}, right), spanNext(spanExpansion(tokens:s:Sonne, []{1, 1}, right), tokens:s:Mond)])}))",
-			sqwi.toQuery().toString());
-		// Could also be a distance at the end ... that's a query planner thing.
+                "focus(254: spanContain(<tokens:base/s:t />, {254: spanOr([spanExpansion(tokens:s:Sonne, []{1, 1}, right), spanNext(spanExpansion(tokens:s:Sonne, []{1, 1}, right), tokens:s:Mond)])}))",
+                sqwi.toQuery().toString());
+        // Could also be a distance at the end ... that's a query planner thing.
     };
 
-	
+
 
     // get query wrapper based on json file
     public SpanQueryWrapper jsonQueryFile (String filename) {
diff --git a/src/test/java/de/ids_mannheim/korap/search/TestKrill.java b/src/test/java/de/ids_mannheim/korap/search/TestKrill.java
index 1fb1f2b..7ecf1c2 100644
--- a/src/test/java/de/ids_mannheim/korap/search/TestKrill.java
+++ b/src/test/java/de/ids_mannheim/korap/search/TestKrill.java
@@ -1019,28 +1019,29 @@
                 "Operation needs operand list");
     };
 
-	
-	@Test
+
+    @Test
     public void searchJSONdistanceWithRegexesBug () throws IOException {
         // Construct index
         KrillIndex ki = new KrillIndex();
         // Indexing test files
         for (String i : new String[] { "00001" }) {
-			// , "00002", "00003", "00004", "00005", "00006", "02439"
+            // , "00002", "00003", "00004", "00005", "00006", "02439"
             ki.addDoc(
                     getClass().getResourceAsStream("/wiki/" + i + ".json.gz"),
                     true);
         };
         ki.commit();
 
-		// "der" []{2,3} [opennlp/p="NN"]
-       String json = getString(getClass().getResource(
+        // "der" []{2,3} [opennlp/p="NN"]
+        String json = getString(getClass().getResource(
                 "/queries/bugs/distances_with_regex_bug.jsonld").getFile());
 
         Result kr = new Krill(json).apply(ki);
 
-		assertEquals(kr.getMatch(0).getSnippetBrackets(),
-					 "Mit Ausnahme von Fremdwörtern und Namen ist das A der einzige Buchstabe im Deutschen, [[der zweifach am Anfang]] eines Wortes stehen darf, etwa im Wort Aal.");
+        assertEquals(
+                kr.getMatch(0).getSnippetBrackets(),
+                "Mit Ausnahme von Fremdwörtern und Namen ist das A der einzige Buchstabe im Deutschen, [[der zweifach am Anfang]] eines Wortes stehen darf, etwa im Wort Aal.");
 
     };
 
@@ -1128,22 +1129,24 @@
         assertEquals(kr.getTotalResults(), 1);
     };
 
-	
-	@Test
+
+    @Test
     public void queryJSONzeroRepetitionBug () throws IOException {
-		// der{0}
-		KrillIndex ki = new KrillIndex();
-		ki.addDoc(getClass().getResourceAsStream("/wiki/00001.json.gz"), true);
-		ki.commit();
-			
-		String json = getString(getClass().getResource(
-									"/queries/bugs/zero_repetition_bug.jsonld").getFile());
+        // der{0}
+        KrillIndex ki = new KrillIndex();
+        ki.addDoc(getClass().getResourceAsStream("/wiki/00001.json.gz"), true);
+        ki.commit();
 
-		Result kr = new Krill(json).apply(ki);
+        String json = getString(getClass().getResource(
+                "/queries/bugs/zero_repetition_bug.jsonld").getFile());
 
-		assertEquals(783, kr.getError(0).getCode());
-		assertEquals("This query can't match anywhere", kr.getError(0).getMessage());
-	};
+        Result kr = new Krill(json).apply(ki);
+
+        assertEquals(783, kr.getError(0).getCode());
+        assertEquals("This query can't match anywhere", kr.getError(0)
+                .getMessage());
+    };
+
 
     /**
      * This is a Schreibgebrauch ressource that didn't work for