Refactoring of KorapQuery
diff --git a/Changes b/Changes
index 3e98af2..e327a80 100644
--- a/Changes
+++ b/Changes
@@ -1,9 +1,10 @@
-0.49.3 2014-01-26
+0.49.3 2014-01-29
         - [documentation] Improved documentation for API classes (diewald)
 	- [documentation] Improved documentation for various queries (margaretha)
 	- [feature] Added deserialization of SpanSubSpanQueries (margaretha)
 	- [bugfix] Null filters are now correctly extended (diewald)
-	- [cleanup] Refactoring of KorapResult and KorapResponse (diewald)
+	- [cleanup] Refactoring of KorapResult, KorapResponse, KorapQuery,
+	  deprecated operation:or in favor of operation:junction (diewald)
 
 0.49.2 2014-12-05
 	- [documentation] Improved documentation for various queries (margaretha)
diff --git a/src/main/java/de/ids_mannheim/korap/KorapQuery.java b/src/main/java/de/ids_mannheim/korap/KorapQuery.java
index 89f4492..2e8d299 100644
--- a/src/main/java/de/ids_mannheim/korap/KorapQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/KorapQuery.java
@@ -30,14 +30,33 @@
   negation terms (and negation subqueries), like
   [base=Der]([base=alte]|[base=junge])[base=Mann & p!=ADJA]![base=war | base=lag]
   Search for all documents containing "s:Der" and ("s:alte" or "s:junge") and "s:Mann"
