Improved error handling for antlr3 and antlr4 trees:
- Error listeners and message formatters
- Cosmas grammar incorporates setter method for error listener
- No more QueryExceptions, instead return empty query serialization along with error msg
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/util/Antlr3DescriptiveErrorListener.java b/src/main/java/de/ids_mannheim/korap/query/serialize/util/Antlr3DescriptiveErrorListener.java
new file mode 100644
index 0000000..2a0fb63
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/util/Antlr3DescriptiveErrorListener.java
@@ -0,0 +1,85 @@
+package de.ids_mannheim.korap.query.serialize.util;
+
+import java.util.ArrayList;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import de.ids_mannheim.korap.query.cosmas2.IErrorReporter;
+import de.ids_mannheim.korap.query.serialize.QueryUtils;
+
+/**
+ * Custom descriptive error listener for Antlr3 grammars. Requires interface IErrorReporter to be present in 
+ * grammar destination (generated source directory).
+ * @author Joachim Bingel (bingel@ids-mannheim.de)
+ *
+ */
+public class Antlr3DescriptiveErrorListener implements IErrorReporter {
+
+	private String query;
+	private String offendingSymbol;
+	private String expected;
+	private int charPosition;
+
+	public Antlr3DescriptiveErrorListener(String query) {
+		this.query = query;
+	};
+
+	@Override
+	public void reportError(String error) {
+		String charPositionStr = null;
+		String offendingSymbol = null;
+		String expected = null;
+		Pattern p = Pattern.compile("line \\d+:(\\d+).* '(.+?)' expecting (.+)");
+		Matcher m = p.matcher(error);
+		if (m.find()) {
+			charPositionStr = m.group(1);
+			offendingSymbol = m.group(2);
+			expected = m.group(3);
+		}
+		if (charPositionStr != null)
+			this.charPosition = Integer.parseInt(charPositionStr);
+		if (offendingSymbol != null)
+			this.offendingSymbol = offendingSymbol;
+		if (expected != null)
+			this.expected = expected;
+	}
+
+	public ArrayList<Object> generateFullErrorMsg() {
+		ArrayList<Object> errorSpecs = new ArrayList<Object>();
+		String msg = getDetailedErrorMessage(); 
+		errorSpecs.add(StatusCodes.MALFORMED_QUERY);
+		errorSpecs.add(msg);
+		errorSpecs.add(getCharPosition());
+		return errorSpecs;
+	}
+
+	private String getDetailedErrorMessage() {
+		// default message, in case no detailed info is available;
+		String msg = "Malformed query. Could not parse."; 
+		char offendingSymbol = query.charAt(0);
+		if (query.length() > charPosition) offendingSymbol = query.charAt(charPosition);
+		msg = "Failing to parse at symbol: '"+offendingSymbol+"'";
+		if (expected != null) {
+			if (expected.equals("EOF") || expected.equals("<EOF>")) {
+				msg += " Expected end of query.";
+			} else {
+				msg += " Expected '"+expected+"'";
+			}
+		}
+		// check for unbalanced parantheses
+		SimpleEntry<String, Integer> unbalanced = QueryUtils.checkUnbalancedPars(query); 
+		if (unbalanced != null) {
+			msg = unbalanced.getKey();
+			charPosition = unbalanced.getValue();
+		}
+
+		return msg;
+	}
+
+	public int getCharPosition() {
+		return charPosition;
+	}
+
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/util/Antlr4DescriptiveErrorListener.java b/src/main/java/de/ids_mannheim/korap/query/serialize/util/Antlr4DescriptiveErrorListener.java
new file mode 100644
index 0000000..5b7115e
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/util/Antlr4DescriptiveErrorListener.java
@@ -0,0 +1,72 @@
+package de.ids_mannheim.korap.query.serialize.util;
+
+import java.util.ArrayList;
+import java.util.AbstractMap.SimpleEntry;
+
+import org.antlr.v4.runtime.BaseErrorListener;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+
+import de.ids_mannheim.korap.query.serialize.QueryUtils;
+
+public class Antlr4DescriptiveErrorListener extends BaseErrorListener {
+    
+    String query;
+    String message;
+	int line;
+    int charPosition;
+        
+    public Antlr4DescriptiveErrorListener(String query) {
+    	this.query = query;
+    };
+
+    @Override
+    public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol,
+                            int line, int charPositionInLine,
+                            String msg, RecognitionException e)
+    {
+    	this.message = msg;
+    	this.line = line;
+    	this.charPosition = charPositionInLine;
+    }
+    
+    public String getMessage() {
+		return message;
+	}
+
+	public int getLine() {
+		return line;
+	}
+
+	public int getCharPosition() {
+		return charPosition;
+	}
+	
+	public ArrayList<Object> generateFullErrorMsg() {
+		ArrayList<Object> errorSpecs = new ArrayList<Object>();
+		String msg = getDetailedErrorMessage(); 
+		errorSpecs.add(StatusCodes.MALFORMED_QUERY);
+		errorSpecs.add(msg);
+		errorSpecs.add(getCharPosition());
+		return errorSpecs;
+	}
+
+	private String getDetailedErrorMessage() {
+		// default message, in case no detailed info is available;
+		String msg = "Malformed query. Could not parse."; 
+		char offendingSymbol = query.charAt(0);
+		if (query.length() > charPosition) offendingSymbol = query.charAt(charPosition);
+		msg = "Failing to parse at symbol: '"+offendingSymbol+"'";
+		// check for unbalanced parantheses
+		SimpleEntry<String, Integer> unbalanced = QueryUtils.checkUnbalancedPars(query); 
+		if (unbalanced != null) {
+			msg = unbalanced.getKey();
+			charPosition = unbalanced.getValue();
+		}
+		
+		return msg;
+	}
+	
+  
+
+}
\ No newline at end of file
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/util/StatusCodes.java b/src/main/java/de/ids_mannheim/korap/query/serialize/util/StatusCodes.java
new file mode 100644
index 0000000..6bcf7e9
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/util/StatusCodes.java
@@ -0,0 +1,10 @@
+package de.ids_mannheim.korap.query.serialize.util;
+
+public class StatusCodes {
+	public final static int NO_QUERY = 301;
+	public final static int MALFORMED_QUERY = 302;
+	public final static int DEPRECATED_QUERY_ELEMENT = 303;
+	public final static int UNDEFINED_CLASS_REFERENCE = 304;
+	public final static int INCOMPATIBLE_OPERATOR_AND_OPERAND = 305;
+	public final static int UNKNOWN_QUERY_ELEMENT = 306;
+}