- new date operators in collection queries ("in", "on", "since" instead "=", "<",...)
- disambiguation date/string in collection queries
- jsonify collection tests
diff --git a/src/main/antlr/CollectionQuery.g4 b/src/main/antlr/CollectionQuery.g4
index d3b25b4..f46bbbf 100644
--- a/src/main/antlr/CollectionQuery.g4
+++ b/src/main/antlr/CollectionQuery.g4
@@ -38,13 +38,18 @@
 COLON				: ':';
 DASH				: '-';
 TILDE				: '~';
+SINCE				: 'since';
+UNTIL				: 'until';
+IN					: 'in';
+ON					: 'on';
 WS 					: ( ' ' | '\t' | '\r' | '\n' )+ -> skip ;
 fragment NO_RE      : ~[ \t\/];
 fragment ALPHABET   : ~('\t' | ' ' | '/' | '*' | '?' | '+' | '{' | '}' | '[' | ']'
                     | '(' | ')' | '|' | '"' | ',' | ':' | '\'' | '\\' | '!' | '=' | '~' | '&' | '^' | '<' | '>' );
 fragment ALPHA		: [a-zA-Z];
-DIGIT				: [0-9];
 
+
+DIGIT		: [0-9];
 DATE
 : DIGIT DIGIT DIGIT DIGIT (DASH DIGIT DIGIT (DASH DIGIT DIGIT)?)?
 ;
@@ -52,7 +57,9 @@
 NL                  : [\r\n] -> skip;
 ws                  : WS+;
 
-WORD                : ALPHABET* ALPHA ALPHABET*;  // needs to have at least one alphabetical letter (non-numeric)
+WORD				: ALPHABET+;
+//WORD                : ALPHABET* ALPHA ALPHABET*;  // needs to have at least one alphabetical letter (non-numeric)
+
 
 /*
  * Regular expressions
@@ -82,16 +89,29 @@
 : DATE
 ;
 
+dateOp
+: SINCE
+| UNTIL
+| IN
+| ON
+;
+
 operator
 :	(NEG? EQ) | LT | GT | LEQ | GEQ | TILDE;
 
 expr
-: meta
+: constraint
+| dateconstraint
 | token
 ;
 
-meta
-: (value operator)? field operator value
+dateconstraint
+: field dateOp date
+//| date dateOp field dateOp date
+;
+
+constraint
+: field operator value
 ;
 
 token
@@ -139,8 +159,9 @@
 	
 value
 : WORD
+| DIGIT+
+| DATE
 | multiword
-| date
 | regex
 ;
 
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/CollectionQueryTree.java b/src/main/java/de/ids_mannheim/korap/query/serialize/CollectionQueryTree.java
index 46d4b3f..82f3a80 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/CollectionQueryTree.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/CollectionQueryTree.java
@@ -3,6 +3,7 @@
 import de.ids_mannheim.korap.query.serialize.util.CollectionQueryLexer;
 import de.ids_mannheim.korap.query.serialize.util.CollectionQueryParser;
 import de.ids_mannheim.korap.util.QueryException;
+
 import org.antlr.v4.runtime.*;
 import org.antlr.v4.runtime.tree.*;
 
@@ -16,18 +17,9 @@
 public class CollectionQueryTree extends Antlr4AbstractSyntaxTree {
 
     private Parser parser;
-    private boolean verbose;
+    private static boolean verbose;
     private List<ParseTree> visited = new ArrayList<ParseTree>();
 
-
-    public CollectionQueryTree() {
-        verbose = false;
-    }
-
-    public CollectionQueryTree(boolean verbose) {
-        this.verbose = verbose;
-    }
-
     /**
      * Keeps track of active object.
      */
@@ -42,6 +34,13 @@
     LinkedList<Integer> objectsToPop = new LinkedList<Integer>();
     Integer stackedObjects = 0;
     
+    public CollectionQueryTree() {
+	}
+    
+    public CollectionQueryTree(boolean verbose) {
+    	CollectionQueryTree.verbose = verbose;
+	}
+    
     public CollectionQueryTree(String query) throws QueryException {
 		process(query);
 	}
@@ -64,9 +63,7 @@
         String nodeCat = getNodeCat(node);
         openNodeCats.push(nodeCat);
 
-
         stackedObjects = 0;
-
         if (verbose) {
             System.err.println(" " + objectStack);
             System.out.println(openNodeCats);
@@ -88,59 +85,39 @@
             stackedObjects++;
         }
 
