Added optionality for quantified queries
diff --git a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanAlterQueryWrapper.java b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanAlterQueryWrapper.java
index fd7c65d..5d5c4dc 100644
--- a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanAlterQueryWrapper.java
+++ b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanAlterQueryWrapper.java
@@ -17,12 +17,19 @@
     private SpanQuery query;
     private List<SpanQuery> alternatives;
     private boolean isNull = true;
+    private boolean isOptional = false;
 
     public SpanAlterQueryWrapper (String field) {
 	this.field = field;
 	this.alternatives = new ArrayList<>();
     };
 
+    public SpanAlterQueryWrapper (String field, SpanQuery query) {
+	this.field = field;
+	this.alternatives = new ArrayList<>();
+	this.alternatives.add(query);
+    };
+
     public SpanAlterQueryWrapper (String field, String ... terms) {
 	this.field = field;
 	this.alternatives = new ArrayList<>();
@@ -33,7 +40,11 @@
     };
 
     public SpanAlterQueryWrapper or (String term) {
-	this.alternatives.add(new SpanTermQuery(new Term(this.field, term)));
+	return this.or(new SpanTermQuery(new Term(this.field, term)));
+    };
+
+    public SpanAlterQueryWrapper or (SpanQuery query) {
+	this.alternatives.add(query);
 	this.isNull = false;
 	return this;
     };
@@ -41,6 +52,12 @@
     public SpanAlterQueryWrapper or (SpanQueryWrapperInterface term) {
 	if (term.isNull())
 	    return this;
+
+	// If one operand is optional, the whole group can be optional
+	// a | b* | c
+	if (term.isOptional())
+	    this.isOptional = true;
+
 	this.alternatives.add( term.toQuery() );
 	this.isNull = false;
 	return this;
@@ -75,7 +92,7 @@
     };
 
     public boolean isOptional () {
-	return false;
+	return this.isOptional;
     };
 
     public boolean isNull () {
diff --git a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanClassQueryWrapper.java b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanClassQueryWrapper.java
index 5a6c1a4..50671f6 100644
--- a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanClassQueryWrapper.java
+++ b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanClassQueryWrapper.java
@@ -43,7 +43,7 @@
     };
 
     public boolean isOptional () {
-	return false;
+	return this.subquery.isOptional();
     };
 
     public boolean isNull () {
diff --git a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanMatchModifyQueryWrapper.java b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanMatchModifyQueryWrapper.java
index 85926e2..4476f76 100644
--- a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanMatchModifyQueryWrapper.java
+++ b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanMatchModifyQueryWrapper.java
@@ -39,7 +39,7 @@
     };
 
     public boolean isOptional () {
-	return false;
+	return this.subquery.isOptional();
     };
 
     public boolean isNull () {
diff --git a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanRepetitionQueryWrapper.java b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanRepetitionQueryWrapper.java
index 68ffb5b..d4b8c54 100644
--- a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanRepetitionQueryWrapper.java
+++ b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanRepetitionQueryWrapper.java
@@ -10,7 +10,7 @@
     private SpanQueryWrapperInterface subquery;
     private int min = 1;
     private int max = 1;
-    private boolean optional = false;
+    private boolean isOptional = false;
     private boolean isNull = false;
 
     public SpanRepetitionQueryWrapper (SpanQueryWrapperInterface subquery, int exact) {
@@ -18,6 +18,7 @@
 
 	if (exact < 1 || this.subquery.isNull()) {
 	    this.isNull = true;
+	    this.isOptional = true;
 	    return;
 	};
 	
@@ -34,7 +35,8 @@
 	};
 	
 	if (min == 0) {
-	    this.optional = true;
+	    this.isOptional = true;
+	    min = 1;
 	    if (max == 0)
 		this.isNull = true;
 	};
@@ -45,11 +47,13 @@
     public SpanQuery toQuery () {
 	if (this.isNull)
 	    return (SpanQuery) null;
+	if (this.min == 1 && this.max == 1)
+	    return this.subquery.toQuery();
 	return new SpanRepetitionQuery(this.subquery.toQuery(), this.min, this.max, true);
     };
 
     public boolean isOptional () {
-	return this.optional;
+	return this.isOptional;
     };
 
     public boolean isNull () {
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 e3b755d..f758a8b 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
@@ -8,6 +8,7 @@
 import de.ids_mannheim.korap.query.SpanMultipleDistanceQuery;
 
 import de.ids_mannheim.korap.query.wrap.SpanSegmentQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanAlterQueryWrapper;
 import de.ids_mannheim.korap.query.wrap.SpanRegexQueryWrapper;
 import de.ids_mannheim.korap.query.wrap.SpanWildcardQueryWrapper;
 
@@ -23,8 +24,12 @@
     private String field;
     private ArrayList<SpanQuery> segments;
     private ArrayList<DistanceConstraint> constraints;
-    private boolean isInOrder = true;
-    private boolean isNull = true;
+    private boolean
+	isInOrder = true,
+	isNull = true,
+	isOptional = true,
+	lastIsOptional = false,
+	firstIsOptional = false;
 
     public SpanSequenceQueryWrapper (String field) {
 	this.field = field;
@@ -35,6 +40,7 @@
 	this(field);
 	for (int i = 0; i < terms.length; i++) {
 	    this.segments.add((SpanQuery) new SpanTermQuery(new Term(field, terms[i])));
+	    this.isOptional = false;
 	};
 	this.isNull = false;
     };
@@ -42,6 +48,7 @@
     public SpanSequenceQueryWrapper (String field, SpanQuery sq) {
 	this(field);
 	this.segments.add((SpanQuery) sq);
+	this.isOptional = false;
 	this.isNull = false;
     };
 
@@ -50,6 +57,14 @@
 	if (!sswq.isNull()) {
 	    this.segments.add((SpanQuery) sswq.toQuery());
 	    this.isNull = false;
+	    if (sswq.isOptional()) {
+		this.isOptional = true;
+		this.lastIsOptional = true;
+		this.firstIsOptional = true;
+	    }
+	    else {
+		this.isOptional = false;
+	    };
 	};
     };
 
@@ -58,6 +73,7 @@
 	if (!re.isNull()) {
 	    this.segments.add((SpanQuery) re.toQuery());
 	    this.isNull = false;
+	    this.isOptional = false;
 	};
     };
 
