Merge branch 'master' of ssh://korap.ids-mannheim.de:29418/KorAP/Koral
diff --git a/pom.xml b/pom.xml
index 2516d11..e59fd5c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -52,6 +52,11 @@
</dependency>
<dependency>
<groupId>org.antlr</groupId>
+ <artifactId>antlr4-runtime</artifactId>
+ <version>4.5.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.antlr</groupId>
<artifactId>antlr4-maven-plugin</artifactId>
<version>4.2</version>
</dependency>
@@ -117,6 +122,26 @@
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
+ <dependency>
+ <groupId>eu.clarin.sru.fcs</groupId>
+ <artifactId>fcs-simple-endpoint</artifactId>
+ <version>1.3.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.lucene</groupId>
+ <artifactId>lucene-core</artifactId>
+ <version>5.2.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.lucene</groupId>
+ <artifactId>lucene-analyzers-common</artifactId>
+ <version>5.2.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.lucene</groupId>
+ <artifactId>lucene-queryparser</artifactId>
+ <version>5.2.1</version>
+ </dependency>
</dependencies>
<build>
<sourceDirectory>${basedir}/src/main/java</sourceDirectory>
diff --git a/src/main/java/de/ids_mannheim/korap/query/object/KoralContext.java b/src/main/java/de/ids_mannheim/korap/query/object/KoralContext.java
new file mode 100644
index 0000000..abdde0b
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/object/KoralContext.java
@@ -0,0 +1,22 @@
+package de.ids_mannheim.korap.query.object;
+
+/**
+ * @author margaretha
+ *
+ */
+public enum KoralContext {
+ SENTENCE("s"), PARAGRAPH("p"), TEXT("t");
+
+ private final String key;
+ public static final String FOUNDRY ="base";
+ public static final String LAYER="s"; // surface
+
+ KoralContext(String key){
+ this.key = key;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/object/KoralGroup.java b/src/main/java/de/ids_mannheim/korap/query/object/KoralGroup.java
new file mode 100644
index 0000000..090ecbc
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/object/KoralGroup.java
@@ -0,0 +1,153 @@
+package de.ids_mannheim.korap.query.object;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import de.ids_mannheim.korap.query.serialize.MapBuilder;
+import de.ids_mannheim.korap.query.object.KoralObject;
+import de.ids_mannheim.korap.query.object.KoralOperation;
+import de.ids_mannheim.korap.query.object.KoralType;
+
+/**
+ * @author margaretha
+ *
+ */
+public class KoralGroup implements KoralObject {
+
+ private static final KoralType type = KoralType.GROUP;
+
+ private KoralOperation operation;
+
+ private boolean inOrder = false;
+ private List<KoralObject> operands;
+ private List<Distance> distances;
+ private List<Frame> frames;
+
+ public KoralGroup (KoralOperation operation) {
+ this.operation = operation;
+ }
+
+ public boolean isInOrder() {
+ return inOrder;
+ }
+
+ public void setInOrder(boolean inOrder) {
+ this.inOrder = inOrder;
+ }
+
+ public List<KoralObject> getOperands() {
+ return operands;
+ }
+
+ public void setOperands(List<KoralObject> operands) {
+ this.operands = operands;
+ }
+
+ public List<Distance> getDistances() {
+ return distances;
+ }
+
+ public void setDistances(List<Distance> distances) {
+ this.distances = distances;
+ }
+
+ public List<Frame> getFrames() {
+ return frames;
+ }
+
+ public void setFrames(List<Frame> frames) {
+ this.frames = frames;
+ }
+
+ @Override
+ public Map<String, Object> buildMap() {
+ Map<String, Object> map = new LinkedHashMap<String, Object>();
+ map.put("@type", type.toString());
+ map.put("operation", operation.toString());
+
+ if (getDistances() != null) {
+ map.put("inOrder", isInOrder());
+ List<Map<String, Object>> distanceList = new ArrayList<Map<String, Object>>();
+ for (Distance d : getDistances()) {
+ distanceList.add(d.buildMap());
+ }
+ map.put("distances", distanceList);
+ }
+
+ List<Map<String, Object>> operandList = new ArrayList<Map<String, Object>>();
+ for (Object o : getOperands()) {
+ operandList.add(MapBuilder.buildQueryMap(o));
+ }
+ map.put("operands", operandList);
+ return map;
+ }
+
+ public enum Frame{
+ SUCCEDS("succeeds"), SUCCEDS_DIRECTLY("succeedsDirectly"), OVERLAPS_RIGHT("overlapsRight"),
+ ALIGNS_RIGHT("alignsRight"), IS_WITHIN("isWithin"), STARTS_WITH("startsWith"),
+ MATCHES("matches"), ALIGNS_LEFT("alignsLeft"), IS_AROUND("isAround"), ENDS_WITH("endsWith"),
+ OVERLAPS_LEFT("overlapsLeft"), PRECEEDS_DIRECTLY("precedesDirectly"), PRECEDES("precedes");
+
+ private String value;
+ Frame(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return "frame:"+value;
+ }
+ }
+
+ public class Distance implements KoralObject {
+
+ private final KoralType type = KoralType.DISTANCE;
+ private String key;
+ private String min;
+ private String max;
+
+ public Distance (String key, int min, int max) {
+ this.key = key;
+ this.min = String.valueOf(min);
+ this.max = String.valueOf(max);
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getMin() {
+ return min;
+ }
+
+ public void setMin(String min) {
+ this.min = min;
+ }
+
+ public String getMax() {
+ return max;
+ }
+
+ public void setMax(String max) {
+ this.max = max;
+ }
+
+ @Override
+ public Map<String, Object> buildMap() {
+ Map<String, Object> distanceMap = new LinkedHashMap<String, Object>();
+ distanceMap.put("@type", type.toString());
+ distanceMap.put("key", getKey());
+ distanceMap.put("min", getMin());
+ distanceMap.put("max", getMax());
+ return distanceMap;
+
+ }
+
+ }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/object/KoralMatchOperator.java b/src/main/java/de/ids_mannheim/korap/query/object/KoralMatchOperator.java
new file mode 100644
index 0000000..bd5f6ea
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/object/KoralMatchOperator.java
@@ -0,0 +1,20 @@
+package de.ids_mannheim.korap.query.object;
+
+/**
+ * @author margaretha
+ *
+ */
+public enum KoralMatchOperator {
+ EQUALS("eq"), NOT_EQUALS("ne");
+
+ String value;
+
+ KoralMatchOperator (String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return "match:" + value;
+ };
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/object/KoralObject.java b/src/main/java/de/ids_mannheim/korap/query/object/KoralObject.java
new file mode 100644
index 0000000..21fcf67
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/object/KoralObject.java
@@ -0,0 +1,12 @@
+package de.ids_mannheim.korap.query.object;
+
+import java.util.Map;
+
+/**
+ * @author margaretha
+ *
+ */
+public interface KoralObject {
+
+ public Map<String, Object> buildMap();
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/object/KoralOperation.java b/src/main/java/de/ids_mannheim/korap/query/object/KoralOperation.java
new file mode 100644
index 0000000..972bfd3
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/object/KoralOperation.java
@@ -0,0 +1,15 @@
+package de.ids_mannheim.korap.query.object;
+
+/**
+ * @author margaretha
+ *
+ */
+public enum KoralOperation {
+ SEQUENCE, POSITION, DISJUNCTION, REPETITION, CLASS, MERGE, RELATION;
+
+ @Override
+ public String toString() {
+ return "operation:" + super.toString().toLowerCase();
+ }
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/object/KoralRelation.java b/src/main/java/de/ids_mannheim/korap/query/object/KoralRelation.java
new file mode 100644
index 0000000..84a1ff8
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/object/KoralRelation.java
@@ -0,0 +1,15 @@
+package de.ids_mannheim.korap.query.object;
+
+/**
+ * @author margaretha
+ *
+ */
+public enum KoralRelation {
+
+ AND, OR;
+
+ @Override
+ public String toString() {
+ return "relation:" + super.toString().toLowerCase();
+ }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/object/KoralSpan.java b/src/main/java/de/ids_mannheim/korap/query/object/KoralSpan.java
new file mode 100644
index 0000000..4310bb9
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/object/KoralSpan.java
@@ -0,0 +1,62 @@
+package de.ids_mannheim.korap.query.object;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import de.ids_mannheim.korap.query.serialize.util.KoralException;
+import de.ids_mannheim.korap.query.serialize.util.StatusCodes;
+
+/**
+ * @author margaretha
+ *
+ */
+public class KoralSpan implements KoralObject{
+
+ private static final KoralType type = KoralType.SPAN;
+ private final KoralObject wrappedObject;
+ private KoralObject attribute;
+
+ public KoralSpan(KoralTerm term) throws KoralException {
+ if (term == null){
+ throw new KoralException(StatusCodes.MALFORMED_QUERY, "KoralSpan must not wrap null.");
+ }
+ this.wrappedObject = term;
+ }
+
+ public KoralSpan(KoralTermGroup termGroup) throws KoralException {
+ if (termGroup == null){
+ throw new KoralException(StatusCodes.MALFORMED_QUERY,"KoralSpan must not wrap null.");
+ }
+ this.wrappedObject = termGroup;
+ }
+
+ public KoralObject getWrappedObject() {
+ return wrappedObject;
+ }
+
+ public KoralObject getAttribute() {
+ return attribute;
+ }
+
+ public void setAttribute(KoralTerm attribute) {
+ this.attribute = attribute;
+ }
+
+ public void setAttribute(KoralTermGroup attributes) {
+ this.attribute = attributes;
+ }
+
+ @Override
+ public Map<String, Object> buildMap() {
+ Map<String, Object> map = new LinkedHashMap<String, Object>();
+ map.put("@type", type.toString());
+ if (wrappedObject == null){
+ throw new NullPointerException("KoralSpan does not have a wrapped object.");
+ }
+ map.put("wrap", wrappedObject.buildMap());
+ if(attribute != null){
+ map.put("wrap", attribute.buildMap());
+ }
+ return map;
+ }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/object/KoralTerm.java b/src/main/java/de/ids_mannheim/korap/query/object/KoralTerm.java
new file mode 100644
index 0000000..f9f60cd
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/object/KoralTerm.java
@@ -0,0 +1,138 @@
+package de.ids_mannheim.korap.query.object;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import de.ids_mannheim.korap.query.serialize.util.KoralException;
+import de.ids_mannheim.korap.query.serialize.util.StatusCodes;
+import de.ids_mannheim.korap.query.object.KoralMatchOperator;
+import de.ids_mannheim.korap.query.object.KoralType;
+
+/**
+ * @author margaretha
+ *
+ */
+public class KoralTerm implements KoralObject {
+
+ private static final KoralType koralType = KoralType.TERM;
+
+ private final String key;
+ private String value;
+ private String layer;
+ private String foundry;
+ private KoralMatchOperator operator; // match
+
+ private KoralTermType type;
+
+ private boolean caseSensitive = true;
+
+ public KoralTerm(String key) throws KoralException {
+ if (key == null){
+ throw new KoralException(StatusCodes.MALFORMED_QUERY,
+ "KoralTerm key cannot be null.");
+ }
+ this.key = key;
+ }
+
+ public KoralTerm(KoralContext context) throws KoralException {
+ if (context.getKey() == null){
+ throw new KoralException(StatusCodes.MALFORMED_QUERY,
+ "KoralTerm key cannot be null.");
+ }
+ this.key = context.getKey();
+ this.foundry = KoralContext.FOUNDRY;
+ this.layer = KoralContext.LAYER;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public String getLayer() {
+ return layer;
+ }
+
+ public void setLayer(String layer) {
+ this.layer = layer;
+ }
+
+ public String getFoundry() {
+ return foundry;
+ }
+
+ public void setFoundry(String foundry) {
+ this.foundry = foundry;
+ }
+
+ public KoralMatchOperator getOperator() {
+ return operator;
+ }
+
+ public void setOperator(KoralMatchOperator operator) {
+ this.operator = operator;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public KoralTermType getType() {
+ return type;
+ }
+
+ public void setType(KoralTermType regex) {
+ this.type = regex;
+ }
+
+ public boolean isCaseSensitive() {
+ return caseSensitive;
+ }
+
+ public void setCaseSensitive(boolean isCaseSensitive) {
+ this.caseSensitive = isCaseSensitive;
+ }
+
+ @Override
+ public Map<String, Object> buildMap() {
+ Map<String, Object> map = new LinkedHashMap<String, Object>();
+ map.put("@type", koralType.toString());
+ if (!isCaseSensitive()) {
+ map.put("caseInsensitive", "true");
+ }
+
+ map.put("key", getKey());
+ if (value != null){
+ map.put("value", getValue());
+ }
+ map.put("foundry", getFoundry());
+ map.put("layer", getLayer());
+ if (type != null){
+ map.put("type", getType().toString());
+ }
+ if (operator !=null){
+ map.put("match", getOperator().toString());
+ }
+ return map;
+ }
+
+ public enum KoralTermType {
+ STRING("type:string"), REGEX("type:regex"), WILDCARD("type:wildcard"), PUNCT(
+ "type:punct");
+
+ String value;
+
+ KoralTermType (String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+ }
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/object/KoralTermGroup.java b/src/main/java/de/ids_mannheim/korap/query/object/KoralTermGroup.java
new file mode 100644
index 0000000..eaae803
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/object/KoralTermGroup.java
@@ -0,0 +1,57 @@
+package de.ids_mannheim.korap.query.object;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import de.ids_mannheim.korap.query.serialize.MapBuilder;
+import de.ids_mannheim.korap.query.serialize.util.KoralException;
+
+/**
+ * @author margaretha
+ *
+ */
+public class KoralTermGroup implements KoralObject {
+
+ private static final KoralType type = KoralType.TERMGROUP;
+
+ private String relation;
+ private List<KoralObject> operands = new ArrayList<KoralObject>();
+
+ public KoralTermGroup (KoralRelation relation, List<KoralObject> operands)
+ throws KoralException {
+ this.relation = relation.toString();
+ this.operands = operands;
+ }
+
+ public String getRelation() {
+ return relation;
+ }
+
+ public void setRelation(String relation) {
+ this.relation = relation;
+ }
+
+ public List<KoralObject> getOperands() {
+ return operands;
+ }
+
+ public void setOperands(List<KoralObject> operands) {
+ this.operands = operands;
+ }
+
+ @Override
+ public Map<String, Object> buildMap() {
+ Map<String, Object> map = new LinkedHashMap<String, Object>();
+ map.put("@type", type.toString());
+ map.put("relation", getRelation());
+
+ List<Map<String, Object>> operandList = new ArrayList<Map<String, Object>>();
+ for (Object o : getOperands()) {
+ operandList.add(MapBuilder.buildQueryMap(o));
+ }
+ map.put("operands", operandList);
+ return map;
+ }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/object/KoralToken.java b/src/main/java/de/ids_mannheim/korap/query/object/KoralToken.java
new file mode 100644
index 0000000..06a39ce
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/object/KoralToken.java
@@ -0,0 +1,36 @@
+package de.ids_mannheim.korap.query.object;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import de.ids_mannheim.korap.query.object.KoralObject;
+import de.ids_mannheim.korap.query.object.KoralType;
+
+/**
+ * @author margaretha
+ *
+ */
+public class KoralToken implements KoralObject {
+
+ private final static KoralType type = KoralType.TOKEN;
+ private KoralObject wrappedObject;
+
+ public KoralToken (KoralObject wrappedObject) {
+ this.wrappedObject = wrappedObject;
+ }
+
+ public KoralObject getWrappedObject() {
+ return wrappedObject;
+ }
+ public void setWrappedObject(KoralObject wrappedObject) {
+ this.wrappedObject = wrappedObject;
+ }
+
+ @Override
+ public Map<String, Object> buildMap() {
+ Map<String, Object> map = new LinkedHashMap<String, Object>();
+ map.put("@type", type.toString());
+ map.put("wrap", wrappedObject.buildMap());
+ return map;
+ }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/object/KoralType.java b/src/main/java/de/ids_mannheim/korap/query/object/KoralType.java
new file mode 100644
index 0000000..df7608f
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/object/KoralType.java
@@ -0,0 +1,23 @@
+package de.ids_mannheim.korap.query.object;
+
+/**
+ * @author margaretha
+ *
+ */
+public enum KoralType {
+ TERMGROUP("koral:termGroup"), TERM("koral:term"), TOKEN("koral:token"), SPAN(
+ "koral:span"), GROUP("koral:group"), BOUNDARY("koral:boundary"), RELATION(
+ "koral:relation"), DISTANCE("koral:distance"), REFERENCE(
+ "koral:reference");
+
+ String value;
+
+ KoralType (String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/parse/fcsql/ExpressionParser.java b/src/main/java/de/ids_mannheim/korap/query/parse/fcsql/ExpressionParser.java
new file mode 100644
index 0000000..488b4c8
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/parse/fcsql/ExpressionParser.java
@@ -0,0 +1,219 @@
+package de.ids_mannheim.korap.query.parse.fcsql;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import de.ids_mannheim.korap.query.object.KoralMatchOperator;
+import de.ids_mannheim.korap.query.object.KoralObject;
+import de.ids_mannheim.korap.query.object.KoralRelation;
+import de.ids_mannheim.korap.query.object.KoralTerm;
+import de.ids_mannheim.korap.query.object.KoralTerm.KoralTermType;
+import de.ids_mannheim.korap.query.object.KoralTermGroup;
+import de.ids_mannheim.korap.query.object.KoralToken;
+import de.ids_mannheim.korap.query.serialize.util.KoralException;
+import de.ids_mannheim.korap.query.serialize.util.StatusCodes;
+import eu.clarin.sru.server.fcs.parser.Expression;
+import eu.clarin.sru.server.fcs.parser.ExpressionAnd;
+import eu.clarin.sru.server.fcs.parser.ExpressionGroup;
+import eu.clarin.sru.server.fcs.parser.ExpressionNot;
+import eu.clarin.sru.server.fcs.parser.ExpressionOr;
+import eu.clarin.sru.server.fcs.parser.Operator;
+import eu.clarin.sru.server.fcs.parser.QueryNode;
+import eu.clarin.sru.server.fcs.parser.RegexFlag;
+
+/**
+ * @author margaretha
+ *
+ */
+public class ExpressionParser {
+
+ private static final String FOUNDRY_CNX = "cnx";
+ private static final String FOUNDRY_OPENNLP = "opennlp";
+ private static final String FOUNDRY_TT = "tt";
+ private static final String FOUNDRY_MATE = "mate";
+ private static final String FOUNDRY_XIP = "xip";
+
+ private List<String> supportedFoundries = Arrays
+ .asList(new String[] { FOUNDRY_CNX, FOUNDRY_OPENNLP, FOUNDRY_TT,
+ FOUNDRY_MATE, FOUNDRY_XIP });
+
+ public KoralObject parseExpression(QueryNode queryNode) throws KoralException {
+ return parseExpression(queryNode, false, true);
+ }
+
+ public KoralObject parseExpression(QueryNode queryNode, boolean isNot,
+ boolean isToken) throws KoralException {
+
+ if (queryNode instanceof Expression) {
+ return parseSimpleExpression((Expression) queryNode, isNot, isToken);
+ }
+ else if (queryNode instanceof ExpressionAnd) {
+ List<QueryNode> operands = queryNode.getChildren();
+ if (isNot) {
+ return parseBooleanExpression(operands, KoralRelation.OR);
+ }
+ else {
+ return parseBooleanExpression(operands, KoralRelation.AND);
+ }
+ }
+ else if (queryNode instanceof ExpressionGroup) {
+ // Ignore the group
+ return parseExpression(queryNode.getFirstChild());
+ }
+ else if (queryNode instanceof ExpressionNot) {
+ boolean negation = isNot ? false : true;
+ return parseExpression(queryNode.getChild(0), negation, isToken);
+ }
+ else if (queryNode instanceof ExpressionOr) {
+ List<QueryNode> operands = queryNode.getChildren();
+ if (isNot) {
+ return parseBooleanExpression(operands, KoralRelation.AND);
+ }
+ else {
+ return parseBooleanExpression(operands, KoralRelation.OR);
+ }
+ }
+ // else if (queryNode instanceof ExpressionWildcard) {
+ // for distance query, using empty token
+ // }
+ else {
+ throw new KoralException(StatusCodes.QUERY_TOO_COMPLEX,
+ "FCS diagnostic 11: Query is too complex.");
+ }
+ }
+
+ private KoralToken parseBooleanExpression(List<QueryNode> operands,
+ KoralRelation relation) throws KoralException {
+ List<KoralObject> terms = new ArrayList<>();
+ for (QueryNode node : operands) {
+ terms.add(parseExpression(node, false, false));
+ }
+ KoralTermGroup termGroup = new KoralTermGroup(relation, terms);
+ return new KoralToken(termGroup);
+ }
+
+ private KoralObject parseSimpleExpression(Expression expression, boolean isNot,
+ boolean isToken) throws KoralException {
+ KoralTerm koralTerm = parseTerm(expression, isNot);
+ if (isToken) {
+ return new KoralToken(koralTerm);
+ }
+ else {
+ return koralTerm;
+ }
+ }
+
+ public KoralTerm parseTerm(Expression expression, boolean isNot) throws KoralException {
+ KoralTerm koralTerm = null;
+ koralTerm = new KoralTerm(expression.getRegexValue());
+ koralTerm.setType(KoralTermType.REGEX);
+ parseLayerIdentifier(koralTerm, expression.getLayerIdentifier());
+ parseQualifier(koralTerm, expression.getLayerQualifier());
+ parseOperator(koralTerm, expression.getOperator(), isNot);
+ parseRegexFlags(koralTerm, expression.getRegexFlags());
+ return koralTerm;
+ }
+
+ private void parseLayerIdentifier(KoralTerm koralTerm, String identifier) throws KoralException {
+ String layer = null;
+ if (identifier == null) {
+ throw new KoralException(StatusCodes.MALFORMED_QUERY,
+ "FCS diagnostic 10: Layer identifier is missing.");
+ }
+ else if (identifier.equals("text")) {
+ layer = "orth";
+ }
+ else if (identifier.equals("pos")) {
+ layer = "p";
+ }
+ else if (identifier.equals("lemma")) {
+ layer = "l";
+ }
+ else {
+ throw new KoralException(StatusCodes.UNKNOWN_QUERY_ELEMENT,
+ "SRU diagnostic 48: Layer " + identifier
+ + " is unsupported.");
+ }
+
+ koralTerm.setLayer(layer);
+ }
+
+ private void parseQualifier(KoralTerm koralTerm, String qualifier) throws KoralException {
+ String layer = koralTerm.getLayer();
+ if (layer == null) {
+ return;
+ }
+ // Set default foundry
+ if (qualifier == null) {
+ if (layer.equals("orth")) {
+ qualifier = FOUNDRY_OPENNLP;
+ }
+ else {
+ qualifier = FOUNDRY_TT;
+ }
+ }
+ else if (qualifier.equals(FOUNDRY_OPENNLP) && layer.equals("l")) {
+ throw new KoralException(StatusCodes.UNKNOWN_QUERY_ELEMENT,
+ "SRU diagnostic 48: Layer lemma with qualifier opennlp is unsupported.");
+ }
+ else if (!supportedFoundries.contains(qualifier)) {
+ throw new KoralException(StatusCodes.UNKNOWN_QUERY_ELEMENT,
+ "SRU diagnostic 48: Qualifier " + qualifier
+ + " is unsupported.");
+ }
+
+ koralTerm.setFoundry(qualifier);
+ }
+
+ private void parseOperator(KoralTerm koralTerm, Operator operator,
+ boolean isNot) throws KoralException {
+ KoralMatchOperator matchOperator = null;
+ if (operator == null || operator == Operator.EQUALS) {
+ matchOperator = isNot ? KoralMatchOperator.NOT_EQUALS : KoralMatchOperator.EQUALS;
+ }
+ else if (operator == Operator.NOT_EQUALS) {
+ matchOperator = isNot ? KoralMatchOperator.EQUALS : KoralMatchOperator.NOT_EQUALS;
+ }
+ else {
+ throw new KoralException(StatusCodes.UNKNOWN_QUERY_ELEMENT,
+ "SRU diagnostic 37:" + operator.name()
+ + " is unsupported.");
+ }
+ koralTerm.setOperator(matchOperator);
+ }
+
+ private void parseRegexFlags(KoralTerm koralTerm, Set<RegexFlag> set) throws KoralException {
+ // default case sensitive
+ if (set == null) return;
+
+ ArrayList<String> names = new ArrayList<String>();
+ Iterator<RegexFlag> i = set.iterator();
+ while (i.hasNext()) {
+ RegexFlag f = i.next();
+ if (f == RegexFlag.CASE_SENSITVE) {
+ koralTerm.setCaseSensitive(true);
+ }
+ else if (f == RegexFlag.CASE_INSENSITVE) {
+ koralTerm.setCaseSensitive(false);
+ }
+ else {
+ names.add(f.name());
+ }
+ }
+
+ if (names.size() == 1) {
+ throw new KoralException(StatusCodes.UNKNOWN_QUERY_ELEMENT,
+ "SRU diagnostic 48: Regexflag: " + names.get(0)
+ + " is unsupported.");
+ }
+ else if (names.size() > 1) {
+ throw new KoralException(StatusCodes.UNKNOWN_QUERY_ELEMENT,
+ "SRU diagnostic 48: Regexflags: " + names.toString()
+ + " are unsupported.");
+ }
+ }
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/parse/fcsql/FCSSRUQueryParser.java b/src/main/java/de/ids_mannheim/korap/query/parse/fcsql/FCSSRUQueryParser.java
new file mode 100644
index 0000000..85be144
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/parse/fcsql/FCSSRUQueryParser.java
@@ -0,0 +1,122 @@
+package de.ids_mannheim.korap.query.parse.fcsql;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import de.ids_mannheim.korap.query.object.KoralContext;
+import de.ids_mannheim.korap.query.serialize.util.KoralException;
+import de.ids_mannheim.korap.query.serialize.util.StatusCodes;
+import de.ids_mannheim.korap.query.object.KoralGroup;
+import de.ids_mannheim.korap.query.object.KoralObject;
+import de.ids_mannheim.korap.query.object.KoralOperation;
+import de.ids_mannheim.korap.query.object.KoralSpan;
+import de.ids_mannheim.korap.query.object.KoralTerm;
+import de.ids_mannheim.korap.query.object.KoralGroup.Frame;
+import eu.clarin.sru.server.fcs.parser.QueryDisjunction;
+import eu.clarin.sru.server.fcs.parser.QueryGroup;
+import eu.clarin.sru.server.fcs.parser.QueryNode;
+import eu.clarin.sru.server.fcs.parser.QuerySegment;
+import eu.clarin.sru.server.fcs.parser.QuerySequence;
+import eu.clarin.sru.server.fcs.parser.QueryWithWithin;
+import eu.clarin.sru.server.fcs.parser.SimpleWithin;
+import eu.clarin.sru.server.fcs.parser.SimpleWithin.Scope;
+
+/**
+ * @author margaretha
+ *
+ */
+public class FCSSRUQueryParser {
+
+ private ExpressionParser expressionParser;
+
+ public FCSSRUQueryParser () {
+ this.expressionParser = new ExpressionParser();
+ }
+
+ public KoralObject parseQueryNode(QueryNode queryNode) throws KoralException {
+
+ if (queryNode instanceof QuerySegment) {
+ return parseQuerySegment((QuerySegment) queryNode);
+ }
+ else if (queryNode instanceof QueryGroup) {
+ return parseQueryNode(queryNode.getChild(0));
+ }
+ else if (queryNode instanceof QuerySequence) {
+ return parseGroupQuery(queryNode.getChildren(),
+ KoralOperation.SEQUENCE);
+ }
+ else if (queryNode instanceof QueryDisjunction) {
+ return parseGroupQuery(queryNode.getChildren(),
+ KoralOperation.DISJUNCTION);
+ }
+ else if (queryNode instanceof QueryWithWithin) {
+ return parseWithinQuery((QueryWithWithin)queryNode);
+ }
+ else if (queryNode instanceof SimpleWithin) {
+ SimpleWithin withinNode = (SimpleWithin) queryNode;
+ return parseWithinScope(withinNode.getScope());
+ }
+ else {
+ throw new KoralException(StatusCodes.QUERY_TOO_COMPLEX,
+ "FCS diagnostic 11:" + queryNode.getNodeType().name()
+ + " is currently unsupported.");
+ }
+ }
+ private KoralObject parseWithinQuery(QueryWithWithin queryNode) throws KoralException {
+ KoralGroup koralGroup = new KoralGroup(KoralOperation.POSITION);
+ koralGroup.setFrames(Arrays.asList(Frame.IS_AROUND));
+
+ List<KoralObject> operands = new ArrayList<KoralObject>();
+ operands.add(parseQueryNode(queryNode.getWithin()));
+ operands.add(parseQueryNode(queryNode.getQuery()));
+ koralGroup.setOperands(operands);
+ return koralGroup;
+ }
+
+ private KoralSpan parseWithinScope(Scope scope) throws KoralException{
+ if (scope == null){
+ throw new KoralException(StatusCodes.MALFORMED_QUERY,
+ "FCS diagnostic 11: Within context is missing.");
+ }
+
+ KoralContext contextSpan;
+ if (scope == Scope.SENTENCE) {
+ contextSpan = KoralContext.SENTENCE;
+ }
+ else if (scope == Scope.PARAGRAPH){
+ contextSpan = KoralContext.PARAGRAPH;
+ }
+ else if (scope == Scope.TEXT){
+ contextSpan = KoralContext.TEXT;
+ }
+ else{
+ throw new KoralException(StatusCodes.QUERY_TOO_COMPLEX,
+ "FCS diagnostic 11: Within scope " + scope.toString()
+ + " is currently unsupported.");
+ }
+
+ return new KoralSpan(new KoralTerm(contextSpan));
+ }
+
+ private KoralGroup parseGroupQuery(List<QueryNode> children,
+ KoralOperation operation) throws KoralException {
+ KoralGroup koralGroup = new KoralGroup(operation);
+ List<KoralObject> operands = new ArrayList<KoralObject>();
+ for (QueryNode child : children) {
+ operands.add(parseQueryNode(child));
+ }
+ koralGroup.setOperands(operands);
+ return koralGroup;
+ }
+
+ private KoralObject parseQuerySegment(QuerySegment segment) throws KoralException {
+ if ((segment.getMinOccurs() == 1) && (segment.getMaxOccurs() == 1)) {
+ return expressionParser.parseExpression(segment.getExpression());
+ }
+ else {
+ throw new KoralException(StatusCodes.QUERY_TOO_COMPLEX,
+ "FCS diagnostic 11: Query is too complex.");
+ }
+ }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/CqlQueryProcessor.java b/src/main/java/de/ids_mannheim/korap/query/serialize/CqlQueryProcessor.java
index b06731c..2dd8985 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/CqlQueryProcessor.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/CqlQueryProcessor.java
@@ -12,7 +12,7 @@
/**
* @author margaretha
- * @date 09.05.14
+ * @date 30.05.16
*/
public class CqlQueryProcessor extends AbstractQueryProcessor {
@@ -22,51 +22,40 @@
private static final String INDEX_WORDS = "words";
private static final String TERM_RELATION_CQL_1_1 = "scr";
private static final String TERM_RELATION_CQL_1_2 = "=";
- private static final String SUPPORTED_RELATION_EXACT = "exact"; // not
- // in
- // the
- // doc
+ private static final String SUPPORTED_RELATION_EXACT = "exact"; // not in the doc
private static final String OPERATION_OR = "operation:or";
private static final String OPERATION_SEQUENCE = "operation:sequence";
private static final String OPERATION_POSITION = "operation:position";
- private static final String KORAP_CONTEXT = "http://ids-mannheim.de/ns/KorAP/json-ld/v0.1/context.jsonld";
- private LinkedHashMap<String, Object> requestMap;
private String version;
private boolean isCaseSensitive; // default true
-
public CqlQueryProcessor (String query) {
this(query, VERSION_1_2, true);
}
-
public CqlQueryProcessor (String query, String version) {
this(query, version, true);
}
-
public CqlQueryProcessor (String query, String version,
- boolean isCaseSensitive) {
+ boolean isCaseSensitive) {
+ super();
this.version = version;
this.isCaseSensitive = isCaseSensitive;
- this.requestMap = new LinkedHashMap<>();
- requestMap.put("@context", KORAP_CONTEXT);
process(query);
}
-
@Override
- public Map<String, Object> getRequestMap () {
+ public Map<String, Object> getRequestMap() {
return this.requestMap;
}
-
@Override
- public void process (String query) {
+ public void process(String query) {
if ((query == null) || query.isEmpty())
- addError(StatusCodes.MALFORMED_QUERY,
- "SRU diagnostic 27: An empty query is unsupported.");
+ addError(StatusCodes.NO_QUERY,
+ "SRU diagnostic 27: Empty query is unsupported.");
CQLNode cqlNode = parseQuerytoCQLNode(query);
Map<String, Object> queryMap = parseCQLNode(cqlNode);
@@ -74,8 +63,7 @@
// requestMap.put("query", sentenceWrapper(queryMap));
}
-
- private Map<String, Object> sentenceWrapper (Map<String, Object> m) {
+ private Map<String, Object> sentenceWrapper(Map<String, Object> m) {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("@type", "koral:group");
map.put("operation", OPERATION_POSITION);
@@ -93,8 +81,7 @@
return map;
}
-
- private CQLNode parseQuerytoCQLNode (String query) {
+ private CQLNode parseQuerytoCQLNode(String query) {
try {
int compat = -1;
switch (version) {
@@ -113,8 +100,7 @@
}
}
-
- private Map<String, Object> parseCQLNode (CQLNode node) {
+ private Map<String, Object> parseCQLNode(CQLNode node) {
if (node instanceof CQLTermNode) {
return parseTermNode((CQLTermNode) node);
@@ -134,8 +120,7 @@
}
}
-
- private Map<String, Object> parseTermNode (CQLTermNode node) {
+ private Map<String, Object> parseTermNode(CQLTermNode node) {
checkTermNode(node);
final String term = node.getTerm();
if ((term == null) || term.isEmpty()) {
@@ -151,8 +136,7 @@
}
}
-
- private Map<String, Object> parseAndNode (CQLAndNode node) {
+ private Map<String, Object> parseAndNode(CQLAndNode node) {
checkBooleanModifier(node);
Map<String, Object> map = new LinkedHashMap<String, Object>();
@@ -177,8 +161,7 @@
return map;
}
-
- private Map<String, Object> parseOrNode (CQLOrNode node) {
+ private Map<String, Object> parseOrNode(CQLOrNode node) {
checkBooleanModifier(node);
Map<String, Object> map = new LinkedHashMap<String, Object>();
@@ -193,8 +176,7 @@
return map;
}
-
- private Map<String, Object> writeSequence (String str) {
+ private Map<String, Object> writeSequence(String str) {
Map<String, Object> sequenceMap = new LinkedHashMap<String, Object>();
sequenceMap.put("@type", "koral:group");
sequenceMap.put("operation", OPERATION_SEQUENCE);
@@ -209,8 +191,7 @@
return sequenceMap;
}
-
- private Map<String, Object> writeTerm (String term) {
+ private Map<String, Object> writeTerm(String term) {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("@type", "koral:term");
if (!isCaseSensitive) {
@@ -226,8 +207,7 @@
return tokenMap;
}
-
- private void checkBooleanModifier (CQLBooleanNode node) {
+ private void checkBooleanModifier(CQLBooleanNode node) {
List<Modifier> modifiers = node.getModifiers();
if ((modifiers != null) && !modifiers.isEmpty()) {
Modifier modifier = modifiers.get(0);
@@ -237,8 +217,7 @@
}
}
-
- private void checkTermNode (CQLTermNode node) {
+ private void checkTermNode(CQLTermNode node) {
// only allow "cql.serverChoice" and "words" index
if (!(INDEX_CQL_SERVERCHOICE.equals(node.getIndex()) || INDEX_WORDS
.equals(node.getIndex()))) {
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/FCSQLQueryProcessor.java b/src/main/java/de/ids_mannheim/korap/query/serialize/FCSQLQueryProcessor.java
new file mode 100644
index 0000000..369e8e3
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/FCSQLQueryProcessor.java
@@ -0,0 +1,109 @@
+package de.ids_mannheim.korap.query.serialize;
+
+import java.util.Map;
+
+import de.ids_mannheim.korap.query.parse.fcsql.FCSSRUQueryParser;
+import de.ids_mannheim.korap.query.serialize.util.KoralException;
+import de.ids_mannheim.korap.query.serialize.util.StatusCodes;
+import eu.clarin.sru.server.SRUQueryBase;
+import eu.clarin.sru.server.fcs.Constants;
+import eu.clarin.sru.server.fcs.parser.QueryNode;
+import eu.clarin.sru.server.fcs.parser.QueryParser;
+import eu.clarin.sru.server.fcs.parser.QueryParserException;
+
+/**
+ * @author margaretha
+ *
+ */
+public class FCSQLQueryProcessor extends AbstractQueryProcessor {
+
+ public static final class FCSSRUQuery extends SRUQueryBase<QueryNode> {
+
+ private FCSSRUQuery (String rawQuery, QueryNode parsedQuery) {
+ super(rawQuery, parsedQuery);
+ }
+
+ @Override
+ public String getQueryType() {
+ return Constants.FCS_QUERY_TYPE_FCS;
+ }
+ }
+
+ private static final String VERSION_2_0 = "2.0";
+
+ private final QueryParser fcsParser = new QueryParser();
+ private String version;
+
+ public FCSQLQueryProcessor (String query, String version) {
+ super();
+ this.version = version;
+ process(query);
+ }
+
+ @Override
+ public Map<String, Object> getRequestMap() {
+ return this.requestMap;
+ }
+
+ @Override
+ public void process(String query) {
+ if (isVersionValid()) {
+ FCSSRUQuery fcsSruQuery = parseQueryStringtoFCSQuery(query);
+ if (fcsSruQuery != null) {
+ QueryNode fcsQueryNode = fcsSruQuery.getParsedQuery();
+ try {
+ parseFCSQueryToKoralQuery(fcsQueryNode);
+ } catch (KoralException e) {
+ addError(e.getStatusCode(), e.getMessage());
+ }
+ }
+ }
+ }
+
+ private boolean isVersionValid() {
+ if (version == null || version.isEmpty()) {
+ addError(StatusCodes.MISSING_VERSION,
+ "SRU diagnostic 7: Version number is missing.");
+ return false;
+ }
+ else if (!version.equals(VERSION_2_0)) {
+ addError(StatusCodes.MISSING_VERSION,
+ "SRU diagnostic 5: Only supports SRU version 2.0.");
+ return false;
+ }
+ return true;
+ }
+
+ private FCSSRUQuery parseQueryStringtoFCSQuery(String query) {
+ if ((query == null) || query.isEmpty())
+ addError(StatusCodes.NO_QUERY,
+ "SRU diagnostic 1: No query has been passed.");
+ FCSSRUQuery fcsQuery = null;
+ try {
+ QueryNode parsedQuery = fcsParser.parse(query);
+ fcsQuery = new FCSSRUQuery(query, parsedQuery);
+ if (fcsQuery == null) {
+ addError(StatusCodes.UNKNOWN_QUERY_ERROR,
+ "FCS diagnostic 10: Unexpected error while parsing query.");
+ }
+ }
+ catch (QueryParserException e) {
+ addError(
+ StatusCodes.UNKNOWN_QUERY_ERROR,
+ "FCS diagnostic 10: Query cannot be parsed, "
+ + e.getMessage());
+ }
+ catch (Exception e) {
+ addError(StatusCodes.UNKNOWN_QUERY_ERROR, "FCS diagnostic 10: "
+ + "Unexpected error while parsing query.");
+ }
+ return fcsQuery;
+ }
+
+ private void parseFCSQueryToKoralQuery(QueryNode queryNode) throws KoralException {
+ FCSSRUQueryParser parser = new FCSSRUQueryParser();
+ Object o = parser.parseQueryNode(queryNode);
+ Map<String, Object> queryMap = MapBuilder.buildQueryMap(o);
+ if (queryMap != null) requestMap.put("query", queryMap);
+ }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/MapBuilder.java b/src/main/java/de/ids_mannheim/korap/query/serialize/MapBuilder.java
new file mode 100644
index 0000000..9d1c2ee
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/MapBuilder.java
@@ -0,0 +1,42 @@
+package de.ids_mannheim.korap.query.serialize;
+
+import java.util.Map;
+
+import de.ids_mannheim.korap.query.object.KoralGroup;
+import de.ids_mannheim.korap.query.object.KoralSpan;
+import de.ids_mannheim.korap.query.object.KoralTerm;
+import de.ids_mannheim.korap.query.object.KoralTermGroup;
+import de.ids_mannheim.korap.query.object.KoralToken;
+
+/**
+ * @author margaretha
+ *
+ */
+public class MapBuilder {
+
+ public static Map<String, Object> buildQueryMap(Object o) {
+ if (o != null) {
+ if (o instanceof KoralToken) {
+ KoralToken token = (KoralToken) o;
+ return token.buildMap();
+ }
+ else if (o instanceof KoralGroup) {
+ KoralGroup group = (KoralGroup) o;
+ return group.buildMap();
+ }
+ if (o instanceof KoralTerm) {
+ KoralTerm term = (KoralTerm) o;
+ return term.buildMap();
+ }
+ else if (o instanceof KoralTermGroup) {
+ KoralTermGroup termGroup = (KoralTermGroup) o;
+ return termGroup.buildMap();
+ }
+ else if (o instanceof KoralSpan){
+ KoralSpan span = (KoralSpan) o;
+ return span.buildMap();
+ }
+ }
+ return null;
+ }
+}
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 dcf0d3d..d4cb27e 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
@@ -12,11 +12,10 @@
import java.util.*;
/**
- * Main class for Koral, serializes queries from concrete QLs to
- * KoralQuery
+ * Main class for Koral, serializes queries from concrete QLs to KoralQuery
*
- * @author Joachim Bingel (bingel@ids-mannheim.de),
- * Michael Hanl (hanl@ids-mannheim.de)
+ * @author Joachim Bingel (bingel@ids-mannheim.de), Michael Hanl
+ * (hanl@ids-mannheim.de), Eliza Margaretha (margaretha@ids-mannheim.de)
* @version 0.3.0
* @since 0.1.0
*/
@@ -28,10 +27,11 @@
+
static {
qlProcessorAssignment = new HashMap<String, Class<? extends AbstractQueryProcessor>>();
- qlProcessorAssignment.put("poliqarpplus",
- PoliqarpPlusQueryProcessor.class);
+ qlProcessorAssignment.put("poliqarpplus",
+ PoliqarpPlusQueryProcessor.class);
qlProcessorAssignment.put("cosmas2", Cosmas2QueryProcessor.class);
qlProcessorAssignment.put("annis", AnnisQueryProcessor.class);
qlProcessorAssignment.put("cql", CqlQueryProcessor.class);
@@ -50,18 +50,16 @@
private org.slf4j.Logger log = LoggerFactory
.getLogger(QuerySerializer.class);
-
- public QuerySerializer () {
+ public QuerySerializer() {
this.errors = new LinkedList<>();
this.warnings = new LinkedList<>();
this.messages = new LinkedList<>();
}
-
/**
* @param args
*/
- public static void main (String[] args) {
+ public static void main(String[] args) {
/*
* just for testing...
*/
@@ -74,8 +72,7 @@
System.err
.println("Usage: QuerySerializer \"query\" queryLanguage");
System.exit(1);
- }
- else {
+ } else {
queries = new String[] { args[0] };
ql = args[1];
}
@@ -84,141 +81,118 @@
try {
jg.run(q, ql);
System.out.println();
- }
- catch (NullPointerException npe) {
+ } catch (NullPointerException npe) {
npe.printStackTrace();
System.out.println("null\n");
- }
- catch (IOException e) {
+ } catch (IOException e) {
e.printStackTrace();
}
}
}
-
/**
- * Runs the QuerySerializer by initializing the relevant
- * AbstractSyntaxTree implementation (depending on specified query
- * language) and transforms and writes the tree's requestMap to
- * the specified output file.
- *
- * @param query
- * The query string
- * @param queryLanguage
- * The query language. As of 17 Dec 2014, this must be
- * one of 'poliqarpplus', 'cosmas2', 'annis' or 'cql'.
- * @throws IOException
- */
- public void run (String query, String queryLanguage) throws IOException {
+ * Runs the QuerySerializer by initializing the relevant AbstractSyntaxTree
+ * implementation (depending on specified query language) and transforms and
+ * writes the tree's requestMap to the specified output file.
+ *
+ * @param query
+ * The query string
+ * @param queryLanguage
+ * The query language. As of 17 Dec 2014, this must be one of
+ * 'poliqarpplus', 'cosmas2', 'annis' or 'cql'.
+ * @throws IOException
+ */
+ public void run(String query, String queryLanguage) throws IOException {
if (queryLanguage.equalsIgnoreCase("poliqarp")) {
ast = new PoliqarpPlusQueryProcessor(query);
- }
- else if (queryLanguage.equalsIgnoreCase("cosmas2")) {
+ } else if (queryLanguage.equalsIgnoreCase("cosmas2")) {
ast = new Cosmas2QueryProcessor(query);
- }
- else if (queryLanguage.equalsIgnoreCase("poliqarpplus")) {
+ } else if (queryLanguage.equalsIgnoreCase("poliqarpplus")) {
ast = new PoliqarpPlusQueryProcessor(query);
- }
- else if (queryLanguage.equalsIgnoreCase("cql")) {
+ } else if (queryLanguage.equalsIgnoreCase("cql")) {
ast = new CqlQueryProcessor(query);
- }
- else if (queryLanguage.equalsIgnoreCase("annis")) {
+ } else if (queryLanguage.equalsIgnoreCase("fcsql")) {
+ ast = new FCSQLQueryProcessor(query, "2.0");
+ }else if (queryLanguage.equalsIgnoreCase("annis")) {
ast = new AnnisQueryProcessor(query);
- }
- else {
- throw new IllegalArgumentException(queryLanguage
- + " is not a supported query language!");
+ } else {
+ throw new IllegalArgumentException(queryLanguage
+ + " is not a supported query language!");
}
toJSON();
}
-
- public QuerySerializer setQuery (String query, String ql, String version) {
+ public QuerySerializer setQuery(String query, String ql, String version) {
ast = new DummyQueryProcessor();
if (query == null || query.isEmpty()) {
ast.addError(StatusCodes.NO_QUERY, "You did not specify a query!");
- }
- else if (ql == null || ql.isEmpty()) {
+ } else if (ql == null || ql.isEmpty()) {
ast.addError(StatusCodes.NO_QUERY,
"You did not specify any query language!");
- }
- else if (ql.equalsIgnoreCase("poliqarp")) {
+ } else if (ql.equalsIgnoreCase("poliqarp")) {
ast = new PoliqarpPlusQueryProcessor(query);
- }
- else if (ql.equalsIgnoreCase("cosmas2")) {
+ } else if (ql.equalsIgnoreCase("cosmas2")) {
ast = new Cosmas2QueryProcessor(query);
- }
- else if (ql.equalsIgnoreCase("poliqarpplus")) {
+ } else if (ql.equalsIgnoreCase("poliqarpplus")) {
ast = new PoliqarpPlusQueryProcessor(query);
- }
- else if (ql.equalsIgnoreCase("cql")) {
- if (version == null)
+ }else if (ql.equalsIgnoreCase("cql")) {
+ if (version == null) {
ast = new CqlQueryProcessor(query);
- else
+ } else {
ast = new CqlQueryProcessor(query, version);
- }
- else if (ql.equalsIgnoreCase("annis")) {
+ }
+ } else if (ql.equalsIgnoreCase("fcsql")) {
+ if (version == null) {
+ ast.addError(StatusCodes.MISSING_VERSION,
+ "SRU Version is missing!");
+ } else {
+ ast = new FCSQLQueryProcessor(query, version);
+ }
+ } else if (ql.equalsIgnoreCase("annis")) {
ast = new AnnisQueryProcessor(query);
- }
- else {
- ast.addError(StatusCodes.UNKNOWN_QL, ql
- + " is not a supported query language!");
+ }else {
+ ast.addError(StatusCodes.UNKNOWN_QUERY_LANGUAGE,
+ ql + " is not a supported query language!");
}
return this;
}
-
- public QuerySerializer setQuery (String query, String ql) {
+ public QuerySerializer setQuery(String query, String ql) {
return setQuery(query, ql, "");
}
-
- public void setVerbose (boolean verbose) {
+ public void setVerbose(boolean verbose) {
AbstractQueryProcessor.verbose = verbose;
}
-
- public final String toJSON () {
+ public final String toJSON() {
String ser;
try {
ser = mapper.writeValueAsString(raw());
qllogger.info("Serialized query: " + ser);
- }
- catch (JsonProcessingException e) {
+ } catch (JsonProcessingException e) {
return "";
}
return ser;
}
-
- public final Map build () {
+ public final Map build() {
return raw();
}
-
- private Map raw () {
+ private Map raw() {
if (ast != null) {
- Map<String, Object> requestMap = new HashMap<>(ast.getRequestMap());
+ Map<String, Object> requestMap = ast.getRequestMap();
Map meta = (Map) requestMap.get("meta");
Map collection = (Map) requestMap.get("collection");
List errors = (List) requestMap.get("errors");
List warnings = (List) requestMap.get("warnings");
List messages = (List) requestMap.get("messages");
- collection = mergeCollection(collection, this.collection);
- requestMap.put("collection", collection);
-
- if (meta == null)
- meta = new HashMap();
- if (errors == null)
- errors = new LinkedList();
- if (warnings == null)
- warnings = new LinkedList();
- if (messages == null)
- messages = new LinkedList();
-
+ this.collection = mergeCollection(collection, this.collection);
+ requestMap.put("collection", this.collection);
if (this.meta != null) {
- meta.putAll(this.meta);
- requestMap.put("meta", meta);
+ this.meta.putAll(meta);
+ requestMap.put("meta", this.meta);
}
if (this.errors != null && !this.errors.isEmpty()) {
errors.addAll(this.errors);
@@ -232,43 +206,37 @@
messages.addAll(this.messages);
requestMap.put("messages", messages);
}
+
return cleanup(requestMap);
}
return new HashMap<>();
}
-
- private Map<String, Object> cleanup (Map<String, Object> requestMap) {
+ private Map<String, Object> cleanup(Map<String, Object> requestMap) {
Iterator<Map.Entry<String, Object>> set = requestMap.entrySet()
.iterator();
while (set.hasNext()) {
Map.Entry<String, Object> entry = set.next();
- if (entry.getValue() instanceof List
- && ((List) entry.getValue()).isEmpty())
+ if (entry.getValue() instanceof List
+ && ((List) entry.getValue()).isEmpty())
set.remove();
- else if (entry.getValue() instanceof Map
- && ((Map) entry.getValue()).isEmpty())
+ else if (entry.getValue() instanceof Map
+ && ((Map) entry.getValue()).isEmpty())
set.remove();
- else if (entry.getValue() instanceof String
- && ((String) entry.getValue()).isEmpty())
+ else if (entry.getValue() instanceof String
+ && ((String) entry.getValue()).isEmpty())
set.remove();
}
return requestMap;
}
-
- private Map<String, Object> mergeCollection (
- Map<String, Object> collection1, Map<String, Object> collection2) {
+ private Map<String, Object> mergeCollection(
+ Map<String, Object> collection1, Map<String, Object> collection2) {
if (collection1 == null || collection1.isEmpty()) {
return collection2;
- }
- else if (collection2 == null || collection2.isEmpty()) {
+ } else if (collection2 == null || collection2.isEmpty()) {
return collection1;
- }
- else if (collection1.equals(collection2)) {
- return collection1;
- }
- else {
+ } else {
LinkedHashMap<String, Object> docGroup = KoralObjectGenerator
.makeDocGroup("and");
ArrayList<Object> operands = (ArrayList<Object>) docGroup
@@ -279,9 +247,8 @@
}
}
-
@Deprecated
- public QuerySerializer addMeta (String cli, String cri, int cls, int crs,
+ public QuerySerializer addMeta(String cli, String cri, int cls, int crs,
int num, int pageIndex) {
MetaQueryBuilder meta = new MetaQueryBuilder();
meta.setSpanContext(cls, cli, crs, cri);
@@ -291,14 +258,17 @@
return this;
}
-
- public QuerySerializer setMeta (Map<String, Object> meta) {
+ public QuerySerializer setMeta(Map<String, Object> meta) {
this.meta = meta;
return this;
}
+ public QuerySerializer setMeta(MetaQueryBuilder meta) {
+ this.meta = meta.raw();
+ return this;
+ }
- public QuerySerializer setCollection (String collection) {
+ public QuerySerializer setCollection(String collection) {
CollectionQueryProcessor tree = new CollectionQueryProcessor();
tree.process(collection);
Map<String, Object> collectionRequest = tree.getRequestMap();
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/util/KoralException.java b/src/main/java/de/ids_mannheim/korap/query/serialize/util/KoralException.java
new file mode 100644
index 0000000..34d4569
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/util/KoralException.java
@@ -0,0 +1,30 @@
+package de.ids_mannheim.korap.query.serialize.util;
+
+public class KoralException extends Exception{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 5463242042200109417L;
+ private int statusCode;
+
+ public KoralException(int code, String message) {
+ super(message);
+ this.statusCode = code;
+ }
+
+ public KoralException(int code, String message, Throwable cause) {
+ super(message, cause);
+ this.statusCode = code;
+ }
+
+ public int getStatusCode() {
+ return statusCode;
+ }
+
+ public void setStatusCode(int statusCode) {
+ this.statusCode = statusCode;
+ }
+
+
+}
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
index 10614ea..1f896b3 100644
--- 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
@@ -7,7 +7,9 @@
public final static int INVALID_CLASS_REFERENCE = 304;
public final static int INCOMPATIBLE_OPERATOR_AND_OPERAND = 305;
public final static int UNKNOWN_QUERY_ELEMENT = 306;
- public final static int UNKNOWN_QL = 307;
+ public final static int UNKNOWN_QUERY_LANGUAGE = 307;
public final static int UNBOUND_ANNIS_RELATION = 308;
- public final static int UNKNOWN_QUERY_ERROR = 399;
-}
+ public final static int MISSING_VERSION = 309;
+ public final static int QUERY_TOO_COMPLEX = 310;
+ public final static int UNKNOWN_QUERY_ERROR = 399;
+}
\ No newline at end of file
diff --git a/src/test/java/de/ids_mannheim/korap/query/serialize/CqlQueryProcessorTest.java b/src/test/java/de/ids_mannheim/korap/query/serialize/CqlQueryProcessorTest.java
index 6f2bdd9..5af4b60 100644
--- a/src/test/java/de/ids_mannheim/korap/query/serialize/CqlQueryProcessorTest.java
+++ b/src/test/java/de/ids_mannheim/korap/query/serialize/CqlQueryProcessorTest.java
@@ -3,81 +3,58 @@
import static org.junit.Assert.assertEquals;
import java.io.IOException;
+import java.util.List;
import org.junit.Test;
import org.z3950.zing.cql.CQLParseException;
-import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import de.ids_mannheim.korap.query.serialize.CqlQueryProcessor;
-import de.ids_mannheim.korap.query.serialize.Cosmas2QueryProcessor;
-
-
public class CqlQueryProcessorTest {
String query;
- String version = "1.2";
+ String VERSION = "1.2";
ObjectMapper mapper = new ObjectMapper();
-
- @Test
- public void testExceptions () throws CQLParseException, IOException {
- query = "(Kuh) prox (Germ) ";
- try {
- CqlQueryProcessor cqlTree = new CqlQueryProcessor(query, version);
- }
- catch (Exception e) {
- int errorCode = Integer.parseInt(e.getMessage().split(":")[0]
- .replace("SRU diagnostic ", ""));
- assertEquals(48, errorCode);
- }
-
- query = "(Kuh) or/rel.combine=sum (Germ) ";
- try {
- CqlQueryProcessor cqlTree = new CqlQueryProcessor(query, version);
- }
- catch (Exception e) {
- int errorCode = Integer.parseInt(e.getMessage().split(":")[0]
- .replace("SRU diagnostic ", ""));
- assertEquals(20, errorCode);
- }
-
- query = "dc.title any Germ ";
- try {
- CqlQueryProcessor cqlTree = new CqlQueryProcessor(query, version);
- }
- catch (Exception e) {
- int errorCode = Integer.parseInt(e.getMessage().split(":")[0]
- .replace("SRU diagnostic ", ""));
- assertEquals(16, errorCode);
- }
-
- query = "cql.serverChoice any Germ ";
- try {
- CqlQueryProcessor cqlTree = new CqlQueryProcessor(query, version);
- }
- catch (Exception e) {
- int errorCode = Integer.parseInt(e.getMessage().split(":")[0]
- .replace("SRU diagnostic ", ""));
- assertEquals(19, errorCode);
- }
-
- query = "";
- try {
- CqlQueryProcessor cqlTree = new CqlQueryProcessor(query, version);
- }
- catch (Exception e) {
- int errorCode = Integer.parseInt(e.getMessage().split(":")[0]
- .replace("SRU diagnostic ", ""));
- assertEquals(27, errorCode);
- }
+ private List<Object> getError(CqlQueryProcessor processor) {
+ List<Object> errors = (List<Object>) processor.requestMap.get("errors");
+ return (List<Object>) errors.get(0);
}
+ @Test
+ public void testExceptions() throws CQLParseException, IOException {
+ query = "(Kuh) prox (Germ) ";
+ CqlQueryProcessor cqlTree = new CqlQueryProcessor(query, VERSION);
+ List<Object> error = getError(cqlTree);
+ assertEquals(
+ "SRU diagnostic 48: Only basic search including term-only "
+ + "and boolean (AND,OR) operator queries are currently supported.",
+ error.get(1));
+
+ query = "(Kuh) or/rel.combine=sum (Germ) ";
+ error = getError(new CqlQueryProcessor(query, VERSION));
+ assertEquals(
+ "SRU diagnostic 20: Relation modifier rel.combine = sum is not supported.",
+ error.get(1));
+
+ query = "dc.title any Germ ";
+ error = getError(new CqlQueryProcessor(query, VERSION));
+ assertEquals("SRU diagnostic 16: Index dc.title is not supported.",
+ error.get(1));
+
+ query = "cql.serverChoice any Germ ";
+ error = getError(new CqlQueryProcessor(query, VERSION));
+ assertEquals("SRU diagnostic 19: Relation any is not supported.",
+ error.get(1));
+
+ query = "";
+ error = getError(new CqlQueryProcessor(query, VERSION));
+ assertEquals("SRU diagnostic 27: Empty query is unsupported.",
+ error.get(1));
+ }
@Test
- public void testAndQuery () throws CQLParseException, IOException,
- Exception {
+ public void testAndQuery() throws CQLParseException, IOException, Exception {
query = "(Sonne) and (scheint)";
String jsonLd = "{@type : koral:group, operation : operation:sequence, inOrder : false,"
+ "distances:[ "
@@ -87,19 +64,20 @@
+ "{@type : koral:token,wrap : {@type : koral:term,key : scheint,layer : orth,match : match:eq}"
+ "}]}";
- CqlQueryProcessor cqlTree = new CqlQueryProcessor(query, version);
+ CqlQueryProcessor cqlTree = new CqlQueryProcessor(query, VERSION);
String serializedQuery = mapper.writeValueAsString(cqlTree
.getRequestMap().get("query"));
assertEquals(jsonLd.replace(" ", ""), serializedQuery.replace("\"", ""));
- // /System.out.println(serializedQuery);
- //CosmasTree ct = new CosmasTree("Sonne und scheint");
- //serializedQuery = mapper.writeValueAsString(ct.getRequestMap().get("query"));
- //assertEquals(jsonLd.replace(" ", ""), serializedQuery.replace("\"", ""));
+ // /System.out.println(serializedQuery);
+ // CosmasTree ct = new CosmasTree("Sonne und scheint");
+ // serializedQuery =
+ // mapper.writeValueAsString(ct.getRequestMap().get("query"));
+ // assertEquals(jsonLd.replace(" ", ""),
+ // serializedQuery.replace("\"", ""));
}
-
@Test
- public void testBooleanQuery () throws CQLParseException, IOException,
+ public void testBooleanQuery() throws CQLParseException, IOException,
Exception {
query = "((Sonne) or (Mond)) and (scheint)";
String jsonLd = "{@type:koral:group, operation:operation:sequence, inOrder : false, distances:["
@@ -111,12 +89,11 @@
+ "]},"
+ "{@type:koral:token, wrap:{@type:koral:term, key:scheint, layer:orth, match:match:eq}}"
+ "]}";
- CqlQueryProcessor cqlTree = new CqlQueryProcessor(query, version);
+ CqlQueryProcessor cqlTree = new CqlQueryProcessor(query, VERSION);
String serializedQuery = mapper.writeValueAsString(cqlTree
.getRequestMap().get("query"));
assertEquals(jsonLd.replace(" ", ""), serializedQuery.replace("\"", ""));
-
query = "(scheint) and ((Sonne) or (Mond))";
jsonLd = "{@type:koral:group, operation:operation:sequence, inOrder : false, distances:["
+ "{@type:koral:distance, key:s, min:0, max:0}"
@@ -126,23 +103,22 @@
+ "{@type:koral:token, wrap:{@type:koral:term, key:Sonne, layer:orth, match:match:eq}},"
+ "{@type:koral:token, wrap:{@type:koral:term, key:Mond, layer:orth, match:match:eq}}"
+ "]}" + "]}";
- cqlTree = new CqlQueryProcessor(query, version);
+ cqlTree = new CqlQueryProcessor(query, VERSION);
serializedQuery = mapper.writeValueAsString(cqlTree.getRequestMap()
.get("query"));
assertEquals(jsonLd.replace(" ", ""), serializedQuery.replace("\"", ""));
}
-
@Test
- public void testOrQuery () throws CQLParseException, IOException, Exception {
+ public void testOrQuery() throws CQLParseException, IOException, Exception {
query = "(Sonne) or (Mond)";
String jsonLd = "{@type:koral:group, operation:operation:or, operands:["
+ "{@type:koral:token, wrap:{@type:koral:term, key:Sonne, layer:orth, match:match:eq}},"
+ "{@type:koral:token, wrap:{@type:koral:term, key:Mond, layer:orth, match:match:eq}}"
+ "]}";
- CqlQueryProcessor cqlTree = new CqlQueryProcessor(query, version);
+ CqlQueryProcessor cqlTree = new CqlQueryProcessor(query, VERSION);
String serializedQuery = mapper.writeValueAsString(cqlTree
.getRequestMap().get("query"));
assertEquals(jsonLd.replace(" ", ""), serializedQuery.replace("\"", ""));
@@ -156,7 +132,7 @@
+ "{@type:koral:token, wrap:{@type:koral:term, key:Mond, layer:orth, match:match:eq}}"
+ "]}";
- cqlTree = new CqlQueryProcessor(query, version);
+ cqlTree = new CqlQueryProcessor(query, VERSION);
serializedQuery = mapper.writeValueAsString(cqlTree.getRequestMap()
.get("query"));
assertEquals(jsonLd.replace(" ", ""), serializedQuery.replace("\"", ""));
@@ -171,27 +147,25 @@
+ "{@type:koral:token, wrap:{@type:koral:term, key:Mond, layer:orth, match:match:eq}},"
+ "{@type:koral:token, wrap:{@type:koral:term, key:scheint, layer:orth, match:match:eq}}"
+ "]}" + "]}";
- cqlTree = new CqlQueryProcessor(query, version);
+ cqlTree = new CqlQueryProcessor(query, VERSION);
serializedQuery = mapper.writeValueAsString(cqlTree.getRequestMap()
.get("query"));
assertEquals(jsonLd.replace(" ", ""), serializedQuery.replace("\"", ""));
}
-
@Test
- public void testTermQuery () throws CQLParseException, IOException,
+ public void testTermQuery() throws CQLParseException, IOException,
Exception {
query = "Sonne";
String jsonLd = "{@type:koral:token, wrap:{@type:koral:term, key:Sonne, layer:orth, match:match:eq}}";
- CqlQueryProcessor cqlTree = new CqlQueryProcessor(query, version);
+ CqlQueryProcessor cqlTree = new CqlQueryProcessor(query, VERSION);
String serializedQuery = mapper.writeValueAsString(cqlTree
.getRequestMap().get("query"));
assertEquals(jsonLd.replace(" ", ""), serializedQuery.replace("\"", ""));
}
-
@Test
- public void testPhraseQuery () throws CQLParseException, IOException,
+ public void testPhraseQuery() throws CQLParseException, IOException,
Exception {
query = "\"der Mann\"";
String jsonLd = "{@type:koral:group, operation:operation:sequence, operands:["
@@ -199,12 +173,11 @@
+ "{@type:koral:token, wrap:{@type:koral:term, key:Mann, layer:orth, match:match:eq}}"
+ "]}";
- CqlQueryProcessor cqlTree = new CqlQueryProcessor(query, version);
+ CqlQueryProcessor cqlTree = new CqlQueryProcessor(query, VERSION);
String serializedQuery = mapper.writeValueAsString(cqlTree
.getRequestMap().get("query"));
assertEquals(jsonLd.replace(" ", ""), serializedQuery.replace("\"", ""));
-
query = "der Mann schläft";
jsonLd = "{@type:koral:group, operation:operation:sequence, operands:["
+ "{@type:koral:token, wrap:{@type:koral:term, key:der, layer:orth, match:match:eq}},"
@@ -212,7 +185,7 @@
+ "{@type:koral:token, wrap:{@type:koral:term, key:schläft, layer:orth, match:match:eq}}"
+ "]}";
- cqlTree = new CqlQueryProcessor(query, version);
+ cqlTree = new CqlQueryProcessor(query, VERSION);
serializedQuery = mapper.writeValueAsString(cqlTree.getRequestMap()
.get("query"));
assertEquals(jsonLd.replace(" ", ""), serializedQuery.replace("\"", ""));
diff --git a/src/test/java/de/ids_mannheim/korap/query/serialize/FCSQLComplexTest.java b/src/test/java/de/ids_mannheim/korap/query/serialize/FCSQLComplexTest.java
new file mode 100644
index 0000000..3973aea
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/query/serialize/FCSQLComplexTest.java
@@ -0,0 +1,173 @@
+package de.ids_mannheim.korap.query.serialize;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.junit.Test;
+
+/**
+ * @author margaretha
+ *
+ */
+public class FCSQLComplexTest {
+
+ // -------------------------------------------------------------------------
+ // simple-query ::= '(' main_query ')' /* grouping */
+ // | implicit-query
+ // | segment-query
+ // -------------------------------------------------------------------------
+ // implicit-query ::= flagged-regexp
+ // segment-query ::= "[" expression? "]"
+ // -------------------------------------------------------------------------
+ // simple-query ::= '(' main_query ')' /* grouping */
+ @Test
+ public void testGroupQuery() throws IOException {
+ String query = "(\"blaue\"|\"grüne\")";
+ String jsonLd = "{@type:koral:group,"
+ + "operation:operation:disjunction,"
+ + "operands:["
+ + "{@type:koral:token, wrap:{@type:koral:term,key:blaue,foundry:opennlp,layer:orth,type:type:regex,match:match:eq}},"
+ + "{@type:koral:token, wrap:{@type:koral:term,key:grüne,foundry:opennlp,layer:orth,type:type:regex,match:match:eq}}]}";;
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+
+ // group and disjunction
+ query = "([pos=\"NN\"]|[cnx:pos=\"N\"]|[text=\"Mann\"])";
+ jsonLd = "{@type:koral:token,wrap:{@type:koral:term,key:N,foundry:cnx,layer:p,type:type:regex,match:match:eq}}";
+ FCSQLQueryProcessorTest
+ .validateNode(query, "/query/operands/1", jsonLd);
+
+ // sequence and disjunction
+ query = "([pos=\"NN\"]|[cnx:pos=\"N\"])[text=\"Mann\"]";
+ jsonLd = "{@type:koral:group,"
+ + "operation:operation:sequence,"
+ + "operands:["
+ + "{@type:koral:group,"
+ + "operation:operation:disjunction,"
+ + "operands:[{@type:koral:token,wrap:{@type:koral:term,key:NN,foundry:tt,layer:p,type:type:regex,match:match:eq}},"
+ + "{@type:koral:token,wrap:{@type:koral:term,key:N,foundry:cnx,layer:p,type:type:regex,match:match:eq}}"
+ + "]},"
+ + "{@type:koral:token,wrap:{@type:koral:term,key:Mann,foundry:opennlp,layer:orth,type:type:regex,match:match:eq}}"
+ + "]}";
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+
+ // group and sequence
+ query = "([text=\"blaue\"][pos=\"NN\"])";
+ jsonLd = "{@type:koral:group,"
+ + "operation:operation:sequence,"
+ + "operands:["
+ + "{@type:koral:token,wrap:{@type:koral:term,key:blaue,foundry:opennlp,layer:orth,type:type:regex,match:match:eq}},"
+ + "{@type:koral:token,wrap:{@type:koral:term,key:NN,foundry:tt,layer:p,type:type:regex,match:match:eq}}"
+ + "]}";
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+ }
+
+ // -------------------------------------------------------------------------
+ // main-query ::= simple-query
+ // | simple-query "|" main-query /* or */
+ // | simple-query main-query /* sequence */
+ // | simple-query quantifier /* quatification */
+ // -------------------------------------------------------------------------
+
+ // | simple-query "|" main-query /* or */
+ @Test
+ public void testOrQuery() throws IOException {
+ String query = "\"man\"|\"Mann\"";
+ String jsonLd = "{@type:koral:group,"
+ + "operation:operation:disjunction,"
+ + "operands:["
+ + "{@type:koral:token,wrap:{@type:koral:term,key:man,foundry:opennlp,layer:orth,type:type:regex,match:match:eq}},"
+ + "{@type:koral:token,wrap:{@type:koral:term,key:Mann,foundry:opennlp,layer:orth,type:type:regex,match:match:eq}}]}";
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+
+ query = "[pos=\"NN\"]|\"Mann\"";
+ jsonLd = "{@type:koral:token,wrap:{@type:koral:term,key:NN,foundry:tt,layer:p,type:type:regex,match:match:eq}}";
+ FCSQLQueryProcessorTest
+ .validateNode(query, "/query/operands/0", jsonLd);
+
+ // group with two segment queries
+ query = "[pos=\"NN\"]|[text=\"Mann\"]";
+ jsonLd = "{@type:koral:group,"
+ + "operation:operation:disjunction,"
+ + "operands:["
+ + "{@type:koral:token, wrap:{@type:koral:term,key:NN,foundry:tt,layer:p,type:type:regex,match:match:eq}},"
+ + "{@type:koral:token, wrap:{@type:koral:term,key:Mann,foundry:opennlp,layer:orth,type:type:regex,match:match:eq}}]}";
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+
+ query = "[pos=\"NN\"]&[text=\"Mann\"]";
+ List<Object> error = FCSQLQueryProcessorTest
+ .getError(new FCSQLQueryProcessor(query, "2.0"));
+ assertEquals(399, error.get(0));
+ String msg = (String) error.get(1);
+ assertEquals(true, msg.startsWith("FCS diagnostic 10"));
+ }
+
+ // | simple-query main-query /* sequence */
+ @Test
+ public void testSequenceQuery() throws IOException {
+ String query = "\"blaue|grüne\" [pos = \"NN\"]";
+ String jsonLd = "{@type:koral:group, "
+ + "operation:operation:sequence, "
+ + "operands:["
+ + "{@type:koral:token, wrap:{@type:koral:term, key:blaue|grüne, foundry:opennlp, layer:orth, type:type:regex, match:match:eq}},"
+ + "{@type:koral:token, wrap:{@type:koral:term, key:NN, foundry:tt, layer:p, type:type:regex, match:match:eq}}"
+ + "]}";
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+
+ query = "[text=\"blaue|grüne\"][pos = \"NN\"]";
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+
+ query = "\"blaue\" \"grüne\" [pos = \"NN\"]";
+ jsonLd = "{@type:koral:token, wrap:{@type:koral:term, key:grüne, foundry:opennlp, layer:orth, type:type:regex, match:match:eq}}";
+ FCSQLQueryProcessorTest
+ .validateNode(query, "/query/operands/1", jsonLd);
+
+ }
+
+ // | simple-query quantifier /* quatification */
+ @Test
+ public void testQueryWithQuantifier() throws IOException {
+
+ }
+
+ // -------------------------------------------------------------------------
+ // query ::= main-query within-part?
+ // -------------------------------------------------------------------------
+ // within-part ::= simple-within-part
+ // simple-within-part ::= "within" simple-within-scope
+
+ @Test
+ public void testWithinQuery() throws IOException {
+ String query = "[cnx:pos=\"VVFIN\"] within s";
+ String jsonLd = "{@type:koral:group,"
+ + "operation:operation:position,"
+ + "operands:["
+ + "{@type:koral:span,wrap:{@type:koral:term,key:s,foundry:base,layer:s}},"
+ + "{@type:koral:token,wrap:{@type:koral:term,key:VVFIN,foundry:cnx,layer:p,type:type:regex,match:match:eq}}"
+ + "]}";
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+
+ query = "[cnx:pos=\"VVFIN\"] within sentence";
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+
+ query = "[cnx:pos=\"VVFIN\"] within p";
+ jsonLd = "{@type:koral:span,wrap:{@type:koral:term,key:p,foundry:base,layer:s}}";
+ FCSQLQueryProcessorTest
+ .validateNode(query, "/query/operands/0", jsonLd);
+
+ query = "[cnx:pos=\"VVFIN\"] within text";
+ jsonLd = "{@type:koral:span,wrap:{@type:koral:term,key:t,foundry:base,layer:s}}";
+ FCSQLQueryProcessorTest
+ .validateNode(query, "/query/operands/0", jsonLd);
+
+ query = "[cnx:pos=\"VVFIN\"] within u";
+ List<Object> error = FCSQLQueryProcessorTest
+ .getError(new FCSQLQueryProcessor(query, "2.0"));
+ assertEquals(310, error.get(0));
+ assertEquals(
+ "FCS diagnostic 11: Within scope UTTERANCE is currently unsupported.",
+ (String) error.get(1));
+ }
+
+}
diff --git a/src/test/java/de/ids_mannheim/korap/query/serialize/FCSQLQueryProcessorTest.java b/src/test/java/de/ids_mannheim/korap/query/serialize/FCSQLQueryProcessorTest.java
new file mode 100644
index 0000000..7fa90d0
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/query/serialize/FCSQLQueryProcessorTest.java
@@ -0,0 +1,303 @@
+package de.ids_mannheim.korap.query.serialize;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * @author margaretha
+ *
+ */
+public class FCSQLQueryProcessorTest {
+
+ static QuerySerializer qs = new QuerySerializer();
+ static ObjectMapper mapper = new ObjectMapper();
+ static JsonNode node;
+
+ public static void runAndValidate(String query, String jsonLD)
+ throws JsonProcessingException {
+ FCSQLQueryProcessor processor = new FCSQLQueryProcessor(query, "2.0");
+ String serializedQuery = mapper.writeValueAsString(processor
+ .getRequestMap().get("query"));
+ assertEquals(jsonLD.replace(" ", ""), serializedQuery.replace("\"", ""));
+ }
+
+ public static void validateNode(String query, String path, String jsonLd)
+ throws JsonProcessingException, IOException {
+ qs.setQuery(query, "fcsql", "2.0");
+ node = mapper.readTree(qs.toJSON());
+ String serializedQuery = mapper.writeValueAsString(node.at(path));
+ assertEquals(jsonLd.replace(" ", ""), serializedQuery.replace("\"", ""));
+ }
+
+ public static List<Object> getError(FCSQLQueryProcessor processor) {
+ List<Object> errors = (List<Object>) processor.requestMap.get("errors");
+ return (List<Object>) errors.get(0);
+ }
+
+ @Test
+ public void testVersion() throws JsonProcessingException {
+ List<Object> error = getError(new FCSQLQueryProcessor("\"Sonne\"",
+ "1.0"));
+ assertEquals(309, error.get(0));
+ assertEquals("SRU diagnostic 5: Only supports SRU version 2.0.",
+ error.get(1));
+
+ error = getError(new FCSQLQueryProcessor("\"Sonne\"", null));
+ assertEquals(309, error.get(0));
+ assertEquals("SRU diagnostic 7: Version number is missing.",
+ error.get(1));
+ }
+
+ // regexp ::= quoted-string
+ @Test
+ public void testTermQuery() throws JsonProcessingException {
+ String query = "\"Sonne\"";
+ String jsonLd = "{@type:koral:token, wrap:{@type:koral:term, key:Sonne, "
+ + "foundry:opennlp, layer:orth, type:type:regex, match:match:eq}}";
+ runAndValidate(query, jsonLd);
+ }
+
+ @Test
+ public void testRegex() throws JsonProcessingException {
+ String query = "[text=\"M(a|ä)nn(er)?\"]";
+ String jsonLd = "{@type:koral:token,wrap:{@type:koral:term,"
+ + "key:M(a|ä)nn(er)?,foundry:opennlp,layer:orth,type:type:regex,match:match:eq}}";
+ runAndValidate(query, jsonLd);
+
+ query = "\".*?Mann.*?\"";
+ jsonLd = "{@type:koral:token,wrap:{@type:koral:term,key:.*?Mann.*?,"
+ + "foundry:opennlp,layer:orth,type:type:regex,match:match:eq}}";
+ runAndValidate(query, jsonLd);
+
+ query = "\"z.B.\"";
+ jsonLd = "{@type:koral:token,wrap:{@type:koral:term,key:z.B.,"
+ + "foundry:opennlp,layer:orth,type:type:regex,match:match:eq}}";
+ runAndValidate(query, jsonLd);
+
+ query = "\"Sonne&scheint\"";
+ jsonLd = "{@type:koral:token,wrap:{@type:koral:term,key:Sonne&scheint,"
+ + "foundry:opennlp,layer:orth,type:type:regex,match:match:eq}}";
+ runAndValidate(query, jsonLd);
+
+ // Not possible
+ // query = "\"a\\.\"";
+ }
+
+ // flagged-regexp ::= regexp
+ // | regexp "/" regexp-flag+
+ @Test
+ public void testTermQueryWithRegexFlag() throws IOException {
+ String query = "\"Fliegen\" /c";
+ String jsonLd = "{@type:koral:token, wrap:{@type:koral:term, caseInsensitive:true, "
+ + "key:Fliegen, foundry:opennlp, layer:orth, type:type:regex, match:match:eq}}";
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+
+ query = "\"Fliegen\" /i";
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+
+ query = "\"Fliegen\" /C";
+ jsonLd = "{@type:koral:term, key:Fliegen, foundry:opennlp, layer:orth, type:type:regex, match:match:eq}";
+ FCSQLQueryProcessorTest.validateNode(query, "/query/wrap", jsonLd);
+
+ query = "\"Fliegen\" /I";
+ FCSQLQueryProcessorTest.validateNode(query, "/query/wrap", jsonLd);
+
+ query = "\"Fliegen\" /l";
+ List<Object> error = FCSQLQueryProcessorTest
+ .getError(new FCSQLQueryProcessor(query, "2.0"));
+ assertEquals(306, error.get(0));
+ String msg = (String) error.get(1);
+ assertEquals(true, msg.startsWith("SRU diagnostic 48: Regexflags"));
+
+ query = "\"Fliegen\" /d";
+ error = FCSQLQueryProcessorTest.getError(new FCSQLQueryProcessor(query,
+ "2.0"));
+ assertEquals(306, error.get(0));
+ assertEquals(
+ "SRU diagnostic 48: Regexflag: IGNORE_DIACRITICS is unsupported.",
+ (String) error.get(1));
+ }
+
+ // operator ::= "=" /* equals */
+ // | "!=" /* non-equals */
+ @Test
+ public void testOperator() throws IOException {
+ String query = "[cnx:pos != \"N\"]";
+ String jsonLd = "{@type:koral:token, wrap:{@type:koral:term, key:N, "
+ + "foundry:cnx, layer:p, type:type:regex, match:match:ne}}";
+ runAndValidate(query, jsonLd);
+ }
+
+
+ // attribute operator flagged-regexp
+ // -------------------------------------------------------------------------
+ // attribute ::= simple-attribute | qualified-attribute
+ // -------------------------------------------------------------------------
+
+ // simple-attribute ::= identifier
+ @Test
+ public void testTermQueryWithSpecificLayer() throws JsonProcessingException {
+ String query = "[text = \"Sonne\"]";
+ String jsonLd = "{@type:koral:token, wrap:{@type:koral:term, key:Sonne, "
+ + "foundry:opennlp, layer:orth, type:type:regex, match:match:eq}}";
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+
+ query = "[lemma = \"sein\"]";
+ jsonLd = "{@type:koral:token, wrap:{@type:koral:term, key:sein, "
+ + "foundry:tt, layer:l, type:type:regex, match:match:eq}}";
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+
+ query = "[pos = \"NN\"]";
+ jsonLd = "{@type:koral:token, wrap:{@type:koral:term, key:NN, "
+ + "foundry:tt, layer:p, type:type:regex, match:match:eq}}";
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+ }
+
+ // qualified-attribute ::= identifier ":" identifier
+ @Test
+ public void testTermQueryWithQualifier() throws JsonProcessingException {
+ String query = "[mate:lemma = \"sein\"]";
+ String jsonLd = "{@type:koral:token, wrap:{@type:koral:term, key:sein, "
+ + "foundry:mate, layer:l, type:type:regex, match:match:eq}}";
+ runAndValidate(query, jsonLd);
+
+ query = "[cnx:pos = \"N\"]";
+ jsonLd = "{@type:koral:token, wrap:{@type:koral:term, key:N, "
+ + "foundry:cnx, layer:p, type:type:regex, match:match:eq}}";
+ runAndValidate(query, jsonLd);
+ }
+
+ @Test
+ public void testTermQueryException() throws JsonProcessingException {
+ String query = "[opennlp:lemma = \"sein\"]";
+ List<Object> error = getError(new FCSQLQueryProcessor(query, "2.0"));
+ assertEquals(306, error.get(0));
+ assertEquals(
+ "SRU diagnostic 48: Layer lemma with qualifier opennlp is unsupported.",
+ error.get(1));
+
+ query = "[malt:lemma = \"sein\"]";
+ error = getError(new FCSQLQueryProcessor(query, "2.0"));
+ assertEquals(306, error.get(0));
+ assertEquals("SRU diagnostic 48: Qualifier malt is unsupported.",
+ error.get(1));
+
+ query = "[cnx:morph = \"heit\"]";
+ error = getError(new FCSQLQueryProcessor(query, "2.0"));
+ assertEquals(306, error.get(0));
+ assertEquals("SRU diagnostic 48: Layer morph is unsupported.",
+ error.get(1));
+
+ }
+
+ // segment-query ::= "[" expression? "]"
+ // -------------------------------------------------------------------------
+ // expression ::= basic-expression
+ // | expression "|" expression /* or */
+ // | expression "&" expression /* and */
+ // -------------------------------------------------------------------------
+
+ // | expression "|" expression /* or */
+ @Test
+ public void testExpressionOr() throws IOException {
+ String query = "[mate:lemma=\"sein\" | mate:pos=\"PPOSS\"]";
+ String jsonLd = "{@type: koral:token,"
+ + " wrap: { @type: koral:termGroup,"
+ + "relation: relation:or,"
+ + " operands:["
+ + "{@type: koral:term, key: sein, foundry: mate, layer: l, type:type:regex, match: match:eq},"
+ + "{@type: koral:term, key: PPOSS, foundry: mate, layer: p, type:type:regex, match: match:eq}]}}";
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+
+ query = "[cnx:lemma=\"sein\" | mate:lemma=\"sein\" | mate:pos=\"PPOSS\"]";
+ jsonLd = "{@type: koral:term, key: sein, foundry: cnx, layer: l, type:type:regex, match: match:eq}";
+ FCSQLQueryProcessorTest.validateNode(query, "/query/wrap/operands/0",
+ jsonLd);
+
+ }
+
+ // | expression "&" expression /* and */
+ @Test
+ public void testExpressionAnd() throws IOException {
+ String query = "[mate:lemma=\"sein\" & mate:pos=\"PPOSS\"]";
+ String jsonLd = "{@type: koral:token,"
+ + " wrap: { @type: koral:termGroup,"
+ + "relation: relation:and,"
+ + " operands:["
+ + "{@type: koral:term, key: sein, foundry: mate, layer: l, type:type:regex, match: match:eq},"
+ + "{@type: koral:term, key: PPOSS, foundry: mate, layer: p, type:type:regex, match: match:eq}]}}";
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+ }
+
+ // -------------------------------------------------------------------------
+ // basic-expression ::= '(' expression ')' /* grouping */
+ // | "!" expression /* not */
+ // | attribute operator flagged-regexp
+ // -------------------------------------------------------------------------
+
+ // basic-expression ::= '(' expression ')' /* grouping */
+
+ @Test
+ public void testExpressionGroup() throws JsonProcessingException {
+ String query = "[(text=\"blau\"|pos=\"ADJ\")]";
+ String jsonLd = "{@type: koral:token,"
+ + "wrap: {@type: koral:termGroup,"
+ + "relation: relation:or,"
+ + "operands: ["
+ + "{@type: koral:term, key: blau, foundry: opennlp, layer: orth, type:type:regex,match: match:eq},"
+ + "{@type: koral:term, key: ADJ, foundry: tt, layer: p, type:type:regex, match: match:eq}]}}";
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+ }
+
+ // "!" expression /* not */
+ @Test
+ public void testExpressionNot() throws IOException {
+ String jsonLd = "{@type:koral:token, wrap:{@type:koral:term, key:NN, "
+ + "foundry:tt, layer:p, type:type:regex, match:match:eq}}";
+ String query = "[!pos != \"NN\"]";
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+ query = "[!!pos = \"NN\"]";
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+ query = "[!!!pos != \"NN\"]";
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+
+ query = "[mate:lemma=\"sein\" & !mate:pos=\"PPOSS\"]";
+ jsonLd = "{@type: koral:token,"
+ + " wrap: { "
+ + "@type: koral:termGroup,"
+ + "relation: relation:and,"
+ + " operands:["
+ + "{@type: koral:term, key: sein, foundry: mate, layer: l, type:type:regex, match: match:eq},"
+ + "{@type: koral:term, key: PPOSS, foundry: mate, layer: p, type:type:regex, match: match:ne}]}}";
+ FCSQLQueryProcessorTest.runAndValidate(query, jsonLd);
+ }
+
+
+ @Test
+ public void testWrongQuery() throws IOException {
+ String query = "!(mate:lemma=\"sein\" | mate:pos=\"PPOSS\")";
+ List<Object> error = getError(new FCSQLQueryProcessor(query, "2.0"));
+ assertEquals(399, error.get(0));
+ assertEquals(true,
+ error.get(1).toString().startsWith("FCS diagnostic 10"));
+
+ query = "![mate:lemma=\"sein\" | mate:pos=\"PPOSS\"]";
+ error = getError(new FCSQLQueryProcessor(query, "2.0"));
+ assertEquals(true,
+ error.get(1).toString().startsWith("FCS diagnostic 10"));
+
+ query = "(\"blaue\"&\"grüne\")";
+ error = getError(new FCSQLQueryProcessor(query, "2.0"));
+ assertEquals(true,
+ error.get(1).toString().startsWith("FCS diagnostic 10"));
+ }
+
+}