-
 */
 
 /**
- * @author Nils Diewald
- *
  * KorapQuery implements a simple API for wrapping
- * KorAP Lucene Index specific query classes.
+ * KorAP Lucene Index specific query classes and provides
+ * deserialization of JSON-LD queries.
+ *
+ * Build complex queries.
+ * <blockquote><pre>
+ *   KorapQuery kq = new KorapQuery("tokens");
+ *   SpanQueryWrapper sqw = (SpanQueryWrapper)
+ *     kq.seq(
+ *       kq.empty(),
+ *       kq.seg(
+ *         kq.re("mate/p=N.*"),
+ *         kq.re("opennlp/p=N.*")
+ *       )
+ *     );
+ * </pre></blockquote>
+ *
+ * Deserialize from JSON input.
+ * <blockquote><pre>
+ *   SpanQueryWrapper = new KorapQuery("tokens").fromJson("{... JsonString ...}");
+ * </pre></blockquote>
+ *
+ * @author diewald
+ *
  */
 public class KorapQuery extends Notifications {
     private String field;
@@ -49,841 +68,1036 @@
     // This advices the java compiler to ignore all loggings
     public static final boolean DEBUG = false;
 
+    // This is obsolete!
     public static final byte
-	OVERLAP      = SpanWithinQuery.OVERLAP,
-	REAL_OVERLAP = SpanWithinQuery.REAL_OVERLAP,
-	WITHIN       = SpanWithinQuery.WITHIN,
-	REAL_WITHIN  = SpanWithinQuery.REAL_WITHIN,
-	ENDSWITH     = SpanWithinQuery.ENDSWITH,
-	STARTSWITH   = SpanWithinQuery.STARTSWITH,
-	MATCH        = SpanWithinQuery.MATCH;
+        OVERLAP      = SpanWithinQuery.OVERLAP,
+        REAL_OVERLAP = SpanWithinQuery.REAL_OVERLAP,
+        WITHIN       = SpanWithinQuery.WITHIN,
+        REAL_WITHIN  = SpanWithinQuery.REAL_WITHIN,
+        ENDSWITH     = SpanWithinQuery.ENDSWITH,
+        STARTSWITH   = SpanWithinQuery.STARTSWITH,
+        MATCH        = SpanWithinQuery.MATCH;
 
     private static final int MAX_CLASS_NUM = 255; // 127;
 
     /**
-     * Constructs a new base object for query generation.
+     * Constructs a new object for query generation.
+     *
      * @param field The specific index field for the query.
      */
     public KorapQuery (String field) {
-	this.field = field;
-	this.json = new ObjectMapper();
+        this.field = field;
+        this.json = new ObjectMapper();
     };
 
+
+    // Private class for korap:boundary objects
+    private class Boundary {
+        public int min, max;
+
+        // Constructor for boundaries
+        public Boundary (JsonNode json, int defaultMin, int defaultMax)
+            throws QueryException {
+            
+            // No @type defined
+            if (!json.has("@type")) {
+                throw new QueryException(
+                    701,
+                    "JSON-LD group has no @type attribute"
+                );
+            };
+
+            // Wrong @type defined
+            if (!json.get("@type").asText().equals("korap:boundary"))
+                throw new QueryException(702, "Boundary definition is invalid");
+
+            // Set min boundary
+            this.min = json.has("min") ?
+                json.get("min").asInt(defaultMin) :
+                defaultMin;
+
+            // Set max boundary
+            this.max = json.has("max") ?
+                json.get("max").asInt(defaultMax) :
+                defaultMax;
+            
+            if (DEBUG)
+                log.trace("Found korap:boundary with {}:{}", min, max);
+        };
+    };
+
+
     /**
-     * Private class for korap:boundary objects
+     * Deserialize JSON-LD query to a {@link SpanQueryWrapper} object.
+     *
+     * <p>
+     * <blockquote><pre>
+     *   KorapQuery kq = new KorapQuery("tokens");
+     *   SpanQueryWrapper sqw = kq.fromJson('{"@type":"korap:token","wrap":{' +
+     *      '"@type":"korap:term","foundry":"opennlp",' +
+     *      '"key":"tree","layer":"orth",' +
+     *      '"match":"match:eq"}}'
+     *   );
+     * </pre></blockquote>
+     *
+     * @param json String representing the JSON query string.
+     * @return {@link SpanQueryWrapper} object. 
+     * @throws QueryException
      */
-    private class Boundary {
-	public int min, max;
+    public SpanQueryWrapper fromJson (String json) throws QueryException {
+        JsonNode jsonN;
+        try {
+            // Read Json string
+            jsonN = this.json.readValue(json, JsonNode.class);
+        }
 
-	public Boundary (JsonNode json, int defaultMin, int defaultMax) throws QueryException {
+        // Something went wrong
+        catch (IOException e) {
+            String msg = e.getMessage();
+            log.warn("Unable to parse JSON: " + msg.split("\n")[0]);
+            throw new QueryException(621, "Unable to parse JSON");
+        };
 
-	    if (!json.has("@type"))
-		throw new QueryException(701, "JSON-LD group has no @type attribute");
+        // The query is nested in a parent query
+        if (!jsonN.has("@type") && jsonN.has("query"))
+            jsonN = jsonN.get("query");
 
-	    if (!json.get("@type").asText().equals("korap:boundary"))
-		throw new QueryException(702, "Boundary definition is invalid");
-
-	    // Set min boundary
-	    if (json.has("min"))
-		this.min = json.get("min").asInt(defaultMin);
-	    else
-		this.min = defaultMin;
-
-	    // Set max boundary
-	    if (json.has("max"))
-		this.max = json.get("max").asInt(defaultMax);
-	    else
-		this.max = defaultMax;
-
-	    if (DEBUG)
-		log.trace("Found korap:boundary with {}:{}", min, max);
-	};
+        // Deserialize from node
+        return this.fromJson(jsonN);
     };
 
-    public SpanQueryWrapper fromJson (String jsonString) throws QueryException {
-	JsonNode json;
-	try {
-	    json = this.json.readValue(jsonString, JsonNode.class);
-	}
-	catch (IOException e) {
-	    String msg = e.getMessage();
-	    log.warn("Unable to parse JSON: " + msg.split("\n")[0]);
-	    throw new QueryException(621, "Unable to parse JSON");
-	};
 
-	if (!json.has("@type") && json.has("query"))
-	    json = json.get("query");
-
-	return this.fromJson(json);
-    };
-
-    // http://fasterxml.github.io/jackson-databind/javadoc/2.2.0/com/fasterxml/jackson/databind/JsonNode.html
+    /**
+     * Deserialize JSON-LD query as a {@link JsonNode} object
+     * to a {@link SpanQueryWrapper} object.
+     *
+     * @param json {@link JsonNode} representing the JSON query string.
+     * @return {@link SpanQueryWrapper} object. 
+     * @throws QueryException
+     */
     // TODO: Exception messages are horrible!
     // TODO: Use the shortcuts implemented in this class instead of the wrapper constructors
-    // TODO: Check for isArray()
     // TODO: Rename this span context!
     public SpanQueryWrapper fromJson (JsonNode json) throws QueryException {
+        int number = 0;
+        if (!json.has("@type"))
+            throw new QueryException(701, "JSON-LD group has no @type attribute");
 
-	int number = 0;
+        // Get @type for branching
+        String type = json.get("@type").asText();
 
-	if (!json.has("@type"))
-	    throw new QueryException(701, "JSON-LD group has no @type attribute");
+        switch (type) {
+        case "korap:group":
+            return this._groupFromJson(json);
 
-	String type = json.get("@type").asText();
+        case "korap:reference":
+            if (json.has("operation") &&
+                !json.get("operation").asText().equals("operation:focus"))
+                throw new QueryException(712, "Unknown reference operation");
 
-	switch (type) {
-
-	case "korap:group":
-	    SpanClassQueryWrapper classWrapper;
-
-	    if (!json.has("operation"))
-		throw new QueryException(703, "Group expects operation");
-
-	    String operation = json.get("operation").asText();
-
-	    if (DEBUG)
-		log.trace("Found {} group", operation);
-
-	    if (!json.has("operands"))
-		throw new QueryException(704, "Operation needs operand list");
-
-	    // Get all operands
-	    JsonNode operands = json.get("operands");
-
-	    if (!operands.isArray())
-		throw new QueryException(704, "Operation needs operand list");
-
-	    if (DEBUG)
-		log.trace("Operands are {}", operands);
-
-	    switch (operation) {
-
-	    case "operation:or":
-		SpanAlterQueryWrapper ssaq = new SpanAlterQueryWrapper(this.field);
-		for (JsonNode operand : operands) {
-		    ssaq.or(this.fromJson(operand));
-		};
-		return ssaq;
-
-	    case "operation:position":
-
-		if (operands.size() != 2)
-		    throw new QueryException(705, "Number of operands is not acceptable");
-
-		// TODO: Check for operands
-		// TODO: LEGACY and not future proof
-		String frame = json.has("frame") ?
-		    json.get("frame").asText() :
-		    "frame:contains";
-
-		if (DEBUG)
-		    log.trace("Position frame is '{}'", frame);
-
-		byte flag = WITHIN;
-		switch (frame) {
-		case "frame:contains":
-		    break;
-		case "frame:strictlyContains":
-		    flag = REAL_WITHIN;
-		    break;
-		case "frame:within":
-		    break;
-		case "frame:startswith":
-		    flag = STARTSWITH;
-		    break;
-		case "frame:endswith":
-		    flag = ENDSWITH;
-		    break;
-		case "frame:matches":
-		    flag = MATCH;
-		    break;
-		case "frame:overlaps":
-		    flag = OVERLAP;
-		    break;
-		case "frame:strictlyOverlaps":
-		    flag = REAL_OVERLAP;
-		    break;
-		case "":
-		    // Temporary workaround for wrongly set overlaps
-		    if (json.has("frames")) {
-			frame = json.get("frames").get(0).asText();
-			if (frame.equals("frames:overlapsLeft") ||
-			    frame.equals("frames:overlapsRight")) {
-			    flag = OVERLAP;
-			    break;
-			};
-		    };
-		default:
-		    throw new QueryException(706, "Frame type is unknown");
-		};
-
-		// Check for exclusion modificator
-		Boolean exclude;
-		if (json.has("exclude") && json.get("exclude").asBoolean())
-		    throw new QueryException(
-			760,
-		        "Exclusion is currently not supported in position operations"
-		    );
-
-		return new SpanWithinQueryWrapper(
-		    this.fromJson(operands.get(0)),
-		    this.fromJson(operands.get(1)),
-		    flag
-		);
-
-	    // TODO: This is DEPRECATED and should be communicated that way
-	    case "operation:submatch":
-
-		if (operands.size() != 1)
-		    throw new QueryException(705, "Number of operands is not acceptable");
-
-		if (json.has("classRef")) {
-		    if (json.has("classRefOp"))
-			throw new QueryException(
-			    761,
-			    "Class reference operators are currently not supported"
-                    );
-
-		    number = json.get("classRef").get(0).asInt();
-		}
-		else if (json.has("spanRef")) {
-		    throw new QueryException(
-			762,
-		        "Span references are currently not supported"
-                    );
-		}; 
-
-		return new SpanMatchModifyQueryWrapper(
-		    this.fromJson(operands.get(0)), number
+            if (!json.has("operands")) {
+                throw new QueryException(
+                    766,
+                    "Peripheral references are currently not supported"
                 );
+            };
 
-	    case "operation:sequence":
+            JsonNode operands = json.get("operands");
 
-		// Sequence with only one operand
-		if (operands.size() == 1)
-		    return this.fromJson(operands.get(0));
+            if (!operands.isArray())
+                throw new QueryException(704, "Operation needs operand list");
 
-		SpanSequenceQueryWrapper sseqqw = this.seq();
+            if (operands.size() == 0)
+                throw new QueryException(704, "Operation needs operand list");
 
-		// Say if the operand order is important
-		if (json.has("inOrder"))
-		    sseqqw.setInOrder(json.get("inOrder").asBoolean());
+            if (operands.size() != 1)
+                throw new QueryException(705, "Number of operands is not acceptable");
 
-		// Introduce distance constraints
-		// ATTENTION: Distances have to be set before segments are added
-		if (json.has("distances")) {
-
-		    // TODO
-		    if (json.has("exclude") && json.get("exclude").asBoolean())
-			throw new QueryException(
-			    763,
-			    "Excluding distance constraints are currently not supported"
-			);
-
-		    if (!json.get("distances").isArray()) {
-			throw new QueryException(
-			    707,
-			    "Distance Constraints have " +
-			    "to be defined as arrays"
-			);
-		    };
-
-		    // TEMPORARY: Workaround for group distances
-		    JsonNode firstDistance = json.get("distances").get(0);
-
-		    if (!firstDistance.has("@type"))
-			throw new QueryException(701, "JSON-LD group has no @type attribute");
-
-		    JsonNode distances;
-		    if (firstDistance.get("@type").asText().equals("korap:group")) {
-			if (!firstDistance.has("operands") ||
-			    !firstDistance.get("operands").isArray())
-			    throw new QueryException(704, "Operation needs operand list");
-
-			distances = firstDistance.get("operands");
-		    }
-
-		    // Support korap distances
-		    // Support cosmas distances
-		    else if (
-			     firstDistance.get("@type").asText().equals("korap:distance")
-			     ||
-			     firstDistance.get("@type").asText().equals("cosmas:distance")) {
-
-			distances = json.get("distances");
-		    }
-
-		    else
-			throw new QueryException(708, "No valid distances defined");
-
-		    // Add all distance constraint to query
-		    for (JsonNode constraint : distances) {
-			String unit = "w";
-			if (constraint.has("key"))
-			    unit = constraint.get("key").asText();
-
-			// There is a maximum of 100 fix
-			int min = 0, max = 100;
-			if (constraint.has("boundary")) {
-			    Boundary b = new Boundary(constraint.get("boundary"), 0,100);
-			    min = b.min;
-			    max = b.max;
-			}
-			else {
-			    if (constraint.has("min"))
-				min = constraint.get("min").asInt(0);
-			    if (constraint.has("max"))
-				max = constraint.get("max").asInt(100);
-			};
-
-			// Add foundry and layer to the unit for new indices
-			if (constraint.has("foundry") &&
-			    constraint.has("layer") &&
-			    constraint.get("foundry").asText().length() > 0 &&
-			    constraint.get("layer").asText().length() > 0) {
-
-			    StringBuilder value = new StringBuilder();
-			    value.append(constraint.get("foundry").asText());
-			    value.append('/');
-			    value.append(constraint.get("layer").asText());
-			    value.append(':').append(unit);
-			    unit = value.toString();
-			};
-
-			// Sanitize boundary
-			if (max < min)
-			    max = min;
-
-			if (DEBUG)
-			    log.trace("Add distance constraint of '{}': {}-{}",
-				      unit, min, max);
-
-			sseqqw.withConstraint(min, max, unit);
-		    };
-		};
-
-		// Add segments to sequence
-		for (JsonNode operand : operands) {
-		    sseqqw.append(this.fromJson(operand));
-		};
-
-		// inOrder was set to false without a distance constraint
-		if (!sseqqw.isInOrder() && !sseqqw.hasConstraints()) {
-		    sseqqw.withConstraint(1,1,"w");
-		};
-
-		return sseqqw;
-
-	    case "operation:class":
-		number = 1;
-
-		if (json.has("classOut")) {
-		    number = json.get("classOut").asInt(0);
-		}
-		// Legacy classes
-		else if (json.has("class")) {
-		    number = json.get("class").asInt(0);
-		};
-
-		if (json.has("classRefCheck"))
-		    this.addWarning(
-		        764,
-			"Class reference checks are currently not supported - results may not be correct"
+            // Reference based on classes
+            if (json.has("classRef")) {
+                if (json.has("classRefOp")) {
+                    throw new QueryException(
+                        761,
+                        "Class reference operators are currently not supported"
                     );
+                };
 
-		if (json.has("classRefOp"))
-		    throw new QueryException(
-		        761,
-			"Class reference operators are currently not supported"
+                number = json.get("classRef").get(0).asInt();
+
+
+                if (number > MAX_CLASS_NUM)
+                    throw new QueryException(
+                        709,
+                        "Valid class numbers exceeded"
                     );
+            }
 
-		if (number > 0) {
-		    if (operands.size() != 1)
-			throw new QueryException(
-			    705,
-			    "Number of operands is not acceptable"
-			);
-
-		    if (DEBUG)
-			log.trace("Found Class definition for {}", number);
-
-		    if (number > MAX_CLASS_NUM) {
-			throw new QueryException(
-			    709, "Valid class numbers exceeded"
-                        );
-		    };
-
-		    SpanQueryWrapper sqw = this.fromJson(operands.get(0));
-
-		    // Problematic
-		    if (sqw.maybeExtension())
-			return sqw.setClassNumber(number);
-
-		    return new SpanClassQueryWrapper(sqw, number);
-		};
-
-		throw new QueryException(710, "Class attribute missing");
-
-	    case "operation:repetition":
-
-		if (operands.size() != 1)
-		    throw new QueryException(
-		        705,
-			"Number of operands is not acceptable"
-		    );
-
-		int min = 0;
-		int max = 100;
-
-		if (json.has("boundary")) {
-		    Boundary b = new Boundary(json.get("boundary"), 0, 100);
-		    min = b.min;
-		    max = b.max;
-		}
-		else {
-		    if (json.has("min"))
-			min = json.get("min").asInt(0);
-		    if (json.has("max"))
-			max = json.get("max").asInt(100);
-
-		    if (DEBUG)
-			log.trace(
-			    "Boundary is set by deprecated {}-{}",
-			    min,
-			    max);
-		};
-
-		// Sanitize max
-		if (max < 0)
-		    max = 100;
-		else if (max > 100)
-		    max = 100;
-
-		// Sanitize min
-		if (min < 0)
-		    min = 0;
-		else if (min > 100)
-		    min = 100;
-		
-		// Check relation between min and max
-		if (min > max)
-		    max = max;
-
-		SpanQueryWrapper sqw = this.fromJson(operands.get(0));
-
-		if (sqw.maybeExtension())
-		    return sqw.setMin(min).setMax(max);
-
-		return new SpanRepetitionQueryWrapper(sqw, min, max);
-
-	    case "operation:relation":
-		throw new QueryException(765, "Relations are currently not supported");
-	    };
-
-	    throw new QueryException(711, "Unknown group operation");
-
-	case "korap:reference":
-	    if (json.has("operation") &&
-		!json.get("operation").asText().equals("operation:focus"))
-		throw new QueryException(712, "Unknown reference operation");
-
-	    if (!json.has("operands"))
-		throw new QueryException(
-		    766, "Peripheral references are currently not supported"
-                );
-
-	    operands = json.get("operands");
-
-	    if (!operands.isArray())
-		throw new QueryException(704, "Operation needs operand list");
-
-	    if (operands.size() == 0)
-		throw new QueryException(704, "Operation needs operand list");
-
-	    if (operands.size() != 1)
-		throw new QueryException(705, "Number of operands is not acceptable");
-
-	    if (json.has("classRef")) {
-		if (json.has("classRefOp")) {
-		    throw new QueryException(
-		        761,
-			"Class reference operators are currently not supported"
-		    );
-		};
-
-		number = json.get("classRef").get(0).asInt();
-
-
-		if (number > MAX_CLASS_NUM)
-		    throw new QueryException(
-		        709, "Valid class numbers exceeded"
+            // Reference based on spans
+            else if (json.has("spanRef")) {
+                JsonNode spanRef = json.get("spanRef");
+                int length=0;
+                if (!spanRef.isArray() || spanRef.size() == 0) {
+                    throw new QueryException(
+                        714,
+                        "Span references expect a start position" +
+                        " and a length parameter"
                     );
-	    }
-	    else if (json.has("spanRef")) {
-	        JsonNode spanRef = json.get("spanRef");
-	        int length=0;
-	        if (!spanRef.isArray() || spanRef.size() == 0)
-	            throw new QueryException(714, "Span reference needs a start position and a length parameters.");
+                };
 	        	        
-	        if (!spanRef.get(1).isMissingNode()){
-	            length = spanRef.get(1).asInt();
-	        }
+                if (!spanRef.get(1).isMissingNode())
+                    length = spanRef.get(1).asInt();
 	            
-            return new SpanSubspanQueryWrapper(
+                return new SpanSubspanQueryWrapper(
                     fromJson(operands.get(0)), spanRef.get(0).asInt(),
-                    length); 
-//		throw new QueryException(
-//		    762,
-//		    "Span references are currently not supported"
-//	        );
-	    };
+                    length
+                ); 
+            };
 
-	    if (DEBUG)
-		log.trace("Wrap class reference {}", number);
+            if (DEBUG) log.trace("Wrap class reference {}", number);
 
-	    return new SpanMatchModifyQueryWrapper(
-	        this.fromJson(operands.get(0)), number
-	    );
+            return new SpanMatchModifyQueryWrapper(
+                this.fromJson(operands.get(0)), number
+            );
 
-	case "korap:token":
+        case "korap:token":
+            // The token is empty and should be treated like []
+            if (!json.has("wrap"))
+                return new SpanRepetitionQueryWrapper();
 
-	    // The token is empty and should be treated like []
-	    if (!json.has("wrap"))
-		return new SpanRepetitionQueryWrapper();
+            return this._segFromJson(json.get("wrap"));
 
-	    return this._segFromJson(json.get("wrap"));
+        case "korap:span":
+            return this._termFromJson(json);
+        };
 
-	case "korap:span":
-	    return this._termFromJson(json);
-	};
-	throw new QueryException(713, "Query type is not supported");
+        // Unknown query type
+        throw new QueryException(713, "Query type is not supported");
+    };
+
+    // Deserialize korap:group
+    private SpanQueryWrapper _groupFromJson (JsonNode json) throws QueryException {
+
+        // No operation
+        if (!json.has("operation"))
+            throw new QueryException(703, "Group expects operation");
+
+        // Get operation
+        String operation = json.get("operation").asText();
+
+        if (DEBUG) log.trace("Found {} group", operation);
+
+        if (!json.has("operands"))
+            throw new QueryException(704, "Operation needs operand list");
+
+        // Get all operands
+        JsonNode operands = json.get("operands");
+
+        if (operands == null || !operands.isArray())
+            throw new QueryException(704, "Operation needs operand list");
+
+        if (DEBUG) log.trace("Operands are {}", operands);
+
+        // Branch on operation
+        switch (operation) {
+        case "operation:junction":
+            return this._operationJunctionFromJson(operands);
+            
+        case "operation:position":
+            return this._operationPositionFromJson(json, operands);
+
+        case "operation:sequence":
+            return this._operationSequenceFromJson(json, operands);
+
+        case "operation:class":
+            return this._operationClassFromJson(json, operands);
+
+        case "operation:repetition":
+            return this._operationRepetitionFromJson(json, operands);
+
+        case "operation:relation":
+            throw new QueryException(765, "Relations are currently not supported");
+
+        case "operation:or": // Deprecated in favor of operation:junction
+            return this._operationJunctionFromJson(operands);
+
+        case "operation:submatch": // Deprecated in favor of korap:reference
+            return this._operationSubmatchFromJson(json, operands);
+        };
+
+        // Unknown
+        throw new QueryException(711, "Unknown group operation");
     };
 
 
+    // Deserialize operation:junction
+    private SpanQueryWrapper _operationJunctionFromJson (JsonNode operands)
+        throws QueryException {
+        SpanAlterQueryWrapper ssaq = new SpanAlterQueryWrapper(this.field);
+        for (JsonNode operand : operands) {
+            ssaq.or(this.fromJson(operand));
+        };
+        return ssaq;
+    };
 
+    // Deserialize operation:position
+    private SpanQueryWrapper _operationPositionFromJson (JsonNode json, JsonNode operands)
+        throws QueryException {
+        if (operands.size() != 2)
+            throw new QueryException(705, "Number of operands is not acceptable");
+
+        String frame = "contains";
+        // Temporary workaround for wrongly set overlaps
+        if (json.has("frames")) {
+            JsonNode frameN = json.get("frames");
+            if (frameN.isArray()) {
+                frameN = json.get("frames").get(0);
+                if (frameN != null && frameN.isValueNode())
+                    frame = frameN.asText().substring(7);
+            };
+        }
+        // <legacyCode>
+        else if (json.has("frame")) {
+            JsonNode frameN = json.get("frame");
+            if (frameN != null && frameN.isValueNode())
+                frame = frameN.asText().substring(6);
+        };
+        // </legacyCode>
+
+        if (DEBUG) log.trace("Position frame is '{}'", frame);
+
+        // Byte flag - should cover all 13 cases, i.e. two bytes long
+        byte flag = WITHIN;
+        switch (frame) {
+        case "contains":
+            break;
+        case "strictlyContains":
+            flag = REAL_WITHIN;
+            break;
+        case "within":
+            break;
+        case "startswith":
+            flag = STARTSWITH;
+            break;
+        case "endswith":
+            flag = ENDSWITH;
+            break;
+        case "matches":
+            flag = MATCH;
+            break;
+        case "overlaps":
+            flag = OVERLAP;
+            break;
+        case "overlapsLeft":
+            // Temporary workaround
+            this.addWarning(
+                769,
+                "Overlap variant currently interpreted as overlap"
+            );
+            flag = OVERLAP;
+            break;
+        case "overlapsRight":
+            // Temporary workaround
+            this.addWarning(
+                769,
+                "Overlap variant currently interpreted as overlap"
+            );
+            flag = OVERLAP;
+            break;
+        case "strictlyOverlaps":
+            flag = REAL_OVERLAP;
+            break;
+        default:
+            throw new QueryException(706, "Frame type is unknown");
+        };
+        
+        // The exclusion operator is no longer relevant
+        // <legacyCode>
+        Boolean exclude;
+        if (json.has("exclude") && json.get("exclude").asBoolean()) {
+            throw new QueryException(
+                760,
+                "Exclusion is currently not supported in position operations"
+            );
+        };
+        // </legacyCode>
+
+        // Create SpanWithin Query
+        return new SpanWithinQueryWrapper(
+            this.fromJson(operands.get(0)),
+            this.fromJson(operands.get(1)),
+            flag
+        );
+    };
+
+    // Deserialize operation:repetition
+    private SpanQueryWrapper _operationRepetitionFromJson (JsonNode json, JsonNode operands)
+        throws QueryException {
+
+        if (operands.size() != 1)
+            throw new QueryException(705, "Number of operands is not acceptable");
+
+        int min = 0;
+        int max = 100;
+
+        if (json.has("boundary")) {
+            Boundary b = new Boundary(json.get("boundary"), 0, 100);
+            min = b.min;
+            max = b.max;
+        }
+        // <legacyCode>
+        else {
+            this.addMessage(0, "Setting boundary by min and max is deprecated");
+
+            // Set minimum value
+            if (json.has("min"))
+                min = json.get("min").asInt(0);
+
+            // Set maximum value
+            if (json.has("max"))
+                max = json.get("max").asInt(100);
+        };
+        // </legacyCode>
+
+        // Sanitize max
+        if (max < 0)
+            max = 100;
+        else if (max > 100)
+            max = 100;
+
+        // Sanitize min
+        if (min < 0)
+            min = 0;
+        else if (min > 100)
+            min = 100;
+                
+        // Check relation between min and max
+        if (min > max)
+            max = max;
+
+        SpanQueryWrapper sqw = this.fromJson(operands.get(0));
+                
+        if (sqw.maybeExtension())
+            return sqw.setMin(min).setMax(max);
+
+        return new SpanRepetitionQueryWrapper(sqw, min, max);
+    };
+
+
+    // Deserialize operation:submatch
+    @Deprecated
+    private SpanQueryWrapper _operationSubmatchFromJson (JsonNode json, JsonNode operands)
+        throws QueryException {
+
+        int number = 1;
+
+        this.addMessage(0, "operation:submatch is deprecated");
+
+        if (operands.size() != 1)
+            throw new QueryException(705, "Number of operands is not acceptable");
+
+        if (json.has("classRef")) {
+            if (json.has("classRefOp")) {
+                throw new QueryException(
+                    761,
+                    "Class reference operators are currently not supported"
+                );
+            };
+
+            number = json.get("classRef").get(0).asInt();
+        }
+        else if (json.has("spanRef")) {
+            throw new QueryException(
+                762,
+                "Span references are currently not supported"
+            );
+        }; 
+
+        return new SpanMatchModifyQueryWrapper(
+            this.fromJson(operands.get(0)), number
+        );
+    };
+
+
+    // Deserialize operation:class
+    private SpanQueryWrapper _operationClassFromJson (JsonNode json, JsonNode operands)
+        throws QueryException {
+        int number = 1;
+
+        // Too many operands
+        if (operands.size() != 1)
+            throw new QueryException(705, "Number of operands is not acceptable");
+
+        // Get class number
+        if (json.has("classOut")) {
+            number = json.get("classOut").asInt(0);
+        }
+        // <legacyCode>
+        else if (json.has("class")) {
+            number = json.get("class").asInt(0);
+        };
+        // </legacyCode>
+
+        // Class reference check
+        if (json.has("classRefCheck")) {
+            this.addWarning(
+                764,
+                "Class reference checks are currently " +
+                "not supported - results may not be correct"
+            );
+        };
+
+        // Class reference operation
+        // This has to be done after class ref check
+        if (json.has("classRefOp")) {
+            throw new QueryException(
+                761,
+                "Class reference operators are currently not supported"
+            );
+        };
+
+        // Number is set
+        if (number > 0) {
+            if (operands.size() != 1) {
+                throw new QueryException(
+                    705,
+                    "Number of operands is not acceptable"
+                );
+            };
+            
+            if (DEBUG) log.trace("Found Class definition for {}", number);
+
+            if (number > MAX_CLASS_NUM) {
+                throw new QueryException(
+                    709,
+                    "Valid class numbers exceeded"
+                );
+            };
+
+            // Serialize operand
+            SpanQueryWrapper sqw = this.fromJson(operands.get(0));
+
+            // Problematic
+            if (sqw.maybeExtension())
+                return sqw.setClassNumber(number);
+
+            return new SpanClassQueryWrapper(sqw, number);
+        };
+
+        throw new QueryException(710, "Class attribute missing");
+    };
+
+
+    // Deserialize operation:sequence
+    private SpanQueryWrapper _operationSequenceFromJson (JsonNode json, JsonNode operands)
+        throws QueryException {
+        // Sequence with only one operand
+        if (operands.size() == 1)
+            return this.fromJson(operands.get(0));
+
+        SpanSequenceQueryWrapper sseqqw = this.seq();
+
+        // Say if the operand order is important
+        if (json.has("inOrder"))
+            sseqqw.setInOrder(json.get("inOrder").asBoolean());
+
+        // Introduce distance constraints
+        // ATTENTION: Distances have to be set before segments are added
+        if (json.has("distances")) {
+
+            // THIS IS NO LONGER NECESSARY, AS IT IS COVERED BY FRAMES
+            if (json.has("exclude") && json.get("exclude").asBoolean()) {
+                throw new QueryException(
+                    763,
+                    "Excluding distance constraints are currently not supported"
+                );
+            };
+
+            if (!json.get("distances").isArray()) {
+                throw new QueryException(
+                    707,
+                    "Distance Constraints have to be defined as arrays"
+                );
+            };
+
+            // TEMPORARY: Workaround for group distances
+            JsonNode firstDistance = json.get("distances").get(0);
+            
+            if (!firstDistance.has("@type")) {
+                throw new QueryException(
+                    701,
+                    "JSON-LD group has no @type attribute"
+                );
+            };
+
+            JsonNode distances;
+            if (firstDistance.get("@type").asText().equals("korap:group")) {
+                if (!firstDistance.has("operands") ||
+                    !firstDistance.get("operands").isArray())
+                    throw new QueryException(704, "Operation needs operand list");
+
+                distances = firstDistance.get("operands");
+            }
+
+            // Support korap distances
+            // Support cosmas distances
+            else if (firstDistance.get("@type").asText().equals("korap:distance")
+                     ||
+                     firstDistance.get("@type").asText().equals("cosmas:distance")) {
+                distances = json.get("distances");
+            }
+
+            else
+                throw new QueryException(708, "No valid distances defined");
+
+            // Add all distance constraint to query
+            for (JsonNode constraint : distances) {
+                String unit = "w";
+                if (constraint.has("key"))
+                    unit = constraint.get("key").asText();
+                
+                // There is a maximum of 100 fix
+                int min = 0, max = 100;
+                if (constraint.has("boundary")) {
+                    Boundary b = new Boundary(constraint.get("boundary"), 0,100);
+                    min = b.min;
+                    max = b.max;
+                }
+                else {
+                    if (constraint.has("min"))
+                        min = constraint.get("min").asInt(0);
+                    if (constraint.has("max"))
+                        max = constraint.get("max").asInt(100);
+                };
+                        
+                // Add foundry and layer to the unit for new indices
+                if (constraint.has("foundry") &&
+                    constraint.has("layer") &&
+                    constraint.get("foundry").asText().length() > 0 &&
+                    constraint.get("layer").asText().length() > 0) {
+                            
+                    StringBuilder value = new StringBuilder();
+                    value.append(constraint.get("foundry").asText());
+                    value.append('/');
+                    value.append(constraint.get("layer").asText());
+                    value.append(':').append(unit);
+                    unit = value.toString();
+                };
+                
+                // Sanitize boundary
+                if (max < min) max = min;
+                        
+                if (DEBUG)
+                    log.trace("Add distance constraint of '{}': {}-{}",
+                              unit, min, max);
+                        
+                sseqqw.withConstraint(min, max, unit);
+            };
+        };
+
+        // Add segments to sequence
+        for (JsonNode operand : operands) {
+            sseqqw.append(this.fromJson(operand));
+        };
+
+        // inOrder was set to false without a distance constraint
+        if (!sseqqw.isInOrder() && !sseqqw.hasConstraints()) {
+            sseqqw.withConstraint(1,1,"w");
+        };
+
+        return sseqqw;
+    };
+
+
+    // Segment
     private SpanQueryWrapper _segFromJson (JsonNode json) throws QueryException {
+        if (!json.has("@type"))
+            throw new QueryException(701, "JSON-LD group has no @type attribute");
 
-	if (!json.has("@type"))
-	    throw new QueryException(701, "JSON-LD group has no @type attribute");
+        String type = json.get("@type").asText();
 
-	String type = json.get("@type").asText();
+        if (DEBUG)
+            log.trace("Wrap new token definition by {}", type);
 
-	if (DEBUG)
-	    log.trace("Wrap new token definition by {}", type);
+        switch (type) {
+        case "korap:term":
+            String match = "match:eq";
+            if (json.has("match"))
+                match = json.get("match").asText();
+            
+            switch (match) {
+            case "match:ne":
+                if (DEBUG)
+                    log.trace("Term is negated");
+                SpanSegmentQueryWrapper ssqw =
+                    (SpanSegmentQueryWrapper) this._termFromJson(json);
+                ssqw.makeNegative();
+                return this.seg().without(ssqw);
+            case "match:eq":
+                return this._termFromJson(json);
+            };
 
-	switch (type) {
+            throw new QueryException(741, "Match relation unknown");
 
-	case "korap:term":
-	    String match = "match:eq";
-	    if (json.has("match"))
-		match = json.get("match").asText();
+        case "korap:termGroup":
 
-	    switch (match) {
-	    case "match:ne":
-		if (DEBUG)
-		    log.trace("Term is negated");
-		SpanSegmentQueryWrapper ssqw =
-		    (SpanSegmentQueryWrapper) this._termFromJson(json);
-		ssqw.makeNegative();
-		return this.seg().without(ssqw);
-	    case "match:eq":
-		return this._termFromJson(json);
-	    };
+            if (!json.has("operands"))
+                throw new QueryException(742, "Term group needs operand list");
 
-	    throw new QueryException(741, "Match relation unknown");
+            // Get operands
+            JsonNode operands = json.get("operands");
 
-	case "korap:termGroup":
+            SpanSegmentQueryWrapper ssegqw = this.seg();
+            
+            if (!json.has("relation"))
+                throw new QueryException(743, "Term group expects a relation");
 
-	    if (!json.has("operands"))
-		throw new QueryException(742, "Term group needs operand list");
+            switch (json.get("relation").asText()) {
+            case "relation:and":
 
-	    // Get operands
-	    JsonNode operands = json.get("operands");
-
-	    SpanSegmentQueryWrapper ssegqw = this.seg();
-
-	    if (!json.has("relation"))
-		throw new QueryException(743, "Term group expects a relation");
-
-	    switch (json.get("relation").asText()) {
-	    case "relation:and":
-
-		for (JsonNode operand : operands) {
-		    SpanQueryWrapper part = this._segFromJson(operand);
-		    if (part instanceof SpanAlterQueryWrapper) {
-			ssegqw.with((SpanAlterQueryWrapper) part);			
-		    }
-		    else if (part instanceof SpanRegexQueryWrapper) {
-			ssegqw.with((SpanRegexQueryWrapper) part);
-		    }
-		    else if (part instanceof SpanSegmentQueryWrapper) {
-			ssegqw.with((SpanSegmentQueryWrapper) part);
-		    }
-		    else {
-			throw new QueryException(
-			    744, "Operand not supported in term group"
+                for (JsonNode operand : operands) {
+                    SpanQueryWrapper part = this._segFromJson(operand);
+                    if (part instanceof SpanAlterQueryWrapper) {
+                        ssegqw.with((SpanAlterQueryWrapper) part);			
+                    }
+                    else if (part instanceof SpanRegexQueryWrapper) {
+                        ssegqw.with((SpanRegexQueryWrapper) part);
+                    }
+                    else if (part instanceof SpanSegmentQueryWrapper) {
+                        ssegqw.with((SpanSegmentQueryWrapper) part);
+                    }
+                    else {
+                        throw new QueryException(
+                            744,
+                            "Operand not supported in term group"
                         );
-		    };
-		};
-		return ssegqw;
+                    };
+                };
+                return ssegqw;
 
-	    case "relation:or":
+            case "relation:or":
 
-		SpanAlterQueryWrapper ssaq = new SpanAlterQueryWrapper(this.field);
-		for (JsonNode operand : operands) {
-		    ssaq.or(this._segFromJson(operand));
-		};
-		return ssaq;
-	    };
-	};
-	throw new QueryException(745, "Token type is not supported");    
+                SpanAlterQueryWrapper ssaq = new SpanAlterQueryWrapper(this.field);
+                for (JsonNode operand : operands) {
+                    ssaq.or(this._segFromJson(operand));
+                };
+                return ssaq;
+            };
+        };
+        throw new QueryException(745, "Token type is not supported");    
     };
 
 
     private SpanQueryWrapper _termFromJson (JsonNode json) throws QueryException {
-	if (!json.has("key") || json.get("key").asText().length() < 1)
-	    throw new QueryException(740, "Key definition is missing in term or span");
+
+        if (!json.has("key") || json.get("key").asText().length() < 1)
+            throw new QueryException(740, "Key definition is missing in term or span");
 	    
-	if (!json.has("@type"))
-	    throw new QueryException(701, "JSON-LD group has no @type attribute");
+        if (!json.has("@type"))
+            throw new QueryException(701, "JSON-LD group has no @type attribute");
 
-	Boolean isTerm = json.get("@type").asText().equals("korap:term") ? true : false;
-	Boolean isCaseInsensitive = false;
+        Boolean isTerm = json.get("@type").asText().equals("korap:term") ? true : false;
+        Boolean isCaseInsensitive = false;
 
-	if (json.has("caseInsensitive") && json.get("caseInsensitive").asBoolean())
-	    isCaseInsensitive = true;
+        if (json.has("caseInsensitive") && json.get("caseInsensitive").asBoolean())
+            isCaseInsensitive = true;
 
-	StringBuilder value = new StringBuilder();
+        StringBuilder value = new StringBuilder();
+        
+        // expect orth? expect lemma? 
+        // s:den | i:den | cnx/l:die | mate/m:mood:ind | cnx/syn:@PREMOD |
+        // mate/m:number:sg | opennlp/p:ART
 
-	// expect orth? expect lemma? 
-	// s:den | i:den | cnx/l:die | mate/m:mood:ind | cnx/syn:@PREMOD |
-	// mate/m:number:sg | opennlp/p:ART
+        if (json.has("foundry") && json.get("foundry").asText().length() > 0)
+            value.append(json.get("foundry").asText()).append('/');
 
-	if (json.has("foundry") && json.get("foundry").asText().length() > 0)
-	    value.append(json.get("foundry").asText()).append('/');
+        // No default foundry defined
+        if (json.has("layer") && json.get("layer").asText().length() > 0) {
+            String layer = json.get("layer").asText();
+            switch (layer) {
 
-	// No default foundry defined
+            case "lemma":
+                layer = "l";
+                break;
 
-	if (json.has("layer") && json.get("layer").asText().length() > 0) {
-	    String layer = json.get("layer").asText();
-	    switch (layer) {
+            case "pos":
+                layer = "p";
+                break;
 
-	    case "lemma":
-		layer = "l";
-		break;
+            case "orth":
+                // TODO: THIS IS A BUG! AND SHOULD BE NAMED "SURFACE" or .
+                layer = "s";
+                break;
 
-	    case "pos":
-		layer = "p";
-		break;
+            case "struct":
+                layer = "s";
+                break;
 
-	    case "orth":
-		// TODO: THIS IS A BUG! AND SHOULD BE NAMED "SURFACE"
-		layer = "s";
-		break;
+            case "const":
+                layer = "c";
+                break;
+            };
 
-	    case "struct":
-		layer = "s";
-		break;
+            if (isCaseInsensitive && isTerm) {
+                if (layer.equals("s")) {
+                    layer = "i";
+                }
+                else {
+                    this.addWarning(
+                        767,
+                        "Case insensitivity is currently not supported for this layer"
+                    );
+                };
+            };
 
-	    case "const":
-		layer = "c";
-		break;
-	    };
+            // Ignore foundry for orth layer
+            if (layer.equals("s") || layer.equals("i"))
+                value.setLength(0);
 
-	    if (isCaseInsensitive && isTerm) {
-		if (layer.equals("s")) {
-		    layer = "i";
-		}
-		else {
-		    this.addWarning(
-			767,
-		        "Case insensitivity is currently not supported for this layer"
-		    );
-		};
-	    };
+            value.append(layer).append(':');
+        };
 
-	    // Ignore foundry for orth layer
-	    if (layer.equals("s") || layer.equals("i"))
-		value.setLength(0);
+        if (json.has("key") && json.get("key").asText().length() > 0) {
+            String key = json.get("key").asText();
+            value.append(isCaseInsensitive ? key.toLowerCase() : key);
+        };
 
-	    value.append(layer).append(':');
-	};
+        if (json.has("value") && json.get("value").asText().length() > 0)
+            value.append(':').append(json.get("value").asText());
 
-	if (json.has("key") && json.get("key").asText().length() > 0) {
-	    String key = json.get("key").asText();
-	    value.append(isCaseInsensitive ? key.toLowerCase() : key);
-	};
+        // Regular expression or wildcard
+        if (isTerm && json.has("type")) {
+            switch (json.get("type").asText()) {
+            case "type:regex":
+                return this.seg(this.re(value.toString(), isCaseInsensitive));
+            case "type:wildcard":
+                return this.seq(this.wc(value.toString(), isCaseInsensitive));
+            case "type:string":
+                break;
+            default:
+                this.addWarning(746, "Term type is not supported - treated as a string");
+            };
+        };
 
-	if (json.has("value") && json.get("value").asText().length() > 0)
-	    value.append(':').append(json.get("value").asText());
+        if (isTerm)
+            return this.seg(value.toString());
 
-	// Regular expression or wildcard
-	if (isTerm && json.has("type")) {
-	    switch (json.get("type").asText()) {
-	    case "type:regex":
-		return this.seg(this.re(value.toString(), isCaseInsensitive));
-	    case "type:wildcard":
-		return this.seq(this.wc(value.toString(), isCaseInsensitive));
-	    case "type:string":
-		break;
-	    default:
-		this.addWarning(746, "Term type is not supported - treated as a string");
-	    };
-	};
+        // Attributes currently not supported
+        if (json.has("attr"))
+            this.addWarning(
+              768,
+              "Attributes are currently not supported - results may not be correct"
+            );
 
-	if (isTerm)
-	    return this.seg(value.toString());
-
-	if (json.has("attr"))
-	    this.addWarning(
-	        768,
-		"Attributes are currently not supported - results may not be correct");
-
-	return this.tag(value.toString());
+        return this.tag(value.toString());
     };
 
-    /*
-    public boolean hasWarning () {
-	if (this.warning != null)
-	    return true;
-	return true;
-    };
 
-    public String getWarning () {
-        return this.warning;
-    };
-
-    public void addWarning (String msg) {
-	if (msg == null)
-	    return;
-	if (this.warning == null)
-	    this.warning = msg;
-	else
-	    this.warning += "; " + msg;
-    };
-
-    public void setWarning (String warning) {
-	this.warning = warning;
-    };
-    */
-
-    // SpanRegexQueryWrapper
     /**
      * Create a query object based on a regular expression.
+     *
+     * <blockquote><pre>
+     *   KorapQuery kq = new KorapQuery("tokens");
+     *   SpanRegexQueryWrapper re = kq.re(".+?");
+     * </pre></blockquote>
+     *
      * @param re The regular expession as a string.
+     * @return A {@link SpanRegexQueryWrapper} object.
      */
     public SpanRegexQueryWrapper re (String re) {
-	return new SpanRegexQueryWrapper(this.field, re, RegExp.ALL, false);
+        return new SpanRegexQueryWrapper(this.field, re, RegExp.ALL, false);
     };
 
+
     /**
      * Create a query object based on a regular expression.
+     *
+     * Supports flags as defined in {@link org.apache.lucene.util.automaton.RegExp}:
+     * <ul>
+     *   <li><tt>RegExp.ALL</tt> - enables all optional regexp syntax</li>
+     *   <li><tt>RegExp.ANYSTRING</tt> - enables anystring (@)</li>
+     *   <li><tt>RegExp.AUTOMATON</tt> - enables named automata (&lt;identifier&gt;)</li>
+     *   <li><tt>RegExp.COMPLEMENT</tt> - enables complement (~)</li>
+     *   <li><tt>RegExp.EMPTY</tt> - enables empty language (#)</li>
+     *   <li><tt>RegExp.INTERSECTION</tt> - enables intersection (&amp;)</li>
+     *   <li><tt>RegExp.INTERVAL</tt> - enables numerical intervals (&lt;n-m&gt;)</li>
+     *   <li><tt>RegExp.NONE</tt> - enables no optional regexp syntax</li>
+     * </ul>
+     *
+     * <blockquote><pre>
+     *   KorapQuery kq = new KorapQuery("tokens");
+     *   SpanRegexQueryWrapper re = kq.re("[Aa]lternatives?", RegExp.NONE);
+     * </pre></blockquote>
+     *
      * @param re The regular expession as a string.
-     * @param flas The regular expession flag as an integer.
+     * @param flags The flag for the regular expression.
+     * @return A {@link SpanRegexQueryWrapper} object.
      */
     public SpanRegexQueryWrapper re (String re, int flags) {
-	return new SpanRegexQueryWrapper(this.field, re, flags, false);
+        return new SpanRegexQueryWrapper(this.field, re, flags, false);
     };
 
+
     /**
      * Create a query object based on a regular expression.
+     *
+     * Supports flags (see above) and case insensitivity.
+     *
+     * <blockquote><pre>
+     *   KorapQuery kq = new KorapQuery("tokens");
+     *   SpanRegexQueryWrapper re = kq.re("alternatives?", RegExp.NONE, true);
+     * </pre></blockquote>
+     *
      * @param re The regular expession as a string.
-     * @param flag The regular expession flag.
+     * @param flags The flag for the regular expression.
      * @param caseinsensitive A boolean value indicating case insensitivity.
+     * @return A {@link SpanRegexQueryWrapper} object.
      */
     public SpanRegexQueryWrapper re (String re, int flags, boolean caseinsensitive) {
-	return new SpanRegexQueryWrapper(this.field, re, flags, caseinsensitive);
+        return new SpanRegexQueryWrapper(this.field, re, flags, caseinsensitive);
     };
 
+
     /**
      * Create a query object based on a regular expression.
+     *
+     * Supports case insensitivity.
+     *
+     * <blockquote><pre>
+     *   KorapQuery kq = new KorapQuery("tokens");
+     *   SpanRegexQueryWrapper re = kq.re("alternatives?", true);
+     * </pre></blockquote>
+     *
      * @param re The regular expession as a string.
-     * @param caseinsensitive A boolean value indicating case insensitivity.
+     * @param flags The flag for the regular expression.
+     * @return A {@link SpanRegexQueryWrapper} object.
      */
     public SpanRegexQueryWrapper re (String re, boolean caseinsensitive) {
-	return new SpanRegexQueryWrapper(this.field, re, RegExp.ALL, caseinsensitive);
+        return new SpanRegexQueryWrapper(this.field, re, RegExp.ALL, caseinsensitive);
     };
 
-    // SpanWildcardQueryWrapper
     /**
      * Create a query object based on a wildcard term.
+     * <tt>*</tt> indicates an optional sequence of arbitrary characters,
+     * <tt>?</tt> indicates a single character,
+     * <tt>\</tt> can be used for escaping.
+     *
      * @param wc The wildcard term as a string.
+     * @return A {@link SpanWildcardQueryWrapper} object.
      */
     public SpanWildcardQueryWrapper wc (String wc) {
-	return new SpanWildcardQueryWrapper(this.field, wc, false);
+        return new SpanWildcardQueryWrapper(this.field, wc, false);
     };
 
     /**
      * Create a query object based on a wildcard term.
+     * <tt>*</tt> indicates an optional sequence of arbitrary characters,
+     * <tt>?</tt> indicates a single character,
+     * <tt>\</tt> can be used for escaping.
+     *
+     * Supports case insensitivity.
+     *
+     * <blockquote><pre>
+     *   KorapQuery kq = new KorapQuery("tokens");
+     *   SpanWildcardQueryWrapper wc = kq.wc("wall*", true);
+     * </pre></blockquote>
+     *
      * @param wc The wildcard term as a string.
      * @param caseinsensitive A boolean value indicating case insensitivity.
+     * @return A {@link SpanWildcardQueryWrapper} object.
      */
     public SpanWildcardQueryWrapper wc (String wc, boolean caseinsensitive) {
-	return new SpanWildcardQueryWrapper(this.field, wc, caseinsensitive);
+        return new SpanWildcardQueryWrapper(this.field, wc, caseinsensitive);
     };
 
 
-    // SpanSegmentQueries
     /**
      * Create a segment query object.
+     *
+     * <blockquote><pre>
+     *   KorapQuery kq = new KorapQuery("tokens");
+     *   SpanSegmentQueryWrapper seg = kq.seg();
+     * </pre></blockquote>
+     *
+     * @return A {@link SpanSegmentQueryWrapper} object.
      */
     public SpanSegmentQueryWrapper seg () {
-	return new SpanSegmentQueryWrapper(this.field);
+        return new SpanSegmentQueryWrapper(this.field);
     };
 
 
     /**
      * Create a segment query object.
+     * Supports sequences of strings or {@link SpanRegexQueryWrapper},
+     * and {@link SpanAlterQueryWrapper} objects.
+     *
+     * <blockquote><pre>
+     *   KorapQuery kq = new KorapQuery("tokens");
+     *   SpanSegmentQueryWrapper seg = kq.seg(
+     *       kq.re("mate/p=.*?"),
+     *       kq.re("opennlp/p=.*?")
+     *   );
+     * </pre></blockquote>
+     *
      * @param terms[] An array of terms, the segment consists of.
+     * @return A {@link SpanSegmentQueryWrapper} object.
      */
+    // Sequence of regular expression queries
     public SpanSegmentQueryWrapper seg (SpanRegexQueryWrapper ... terms) {
-	SpanSegmentQueryWrapper ssq = new SpanSegmentQueryWrapper(this.field);
-	for (SpanRegexQueryWrapper t : terms) {
-	    ssq.with(t);
-	};
-	return ssq;
+        SpanSegmentQueryWrapper ssq = new SpanSegmentQueryWrapper(this.field);
+        for (SpanRegexQueryWrapper t : terms)
+            ssq.with(t);
+        return ssq;
     };
 
+    // Sequence of alternative queries
     public SpanSegmentQueryWrapper seg (SpanAlterQueryWrapper ... terms) {
-	SpanSegmentQueryWrapper ssq = new SpanSegmentQueryWrapper(this.field);
-	for (SpanAlterQueryWrapper t : terms) {
-	    ssq.with(t);
-	};
-	return ssq;
+        SpanSegmentQueryWrapper ssq = new SpanSegmentQueryWrapper(this.field);
+        for (SpanAlterQueryWrapper t : terms)
+            ssq.with(t);
+        return ssq;
     };
 
+    // Sequence of alternative queries
     public SpanSegmentQueryWrapper seg (String ... terms) {
-	SpanSegmentQueryWrapper ssq = new SpanSegmentQueryWrapper(this.field);
-	for (String t : terms) {
-	    ssq.with(t);
-	};
-	return ssq;
+        SpanSegmentQueryWrapper ssq = new SpanSegmentQueryWrapper(this.field);
+        for (String t : terms)
+            ssq.with(t);
+        return ssq;
     };
 
-    // Create an empty segment
+    /**
+     * Create an empty query segment.
+     *
+     * <blockquote><pre>
+     *   KorapQuery kq = new KorapQuery("tokens");
+     *   SpanRepetitionQueryWrapper seg = kq.empty();
+     * </pre></blockquote>
+     */
     public SpanRepetitionQueryWrapper empty () {
-	return new SpanRepetitionQueryWrapper();
+        return new SpanRepetitionQueryWrapper();
     };
 
-    // SpanSegmentAlterQueries
+
+    // TODO: Further JavaDocs
+
+
     /**
      * Create a segment alternation query object.
      * @param terms[] An array of alternative terms.
      */
     public SpanAlterQueryWrapper or (SpanQueryWrapper ... terms) {
-	SpanAlterQueryWrapper ssaq = new SpanAlterQueryWrapper(this.field);
-	for (SpanQueryWrapper t : terms) {
-	    ssaq.or(t);
-	};
-	return ssaq;
+        SpanAlterQueryWrapper ssaq = new SpanAlterQueryWrapper(this.field);
+        for (SpanQueryWrapper t : terms)
+            ssaq.or(t);
+        return ssaq;
     };
 
+
     public SpanAlterQueryWrapper or (String ... terms) {
-	SpanAlterQueryWrapper ssaq = new SpanAlterQueryWrapper(this.field);
-	for (String t : terms) {
-	    ssaq.or(t);
-	};
-	return ssaq;
+        SpanAlterQueryWrapper ssaq = new SpanAlterQueryWrapper(this.field);
+        for (String t : terms)
+            ssaq.or(t);
+        return ssaq;
     };
 
 
@@ -892,7 +1106,7 @@
      * Create a sequence of segments query object.
      */
     public SpanSequenceQueryWrapper seq () {
-	return new SpanSequenceQueryWrapper(this.field);
+        return new SpanSequenceQueryWrapper(this.field);
     };
 
 
@@ -901,11 +1115,10 @@
      * @param terms[] An array of segment defining terms.
      */
     public SpanSequenceQueryWrapper seq (SpanQueryWrapper ... terms) {
-	SpanSequenceQueryWrapper sssq = new SpanSequenceQueryWrapper(this.field);
-	for (SpanQueryWrapper t : terms) {
-	    sssq.append(t);
-	};
-	return sssq;
+        SpanSequenceQueryWrapper sssq = new SpanSequenceQueryWrapper(this.field);
+        for (SpanQueryWrapper t : terms)
+            sssq.append(t);
+        return sssq;
     };
 
 
@@ -914,29 +1127,30 @@
      * @param re A SpanSegmentRegexQuery, starting the sequence.
      */
     public SpanSequenceQueryWrapper seq (SpanRegexQueryWrapper re) {
-	return new SpanSequenceQueryWrapper(this.field, re);
+        return new SpanSequenceQueryWrapper(this.field, re);
     };
 
 
     public SpanSequenceQueryWrapper seq (Object ... terms) {
-	SpanSequenceQueryWrapper ssq = new SpanSequenceQueryWrapper(this.field);
-	for (Object t : terms) {
-	    if (t instanceof SpanQueryWrapper) {
-		ssq.append((SpanQueryWrapper) t);
-	    }
-	    else if (t instanceof SpanRegexQueryWrapper) {
-		ssq.append((SpanRegexQueryWrapper) t);
-	    }
-	    else {
-		log.error("{} is not an acceptable parameter for seq()", t.getClass());
-		return ssq;
-	    };
-	};
-	return ssq;
+        SpanSequenceQueryWrapper ssq = new SpanSequenceQueryWrapper(this.field);
+        for (Object t : terms) {
+            if (t instanceof SpanQueryWrapper) {
+                ssq.append((SpanQueryWrapper) t);
+            }
+            else if (t instanceof SpanRegexQueryWrapper) {
+                ssq.append((SpanRegexQueryWrapper) t);
+            }
+            else {
+                log.error("{} is not an acceptable parameter for seq()", t.getClass());
+                return ssq;
+            };
+        };
+        return ssq;
     };
 
+
     public SpanElementQueryWrapper tag (String element) {
-	return new SpanElementQueryWrapper(this.field, element);
+        return new SpanElementQueryWrapper(this.field, element);
     };
 
     /**
@@ -946,75 +1160,75 @@
      */
     @Deprecated
     public SpanWithinQueryWrapper within (SpanQueryWrapper element,
-					  SpanQueryWrapper embedded) {
-	return new SpanWithinQueryWrapper(element, embedded);
+                                          SpanQueryWrapper embedded) {
+        return new SpanWithinQueryWrapper(element, embedded);
     };
-
+    
     public SpanWithinQueryWrapper contains (SpanQueryWrapper element,
-					  SpanQueryWrapper embedded) {
-	return new SpanWithinQueryWrapper(element, embedded, WITHIN);
+                                            SpanQueryWrapper embedded) {
+        return new SpanWithinQueryWrapper(element, embedded, WITHIN);
     };
 
     public SpanWithinQueryWrapper startswith (SpanQueryWrapper element,
-					      SpanQueryWrapper embedded) {
-	return new SpanWithinQueryWrapper(element, embedded, STARTSWITH);
+                                              SpanQueryWrapper embedded) {
+        return new SpanWithinQueryWrapper(element, embedded, STARTSWITH);
     };
 
     public SpanWithinQueryWrapper endswith (SpanQueryWrapper element,
-					    SpanQueryWrapper embedded) {
-	return new SpanWithinQueryWrapper(element, embedded, ENDSWITH);
+                                            SpanQueryWrapper embedded) {
+        return new SpanWithinQueryWrapper(element, embedded, ENDSWITH);
     };
 
     public SpanWithinQueryWrapper overlaps (SpanQueryWrapper element,
-					    SpanQueryWrapper embedded) {
-	return new SpanWithinQueryWrapper(element, embedded, OVERLAP);
+                                            SpanQueryWrapper embedded) {
+        return new SpanWithinQueryWrapper(element, embedded, OVERLAP);
     }; 
 
     public SpanWithinQueryWrapper matches (SpanQueryWrapper element,
-					   SpanQueryWrapper embedded) {
-	return new SpanWithinQueryWrapper(element, embedded, MATCH);
+                                           SpanQueryWrapper embedded) {
+        return new SpanWithinQueryWrapper(element, embedded, MATCH);
     }; 
 
     // Class
     public SpanClassQueryWrapper _ (byte number, SpanQueryWrapper element) {
-	return new SpanClassQueryWrapper(element, number);
+        return new SpanClassQueryWrapper(element, number);
     };
 
     public SpanClassQueryWrapper _ (int number, SpanQueryWrapper element) {
-	return new SpanClassQueryWrapper(element, number);
+        return new SpanClassQueryWrapper(element, number);
     };
 
     public SpanClassQueryWrapper _ (short number, SpanQueryWrapper element) {
-	return new SpanClassQueryWrapper(element, number);
+        return new SpanClassQueryWrapper(element, number);
     };
 
     public SpanClassQueryWrapper _ (SpanQueryWrapper element) {
-	return new SpanClassQueryWrapper(element);
+        return new SpanClassQueryWrapper(element);
     };
 
     // MatchModify
     public SpanMatchModifyQueryWrapper shrink (byte number, SpanQueryWrapper element) {
-	return new SpanMatchModifyQueryWrapper(element, number);
+        return new SpanMatchModifyQueryWrapper(element, number);
     };
 
     public SpanMatchModifyQueryWrapper shrink (int number, SpanQueryWrapper element) {
-	return new SpanMatchModifyQueryWrapper(element, number);
+        return new SpanMatchModifyQueryWrapper(element, number);
     };
 
     public SpanMatchModifyQueryWrapper shrink (short number, SpanQueryWrapper element) {
-	return new SpanMatchModifyQueryWrapper(element, number);
+        return new SpanMatchModifyQueryWrapper(element, number);
     };
 
     public SpanMatchModifyQueryWrapper shrink (SpanQueryWrapper element) {
-	return new SpanMatchModifyQueryWrapper(element);
+        return new SpanMatchModifyQueryWrapper(element);
     };
 
     // Repetition
     public SpanRepetitionQueryWrapper repeat (SpanQueryWrapper element, int exact) {
-	return new SpanRepetitionQueryWrapper(element, exact);
+        return new SpanRepetitionQueryWrapper(element, exact);
     };
 
     public SpanRepetitionQueryWrapper repeat (SpanQueryWrapper element, int min, int max) {
-	return new SpanRepetitionQueryWrapper(element, min, max);
+        return new SpanRepetitionQueryWrapper(element, min, max);
     };
 };
diff --git a/src/main/java/de/ids_mannheim/korap/KorapResult.java b/src/main/java/de/ids_mannheim/korap/KorapResult.java
index 524f54f..87755cf 100644
--- a/src/main/java/de/ids_mannheim/korap/KorapResult.java
+++ b/src/main/java/de/ids_mannheim/korap/KorapResult.java
@@ -24,6 +24,8 @@
 /**
  * Response class for search results.
  *
+ * TODO: Synopsis
+ *
  * @author diewald
  * @see KorapResponse
  */
@@ -43,8 +45,9 @@
 
     private SearchContext context;
 
-    private short itemsPerPage = ITEMS_PER_PAGE,
-              itemsPerResource = 0;
+    private short
+        itemsPerPage     = ITEMS_PER_PAGE,
+        itemsPerResource = 0;
 
     private JsonNode request;
 
@@ -99,7 +102,7 @@
 
 
     /**
-     * Get number of items shown per page.
+     * Get the number of items (documents) shown per page.
      *
      * @return Number of items shown per page.
      */
@@ -109,7 +112,7 @@
 
 
     /**
-     * Set number of items shown per page.
+     * Set the number of items (documents) shown per page.
      *
      * @param count Number of items shown per page.
      * @return {@link KorapResult} object for chaining.
@@ -142,106 +145,168 @@
     };
 
 
-    @JsonIgnore
-    public void setItemsPerResource (short value) {
-	this.itemsPerResource = value;
-    };
-
-    @JsonIgnore
-    public void setItemsPerResource (int value) {
-	this.itemsPerResource = (short) value;
-    };
-
-    @JsonIgnore
+    /**
+     * Get the number of items shown per resource (document).
+     * Defaults to <tt>0</tt>, which is infinite.
+     *
+     * @return The number of items shown per resource.
+     */
     public short getItemsPerResource () {
-	return this.itemsPerResource;
+        return this.itemsPerResource;
     };
 
+
+    /**
+     * Set the number of items (matches) shown per resource (text).
+     * Defaults to <tt>0</tt>, which is infinite.
+     *
+     * @param value The number of items shown per resource.
+     * @return {@link KorapResult} object for chaining.
+     */
+    public KorapResult setItemsPerResource (short value) {
+        this.itemsPerResource = value;
+        return this;
+    };
+
+
+    /**
+     * Set the number of items (matches) shown per resource (document).
+     * Defaults to <tt>0</tt>, which is infinite.
+     *
+     * @param value The number of items shown per resource.
+     * @return {@link KorapResult} object for chaining.
+     */
+    public KorapResult setItemsPerResource (int value) {
+        this.itemsPerResource = (short) value;
+        return this;
+    };
+
+
+    /**
+     * Get the string representation of the search query.
+     *
+     * @return The string representation of the search query.
+     */
     public String getQuery () {
         return this.query;
     };
 
+
+    /**
+     * Get a certain {@link KorapMatch} by index.
+     *
+     * @param index The numerical index of the match,
+     *        starts with <tt>0</tt>.
+     * @return The {@link KorapMatch} object.
+     */
     @JsonIgnore
     public KorapMatch getMatch (int index) {
         return this.matches.get(index);
     };
 
-    @JsonIgnore
+
+    /**
+     * Get the list of {@link KorapMatch} matches.
+     *
+     * @return The list of {@link KorapMatch} objects.
+     */
     public List<KorapMatch> getMatches() {
         return this.matches;
     };
 
+
+    /**
+     * Get the number of the first match in the result set
+     * (<i>aka</i> the offset). Starts with <tt>0</tt>.
+     *
+     * @return The index number of the first match in the result set.
+     */
     public int getStartIndex () {
         return startIndex;
     };
 
-    @JsonIgnore
-    public KorapResult setContext(SearchContext context) {
+
+    /**
+     * Get the context parameters of the search by means of a
+     * {@link SearchContext} object.
+     *
+     * @return The {@link SearchContext} object.
+     */
+    public SearchContext getContext () {
+        return this.context;
+    };
+
+
+    /**
+     * Set the context parameters of the search by means of a
+     * {@link SearchContext} object.
+     *
+     * @param context The {@link SearchContext} object providing
+     *        search context parameters.
+     * @return {@link KorapResult} object for chaining.
+     */
+    public KorapResult setContext (SearchContext context) {
         this.context = context;
         return this;
-    }
+    };
 
 
-    @JsonIgnore
-    public SearchContext getContext() {
-        return this.context;
-    }
-
-
+    /**
+     * Serialize the result set as a {@link JsonNode}.
+     *
+     * @return {@link JsonNode} representation of the search results.
+     */
     public JsonNode toJsonNode () {
-	ObjectNode json = (ObjectNode) mapper.valueToTree(super.toJsonNode());
+        ObjectNode json = (ObjectNode) mapper.valueToTree(super.toJsonNode());
 
-	if (this.context != null)
-	    json.put("context", this.getContext().toJsonNode());
+        // Relevant context setting
+        if (this.context != null)
+            json.put("context", this.getContext().toJsonNode());
 
-	if (this.itemsPerResource > 0)
-	    json.put("itemsPerResource",
-		     this.itemsPerResource);
 
-	json.put("itemsPerPage",
-		 this.itemsPerPage);
+        // ItemsPerPage
+        json.put("itemsPerPage", this.itemsPerPage);
 
-	// TODO: If test
-	if (this.request != null)
-	    json.put("request", this.request);
+        // Relevant itemsPerResource setting
+        if (this.itemsPerResource > 0)
+            json.put("itemsPerResource", this.itemsPerResource);
 
-	// TODO: If test
-	if (this.request != null)
-	    json.put("request", this.request);
-	if (this.query != null)
-	    json.put("query", this.query);
+        json.put("startIndex", this.startIndex);
 
-	json.put("startIndex", this.startIndex);
+        // Add matches
+        if (this.matches != null)
+            json.putPOJO("matches", this.getMatches());
 
-	json.put("totalResults", this.getTotalResults());
 
-	// Add matches
-	if (this.matches != null)
-	    json.putPOJO("matches", this.getMatches());
+        // TODO: <test>
+        if (this.request != null)
+            json.put("request", this.request);
+        if (this.query != null)
+            json.put("query", this.query);
+        // </test>
 
-	return json;
+        return json;
     };
 
 
     // For Collocation Analysis API
+    @Deprecated
     public String toTokenListJsonString () {
         ObjectNode json = (ObjectNode) mapper.valueToTree(this);
-
-	ArrayNode array = json.putArray("matches");
+        
+        ArrayNode array = json.putArray("matches");
 	
-	// Add matches as token lists
-	for (KorapMatch km : this.getMatches()) {
-	    array.add(km.toTokenList());
-	};
+        // Add matches as token lists
+        for (KorapMatch km : this.getMatches())
+            array.add(km.toTokenList());
 
         try {
             return mapper.writeValueAsString(json);
         }
-	catch (Exception e) {
+        catch (Exception e) {
             log.warn(e.getLocalizedMessage());
         };
-
+        
         return "{}";
     };
-
 };
diff --git a/src/main/java/de/ids_mannheim/korap/KorapSearch.java b/src/main/java/de/ids_mannheim/korap/KorapSearch.java
index 08c2da1..88100df 100644
--- a/src/main/java/de/ids_mannheim/korap/KorapSearch.java
+++ b/src/main/java/de/ids_mannheim/korap/KorapSearch.java
@@ -193,214 +193,200 @@
                             for (JsonNode field : (JsonNode) meta.get("fields")) {
                                 this.addField(field.asText());
                             };
-			}
-			else
-			    this.addField(meta.get("fields").asText());
-		    };
-		};
-	    };
-	}
+                        }
+                        else
+                            this.addField(meta.get("fields").asText());
+                    };
+                };
+            };
+        }
 
