You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ch...@apache.org on 2016/01/12 14:08:06 UTC
[05/30] olingo-odata4 git commit: [OLINGO-834] Filter parser
refactoring first draft
[OLINGO-834] Filter parser refactoring first draft
Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/7b23ad71
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/7b23ad71
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/7b23ad71
Branch: refs/heads/master
Commit: 7b23ad71a7faab152841a3776b4db3ac9dffccf2
Parents: a47e9f6
Author: Christian Amend <ch...@sap.com>
Authored: Wed Dec 9 14:00:48 2015 +0100
Committer: Christian Amend <ch...@sap.com>
Committed: Wed Dec 9 14:11:46 2015 +0100
----------------------------------------------------------------------
.../core/uri/expression/FilterParser.java | 562 +++++++++++++++++++
.../uri/queryoption/expression/AliasImpl.java | 8 +
.../uri/queryoption/expression/BinaryImpl.java | 16 +
.../uri/queryoption/expression/LiteralImpl.java | 14 +
.../uri/queryoption/expression/MethodImpl.java | 24 +
.../uri/queryoption/expression/UnaryImpl.java | 14 +
.../core/uri/expression/FilterParserTest.java | 210 +++++++
7 files changed, 848 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7b23ad71/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/expression/FilterParser.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/expression/FilterParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/expression/FilterParser.java
new file mode 100644
index 0000000..32cbc1a
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/expression/FilterParser.java
@@ -0,0 +1,562 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.core.uri.expression;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
+import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
+import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind;
+import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
+import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind;
+import org.apache.olingo.server.api.uri.queryoption.expression.UnaryOperatorKind;
+import org.apache.olingo.server.core.uri.queryoption.expression.AliasImpl;
+import org.apache.olingo.server.core.uri.queryoption.expression.BinaryImpl;
+import org.apache.olingo.server.core.uri.queryoption.expression.ExpressionImpl;
+import org.apache.olingo.server.core.uri.queryoption.expression.LiteralImpl;
+import org.apache.olingo.server.core.uri.queryoption.expression.MethodImpl;
+import org.apache.olingo.server.core.uri.queryoption.expression.UnaryImpl;
+
+public class FilterParser {
+ private Tokenizer tokenizer;
+
+ private static final Map<TokenKind, BinaryOperatorKind> tokenToBinaryOperator;
+ static {
+ HashMap<TokenKind, BinaryOperatorKind> temp = new HashMap<FilterParser.TokenKind, BinaryOperatorKind>();
+ temp.put(TokenKind.OR_OP, BinaryOperatorKind.OR);
+ temp.put(TokenKind.AND_OP, BinaryOperatorKind.AND);
+
+ temp.put(TokenKind.EQ_OP, BinaryOperatorKind.EQ);
+ temp.put(TokenKind.NE_OP, BinaryOperatorKind.NE);
+
+ temp.put(TokenKind.GT_OP, BinaryOperatorKind.GT);
+ temp.put(TokenKind.GE_OP, BinaryOperatorKind.GE);
+ temp.put(TokenKind.LT_OP, BinaryOperatorKind.LT);
+ temp.put(TokenKind.LE_OP, BinaryOperatorKind.LE);
+
+ temp.put(TokenKind.ADD_OP, BinaryOperatorKind.ADD);
+ temp.put(TokenKind.SUB_OP, BinaryOperatorKind.SUB);
+
+ temp.put(TokenKind.MUL_OP, BinaryOperatorKind.MUL);
+ temp.put(TokenKind.DIV_OP, BinaryOperatorKind.DIV);
+ temp.put(TokenKind.MOD_OP, BinaryOperatorKind.MOD);
+
+ tokenToBinaryOperator = Collections.unmodifiableMap(temp);
+ }
+
+ private static final Map<TokenKind, UnaryOperatorKind> tokenToUnaryOperator;
+ static {
+ HashMap<TokenKind, UnaryOperatorKind> temp = new HashMap<FilterParser.TokenKind, UnaryOperatorKind>();
+ temp.put(TokenKind.MINUS, UnaryOperatorKind.MINUS);
+ temp.put(TokenKind.NOT, UnaryOperatorKind.NOT);
+ tokenToUnaryOperator = Collections.unmodifiableMap(temp);
+ }
+
+ private static final Map<TokenKind, MethodKind> tokenToMethod;
+ static {
+ HashMap<TokenKind, MethodKind> temp = new HashMap<FilterParser.TokenKind, MethodKind>();
+ temp.put(TokenKind.Cast, MethodKind.CAST);
+ temp.put(TokenKind.Ceiling, MethodKind.CEILING);
+ temp.put(TokenKind.Concat, MethodKind.CONCAT);
+ temp.put(TokenKind.Contains, MethodKind.CONTAINS);
+ temp.put(TokenKind.Date, MethodKind.DATE);
+ temp.put(TokenKind.Day, MethodKind.DAY);
+ temp.put(TokenKind.Endswith, MethodKind.ENDSWITH);
+ temp.put(TokenKind.Floor, MethodKind.FLOOR);
+ temp.put(TokenKind.Fractionalseconds, MethodKind.FRACTIONALSECONDS);
+ temp.put(TokenKind.GeoDistance, MethodKind.GEODISTANCE);
+ temp.put(TokenKind.GeoIntersects, MethodKind.GEOINTERSECTS);
+ temp.put(TokenKind.GeoLength, MethodKind.GEOLENGTH);
+ temp.put(TokenKind.Hour, MethodKind.HOUR);
+ temp.put(TokenKind.Indexof, MethodKind.INDEXOF);
+ temp.put(TokenKind.Isof, MethodKind.ISOF);
+ temp.put(TokenKind.Length, MethodKind.LENGTH);
+ temp.put(TokenKind.Maxdatetime, MethodKind.MAXDATETIME);
+ temp.put(TokenKind.Mindatetime, MethodKind.MINDATETIME);
+ temp.put(TokenKind.Minute, MethodKind.MINUTE);
+ temp.put(TokenKind.Month, MethodKind.MONTH);
+ temp.put(TokenKind.Now, MethodKind.NOW);
+ temp.put(TokenKind.Round, MethodKind.ROUND);
+ temp.put(TokenKind.Second, MethodKind.SECOND);
+ temp.put(TokenKind.Startswith, MethodKind.STARTSWITH);
+ temp.put(TokenKind.Substring, MethodKind.SUBSTRING);
+ temp.put(TokenKind.Time, MethodKind.TIME);
+ temp.put(TokenKind.Tolower, MethodKind.TOLOWER);
+ temp.put(TokenKind.Totaloffsetminutes, MethodKind.TOTALOFFSETMINUTES);
+ temp.put(TokenKind.Totalseconds, MethodKind.TOTALSECONDS);
+ temp.put(TokenKind.Toupper, MethodKind.TOUPPER);
+ temp.put(TokenKind.Trim, MethodKind.TRIM);
+ temp.put(TokenKind.Year, MethodKind.YEAR);
+
+ tokenToMethod = Collections.unmodifiableMap(temp);
+ }
+
+ private static final Map<TokenKind, EdmPrimitiveTypeKind> tokenToPrimitiveType;
+ static {
+ /* Enum and null are not present in the map. These have to be handled differently */
+ HashMap<TokenKind, EdmPrimitiveTypeKind> temp = new HashMap<FilterParser.TokenKind, EdmPrimitiveTypeKind>();
+ temp.put(TokenKind.PrimitiveBooleanValue, EdmPrimitiveTypeKind.Boolean);
+ temp.put(TokenKind.PrimitiveStringValue, EdmPrimitiveTypeKind.String);
+ // TODO:Check if int64 is correct here or if it has to be single instead
+ temp.put(TokenKind.PrimitiveIntegerValue, EdmPrimitiveTypeKind.Int64);
+ temp.put(TokenKind.PrimitiveGuidValue, EdmPrimitiveTypeKind.Guid);
+ temp.put(TokenKind.PrimitiveDateValue, EdmPrimitiveTypeKind.Date);
+ temp.put(TokenKind.PrimitiveDateTimeOffsetValue, EdmPrimitiveTypeKind.DateTimeOffset);
+ temp.put(TokenKind.PrimitiveTimeOfDayValue, EdmPrimitiveTypeKind.TimeOfDay);
+ temp.put(TokenKind.PrimitiveDecimalValue, EdmPrimitiveTypeKind.Decimal);
+ temp.put(TokenKind.PrimitiveDoubleValue, EdmPrimitiveTypeKind.Double);
+ temp.put(TokenKind.PrimitiveDurationValue, EdmPrimitiveTypeKind.Duration);
+ temp.put(TokenKind.PrimitiveBinaryValue, EdmPrimitiveTypeKind.Binary);
+
+ tokenToPrimitiveType = Collections.unmodifiableMap(temp);
+ }
+
+ public Expression parse(Tokenizer tokenizer) {
+ // Initialize tokenizer
+ this.tokenizer = tokenizer;
+
+ Expression exp = parseExpression();
+ return exp;
+ }
+
+ private Expression parseExpression() {
+ Expression left = parseAnd();
+
+ while (is(TokenKind.OR_OP) != null) {
+ tokenizer.getText();
+
+ Expression right = parseAnd();
+ left = new BinaryImpl(left, BinaryOperatorKind.OR, right);
+ }
+
+ return left;
+ }
+
+ private Expression parseAnd() {
+ Expression left = parseExprEquality();
+ while (is(TokenKind.AND_OP) != null) {
+ tokenizer.getText();
+
+ Expression right = parseExprEquality();
+ left = new BinaryImpl(left, BinaryOperatorKind.AND, right);
+ }
+ return left;
+ }
+
+ private Expression parseExprEquality() {
+ Expression left = parseExprRel();
+
+ TokenKind nextTokenKind = is(TokenKind.EQ_OP, TokenKind.NE_OP);
+ // Null for everything other than EQ or NE
+ while (nextTokenKind != null) {
+ tokenizer.getText();
+
+ Expression right = parseExprEquality();
+ left = new BinaryImpl(left, tokenToBinaryOperator.get(nextTokenKind), right);
+ nextTokenKind = is(TokenKind.EQ_OP, TokenKind.NE_OP);
+ }
+
+ return left;
+ }
+
+ private Expression parseExprRel() {
+ Expression left = parseExprAdd();
+
+ TokenKind nextTokenKind = is(TokenKind.GT_OP, TokenKind.GE_OP, TokenKind.LT_OP, TokenKind.LE_OP);
+ // Null for everything other than GT or GE or LT or LE
+ while (nextTokenKind != null) {
+ tokenizer.getText();
+
+ Expression right = parseExprAdd();
+ left = new BinaryImpl(left, tokenToBinaryOperator.get(nextTokenKind), right);
+ nextTokenKind = is(TokenKind.GT_OP, TokenKind.GE_OP, TokenKind.LT_OP, TokenKind.LE_OP);
+ }
+
+ return left;
+ }
+
+ private Expression parseExprAdd() {
+ Expression left = parseExprMul();
+
+ TokenKind nextTokenKind = is(TokenKind.ADD_OP, TokenKind.SUB_OP);
+ // Null for everything other than ADD or SUB
+ while (nextTokenKind != null) {
+ tokenizer.getText();
+
+ Expression right = parseExprMul();
+ left = new BinaryImpl(left, tokenToBinaryOperator.get(nextTokenKind), right);
+ nextTokenKind = is(TokenKind.ADD_OP, TokenKind.SUB_OP);
+ }
+
+ return left;
+ }
+
+ private Expression parseExprMul() {
+ Expression left = parseExprUnary();
+
+ TokenKind nextTokenKind = is(TokenKind.MUL_OP, TokenKind.DIV_OP, TokenKind.MOD_OP);
+ // Null for everything other than MUL or DIV or MOD
+ while (nextTokenKind != null) {
+ tokenizer.getText();
+
+ Expression right = parseExprUnary();
+ left = new BinaryImpl(left, tokenToBinaryOperator.get(nextTokenKind), right);
+ nextTokenKind = is(TokenKind.MUL_OP, TokenKind.DIV_OP, TokenKind.MOD_OP);
+ }
+
+ return left;
+ }
+
+ private Expression parseExprUnary() {
+ Expression left = null;
+ TokenKind nextTokenKind = is(TokenKind.MINUS, TokenKind.NOT);
+ // Null for everything other than - or NOT
+ while (nextTokenKind != null) {
+ tokenizer.getText();
+
+ Expression exp = parseExprValue();
+ left = new UnaryImpl(tokenToUnaryOperator.get(nextTokenKind), exp);
+ nextTokenKind = is(TokenKind.MINUS, TokenKind.NOT);
+ }
+
+ if (left == null) {
+ left = parseExprValue();
+ }
+
+ return left;
+ }
+
+ private Expression parseExprValue() {
+ if (is(TokenKind.OPEN) != null) {
+ tokenizer.getText();
+ Expression exp = parseExpression();
+ require(TokenKind.CLOSE);
+ return exp;
+ }
+
+ if (is(TokenKind.ParameterAlias) != null) {
+ return new AliasImpl(tokenizer.getText());
+ }
+
+ if (is(TokenKind.RootExpr) != null) {
+ tokenizer.getText();
+ // TODO: ConsumeRootExpression
+ }
+
+ TokenKind nextPrimitive = isPrimitive();
+ if (nextPrimitive != null) {
+ EdmPrimitiveTypeKind primitiveTypeKind = tokenToPrimitiveType.get(nextPrimitive);
+ EdmPrimitiveType type;
+ if (primitiveTypeKind == null) {
+ if (nextPrimitive == TokenKind.PrimitiveEnumValue) {
+ // TODO: Get enum type
+ type = null;
+ } else {
+ // Null handling
+ type = null;
+ }
+ } else {
+ type = EdmPrimitiveTypeFactory.getInstance(primitiveTypeKind);
+ }
+ return new LiteralImpl(tokenizer.getText(), type);
+ }
+
+ TokenKind nextMethod = isMethod();
+ if (nextMethod != null) {
+ MethodImpl methodImpl = new MethodImpl(tokenToMethod.get(nextMethod));
+ // Consume Method
+ tokenizer.getText();
+ if (is(TokenKind.CLOSE) != null) {
+ // Consume closing bracket
+ tokenizer.getText();
+ } else {
+ // TODO: Remove Cast
+ methodImpl.addParameter((ExpressionImpl) parseExpression());
+ while (is(TokenKind.COMMA) != null) {
+ tokenizer.getText();
+ methodImpl.addParameter((ExpressionImpl) parseExpression());
+ }
+ require(TokenKind.CLOSE);
+ }
+
+ validateMethodParameters(methodImpl);
+
+ return methodImpl;
+ }
+
+ throw new RuntimeException("Unexpected token");
+ }
+
+ private void validateMethodParameters(MethodImpl methodImpl) {
+ // We might validate parameter types in the future
+ int size = methodImpl.getParameters().size();
+ switch (methodImpl.getMethod()) {
+ // Must have two Parameters
+ case CONTAINS:
+ case ENDSWITH:
+ case STARTSWITH:
+ case INDEXOF:
+ case CONCAT:
+ case GEODISTANCE:
+ case GEOINTERSECTS:
+ if (size != 2) {
+ throw new RuntimeException("The method " + methodImpl.getMethod() + " needs exactly two parameters.");
+ }
+ break;
+ // Must have one parameter
+ case LENGTH:
+ case TOLOWER:
+ case TOUPPER:
+ case TRIM:
+ case YEAR:
+ case MONTH:
+ case DAY:
+ case HOUR:
+ case MINUTE:
+ case SECOND:
+ case FRACTIONALSECONDS:
+ case DATE:
+ case TIME:
+ case TOTALOFFSETMINUTES:
+ case TOTALSECONDS:
+ case ROUND:
+ case FLOOR:
+ case CEILING:
+ case GEOLENGTH:
+ if (size != 1) {
+ throw new RuntimeException("The method: '" + methodImpl.getMethod() + "' needs exactly one parameter.");
+ }
+ break;
+ // Must have no parameter
+ case NOW:
+ case MAXDATETIME:
+ case MINDATETIME:
+ if (size != 0) {
+ throw new RuntimeException("The method: '" + methodImpl.getMethod() + "' must have no parameters.");
+ }
+ break;
+ // Variable parameter number
+ case CAST:
+ case ISOF:
+ if (size == 1 || size == 2) {
+ throw new RuntimeException("The method: '" + methodImpl.getMethod() + "' must have one or two parameters.");
+ }
+ break;
+ case SUBSTRING:
+ if (size == 2 || size == 3) {
+ throw new RuntimeException("The method: '" + methodImpl.getMethod() + "' must have two or three parameters.");
+ }
+ break;
+ default:
+ throw new RuntimeException("Unkown method: '" + methodImpl.getMethod() + "'");
+ }
+ }
+
+ private String require(TokenKind required) {
+ if (is(required) == null) {
+ throw new RuntimeException("Requred token: " + required);
+ }
+ return tokenizer.getText();
+ }
+
+ private TokenKind is(TokenKind... kind) {
+ for (int i = 0; i < kind.length; i++) {
+ if (tokenizer.next(kind[i])) {
+ return kind[i];
+ }
+ }
+ return null;
+ }
+
+ private TokenKind isMethod() {
+ return is(TokenKind.Cast,
+ TokenKind.Ceiling,
+ TokenKind.Concat,
+ TokenKind.Contains,
+ TokenKind.Date,
+ TokenKind.Day,
+ TokenKind.Endswith,
+ TokenKind.Floor,
+ TokenKind.Fractionalseconds,
+ TokenKind.GeoDistance,
+ TokenKind.GeoIntersects,
+ TokenKind.GeoLength,
+ TokenKind.Hour,
+ TokenKind.Indexof,
+ TokenKind.Isof,
+ TokenKind.Length,
+ TokenKind.Maxdatetime,
+ TokenKind.Mindatetime,
+ TokenKind.Minute,
+ TokenKind.Month,
+ TokenKind.Now,
+ TokenKind.Round,
+ TokenKind.Second,
+ TokenKind.Startswith,
+ TokenKind.Substring,
+ TokenKind.Time,
+ TokenKind.Tolower,
+ TokenKind.Totaloffsetminutes,
+ TokenKind.Totalseconds,
+ TokenKind.Toupper,
+ TokenKind.Trim,
+ TokenKind.Year);
+ }
+
+ private TokenKind isPrimitive() {
+ return is(TokenKind.PrimitiveNullValue,
+ TokenKind.PrimitiveBooleanValue,
+ TokenKind.PrimitiveStringValue,
+
+ // The order of the next seven expressions is important in order to avoid
+ // finding partly parsed tokens (counter-intuitive as it may be, even a GUID may start with digits ...).
+ TokenKind.PrimitiveDoubleValue,
+ TokenKind.PrimitiveDecimalValue,
+ TokenKind.PrimitiveGuidValue,
+ TokenKind.PrimitiveDateTimeOffsetValue,
+ TokenKind.PrimitiveDateValue,
+ TokenKind.PrimitiveTimeOfDayValue,
+ TokenKind.PrimitiveIntegerValue,
+ TokenKind.PrimitiveDurationValue,
+ TokenKind.PrimitiveBinaryValue,
+ TokenKind.PrimitiveEnumValue);
+ }
+
+ public enum TokenKind {
+ // BINARY
+ OR_OP,
+ AND_OP,
+
+ EQ_OP,
+ NE_OP,
+
+ GT_OP,
+ GE_OP,
+ LT_OP,
+ LE_OP,
+
+ ADD_OP,
+ SUB_OP,
+
+ MUL_OP,
+ DIV_OP,
+ MOD_OP,
+
+ MINUS,
+ NOT,
+
+ // Grouping
+ OPEN,
+ CLOSE,
+
+ // PrimitiveValues
+ PrimitiveNullValue,
+ PrimitiveBooleanValue,
+
+ PrimitiveStringValue,
+ PrimitiveIntegerValue,
+ PrimitiveGuidValue,
+ PrimitiveDateValue,
+ PrimitiveDateTimeOffsetValue,
+ PrimitiveTimeOfDayValue,
+ PrimitiveDecimalValue,
+ PrimitiveDoubleValue,
+ PrimitiveDurationValue,
+ PrimitiveBinaryValue,
+ PrimitiveEnumValue,
+
+ // ExpressionValues
+ ParameterAlias,
+ ArrayOrObject,
+ RootExpr,
+ IT,
+
+ // BuiltInMethods
+ Cast,
+ Ceiling,
+ Concat,
+ Contains,
+ Date,
+ Day,
+ Endswith,
+ Floor,
+ Fractionalseconds,
+ GeoDistance,
+ GeoIntersects,
+ GeoLength,
+ Hour,
+ Indexof,
+ Isof,
+ Length,
+ Maxdatetime,
+ Mindatetime,
+ Minute,
+ Month,
+ Now,
+ Round,
+ Second,
+ Startswith,
+ Substring,
+ Time,
+ Tolower,
+ Totaloffsetminutes,
+ Totalseconds,
+ Toupper,
+ Trim,
+ Year,
+ COMMA
+ }
+
+ public static class Token {
+ TokenKind kind;
+ String text;
+
+ public Token(TokenKind kind, String text) {
+ this.kind = kind;
+ this.text = text;
+ }
+ }
+
+ public static class Tokenizer {
+ private List<Token> tokens;
+ int counter = 0;
+
+ public Tokenizer(List<Token> tokens) {
+ this.tokens = tokens;
+ }
+
+ public boolean next(TokenKind expectedKind) {
+ if (counter < tokens.size() && expectedKind == tokens.get(counter).kind) {
+ return true;
+ }
+ return false;
+ }
+
+ public String getText() {
+ String text = tokens.get(counter).text;
+ counter++;
+ return text;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7b23ad71/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/AliasImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/AliasImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/AliasImpl.java
index 09af93f..5309d73 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/AliasImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/AliasImpl.java
@@ -27,6 +27,14 @@ public class AliasImpl extends ExpressionImpl implements Alias {
private String parameterName;
+ public AliasImpl() {
+ //TODO: Delete Constructor
+ }
+
+ public AliasImpl(String parameterName) {
+ this.parameterName = parameterName;
+ }
+
@Override
public String getParameterName() {
return parameterName;
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7b23ad71/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/BinaryImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/BinaryImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/BinaryImpl.java
index a28f92c..c3530c0 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/BinaryImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/BinaryImpl.java
@@ -31,6 +31,17 @@ public class BinaryImpl extends ExpressionImpl implements Binary {
private ExpressionImpl left;
private ExpressionImpl right;
+ public BinaryImpl() {
+ // TODO: Delete
+ }
+
+ public BinaryImpl(Expression left, BinaryOperatorKind operator, Expression right) {
+ // TODO:DeleteCast
+ this.left = (ExpressionImpl) left;
+ this.operator = operator;
+ this.right = (ExpressionImpl) right;
+ }
+
@Override
public BinaryOperatorKind getOperator() {
return operator;
@@ -67,4 +78,9 @@ public class BinaryImpl extends ExpressionImpl implements Binary {
return visitor.visitBinaryOperator(operator, left, right);
}
+ @Override
+ public String toString() {
+ return "{" + left + " " + operator.name() + " " + right + '}';
+ }
+
}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7b23ad71/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/LiteralImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/LiteralImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/LiteralImpl.java
index 1dd9fa7..e275fdd 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/LiteralImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/LiteralImpl.java
@@ -29,6 +29,15 @@ public class LiteralImpl extends ExpressionImpl implements Literal {
private String text;
private EdmType type;
+ public LiteralImpl() {
+
+ }
+
+ public LiteralImpl(String text, EdmType type) {
+ this.text = text;
+ this.type = type;
+ }
+
@Override
public String getText() {
return text;
@@ -54,4 +63,9 @@ public class LiteralImpl extends ExpressionImpl implements Literal {
return visitor.visitLiteral(this);
}
+ @Override
+ public String toString() {
+ return "" + text;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7b23ad71/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/MethodImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/MethodImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/MethodImpl.java
index 7104a9f..8175c85 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/MethodImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/MethodImpl.java
@@ -33,6 +33,14 @@ public class MethodImpl extends ExpressionImpl implements Method {
private MethodKind method;
private List<ExpressionImpl> parameters = new ArrayList<ExpressionImpl>();
+ public MethodImpl() {
+ // TODO: Delete constructor
+ }
+
+ public MethodImpl(MethodKind method) {
+ this.method = method;
+ }
+
@Override
public MethodKind getMethod() {
return method;
@@ -66,4 +74,20 @@ public class MethodImpl extends ExpressionImpl implements Method {
return visitor.visitMethodCall(method, userParameters);
}
+ @Override
+ public String toString() {
+ String parametersString = "[";
+ boolean first = true;
+ for (Expression exp : parameters) {
+ if(first){
+ first = false;
+ parametersString = parametersString + exp.toString();
+ }else {
+ parametersString = parametersString + ", " + exp.toString();
+ }
+ }
+ parametersString = parametersString + "]";
+ return "{" + method + " " + parametersString + "}";
+ }
+
}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7b23ad71/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/UnaryImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/UnaryImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/UnaryImpl.java
index 796191f..f1edf91 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/UnaryImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/UnaryImpl.java
@@ -30,6 +30,15 @@ public class UnaryImpl extends ExpressionImpl implements Unary {
private UnaryOperatorKind operator;
private ExpressionImpl expression;
+ public UnaryImpl() {
+
+ }
+
+ public UnaryImpl(UnaryOperatorKind operator, Expression expression) {
+ this.operator = operator;
+ this.expression = (ExpressionImpl) expression;
+ }
+
@Override
public UnaryOperatorKind getOperator() {
return operator;
@@ -54,4 +63,9 @@ public class UnaryImpl extends ExpressionImpl implements Unary {
return visitor.visitUnaryOperator(operator, operand);
}
+ @Override
+ public String toString() {
+ return "{" + operator + " " + expression + '}';
+ }
+
}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7b23ad71/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/expression/FilterParserTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/expression/FilterParserTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/expression/FilterParserTest.java
new file mode 100644
index 0000000..7bfc369
--- /dev/null
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/expression/FilterParserTest.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.core.uri.expression;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.ArrayList;
+
+import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
+import org.apache.olingo.server.core.uri.expression.FilterParser.TokenKind;
+import org.apache.olingo.server.core.uri.expression.FilterParser.Tokenizer;
+import org.apache.olingo.server.core.uri.expression.FilterParser.Token;
+import org.junit.Test;
+
+public class FilterParserTest {
+
+ @Test
+ public void equality() {
+ Expression expression = parseExpression(TokenKind.EQ_OP);
+ assertEquals("{5 EQ 5}", expression.toString());
+
+ expression = parseExpression(TokenKind.NE_OP);
+ assertEquals("{5 NE 5}", expression.toString());
+ }
+
+ @Test
+ public void relational() {
+ Expression expression = parseExpression(TokenKind.GT_OP);
+ assertEquals("{5 GT 5}", expression.toString());
+
+ expression = parseExpression(TokenKind.GE_OP);
+ assertEquals("{5 GE 5}", expression.toString());
+
+ expression = parseExpression(TokenKind.LT_OP);
+ assertEquals("{5 LT 5}", expression.toString());
+
+ expression = parseExpression(TokenKind.LE_OP);
+ assertEquals("{5 LE 5}", expression.toString());
+ }
+
+ @Test
+ public void additive() {
+ Expression expression = parseExpression(TokenKind.ADD_OP);
+ assertEquals("{5 ADD 5}", expression.toString());
+
+ expression = parseExpression(TokenKind.SUB_OP);
+ assertEquals("{5 SUB 5}", expression.toString());
+ }
+
+ @Test
+ public void multiplicative() {
+ Expression expression = parseExpression(TokenKind.MUL_OP);
+ assertEquals("{5 MUL 5}", expression.toString());
+
+ expression = parseExpression(TokenKind.DIV_OP);
+ assertEquals("{5 DIV 5}", expression.toString());
+
+ expression = parseExpression(TokenKind.MOD_OP);
+ assertEquals("{5 MOD 5}", expression.toString());
+ }
+
+ @Test
+ public void unary() {
+ ArrayList<Token> tokens = new ArrayList<Token>();
+ tokens.add(new Token(TokenKind.MINUS, ""));
+ tokens.add(new Token(TokenKind.PrimitiveIntegerValue, "5"));
+ Tokenizer tokenizer = new Tokenizer(tokens);
+ Expression expression = new FilterParser().parse(tokenizer);
+ assertEquals("{- 5}", expression.toString());
+
+ tokens = new ArrayList<Token>();
+ tokens.add(new Token(TokenKind.NOT, ""));
+ tokens.add(new Token(TokenKind.PrimitiveIntegerValue, "5"));
+ tokenizer = new Tokenizer(tokens);
+ expression = new FilterParser().parse(tokenizer);
+ assertEquals("{not 5}", expression.toString());
+ }
+
+ @Test
+ public void grouping() {
+ ArrayList<Token> tokens = new ArrayList<Token>();
+ tokens.add(new Token(TokenKind.MINUS, ""));
+ tokens.add(new Token(TokenKind.PrimitiveIntegerValue, "5"));
+ tokens.add(new Token(TokenKind.ADD_OP, ""));
+ tokens.add(new Token(TokenKind.PrimitiveIntegerValue, "5"));
+ Tokenizer tokenizer = new Tokenizer(tokens);
+ Expression expression = new FilterParser().parse(tokenizer);
+ assertEquals("{{- 5} ADD 5}", expression.toString());
+
+ tokens = new ArrayList<Token>();
+ tokens.add(new Token(TokenKind.MINUS, ""));
+ tokens.add(new Token(TokenKind.OPEN, ""));
+ tokens.add(new Token(TokenKind.PrimitiveIntegerValue, "5"));
+ tokens.add(new Token(TokenKind.ADD_OP, ""));
+ tokens.add(new Token(TokenKind.PrimitiveIntegerValue, "5"));
+ tokens.add(new Token(TokenKind.CLOSE, ""));
+ tokenizer = new Tokenizer(tokens);
+ expression = new FilterParser().parse(tokenizer);
+ assertEquals("{- {5 ADD 5}}", expression.toString());
+ }
+
+ @Test
+ public void noParameterMethods() {
+ Expression expression = parseMethod(TokenKind.Now);
+ assertEquals("{now []}", expression.toString());
+
+ expression = parseMethod(TokenKind.Maxdatetime);
+ assertEquals("{maxdatetime []}", expression.toString());
+
+ expression = parseMethod(TokenKind.Mindatetime);
+ assertEquals("{mindatetime []}", expression.toString());
+ }
+
+ @Test
+ public void oneParameterMethods() {
+ Expression expression = parseMethod(TokenKind.Length, TokenKind.PrimitiveStringValue);
+ assertEquals("{length [String1]}", expression.toString());
+
+ expression = parseMethod(TokenKind.Tolower, TokenKind.PrimitiveStringValue);
+ assertEquals("{tolower [String1]}", expression.toString());
+
+ expression = parseMethod(TokenKind.Toupper, TokenKind.PrimitiveStringValue);
+ assertEquals("{toupper [String1]}", expression.toString());
+
+ expression = parseMethod(TokenKind.Trim, TokenKind.PrimitiveStringValue);
+ assertEquals("{trim [String1]}", expression.toString());
+
+ expression = parseMethod(TokenKind.Year, TokenKind.PrimitiveDateValue);
+ assertEquals("{year [Date1]}", expression.toString());
+
+ expression = parseMethod(TokenKind.Month, TokenKind.PrimitiveDateValue);
+ assertEquals("{month [Date1]}", expression.toString());
+
+ expression = parseMethod(TokenKind.Day, TokenKind.PrimitiveDateValue);
+ assertEquals("{day [Date1]}", expression.toString());
+
+ expression = parseMethod(TokenKind.Hour, TokenKind.PrimitiveDateTimeOffsetValue);
+ assertEquals("{hour [DateTimeOffset1]}", expression.toString());
+
+ expression = parseMethod(TokenKind.Minute, TokenKind.PrimitiveDateTimeOffsetValue);
+ assertEquals("{minute [DateTimeOffset1]}", expression.toString());
+
+ expression = parseMethod(TokenKind.Second, TokenKind.PrimitiveDateTimeOffsetValue);
+ assertEquals("{second [DateTimeOffset1]}", expression.toString());
+ }
+
+ @Test
+ public void twoParameterMethods() {
+
+ }
+
+ private Expression parseMethod(TokenKind... kind) {
+ ArrayList<Token> tokens = new ArrayList<Token>();
+ tokens.add(new Token(kind[0], ""));
+
+ for (int i = 1; i < kind.length; i++) {
+ String text = null;
+ switch (kind[i]) {
+ case PrimitiveStringValue:
+ text = "String" + i;
+ break;
+ case PrimitiveDateValue:
+ text = "Date" + i;
+ break;
+ case PrimitiveDateTimeOffsetValue:
+ text = "DateTimeOffset" + i;
+ break;
+ default:
+ text = "" + i;
+ break;
+ }
+ tokens.add(new Token(kind[i], text));
+ }
+
+ tokens.add(new Token(TokenKind.CLOSE, ""));
+ Tokenizer tokenizer = new Tokenizer(tokens);
+ Expression expression = new FilterParser().parse(tokenizer);
+ assertNotNull(expression);
+ return expression;
+ }
+
+ private Expression parseExpression(TokenKind operator) {
+ ArrayList<Token> tokens = new ArrayList<Token>();
+ tokens.add(new Token(TokenKind.PrimitiveIntegerValue, "5"));
+ tokens.add(new Token(operator, ""));
+ tokens.add(new Token(TokenKind.PrimitiveIntegerValue, "5"));
+ Tokenizer tokenizer = new Tokenizer(tokens);
+
+ Expression expression = new FilterParser().parse(tokenizer);
+ assertNotNull(expression);
+ return expression;
+ }
+}