@@ -65,6 +81,7 @@
 	this(field);
 	if (!wc.isNull()) {
 	    this.segments.add((SpanQuery) wc.toQuery());
+	    this.isOptional = false;
 	    this.isNull = false;
 	};
     };
@@ -78,14 +95,74 @@
     };
 
     public SpanSequenceQueryWrapper append (String term) {
-	this.segments.add((SpanQuery) new SpanTermQuery(new Term(field, term)));
+	return this.append((SpanQuery) new SpanTermQuery(new Term(field, term)));
+    };
+    
+    public SpanSequenceQueryWrapper append (SpanQuery query) {
 	this.isNull = false;
+	this.isOptional = false;
+
+	// Check if there has to be alternation magic in action
+	if (this.lastIsOptional) {
+	    SpanAlterQueryWrapper saqw = new SpanAlterQueryWrapper(field, query);
+	    SpanSequenceQueryWrapper ssqw = new SpanSequenceQueryWrapper(field, query);
+	    // Remove last element of the list and prepend it
+	    ssqw.prepend(this.segments.remove(this.segments.size() - 1));
+	    saqw.or(ssqw);
+
+	    // Update boundary optionality
+	    if (this.firstIsOptional && this.segments.size() == 0)
+		this.firstIsOptional = false;
+	    this.lastIsOptional = false;
+
+	    this.segments.add((SpanQuery) saqw.toQuery());
+	}
+	else {
+	    this.segments.add(query);
+	};
+	
 	return this;
     };
 
     public SpanSequenceQueryWrapper append (SpanQueryWrapperInterface ssq) {
 	if (!ssq.isNull()) {
-	    this.segments.add((SpanQuery) ssq.toQuery());
+	    SpanQuery appendQuery = ssq.toQuery();
+	    if (!ssq.isOptional()) {
+		return this.append((SpanQuery) ssq.toQuery());
+	    };
+	    
+	    // Situation is a?b?
+	    if (this.lastIsOptional) {
+		// Remove last element of the list and prepend it
+		SpanQuery lastQuery = this.segments.remove(this.segments.size() - 1);
+		SpanAlterQueryWrapper saqw = new SpanAlterQueryWrapper(field, lastQuery);
+		SpanSequenceQueryWrapper ssqw = new SpanSequenceQueryWrapper(field, appendQuery);
+		ssqw.prepend(lastQuery);
+		saqw.or(appendQuery);
+		saqw.or(ssqw);
+		this.segments.add((SpanQuery) saqw.toQuery());
+	    }
+	    
+	    // Situation is ab?
+	    else if (this.segments.size() != 0) {
+		// Remove last element of the list and prepend it
+		SpanQuery lastQuery = this.segments.remove(this.segments.size() - 1);
+		SpanAlterQueryWrapper saqw = new SpanAlterQueryWrapper(field, lastQuery);
+		SpanSequenceQueryWrapper ssqw = new SpanSequenceQueryWrapper(field, appendQuery);
+		ssqw.prepend(lastQuery);
+		saqw.or(ssqw);
+		this.segments.add((SpanQuery) saqw.toQuery());
+	    }
+	    
+	    // Situation is b?
+	    else {
+		this.segments.add(appendQuery);
+
+		// Update boundary optionality
+		this.firstIsOptional = true;
+		this.isOptional = true;
+		this.lastIsOptional = true;
+	    };
 	    this.isNull = false;
 	};
 	return this;
@@ -93,46 +170,64 @@
 
     public SpanSequenceQueryWrapper append (SpanRegexQueryWrapper srqw) {
 	if (!srqw.isNull()) {
-	    this.segments.add((SpanQuery) srqw.toQuery());
-	    this.isNull = false;
+	    return this.append((SpanQuery) srqw.toQuery());
 	};
 	return this;
     };
     
     public SpanSequenceQueryWrapper append (SpanWildcardQueryWrapper swqw) {
 	if (!swqw.isNull()) {
-	    this.segments.add((SpanQuery) swqw.toQuery());
-	    this.isNull = false;
+	    return this.append((SpanQuery) swqw.toQuery());
 	};
 	return this;
     };
 
     public SpanSequenceQueryWrapper prepend (String term) {
-	this.segments.add(0, (SpanQuery) new SpanTermQuery(new Term(field, term)));
+	return this.prepend(new SpanTermQuery(new Term(field, term)));
+    };
+
+    public SpanSequenceQueryWrapper prepend (SpanQuery query) {
 	this.isNull = false;
+	this.isOptional = false;
+	
+	// Check if there has to be alternation magic in action
+	if (this.firstIsOptional) {
+	    SpanAlterQueryWrapper saqw = new SpanAlterQueryWrapper(field, query);
+	    SpanSequenceQueryWrapper ssqw = new SpanSequenceQueryWrapper(field, query);
+	    // Remove last element of the list and prepend it
+	    ssqw.append(this.segments.remove(0));
+	    saqw.or(ssqw);
+
+	    // Update boundary optionality
+	    if (this.lastIsOptional && this.segments.size() == 0)
+		this.lastIsOptional = false;
+	    this.firstIsOptional = false;
+
+	    this.segments.add(0, (SpanQuery) saqw.toQuery());
+	}
+	else {
+	    this.segments.add(0,query);
+	};
 	return this;
     };
 
     public SpanSequenceQueryWrapper prepend (SpanSegmentQueryWrapper ssq) {
 	if (!ssq.isNull()) {
-	    this.segments.add(0, (SpanQuery) ssq.toQuery());
-	    this.isNull = false;
+	    return this.prepend(ssq.toQuery());
 	};
 	return this;
     };
 
     public SpanSequenceQueryWrapper prepend (SpanRegexQueryWrapper re) {
 	if (!re.isNull()) {
-	    this.segments.add(0, (SpanQuery) re.toQuery());
-	    this.isNull = false;
+	    return this.prepend(re.toQuery());
 	};
 	return this;
     };
 
     public SpanSequenceQueryWrapper prepend (SpanWildcardQueryWrapper swqw) {
 	if (!swqw.isNull()) {
-	    this.segments.add(0, (SpanQuery) swqw.toQuery());
-	    this.isNull = false;
+	    return this.prepend(swqw.toQuery());
 	};
 	return this;
     };
@@ -254,7 +349,7 @@
     };
 
     public boolean isOptional () {
-	return false;
+	return this.isOptional;
     };
 
     public boolean isNull () {
diff --git a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanWithinQueryWrapper.java b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanWithinQueryWrapper.java
index fe6f9c2..68b07fd 100644
--- a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanWithinQueryWrapper.java
+++ b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanWithinQueryWrapper.java
@@ -10,13 +10,19 @@
 
 import org.apache.lucene.search.spans.SpanQuery;
 
+/*
+  Document: Optionality of operands will be ignored - while the optionality of the wrap is herited!
+
+  Idea:
+  - Maybe inherit the optionality when it is in an element and rewrite the query to an alternation if the wrap is
+*/
 
 
 public class SpanWithinQueryWrapper implements SpanQueryWrapperInterface {
     private SpanQueryWrapperInterface element;
     private SpanQueryWrapperInterface wrap;
     private byte flag;
-    private boolean isNull = true;;
+    private boolean isNull = true;
 
     public SpanWithinQueryWrapper (SpanQueryWrapperInterface element, SpanQueryWrapperInterface wrap) {
 	this.element = element;
@@ -30,6 +36,7 @@
 	this.element = element;
 	this.wrap = wrap;
 	this.flag = flag;
+
 	if (!element.isNull() && !wrap.isNull())
 	    this.isNull = false;
     };