-	// Unable to parse JSON
-	catch (IOException e) {
-	    this.addError(621, "Unable to parse JSON");
-	};
+        // Unable to parse JSON
+        catch (IOException e) {
+            this.addError(621, "Unable to parse JSON");
+        };
     };
 
     // Maybe accept queryWrapperStuff
     public KorapSearch (SpanQueryWrapper sqwi) {
-	try {
-	    this.query = sqwi.toQuery();
-	}
-	catch (QueryException q) {
-	    this.addError(q.getErrorCode(), q.getMessage());
-	};
+        try {
+            this.query = sqwi.toQuery();
+        }
+        catch (QueryException q) {
+            this.addError(q.getErrorCode(), q.getMessage());
+        };
     };
-
+    
     public KorapSearch (SpanQuery sq) {
-	this.query = sq;
+        this.query = sq;
     };
 
     // Empty constructor
     public KorapSearch () { };
 
     public long getTimeOut () {
-	return this.timeout;
+        return this.timeout;
     };
 
     public void setTimeOut (long timeout) {
-	this.timeout = timeout;
+        this.timeout = timeout;
     };
 
     public SpanQuery getQuery () {
-	return this.query;
+        return this.query;
     };
 
     public JsonNode getRequest () {
-	return this.request;
+        return this.request;
     };
 
     public KorapSearch setQuery (SpanQueryWrapper sqwi) {
-	// this.copyNotifications(sqwi);
-	try {
-	    this.query = sqwi.toQuery();
-	}
-	catch (QueryException q) {
-	    this.addError(q.getErrorCode(), q.getMessage());
-	};
-	return this;
+        // this.copyNotifications(sqwi);
+        try {
+            this.query = sqwi.toQuery();
+        }
+        catch (QueryException q) {
+            this.addError(q.getErrorCode(), q.getMessage());
+        };
+        return this;
     };
 
     public KorapSearch setQuery (SpanQuery sq) {
-	this.query = sq;
-	return this;
+        this.query = sq;
+        return this;
     };
 
     public SearchContext getContext () {
-	return this.context;
+        return this.context;
     };
 
     public KorapSearch setContext (SearchContext context) {
-	this.context = context;
-	return this;
+        this.context = context;
+        return this;
     };
 
     public int getStartIndex () {
-	return this.startIndex;
+        return this.startIndex;
     };
