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;
+  }
+}