-//        if (nodeCat.equals("orGroup")) {
-//            LinkedHashMap<String, Object> exprGroup = makeDocGroup("or");
-//            putIntoSuperObject(exprGroup);
-//            objectStack.push(exprGroup);
-//            stackedObjects++;
-//        }
-
-        if (nodeCat.equals("meta")) {
+        if (nodeCat.equals("constraint")) {
             ParseTree fieldNode = getFirstChildWithCat(node, "field");
             String field = fieldNode.getChild(0).toStringTree(parser);
-            List<ParseTree> operatorNodes = getChildrenWithCat(node, "operator");
-            List<ParseTree> valueNodes = getChildrenWithCat(node, "value");
-
-            if (valueNodes.size() == 1) {
-                LinkedHashMap<String, Object> term = makeDoc();
-                term.put("key", field);
-                term.putAll(parseValue(valueNodes.get(0)));
-                String match = operatorNodes.get(0).getText();
-                term.put("match", "match:" + interpretMatch(match));
-                if (checkOperatorValueConformance(term) == 1) {
-                	requestMap.put("collection", new LinkedHashMap<String,Object>());
-                	return;
-                }
-                putIntoSuperObject(term);
-            } else { // (valueNodes.size()==2)
-                LinkedHashMap<String, Object> termGroup = makeDocGroup("and");
-                ArrayList<Object> termGroupOperands = (ArrayList<Object>) termGroup.get("operands");
-
-                LinkedHashMap<String, Object> term1 = makeDoc();
-                term1.put("key", field);
-                term1.putAll(parseValue(valueNodes.get(0)));
-                String match1 = operatorNodes.get(0).getText();
-                term1.put("match", "match:" + invertInequation(interpretMatch(match1)));
-                termGroupOperands.add(term1);
-                if (checkOperatorValueConformance(term1) == 1) {
-                	requestMap.put("collection", new LinkedHashMap<String,Object>());
-                	return;
-                }
-
-                LinkedHashMap<String, Object> term2 = makeDoc();
-                term2.put("key", field);
-                term2.putAll(parseValue(valueNodes.get(1)));
-                String match2 = operatorNodes.get(1).getText();
-                term2.put("match", "match:" + interpretMatch(match2));
-                termGroupOperands.add(term2);
-                if (checkOperatorValueConformance(term2) == 1) {
-                	requestMap.put("collection", new LinkedHashMap<String,Object>());
-                	return;
-                }
-
-                putIntoSuperObject(termGroup);
+            ParseTree operatorNode = getFirstChildWithCat(node, "operator");
+            ParseTree valueNode = getFirstChildWithCat(node, "value");
+            LinkedHashMap<String, Object> term = makeDoc();
+            term.put("key", field);
+            term.putAll(parseValue(valueNode));
+            String match = operatorNode.getText();
+            term.put("match", "match:" + interpretMatchOperator(match));
+            if (checkOperatorValueConformance(term) == false) {
+            	requestMap = new LinkedHashMap<String,Object>();
+            	return;
             }
+            putIntoSuperObject(term);
+        }
+        
+        if (nodeCat.equals("dateconstraint")) {
+            ParseTree fieldNode = getFirstChildWithCat(node, "field");
+            String field = fieldNode.getChild(0).toStringTree(parser);
+            ParseTree dateOpNode = getFirstChildWithCat(node, "dateOp");
+            ParseTree dateNode = getFirstChildWithCat(node, "date");
 
+            LinkedHashMap<String, Object> term = makeDoc();
+            term.put("key", field);
+            term.putAll(parseValue(dateNode));
+            String match = dateOpNode.getText();
+            term.put("match", "match:" + interpretMatchOperator(match));
+            if (checkOperatorValueConformance(term) == false) {
+            	requestMap = new LinkedHashMap<String,Object>();
+            	return;
+            }
+            putIntoSuperObject(term);
         }
         
         if (nodeCat.equals("token")) {
@@ -153,7 +130,6 @@
 			if (getNodeCat(node.getChild(0)).equals("key")) {
 				// no 'term' child, but direct key specification: process here
 				LinkedHashMap<String,Object> term = makeTerm();
-				
 				String key = node.getChild(0).getText();
 				if (getNodeCat(node.getChild(0).getChild(0)).equals("regex")) {
 					isRegex = true;
@@ -187,7 +163,7 @@
 			visited.add(node.getChild(0));
 			visited.add(node.getChild(2));
 		}
-        
+
         objectsToPop.push(stackedObjects);
 
 		/*
@@ -218,43 +194,48 @@
 
     }
 
-
-    private int checkOperatorValueConformance(LinkedHashMap<String, Object> term) {
+	/**
+	 * Checks whether the combination of operator and value is legal (inequation operators <,>,<=,>= may only be used with dates).
+	 */
+    private boolean checkOperatorValueConformance(LinkedHashMap<String, Object> term) {
 		String match = (String) term.get("match");
 		String type = (String) term.get("type");
 		if (type == null || type.equals("type:regex")) {
 			if (!(match.equals("match:eq") || match.equals("match:ne") || match.equals("match:contains"))) {
 				addError(302, "You used an inequation operator with a string value.");
-				System.err.println("You used an inequation operator with a string value.");
-				return 1;
+				return false;
 			}
 		}
-		return 0;
+		return true;
 	}
 
 	private LinkedHashMap<String, Object> parseValue(ParseTree valueNode) {
     	LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
+    	if (getNodeCat(valueNode).equals("date")) {
+    		map.put("type", "type:date");
+    		checkDateValidity(valueNode);
+    	}
     	if (getNodeCat(valueNode.getChild(0)).equals("regex")) {
     		String regex = valueNode.getChild(0).getChild(0).toStringTree(parser);
     		map.put("value", regex.substring(1, regex.length()-1));
     		map.put("type", "type:regex");
     	} else if (getNodeCat(valueNode.getChild(0)).equals("multiword")) {
-    		String mw = "";
+    		String mw = ""; // multiword
     		for (int i=1; i<valueNode.getChild(0).getChildCount()-1; i++) {
     			mw += valueNode.getChild(0).getChild(i).getText() + " ";
     		}
     		map.put("value", mw.substring(0, mw.length()-1));
-    	} else if (getNodeCat(valueNode.getChild(0)).equals("date")) {
-    		map.put("type", "type:date");
-    		String value = valueNode.getChild(0).getChild(0).toStringTree(parser);
-    		map.put("value", value);
     	} else {
     		map.put("value", valueNode.getChild(0).toStringTree(parser));
     	}
 		return map;
 	}
 
-	private String interpretMatch(String match) {
+	private void checkDateValidity(ParseTree valueNode) {
+		// TODO ensure month is <= 12, day is <= 31 etc.
+	}
+
+	private String interpretMatchOperator(String match) {
         String out = null;
         switch (match) {
             case "<":
@@ -278,11 +259,30 @@
             case "~":
                 out = "contains";
                 break;    
-               
+            case "!~":
+                out = "notcontains";
+                break;    
+            case "in":
+                out = "eq";
+                break;
+            case "on":
+                out = "eq";
+                break;
+            case "until":
+                out = "leq";
+                break;    
+            case "since":
+                out = "geq";
+                break;
+            default:
+            	out = match;
+            	addError(302, "Unknown operator '"+match+"'.");
+            	break;
         }
         return out;
     }
-
+	
+	@Deprecated
     private String invertInequation(String op) {
         String inv = null;
         switch (op) {
@@ -312,9 +312,8 @@
             ArrayList<Object> topObjectOperands = (ArrayList<Object>) objectStack.get(objStackPosition).get("operands");
             topObjectOperands.add(object);
         } else {
-            // I want the raw object, not a wrapped
-//            requestMap.put("filter", object);
-        	requestMap.put("collection", object);
+        	requestMap = object;
+//        	requestMap.put("collection", object);
         }
     }
 
@@ -449,11 +448,13 @@
         query = "(textClass=wissenschaft & textClass=politik) | textClass=ausland";
         query = "1990<year<2010 & genre=Sport";
         query = "1990<year<2010";
-        query = "pubDate<Sport";
-//    	filter.verbose = true;
+        query = "textClass=Sport & year=2014";
+        query = "textClass=0154";
+        CollectionQueryTree.verbose = true;
         CollectionQueryTree filter = null;
         try {
         	 filter = new CollectionQueryTree(query);
+        	 filter.verbose = true;
         } catch (QueryException e) {
             e.printStackTrace();
         }
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/QuerySerializer.java b/src/main/java/de/ids_mannheim/korap/query/serialize/QuerySerializer.java
index f0ae995..f124a00 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/QuerySerializer.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/QuerySerializer.java
@@ -27,45 +27,45 @@
             .getLogger(QuerySerializer.class);
 
 
-    /**
-     * @param args
-     * @throws QueryException
-     */
-    public static void main(String[] args) {
-        /*
-         * just for testing...
-		 */
-        QuerySerializer jg = new QuerySerializer();
-        int i = 0;
-        String[] queries;
-        if (args.length == 0) {
-            queries = new String[]{
-
-            };
-        } else
-            queries = new String[]{args[0]};
-
-        for (String q : queries) {
-            i++;
-            try {
-                System.out.println(q);
-                String ql = "cosmas2";
-                jg.run(q, ql, System.getProperty("user.home") + "/" + ql + "_" + i + ".jsonld");
-                System.out.println();
-            } catch (NullPointerException npe) {
-                npe.printStackTrace();
-                System.out.println("null\n");
-            } catch (JsonGenerationException e) {
-                e.printStackTrace();
-            } catch (JsonMappingException e) {
-                e.printStackTrace();
-            } catch (IOException e) {
-                e.printStackTrace();
-            } catch (QueryException e) {
-                e.printStackTrace();
-            }
-        }
-    }
+//    /**
+//     * @param args
+//     * @throws QueryException
+//     */
+//    public static void main(String[] args) {
+//        /*
+//         * just for testing...
+//		 */
+//        QuerySerializer jg = new QuerySerializer();
+//        int i = 0;
+//        String[] queries;
+//        if (args.length == 0) {
+//            queries = new String[]{
+//
+//            };
+//        } else
+//            queries = new String[]{args[0]};
+//
+//        for (String q : queries) {
+//            i++;
+//            try {
+//                System.out.println(q);
+//                String ql = "cosmas2";
+//                jg.run(q, ql, System.getProperty("user.home") + "/" + ql + "_" + i + ".jsonld");
+//                System.out.println();
+//            } catch (NullPointerException npe) {
+//                npe.printStackTrace();
+//                System.out.println("null\n");
+//            } catch (JsonGenerationException e) {
+//                e.printStackTrace();
+//            } catch (JsonMappingException e) {
+//                e.printStackTrace();
+//            } catch (IOException e) {
+//                e.printStackTrace();
+//            } catch (QueryException e) {
+//                e.printStackTrace();
+//            }
+//        }
+//    }
 
     /**
      * Runs the QuerySerializer by initializing the relevant AbstractSyntaxTree implementation (depending on specified query language)
@@ -77,24 +77,24 @@
      * @throws IOException
      * @throws QueryException
      */
-    public void run(String query, String queryLanguage, String outFile)
-            throws IOException, QueryException {
-        if (queryLanguage.equals("poliqarp")) {
-            ast = new PoliqarpPlusTree(query);
-        } else if (queryLanguage.toLowerCase().equals("cosmas2")) {
-            ast = new CosmasTree(query);
-        } else if (queryLanguage.toLowerCase().equals("poliqarpplus")) {
-            ast = new PoliqarpPlusTree(query);
-        } else if (queryLanguage.toLowerCase().equals("cql")) {
-            ast = new CQLTree(query);
-        } else if (queryLanguage.toLowerCase().equals("annis")) {
-            ast = new AqlTree(query);
-        } else {
-            throw new QueryException(queryLanguage + " is not a supported query language!");
-        }
-        Map<String, Object> requestMap = ast.getRequestMap();
-//        mapper.writeValue(new File(outFile), requestMap);
-    }
+//    public void run(String query, String queryLanguage, String outFile)
+//            throws IOException, QueryException {
+//        if (queryLanguage.equals("poliqarp")) {
+//            ast = new PoliqarpPlusTree(query);
+//        } else if (queryLanguage.toLowerCase().equals("cosmas2")) {
+//            ast = new CosmasTree(query);
+//        } else if (queryLanguage.toLowerCase().equals("poliqarpplus")) {
+//            ast = new PoliqarpPlusTree(query);
+//        } else if (queryLanguage.toLowerCase().equals("cql")) {
+//            ast = new CQLTree(query);
+//        } else if (queryLanguage.toLowerCase().equals("annis")) {
+//            ast = new AqlTree(query);
+//        } else {
+//            throw new QueryException(queryLanguage + " is not a supported query language!");
+//        }
+//        Map<String, Object> requestMap = ast.getRequestMap();
+////        mapper.writeValue(new File(outFile), requestMap);
+//    }
 
     public QuerySerializer setQuery(String query, String ql, String version)
             throws QueryException {
@@ -147,7 +147,7 @@
             Map<String, Object> requestMap = ast.getRequestMap();
             Map meta = (Map) requestMap.get("meta");
             if (collection != null)
-                requestMap.put("collections", collection);
+                requestMap.put("collection", collection);
             if (this.meta != null) {
                 meta.putAll(this.meta);
                 requestMap.put("meta", meta);