-
+    
     public KorapSearch setStartIndex (int value) {
-	if (value >= 0) {
-	    this.startIndex = value;
-	}
-	else {
-	    this.startIndex = 0;
-	};
-
-	return this;
+        this.startIndex = (value >= 0) ? value : 0;
+        return this;
     };
 
     public KorapSearch setStartPage (int value) {
-	if (value >= 0) {
-	    this.setStartIndex((value * this.getCount()) - this.getCount());
-	}
-	else {
-	    this.startIndex = 0;
-	};
-
-	return this;
+        if (value >= 0)
+            this.setStartIndex((value * this.getCount()) - this.getCount());
+        else
+            this.startIndex = 0;
+        return this;
     };
 
     public short getCount () {
-	return this.count;
+        return this.count;
     };
 
     public short getCountMax () {
-	return this.countMax;
+        return this.countMax;
     };
 
     public int getLimit () {
-	return this.limit;
+        return this.limit;
     };
 
     public KorapSearch setLimit (int limit) {
-	if (limit > 0)
-	    this.limit = limit;
-	return this;
+        if (limit > 0)
+            this.limit = limit;
+        return this;
     };
 
     public boolean doCutOff () {
-	return this.cutOff;
+        return this.cutOff;
     };
 
     public KorapSearch setCutOff (boolean cutOff) {
-	this.cutOff = cutOff;
-	return this;
+        this.cutOff = cutOff;
+        return this;
     };
 
     public KorapSearch setCount (int value) {
-	// Todo: Maybe update startIndex with known startPage!
-	this.setCount((short) value);
-	return this;
+        // Todo: Maybe update startIndex with known startPage!
+        this.setCount((short) value);
+        return this;
     };
 
     public KorapSearch setCount (short value) {
-	if (value > 0) {
-	    if (value <= this.countMax)
-		this.count = value;
-	    else
-		this.count = this.countMax;
-	};
-	return this;
+        if (value > 0)
+            this.count = (value <= this.countMax) ? value : this.countMax;
+        return this;
     };
 
     public KorapSearch setItemsPerResource (short value) {
-	if (value >= 0)
-	    this.itemsPerResource = value;
-	return this;
+        if (value >= 0)
+            this.itemsPerResource = value;
+        return this;
     };
 
     public KorapSearch setItemsPerResource (int value) {
-	return this.setItemsPerResource((short) value);
+        return this.setItemsPerResource((short) value);
     };
 
     public short getItemsPerResource () {
-	return this.itemsPerResource;
+        return this.itemsPerResource;
     };
 
     // Add field to set of fields
     public KorapSearch addField (String field) {
-	this.fields.add(field);
-	return this;
+        this.fields.add(field);
+        return this;
     };
 
     // Get set of fields
     public HashSet<String> getFields () {
-	return this.fields;
+        return this.fields;
     };
 
     public KorapSearch setCollection (KorapCollection kc) {
-	this.collection = kc;
-
-	// Copy messages from the collection
-	this.copyNotificationsFrom(kc);
-	kc.clearNotifications();
-	return this;
+        this.collection = kc;
+        
+        // Copy messages from the collection
+        this.copyNotificationsFrom(kc);
+        kc.clearNotifications();
+        return this;
     };
 
     public KorapCollection getCollection () {
-	if (this.collection == null)
-	    this.collection = new KorapCollection();
-
-	return this.collection;
+        if (this.collection == null)
+            this.collection = new KorapCollection();
+        return this.collection;
     };
 
     public KorapResult run (KorapIndex ki) {
-	if (this.query == null) {
-	    KorapResult kr = new KorapResult();
-	    kr.setRequest(this.request);
+        if (this.query == null) {
+            KorapResult kr = new KorapResult();
+            kr.setRequest(this.request);
 
-	    if (this.hasErrors())
-		kr.copyNotificationsFrom(this);
-	    else
-		kr.addError(700, "No query given");
-	    return kr;
-	};
+            if (this.hasErrors())
+                kr.copyNotificationsFrom(this);
+            else
+                kr.addError(700, "No query given");
+            return kr;
+        };
 
-	if (this.hasErrors()) {
-	    KorapResult kr = new KorapResult();
+        if (this.hasErrors()) {
+            KorapResult kr = new KorapResult();
 
-	    // TODO: dev mode
-	    kr.setRequest(this.request);
-	    kr.copyNotificationsFrom(this);
-	    return kr;
-	};
+            // TODO: dev mode
+            kr.setRequest(this.request);
+            kr.copyNotificationsFrom(this);
+            return kr;
+        };
 
-	this.getCollection().setIndex(ki);
-	KorapResult kr = ki.search(this);
-	kr.setRequest(this.request);
-	kr.copyNotificationsFrom(this);
-	this.clearNotifications();
-	return kr;
+        this.getCollection().setIndex(ki);
+        KorapResult kr = ki.search(this);
+        kr.setRequest(this.request);
+        kr.copyNotificationsFrom(this);
+        this.clearNotifications();
+        return kr;
     };
 };
diff --git a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanWildcardQueryWrapper.java b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanWildcardQueryWrapper.java
index 87d777a..4332856 100644
--- a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanWildcardQueryWrapper.java
+++ b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanWildcardQueryWrapper.java
@@ -10,27 +10,27 @@
 
 public class SpanWildcardQueryWrapper extends SpanQueryWrapper {
     private SpanQuery query;
-
+    
     public SpanWildcardQueryWrapper (String field, String wc) {
-	this(field, wc, false);
+        this(field, wc, false);
     };
 
     public SpanWildcardQueryWrapper (String field, String wc, boolean caseinsensitive) {
-	if (caseinsensitive) {
-	    if (wc.startsWith("s:")) {
-		wc = wc.replaceFirst("s:", "i:");
-	    };
-	    wc = wc.toLowerCase();
-	};
-	WildcardQuery wcquery = new WildcardQuery(new Term(field, wc));
-	query = new SpanMultiTermQueryWrapper<WildcardQuery>( wcquery );
+        if (caseinsensitive) {
+            if (wc.startsWith("s:")) {
+                wc = wc.replaceFirst("s:", "i:");
+            };
+            wc = wc.toLowerCase();
+        };
+        WildcardQuery wcquery = new WildcardQuery(new Term(field, wc));
+        query = new SpanMultiTermQueryWrapper<WildcardQuery>( wcquery );
     };
 
     public SpanQuery toQuery() {
-	return this.query;
+        return this.query;
     };
 
     public boolean isNull () {
-	return false;
+        return false;
     };
 };
diff --git a/src/main/java/de/ids_mannheim/korap/response/KorapResponse.java b/src/main/java/de/ids_mannheim/korap/response/KorapResponse.java
index 806d54c..97ca88c 100644
--- a/src/main/java/de/ids_mannheim/korap/response/KorapResponse.java
+++ b/src/main/java/de/ids_mannheim/korap/response/KorapResponse.java
@@ -32,8 +32,8 @@
 
     private String version, name, node, listener;
     private long
-        totalTexts   = -2, // Not set
-        totalResults = -2; // Not set
+        totalResources = -2, // Not set
+        totalResults   = -2; // Not set
     private String benchmark;
     private boolean timeExceeded = false;
 
@@ -266,47 +266,47 @@
 
 
     /**
-     * Get the total number of texts the total number of
+     * Get the total number of resources the total number of
      * results occur in.
      *
-     * @return The total number of texts the total number of
+     * @return The total number of resources the total number of
      *         results occur in.
      */
-    public long getTotalTexts () {
-        if (this.totalTexts == -2)
+    public long getTotalResources () {
+        if (this.totalResources == -2)
             return (long) 0;
-        return this.totalTexts;
+        return this.totalResources;
     };
 
 
     /**
-     * Set the total number of texts the total number of
+     * Set the total number of resources the total number of
      * results occur in.
      *
-     * @param texts The total number of texts the total
+     * @param resources The total number of resources the total
      *        number of results occur in.
      * @return {@link KorapResponse} object for chaining.
      */
-    public KorapResponse setTotalTexts (long texts) {
-        this.totalTexts = texts;
+    public KorapResponse setTotalResources (long resources) {
+        this.totalResources = resources;
         return this;
     };
 
 
     /**
-     * Increment the total number of texts the total number
+     * Increment the total number of resources the total number
      * of results occur in by a certain value.
      *
-     * @param incr The number of texts the total number of
+     * @param incr The number of resources the total number of
      *        results occur in should be incremented by.
      *        (I don't care that this isn't English!)
      * @return {@link KorapResponse} object for chaining.
      */
-    public KorapResponse incrTotalTexts (int i) {
-        if (this.totalTexts < 0)
-            this.totalTexts = i;
+    public KorapResponse incrTotalResources (int i) {
+        if (this.totalResources < 0)
+            this.totalResources = i;
         else
-            this.totalTexts += i;
+            this.totalResources += i;
         return this;
     };
 
@@ -350,8 +350,8 @@
             json.put("benchmark", this.getBenchmark());
 
         // totalTexts is set
-        if (this.totalTexts != -2)
-            json.put("totalTexts", this.totalTexts);
+        if (this.totalResources != -2)
+            json.put("totalResources", this.totalResources);
         
         // totalResults is set
         if (this.totalResults != -2)
diff --git a/src/main/java/de/ids_mannheim/korap/response/Notifications.java b/src/main/java/de/ids_mannheim/korap/response/Notifications.java
index c3d7edd..8ecf0b3 100644
--- a/src/main/java/de/ids_mannheim/korap/response/Notifications.java
+++ b/src/main/java/de/ids_mannheim/korap/response/Notifications.java
@@ -421,9 +421,10 @@
 
 
     /**
-     * Serialize Notifications as a JsonNode.
+     * Serialize Notifications as a {@link JsonNode}.
      *
-     * @return JsonNode representation of all warnings, errors, and messages
+     * @return {@link JsonNode} representation of all warnings,
+     *         errors, and messages.
      */
     public JsonNode toJsonNode () {
         ObjectNode json =  mapper.createObjectNode();
diff --git a/src/test/java/de/ids_mannheim/korap/collection/TestKorapCollectionJSON.java b/src/test/java/de/ids_mannheim/korap/collection/TestKorapCollectionJSON.java
index b76adbe..bee4da5 100644
--- a/src/test/java/de/ids_mannheim/korap/collection/TestKorapCollectionJSON.java
+++ b/src/test/java/de/ids_mannheim/korap/collection/TestKorapCollectionJSON.java
@@ -20,49 +20,66 @@
 
     @Test
     public void collection1 () {
-	String metaQuery = _getJSONString("collection_1.jsonld");
-	KorapCollection kc = new KorapCollection(metaQuery);
-	assertEquals(kc.toString(), "filter with QueryWrapperFilter(+pubDate:20000101); ");
+        String metaQuery = _getJSONString("collection_1.jsonld");
+        KorapCollection kc = new KorapCollection(metaQuery);
+        assertEquals(
+            kc.toString(),
+            "filter with QueryWrapperFilter(+pubDate:20000101); "
+        );
     };
 
     @Test
     public void collection2 () {
-	String metaQuery = _getJSONString("collection_2.jsonld");
-	KorapCollection kc = new KorapCollection(metaQuery);
-	assertEquals(kc.toString(), "filter with QueryWrapperFilter(+(+pubDate:[19900000 TO 99999999] +pubDate:[0 TO 20061099])); ");
+        String metaQuery = _getJSONString("collection_2.jsonld");
+        KorapCollection kc = new KorapCollection(metaQuery);
+        assertEquals(
+            kc.toString(),
+            "filter with QueryWrapperFilter(+(+pubDate:"+
+            "[19900000 TO 99999999] +pubDate:[0 TO 20061099])); "
+        );
     };
 
+
     @Test
     public void collection3 () {
-	String metaQuery = _getJSONString("collection_3.jsonld");
-	KorapCollection kc = new KorapCollection(metaQuery);
-	assertEquals(kc.toString(), "");
+        String metaQuery = _getJSONString("collection_3.jsonld");
+        KorapCollection kc = new KorapCollection(metaQuery);
+        assertEquals(kc.toString(), "");
     };
 
+
     @Test
     public void collection5 () {
-	String metaQuery = _getJSONString("collection_5.jsonld");
-	KorapCollection kc = new KorapCollection(metaQuery);
-	assertEquals(kc.toString(), "filter with QueryWrapperFilter(+(pubDate:[19900000 TO 99999999] title:Mannheim)); ");
+        String metaQuery = _getJSONString("collection_5.jsonld");
+        KorapCollection kc = new KorapCollection(metaQuery);
+        assertEquals(
+            kc.toString(),
+            "filter with QueryWrapperFilter(+(pubDate:"+
+            "[19900000 TO 99999999] title:Mannheim)); "
+        );
     };
 
+
     @Test
     public void nocollectiontypegiven () {
-	String metaQuery = _getJSONString("multiterm_rewrite_collection.jsonld");
-	KorapCollection kc = new KorapCollection(metaQuery);
-	assertEquals(701, kc.getError(0).getCode());
+        String metaQuery = _getJSONString("multiterm_rewrite_collection.jsonld");
+        KorapCollection kc = new KorapCollection(metaQuery);
+        assertEquals(701, kc.getError(0).getCode());
     };
 
 
-    @Ignore
+    @Test
     public void noCollection () {
-	String metaQuery = _getJSONString("no_collection.jsonld");
-	// TODO!!!
-	// Use KorapSearch and test
+        String metaQuery = _getJSONString("no_collection.jsonld");
+        KorapCollection kc = new KorapCollection(metaQuery);
+        assertEquals(
+            "filter with QueryWrapperFilter(+corpusID:WPD); ",
+            kc.toString()
+        );
     };
 
 
     private String _getJSONString (String file) {
-	return getString(getClass().getResource(path + file).getFile());
+        return getString(getClass().getResource(path + file).getFile());
     };
 };