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:15 UTC

[14/30] olingo-odata4 git commit: [OLINGO-834] Type checks in ExpressionParser

[OLINGO-834] Type checks in ExpressionParser

Signed-off-by: Christian Amend <ch...@sap.com>


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/104ecf43
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/104ecf43
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/104ecf43

Branch: refs/heads/master
Commit: 104ecf43d255722e99e18641a1444e33a0ff11b1
Parents: 2f3bc28
Author: Klaus Straubinger <kl...@sap.com>
Authored: Wed Dec 16 16:14:12 2015 +0100
Committer: Christian Amend <ch...@sap.com>
Committed: Wed Dec 16 16:26:51 2015 +0100

----------------------------------------------------------------------
 .../core/uri/parser/ExpressionParser.java       | 413 +++++++++++++++----
 .../core/uri/parser/UriParseTreeVisitor.java    |  24 +-
 .../server/core/uri/parser/UriTokenizer.java    |   5 +
 .../uri/queryoption/expression/BinaryImpl.java  |  10 +-
 .../uri/queryoption/expression/MethodImpl.java  |  70 ++++
 .../uri/queryoption/expression/UnaryImpl.java   |   9 +-
 .../core/uri/parser/ExpressionParserTest.java   | 142 ++++++-
 .../core/uri/parser/UriTokenizerTest.java       |   6 +-
 .../queryoption/expression/ExpressionTest.java  |  64 +--
 9 files changed, 616 insertions(+), 127 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/104ecf43/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java
index 3b04089..61c023d 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java
@@ -24,13 +24,26 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.olingo.commons.api.edm.Edm;
 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.commons.api.edm.EdmType;
+import org.apache.olingo.commons.api.edm.EdmTypeDefinition;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.uri.queryoption.expression.Alias;
+import org.apache.olingo.server.api.uri.queryoption.expression.Binary;
 import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind;
+import org.apache.olingo.server.api.uri.queryoption.expression.Enumeration;
 import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
+import org.apache.olingo.server.api.uri.queryoption.expression.LambdaRef;
+import org.apache.olingo.server.api.uri.queryoption.expression.Literal;
+import org.apache.olingo.server.api.uri.queryoption.expression.Member;
 import org.apache.olingo.server.api.uri.queryoption.expression.Method;
 import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind;
+import org.apache.olingo.server.api.uri.queryoption.expression.TypeLiteral;
+import org.apache.olingo.server.api.uri.queryoption.expression.Unary;
 import org.apache.olingo.server.api.uri.queryoption.expression.UnaryOperatorKind;
 import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
 import org.apache.olingo.server.core.uri.queryoption.expression.AliasImpl;
@@ -72,10 +85,10 @@ public class ExpressionParser {
     tokenToUnaryOperator = Collections.unmodifiableMap(temp);
   }
 
+  // 'cast' and 'isof' are handled specially.
   private static final Map<TokenKind, MethodKind> tokenToMethod;
   static {
     Map<TokenKind, MethodKind> temp = new HashMap<TokenKind, MethodKind>();
-    temp.put(TokenKind.CastMethod, MethodKind.CAST);
     temp.put(TokenKind.CeilingMethod, MethodKind.CEILING);
     temp.put(TokenKind.ConcatMethod, MethodKind.CONCAT);
     temp.put(TokenKind.ContainsMethod, MethodKind.CONTAINS);
@@ -89,7 +102,6 @@ public class ExpressionParser {
     temp.put(TokenKind.GeoLengthMethod, MethodKind.GEOLENGTH);
     temp.put(TokenKind.HourMethod, MethodKind.HOUR);
     temp.put(TokenKind.IndexofMethod, MethodKind.INDEXOF);
-    temp.put(TokenKind.IsofMethod, MethodKind.ISOF);
     temp.put(TokenKind.LengthMethod, MethodKind.LENGTH);
     temp.put(TokenKind.MaxdatetimeMethod, MethodKind.MAXDATETIME);
     temp.put(TokenKind.MindatetimeMethod, MethodKind.MINDATETIME);
@@ -131,8 +143,16 @@ public class ExpressionParser {
     tokenToPrimitiveType = Collections.unmodifiableMap(temp);
   }
 
+  private final Edm edm;
+  private final OData odata;
+
   private UriTokenizer tokenizer;
 
+  public ExpressionParser(final Edm edm, final OData odata) {
+    this.edm = edm;
+    this.odata = odata;
+  }
+
   public Expression parse(UriTokenizer tokenizer) throws UriParserException {
     // Initialize tokenizer.
     this.tokenizer = tokenizer;
@@ -144,7 +164,10 @@ public class ExpressionParser {
     Expression left = parseAnd();
     while (tokenizer.next(TokenKind.OrOperator)) {
       final Expression right = parseAnd();
-      left = new BinaryImpl(left, BinaryOperatorKind.OR, right);
+      checkType(left, EdmPrimitiveTypeKind.Boolean);
+      checkType(right, EdmPrimitiveTypeKind.Boolean);
+      left = new BinaryImpl(left, BinaryOperatorKind.OR, right,
+          odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean));
     }
     return left;
   }
@@ -153,7 +176,10 @@ public class ExpressionParser {
     Expression left = parseExprEquality();
     while (tokenizer.next(TokenKind.AndOperator)) {
       final Expression right = parseExprEquality();
-      left = new BinaryImpl(left, BinaryOperatorKind.AND, right);
+      checkType(left, EdmPrimitiveTypeKind.Boolean);
+      checkType(right, EdmPrimitiveTypeKind.Boolean);
+      left = new BinaryImpl(left, BinaryOperatorKind.AND, right,
+          odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean));
     }
     return left;
   }
@@ -164,12 +190,15 @@ public class ExpressionParser {
     // Null for everything other than EQ or NE
     while (operatorTokenKind != null) {
       final Expression right = parseExprEquality();
-      left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right);
+      checkEqualityTypes(left, right);
+      left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right,
+          odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean));
       operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.EqualsOperator, TokenKind.NotEqualsOperator);
     }
     return left;
   }
 
+  // TODO: The 'isof' method has relational precedence and should appear here.
   private Expression parseExprRel() throws UriParserException {
     Expression left = parseExprAdd();
     TokenKind operatorTokenKind = ParserHelper.next(tokenizer,
@@ -178,7 +207,9 @@ public class ExpressionParser {
     // Null for everything other than GT or GE or LT or LE
     while (operatorTokenKind != null) {
       final Expression right = parseExprAdd();
-      left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right);
+      checkRelationTypes(left, right);
+      left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right,
+          odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean));
       operatorTokenKind = ParserHelper.next(tokenizer,
           TokenKind.GreaterThanOperator, TokenKind.GreaterThanOrEqualsOperator,
           TokenKind.LessThanOperator, TokenKind.LessThanOrEqualsOperator);
@@ -192,7 +223,9 @@ public class ExpressionParser {
     // Null for everything other than ADD or SUB
     while (operatorTokenKind != null) {
       final Expression right = parseExprMul();
-      left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right);
+      checkAddSubTypes(left, right, operatorTokenKind == TokenKind.AddOperator);
+      left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right,
+          odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Double));
       operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.AddOperator, TokenKind.SubOperator);
     }
     return left;
@@ -205,28 +238,61 @@ public class ExpressionParser {
     // Null for everything other than MUL or DIV or MOD
     while (operatorTokenKind != null) {
       final Expression right = parseExprUnary();
-      left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right);
+      checkType(left,
+          EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
+          EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
+          EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double);
+      checkType(right,
+          EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
+          EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
+          EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double);
+      left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right,
+          odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Double));
       operatorTokenKind = ParserHelper.next(tokenizer,
           TokenKind.MulOperator, TokenKind.DivOperator, TokenKind.ModOperator);
     }
     return left;
   }
 
+  // TODO: The 'cast' method has unary precedence and should appear here.
   private Expression parseExprUnary() throws UriParserException {
     Expression left = null;
     TokenKind operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.MINUS, TokenKind.NotOperator);
     // Null for everything other than - or NOT
     while (operatorTokenKind != null) {
-      final Expression expression = parseExprValue();
-      left = new UnaryImpl(tokenToUnaryOperator.get(operatorTokenKind), expression);
+      final Expression expression = parseExprPrimary();
+      if (operatorTokenKind == TokenKind.NotOperator) {
+        checkType(expression, EdmPrimitiveTypeKind.Boolean);
+      } else {
+        checkType(expression,
+            EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
+            EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
+            EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double,
+            EdmPrimitiveTypeKind.Duration);
+      }
+      left = new UnaryImpl(tokenToUnaryOperator.get(operatorTokenKind), expression, getType(expression));
       operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.MINUS, TokenKind.NotOperator);
     }
     if (left == null) {
-      left = parseExprValue();
+      left = parseExprPrimary();
     }
     return left;
   }
 
+  private Expression parseExprPrimary() throws UriParserException {
+    final Expression left = parseExprValue();
+    if (isEnumType(left) && tokenizer.next(TokenKind.HasOperator)) {
+      ParserHelper.requireNext(tokenizer, TokenKind.EnumValue);
+      final String primitiveValueLiteral = tokenizer.getText();
+      final Expression right = new LiteralImpl(primitiveValueLiteral, getEnumType(primitiveValueLiteral));
+      checkEnumLiteral(right);
+      return new BinaryImpl(left, BinaryOperatorKind.HAS, right,
+          odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean));
+    } else {
+      return left;
+    }
+  }
+
   private Expression parseExprValue() throws UriParserException {
     if (tokenizer.next(TokenKind.OPEN)) {
       final Expression expression = parseExpression();
@@ -251,44 +317,34 @@ public class ExpressionParser {
       // TODO: Consume $it expression.
     }
 
-    if (tokenizer.next(TokenKind.QualifiedName)) {
-      // TODO: Consume typecast or bound-function expression.
-    }
-
     TokenKind nextPrimitive = ParserHelper.nextPrimitive(tokenizer);
     if (nextPrimitive != null) {
+      final String primitiveValueLiteral = tokenizer.getText();
       final EdmPrimitiveTypeKind primitiveTypeKind = tokenToPrimitiveType.get(nextPrimitive);
       EdmPrimitiveType type;
       if (primitiveTypeKind == null) {
         if (nextPrimitive == TokenKind.EnumValue) {
-          // TODO: Get enum type.
-          type = null;
+          type = getEnumType(primitiveValueLiteral);
         } else {
           // Null handling
           type = null;
         }
       } else {
-        type = EdmPrimitiveTypeFactory.getInstance(primitiveTypeKind);
+        type = odata.createPrimitiveTypeInstance(primitiveTypeKind);
       }
-      return new LiteralImpl(tokenizer.getText(), type);
+      return new LiteralImpl(primitiveValueLiteral, type);
     }
 
+    // The method token text includes the opening parenthesis so that method calls can be recognized unambiguously.
+    // OData identifiers have to be considered after that.
     TokenKind nextMethod = nextMethod();
     if (nextMethod != null) {
       MethodKind methodKind = tokenToMethod.get(nextMethod);
-      List<Expression> parameters = new ArrayList<Expression>();
-      // The method token text includes the opening parenthesis!
-      if (!tokenizer.next(TokenKind.CLOSE)) {
-        do {
-          parameters.add(parseExpression());
-        } while (tokenizer.next(TokenKind.COMMA));
-        ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
-      }
-
-      MethodImpl methodImpl = new MethodImpl(methodKind, parameters);
-      validateMethodParameters(methodImpl);
+      return new MethodImpl(methodKind, parseMethodParameters(methodKind));
+    }
 
-      return methodImpl;
+    if (tokenizer.next(TokenKind.QualifiedName)) {
+      // TODO: Consume typecast or bound-function expression.
     }
 
     if (tokenizer.next(TokenKind.ODataIdentifier)) {
@@ -298,85 +354,131 @@ public class ExpressionParser {
     throw new UriParserSyntaxException("Unexpected token", UriParserSyntaxException.MessageKeys.SYNTAX);
   }
 
-  private void validateMethodParameters(final Method method) throws UriParserException {
-    // We might validate parameter types in the future.
-    int size = method.getParameters().size();
-    switch (method.getMethod()) {
-    // Must have two Parameters.
-    case CONTAINS:
-    case ENDSWITH:
-    case STARTSWITH:
-    case INDEXOF:
-    case CONCAT:
-    case GEODISTANCE:
-    case GEOINTERSECTS:
-      if (size != 2) {
-        throw new UriParserSemanticException(
-            "The method " + method.getMethod() + " needs exactly two parameters.",
-            null); // TODO: message key
-      }
+  private List<Expression> parseMethodParameters(final MethodKind methodKind) throws UriParserException {
+    List<Expression> parameters = new ArrayList<Expression>();
+    switch (methodKind) {
+    // Must have no parameter.
+    case NOW:
+    case MAXDATETIME:
+    case MINDATETIME:
       break;
+
     // Must have one parameter.
     case LENGTH:
     case TOLOWER:
     case TOUPPER:
     case TRIM:
+      final Expression stringParameter = parseExpression();
+      checkType(stringParameter, EdmPrimitiveTypeKind.String);
+      parameters.add(stringParameter);
+      break;
     case YEAR:
     case MONTH:
     case DAY:
+      final Expression dateParameter = parseExpression();
+      checkType(dateParameter, EdmPrimitiveTypeKind.Date, EdmPrimitiveTypeKind.DateTimeOffset);
+      parameters.add(dateParameter);
+      break;
     case HOUR:
     case MINUTE:
     case SECOND:
     case FRACTIONALSECONDS:
+      final Expression timeParameter = parseExpression();
+      checkType(timeParameter, EdmPrimitiveTypeKind.TimeOfDay, EdmPrimitiveTypeKind.DateTimeOffset);
+      parameters.add(timeParameter);
+      break;
     case DATE:
     case TIME:
     case TOTALOFFSETMINUTES:
+      final Expression dateTimeParameter = parseExpression();
+      checkType(dateTimeParameter, EdmPrimitiveTypeKind.DateTimeOffset);
+      parameters.add(dateTimeParameter);
+      break;
     case TOTALSECONDS:
+      final Expression durationParameter = parseExpression();
+      checkType(durationParameter, EdmPrimitiveTypeKind.Duration);
+      parameters.add(durationParameter);
+      break;
     case ROUND:
     case FLOOR:
     case CEILING:
+      final Expression decimalParameter = parseExpression();
+      checkType(decimalParameter,
+          EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double);
+      parameters.add(decimalParameter);
+      break;
     case GEOLENGTH:
-      if (size != 1) {
-        throw new UriParserSemanticException(
-            "The method '" + method.getMethod() + "' needs exactly one parameter.",
-            null); // TODO: message key
-      }
+      final Expression geoParameter = parseExpression();
+      checkType(geoParameter,
+          EdmPrimitiveTypeKind.GeographyLineString, EdmPrimitiveTypeKind.GeometryLineString);
+      parameters.add(geoParameter);
       break;
-    // Must have no parameter.
-    case NOW:
-    case MAXDATETIME:
-    case MINDATETIME:
-      if (size > 0) {
-        throw new UriParserSemanticException("The method '" + method.getMethod() + "' must have no parameters.",
-            null); // TODO: message key
-      }
+
+    // Must have two parameters.
+    case CONTAINS:
+    case ENDSWITH:
+    case STARTSWITH:
+    case INDEXOF:
+    case CONCAT:
+      final Expression stringParameter1 = parseExpression();
+      checkType(stringParameter1, EdmPrimitiveTypeKind.String);
+      parameters.add(stringParameter1);
+      ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
+      final Expression stringParameter2 = parseExpression();
+      checkType(stringParameter2, EdmPrimitiveTypeKind.String);
+      parameters.add(stringParameter2);
       break;
-    // Variable parameter number
-    case CAST:
-    case ISOF:
-      if (size < 1 || size > 2) {
-        throw new UriParserSemanticException(
-            "The method '" + method.getMethod() + "' must have one or two parameters.",
-            null); // TODO: message key
-      }
+    case GEODISTANCE:
+      final Expression geoParameter1 = parseExpression();
+      checkType(geoParameter1, EdmPrimitiveTypeKind.GeographyPoint, EdmPrimitiveTypeKind.GeometryPoint);
+      parameters.add(geoParameter1);
+      ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
+      final Expression geoParameter2 = parseExpression();
+      checkType(geoParameter2, EdmPrimitiveTypeKind.GeographyPoint, EdmPrimitiveTypeKind.GeometryPoint);
+      parameters.add(geoParameter2);
+      break;
+    case GEOINTERSECTS:
+      final Expression geoPointParameter = parseExpression();
+      checkType(geoPointParameter,
+          EdmPrimitiveTypeKind.GeographyPoint, EdmPrimitiveTypeKind.GeometryPoint);
+      parameters.add(geoPointParameter);
+      ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
+      final Expression geoPolygonParameter = parseExpression();
+      checkType(geoPolygonParameter,
+          EdmPrimitiveTypeKind.GeographyPolygon, EdmPrimitiveTypeKind.GeometryPolygon);
+      parameters.add(geoPolygonParameter);
       break;
+
+    // Can have two or three parameters.
     case SUBSTRING:
-      if (size < 2 || size > 3) {
-        throw new UriParserSemanticException(
-            "The method '" + method.getMethod() + "' must have two or three parameters.",
-            null); // TODO: message key
+      final Expression parameterFirst = parseExpression();
+      checkType(parameterFirst, EdmPrimitiveTypeKind.String);
+      parameters.add(parameterFirst);
+      ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
+      final Expression parameterSecond = parseExpression();
+      checkType(parameterSecond,
+          EdmPrimitiveTypeKind.Int64, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int16,
+          EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte);
+      parameters.add(parameterSecond);
+      if (tokenizer.next(TokenKind.COMMA)) {
+        final Expression parameterThird = parseExpression();
+        checkType(parameterThird,
+            EdmPrimitiveTypeKind.Int64, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int16,
+            EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte);
+        parameters.add(parameterThird);
       }
       break;
+
     default:
-      throw new UriParserSemanticException(
-          "Unkown method '" + method.getMethod() + "'",
-          null); // TODO: message key
+      throw new UriParserSemanticException("Unkown method '" + methodKind.name() + "'",
+          UriParserSemanticException.MessageKeys.NOT_IMPLEMENTED, methodKind.name()); // TODO: better message
     }
+    ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
+    return parameters;
   }
 
   private TokenKind nextMethod() {
     return ParserHelper.next(tokenizer,
-        TokenKind.CastMethod,
         TokenKind.CeilingMethod,
         TokenKind.ConcatMethod,
         TokenKind.ContainsMethod,
@@ -390,7 +492,6 @@ public class ExpressionParser {
         TokenKind.GeoLengthMethod,
         TokenKind.HourMethod,
         TokenKind.IndexofMethod,
-        TokenKind.IsofMethod,
         TokenKind.LengthMethod,
         TokenKind.MaxdatetimeMethod,
         TokenKind.MindatetimeMethod,
@@ -409,4 +510,158 @@ public class ExpressionParser {
         TokenKind.TrimMethod,
         TokenKind.YearMethod);
   }
+
+  private EdmType getType(final Expression expression) throws UriParserException {
+    EdmType type;
+    if (expression instanceof Literal) {
+      type = ((Literal) expression).getType();
+    } else if (expression instanceof TypeLiteral) {
+      type = ((TypeLiteral) expression).getType();
+    } else if (expression instanceof Enumeration) {
+      type = ((Enumeration) expression).getType();
+    } else if (expression instanceof Member) {
+      type = ((Member) expression).getType();
+    } else if (expression instanceof Unary) {
+      type = ((UnaryImpl) expression).getType();
+    } else if (expression instanceof Binary) {
+      type = ((BinaryImpl) expression).getType();
+    } else if (expression instanceof Method) {
+      type = ((MethodImpl) expression).getType();
+    } else if (expression instanceof LambdaRef) {
+      throw new UriParserSemanticException("Type determination not implemented.",
+          UriParserSemanticException.MessageKeys.NOT_IMPLEMENTED, expression.toString());
+    } else if (expression instanceof Alias) {
+      type = null; // The alias would have to be available already parsed.
+    } else {
+      throw new UriParserSemanticException("Unknown expression type.",
+          UriParserSemanticException.MessageKeys.NOT_IMPLEMENTED, expression.toString());
+    }
+    if (type != null && type.getKind() == EdmTypeKind.DEFINITION) {
+      type = ((EdmTypeDefinition) type).getUnderlyingType();
+    }
+    return type;
+  }
+
+  private boolean isType(final Expression expression, final EdmPrimitiveTypeKind... kinds) throws UriParserException {
+    final EdmType expressionType = getType(expression);
+    if (expressionType == null) {
+      return true;
+    }
+    for (final EdmPrimitiveTypeKind kind : kinds) {
+      if (expressionType.equals(odata.createPrimitiveTypeInstance(kind))) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private void checkType(final Expression expression, final EdmPrimitiveTypeKind... kinds) throws UriParserException {
+    if (!isType(expression, kinds)) {
+      throw new UriParserSemanticException("Incompatible type.",
+          UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, // TODO: better message
+          getType(expression) == null ?
+              "" :
+              getType(expression).getFullQualifiedName().getFullQualifiedNameAsString());
+    }
+  }
+
+  private void checkEqualityTypes(final Expression left, final Expression right) throws UriParserException {
+    final EdmType leftType = getType(left);
+    final EdmType rightType = getType(right);
+    if (leftType == null || rightType == null || leftType.equals(rightType)) {
+      return;
+    }
+    if (leftType.getKind() != EdmTypeKind.PRIMITIVE
+        || rightType.getKind() != EdmTypeKind.PRIMITIVE
+        || !(((EdmPrimitiveType) leftType).isCompatible((EdmPrimitiveType) rightType)
+        || ((EdmPrimitiveType) rightType).isCompatible((EdmPrimitiveType) leftType))) {
+      throw new UriParserSemanticException("Incompatible types.",
+          UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, ""); // TODO: better message
+    }
+  }
+
+  private EdmPrimitiveType getEnumType(final String primitiveValueLiteral) throws UriParserException {
+    final String enumTypeName = primitiveValueLiteral.substring(0, primitiveValueLiteral.indexOf('\''));
+    final EdmPrimitiveType type = edm.getEnumType(new FullQualifiedName(enumTypeName));
+    if (type == null) {
+      throw new UriParserSemanticException("Unknown Enum type '" + enumTypeName + "'.",
+          UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, enumTypeName);
+    }
+    return type;
+  }
+
+  private boolean isEnumType(final Expression expression) throws UriParserException {
+    final EdmType expressionType = getType(expression);
+    return expressionType == null
+        || expressionType.getKind() == EdmTypeKind.ENUM
+        || isType(expression,
+            EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
+            EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte);
+  }
+
+  private void checkEnumLiteral(final Expression expression) throws UriParserException {
+    if (expression == null
+        || !(expression instanceof Literal)
+        || ((Literal) expression).getType() == null
+        || ((Literal) expression).getType().getKind() != EdmTypeKind.ENUM) {
+      throw new UriParserSemanticException("Enum literal expected.",
+          UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, ""); // TODO: better message
+    }
+  }
+
+  private void checkRelationTypes(final Expression left, final Expression right) throws UriParserException {
+    final EdmType leftType = getType(left);
+    final EdmType rightType = getType(right);
+    if (leftType == null || rightType == null) {
+      return;
+    }
+    checkType(left,
+        EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
+        EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
+        EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double,
+        EdmPrimitiveTypeKind.Boolean, EdmPrimitiveTypeKind.Guid, EdmPrimitiveTypeKind.String,
+        EdmPrimitiveTypeKind.Date, EdmPrimitiveTypeKind.TimeOfDay,
+        EdmPrimitiveTypeKind.DateTimeOffset, EdmPrimitiveTypeKind.Duration);
+    checkType(right,
+        EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
+        EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
+        EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double,
+        EdmPrimitiveTypeKind.Boolean, EdmPrimitiveTypeKind.Guid, EdmPrimitiveTypeKind.String,
+        EdmPrimitiveTypeKind.Date, EdmPrimitiveTypeKind.TimeOfDay,
+        EdmPrimitiveTypeKind.DateTimeOffset, EdmPrimitiveTypeKind.Duration);
+    if (!(((EdmPrimitiveType) leftType).isCompatible((EdmPrimitiveType) rightType)
+    || ((EdmPrimitiveType) rightType).isCompatible((EdmPrimitiveType) leftType))) {
+      throw new UriParserSemanticException("Incompatible types.",
+          UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, ""); // TODO: better message
+    }
+  }
+
+  private void checkAddSubTypes(final Expression left, final Expression right, final boolean isAdd)
+      throws UriParserException {
+    final EdmType leftType = getType(left);
+    final EdmType rightType = getType(right);
+    if (leftType == null || rightType == null
+        || isType(left,
+            EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
+            EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
+            EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double)
+        && isType(right,
+            EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
+            EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
+            EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double)) {
+      return;
+    }
+    if (isType(left, EdmPrimitiveTypeKind.DateTimeOffset)
+        && (isType(right, EdmPrimitiveTypeKind.Duration)
+        || isType(right, EdmPrimitiveTypeKind.DateTimeOffset) && !isAdd)) {
+      return;
+    }
+    if (isType(left, EdmPrimitiveTypeKind.Duration) && isType(right, EdmPrimitiveTypeKind.Duration)
+        || isType(left, EdmPrimitiveTypeKind.Date)
+        && (isType(right, EdmPrimitiveTypeKind.Duration) || isType(right, EdmPrimitiveTypeKind.Date) && !isAdd)) {
+      return;
+    }
+    throw new UriParserSemanticException("Incompatible types.",
+        UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, ""); // TODO: better message
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/104ecf43/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java
index 3f7f70c..9d29ab2 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java
@@ -738,7 +738,8 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
     return new BinaryImpl(
         (Expression) ctx.vE1.accept(this),
         tokenIndex == UriLexer.ADD ? BinaryOperatorKind.ADD : BinaryOperatorKind.SUB,
-        (Expression) ctx.vE2.accept(this));
+        (Expression) ctx.vE2.accept(this),
+        null);
   }
 
   @Override
@@ -785,7 +786,8 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
     return new BinaryImpl(
         (Expression) ctx.vE1.accept(this),
         BinaryOperatorKind.AND,
-        (Expression) ctx.vE2.accept(this));
+        (Expression) ctx.vE2.accept(this),
+        EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean));
   }
 
   @Override
@@ -815,7 +817,8 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
     return new BinaryImpl(
         (Expression) ctx.vE1.accept(this),
         kind,
-        (Expression) ctx.vE2.accept(this));
+        (Expression) ctx.vE2.accept(this),
+        EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean));
   }
 
   @Override
@@ -843,7 +846,8 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
     return new BinaryImpl(
         (Expression) ctx.vE1.accept(this),
         tokenIndex == UriLexer.EQ_ALPHA ? BinaryOperatorKind.EQ : BinaryOperatorKind.NE,
-        (Expression) ctx.vE2.accept(this));
+        (Expression) ctx.vE2.accept(this),
+        EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean));
   }
 
   @Override
@@ -851,7 +855,8 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
     return new BinaryImpl(
         (Expression) ctx.vE1.accept(this),
         BinaryOperatorKind.HAS,
-        (Expression) ctx.vE2.accept(this));
+        (Expression) ctx.vE2.accept(this),
+        EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean));
   }
 
   @Override
@@ -875,7 +880,8 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
     return new BinaryImpl(
         (Expression) ctx.vE1.accept(this),
         kind,
-        (Expression) ctx.vE2.accept(this));
+        (Expression) ctx.vE2.accept(this),
+        null);
   }
 
   @Override
@@ -883,7 +889,8 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
     return new BinaryImpl(
         (Expression) ctx.vE1.accept(this),
         BinaryOperatorKind.OR,
-        (Expression) ctx.vE2.accept(this));
+        (Expression) ctx.vE2.accept(this),
+        EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean));
   }
 
   @Override
@@ -2294,7 +2301,8 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
   public Expression visitAltUnary(@NotNull final UriParserParser.AltUnaryContext ctx) {
     return new UnaryImpl(
         ctx.unary().NOT() == null ? UnaryOperatorKind.MINUS : UnaryOperatorKind.NOT,
-        (Expression) ctx.commonExpr().accept(this));
+        (Expression) ctx.commonExpr().accept(this),
+        null);
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/104ecf43/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java
index a40f4ec..4b43cd9 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java
@@ -80,6 +80,7 @@ public class UriTokenizer {
     GreaterThanOrEqualsOperator,
     LessThanOperator,
     LessThanOrEqualsOperator,
+    HasOperator,
     AddOperator,
     SubOperator,
     MulOperator,
@@ -140,6 +141,7 @@ public class UriTokenizer {
    * The order in which this method is called with different token kinds is important,
    * not only for performance reasons but also if tokens can start with the same characters
    * (e.g., a qualified name starts with an OData identifier).
+   * The index is advanced to the end of this token if the token is found.
    * @param allowedTokenKind the kind of token to expect
    * @return <code>true</code> if the token is found; <code>false</code> otherwise
    * @see #getText()
@@ -288,6 +290,9 @@ public class UriTokenizer {
     case LessThanOrEqualsOperator:
       found = nextBinaryOperator("le");
       break;
+    case HasOperator:
+      found = nextBinaryOperator("has");
+      break;
     case AddOperator:
       found = nextBinaryOperator("add");
       break;

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/104ecf43/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 3f2e8f2..2439bcf 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
@@ -18,6 +18,7 @@
  */
 package org.apache.olingo.server.core.uri.queryoption.expression;
 
+import org.apache.olingo.commons.api.edm.EdmType;
 import org.apache.olingo.server.api.ODataApplicationException;
 import org.apache.olingo.server.api.uri.queryoption.expression.Binary;
 import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind;
@@ -30,11 +31,14 @@ public class BinaryImpl implements Binary {
   private final Expression left;
   private final BinaryOperatorKind operator;
   private final Expression right;
+  private final EdmType type;
 
-  public BinaryImpl(final Expression left, final BinaryOperatorKind operator, final Expression right) {
+  public BinaryImpl(final Expression left, final BinaryOperatorKind operator, final Expression right,
+      final EdmType type) {
     this.left = left;
     this.operator = operator;
     this.right = right;
+    this.type = type;
   }
 
   @Override
@@ -52,6 +56,10 @@ public class BinaryImpl implements Binary {
     return right;
   }
 
+  public EdmType getType() {
+    return type;
+  }
+
   @Override
   public <T> T accept(final ExpressionVisitor<T> visitor) throws ExpressionVisitException, ODataApplicationException {
     T left = this.left.accept(visitor);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/104ecf43/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 1c8ce64..0346292 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
@@ -22,12 +22,16 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
+import org.apache.olingo.commons.api.edm.EdmType;
 import org.apache.olingo.server.api.ODataApplicationException;
 import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
 import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitException;
 import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitor;
 import org.apache.olingo.server.api.uri.queryoption.expression.Method;
 import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind;
+import org.apache.olingo.server.api.uri.queryoption.expression.TypeLiteral;
+import org.apache.olingo.server.core.ODataImpl;
 
 public class MethodImpl implements Method {
 
@@ -44,6 +48,72 @@ public class MethodImpl implements Method {
     return method;
   }
 
+  public EdmType getType() {
+    EdmPrimitiveTypeKind kind = null;
+    switch (method) {
+    case CONTAINS:
+    case STARTSWITH:
+    case ENDSWITH:
+      kind = EdmPrimitiveTypeKind.Boolean;
+      break;
+    case LENGTH:
+    case INDEXOF:
+      kind = EdmPrimitiveTypeKind.Int32;
+      break;
+    case SUBSTRING:
+    case TOLOWER:
+    case TOUPPER:
+    case TRIM:
+    case CONCAT:
+      kind = EdmPrimitiveTypeKind.String;
+      break;
+    case YEAR:
+    case MONTH:
+    case DAY:
+    case HOUR:
+    case MINUTE:
+    case SECOND:
+      kind = EdmPrimitiveTypeKind.Int32;
+      break;
+    case FRACTIONALSECONDS:
+    case TOTALSECONDS:
+      kind = EdmPrimitiveTypeKind.Decimal;
+      break;
+    case DATE:
+      kind = EdmPrimitiveTypeKind.Date;
+      break;
+    case TIME:
+      kind = EdmPrimitiveTypeKind.TimeOfDay;
+      break;
+    case TOTALOFFSETMINUTES:
+      kind = EdmPrimitiveTypeKind.Int32;
+      break;
+    case MINDATETIME:
+    case MAXDATETIME:
+    case NOW:
+      kind = EdmPrimitiveTypeKind.DateTimeOffset;
+      break;
+    case ROUND:
+    case FLOOR:
+    case CEILING:
+      kind = EdmPrimitiveTypeKind.Double; // Needs to be refined if Decimal must be distinguished from Double.
+      break;
+    case GEODISTANCE:
+    case GEOLENGTH:
+      kind = EdmPrimitiveTypeKind.Double;
+      break;
+    case GEOINTERSECTS:
+      kind = EdmPrimitiveTypeKind.Boolean;
+      break;
+    case CAST:
+      return ((TypeLiteral) parameters.get(parameters.size() - 1)).getType();
+    case ISOF:
+      kind = EdmPrimitiveTypeKind.Boolean;
+      break;
+    }
+    return new ODataImpl().createPrimitiveTypeInstance(kind);
+  }
+
   @Override
   public List<Expression> getParameters() {
     return parameters == null ?

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/104ecf43/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 910997e..86639a6 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
@@ -18,6 +18,7 @@
  */
 package org.apache.olingo.server.core.uri.queryoption.expression;
 
+import org.apache.olingo.commons.api.edm.EdmType;
 import org.apache.olingo.server.api.ODataApplicationException;
 import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
 import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitException;
@@ -29,10 +30,12 @@ public class UnaryImpl implements Unary {
 
   private final UnaryOperatorKind operator;
   private final Expression expression;
+  private final EdmType type;
 
-  public UnaryImpl(final UnaryOperatorKind operator, final Expression expression) {
+  public UnaryImpl(final UnaryOperatorKind operator, final Expression expression, final EdmType type) {
     this.operator = operator;
     this.expression = expression;
+    this.type = type;
   }
 
   @Override
@@ -45,6 +48,10 @@ public class UnaryImpl implements Unary {
     return expression;
   }
 
+  public EdmType getType() {
+    return type;
+  }
+
   @Override
   public <T> T accept(final ExpressionVisitor<T> visitor) throws ExpressionVisitException, ODataApplicationException {
     T operand = expression.accept(visitor);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/104ecf43/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java
index 58f2a1f..183ff22 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java
@@ -21,15 +21,19 @@ package org.apache.olingo.server.core.uri.parser;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import java.util.Locale;
 
+import org.apache.olingo.server.api.OData;
 import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
 import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
 import org.junit.Test;
 
 public class ExpressionParserTest {
 
+  private final OData odata = OData.newInstance();
+
   @Test
   public void equality() throws Exception {
     Expression expression = parseExpression("5 eq 5");
@@ -37,6 +41,12 @@ public class ExpressionParserTest {
 
     expression = parseExpression("5 ne 5");
     assertEquals("{5 NE 5}", expression.toString());
+
+    assertEquals("{1 EQ null}", parseExpression("1 eq null").toString());
+    assertEquals("{null NE 2}", parseExpression("null ne 2").toString());
+    assertEquals("{null EQ null}", parseExpression("null eq null").toString());
+
+    wrongExpression("5 eq '5'");
   }
 
   @Test
@@ -52,6 +62,14 @@ public class ExpressionParserTest {
 
     expression = parseExpression("5 le 5");
     assertEquals("{5 LE 5}", expression.toString());
+
+    assertEquals("{5 LE 5.1}", parseExpression("5 le 5.1").toString());
+
+    assertEquals("{1 GT null}", parseExpression("1 gt null").toString());
+    assertEquals("{null GE 2}", parseExpression("null ge 2").toString());
+    assertEquals("{null LE null}", parseExpression("null le null").toString());
+
+    wrongExpression("5 gt duration'PT5H'");
   }
 
   @Test
@@ -59,8 +77,37 @@ public class ExpressionParserTest {
     Expression expression = parseExpression("5 add 5");
     assertEquals("{5 ADD 5}", expression.toString());
 
-    expression = parseExpression("5 sub 5");
-    assertEquals("{5 SUB 5}", expression.toString());
+    expression = parseExpression("5 sub 5.1");
+    assertEquals("{5 SUB 5.1}", expression.toString());
+
+    expression = parseExpression("2000-02-29 sub 2016-02-29");
+    assertEquals("{2000-02-29 SUB 2016-02-29}", expression.toString());
+
+    expression = parseExpression("2000-02-29T00:00:00Z sub 2016-02-29T01:02:03Z");
+    assertEquals("{2000-02-29T00:00:00Z SUB 2016-02-29T01:02:03Z}", expression.toString());
+
+    expression = parseExpression("duration'PT1H' add duration'PT1M'");
+    assertEquals("{duration'PT1H' ADD duration'PT1M'}", expression.toString());
+
+    expression = parseExpression("2016-01-01 add duration'P60D'");
+    assertEquals("{2016-01-01 ADD duration'P60D'}", expression.toString());
+
+    expression = parseExpression("2000-02-29T00:00:00Z add duration'PT12H'");
+    assertEquals("{2000-02-29T00:00:00Z ADD duration'PT12H'}", expression.toString());
+
+    assertEquals("{1 ADD null}", parseExpression("1 add null").toString());
+    assertEquals("{null ADD 2}", parseExpression("null add 2").toString());
+    assertEquals("{null SUB null}", parseExpression("null sub null").toString());
+
+    wrongExpression("1 add '2'");
+    wrongExpression("'1' add 2");
+    wrongExpression("1 add 2000-02-29");
+    wrongExpression("11:12:13 sub 2000-02-29T11:12:13Z");
+    wrongExpression("2000-02-29 add 2016-02-29");
+    wrongExpression("2000-02-29T00:00:00Z add 2016-02-29T01:02:03Z");
+    wrongExpression("2000-02-29T00:00:00Z add 1");
+    wrongExpression("2000-02-29 sub 1");
+    wrongExpression("duration'P7D' add 2000-02-29");
   }
 
   @Test
@@ -73,6 +120,8 @@ public class ExpressionParserTest {
 
     expression = parseExpression("5 mod 5");
     assertEquals("{5 MOD 5}", expression.toString());
+
+    wrongExpression("1 mod '2'");
   }
 
   @Test
@@ -81,9 +130,12 @@ public class ExpressionParserTest {
     assertEquals("{MINUS 5}", expression.toString());
 
     assertEquals("{MINUS -1}", parseExpression("--1").toString());
+    assertEquals("{MINUS duration'PT1M'}", parseExpression("-duration'PT1M'").toString());
+
+    expression = parseExpression("not false");
+    assertEquals("{NOT false}", expression.toString());
 
-    expression = parseExpression("not 5");
-    assertEquals("{NOT 5}", expression.toString());
+    wrongExpression("-11:12:13");
   }
 
   @Test
@@ -111,6 +163,8 @@ public class ExpressionParserTest {
 
     expression = parseMethod(TokenKind.MindatetimeMethod);
     assertEquals("{mindatetime []}", expression.toString());
+
+    wrongExpression("now(1)");
   }
 
   @Test
@@ -148,16 +202,79 @@ public class ExpressionParserTest {
 
     expression = parseMethod(TokenKind.SecondMethod, dateTimeOffsetValue);
     assertEquals("{second [" + dateTimeOffsetValue + "]}", expression.toString());
+
+    expression = parseMethod(TokenKind.DateMethod, dateTimeOffsetValue);
+    assertEquals("{date [" + dateTimeOffsetValue + "]}", expression.toString());
+
+    expression = parseMethod(TokenKind.TotalsecondsMethod, "duration'PT1H'");
+    assertEquals("{totalseconds [duration'PT1H']}", expression.toString());
+
+    expression = parseMethod(TokenKind.RoundMethod, "3.141592653589793");
+    assertEquals("{round [3.141592653589793]}", expression.toString());
+
+    assertEquals("{hour [null]}", parseMethod(TokenKind.HourMethod, new String[] { null }).toString());
+
+    wrongExpression("trim()");
+    wrongExpression("trim(1)");
+    wrongExpression("ceiling('1.2')");
+  }
+
+  @Test
+  public void twoParameterMethods() throws Exception {
+    Expression expression = parseMethod(TokenKind.ContainsMethod, "'a'", "'b'");
+    assertEquals("{contains ['a', 'b']}", expression.toString());
+
+    expression = parseMethod(TokenKind.EndswithMethod, "'a'", "'b'");
+    assertEquals("{endswith ['a', 'b']}", expression.toString());
+
+    expression = parseMethod(TokenKind.StartswithMethod, "'a'", "'b'");
+    assertEquals("{startswith ['a', 'b']}", expression.toString());
+
+    expression = parseMethod(TokenKind.IndexofMethod, "'a'", "'b'");
+    assertEquals("{indexof ['a', 'b']}", expression.toString());
+
+    expression = parseMethod(TokenKind.ConcatMethod, "'a'", "'b'");
+    assertEquals("{concat ['a', 'b']}", expression.toString());
+
+    // TODO: Geo methods.
+//    expression = parseMethod(TokenKind.GeoDistanceMethod,
+//        "geography'SRID=0;Point(1.2 3.4)'", "geography'SRID=0;Point(5.6 7.8)'");
+//    assertEquals("{geo.distance [geography'SRID=0;Point(1.2 3.4)', geography'SRID=0;Point(5.6 7.8)']}",
+//        expression.toString());
+//
+//    expression = parseMethod(TokenKind.GeoIntersectsMethod);
+//    assertEquals("{geo.intersects []}", expression.toString());
+
+    assertEquals("{startswith [null, 'b']}", parseMethod(TokenKind.StartswithMethod, null, "'b'").toString());
+    assertEquals("{indexof ['a', null]}", parseMethod(TokenKind.IndexofMethod, "'a'", null).toString());
+
+    wrongExpression("concat('a')");
+    wrongExpression("endswith('a',1)");
+}
+
+  @Test
+  public void variableParameterNumberMethods() throws Exception {
+    Expression expression = parseMethod(TokenKind.SubstringMethod, "'abc'", "1", "2");
+    assertEquals("{substring ['abc', 1, 2]}", expression.toString());
+    expression = parseMethod(TokenKind.SubstringMethod, "'abc'", "1");
+    assertEquals("{substring ['abc', 1]}", expression.toString());
+
+    wrongExpression("substring('abc')");
+    wrongExpression("substring('abc',1,2,3)");
+    wrongExpression("substring(1,2)");
   }
 
   private Expression parseMethod(TokenKind kind, String... parameters) throws UriParserException {
     String expressionString = kind.name().substring(0, kind.name().indexOf("Method"))
         .toLowerCase(Locale.ROOT).replace("geo", "geo.") + '(';
-    for (int i = 0; i < parameters.length; i++) {
-      if (i > 0) {
+    boolean first = true;
+    for (final String parameter : parameters) {
+      if (first) {
+        first = false;
+      } else {
         expressionString += ',';
       }
-      expressionString += parameters[i];
+      expressionString += parameter;
     }
     expressionString += ')';
 
@@ -168,9 +285,18 @@ public class ExpressionParserTest {
 
   private Expression parseExpression(final String expressionString) throws UriParserException {
     UriTokenizer tokenizer = new UriTokenizer(expressionString);
-    Expression expression = new ExpressionParser().parse(tokenizer);
+    Expression expression = new ExpressionParser(null, odata).parse(tokenizer);
     assertNotNull(expression);
     assertTrue(tokenizer.next(TokenKind.EOF));
     return expression;
   }
+
+  private void wrongExpression(final String expressionString) {
+    try {
+      new ExpressionParser(null, odata).parse(new UriTokenizer(expressionString));
+      fail("Expected exception not thrown.");
+    } catch (final UriParserException e) {
+      assertNotNull(e);
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/104ecf43/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/UriTokenizerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/UriTokenizerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/UriTokenizerTest.java
index b5614ad..1b2d508 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/UriTokenizerTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/UriTokenizerTest.java
@@ -412,7 +412,7 @@ public class UriTokenizerTest {
     assertTrue(tokenizer.next(TokenKind.IntegerValue));
     assertTrue(tokenizer.next(TokenKind.EOF));
 
-    tokenizer = new UriTokenizer("1 gt 2 or 3 ge 4 or 5 lt 6");
+    tokenizer = new UriTokenizer("1 gt 2 or 3 ge 4 or 5 lt 6 or 7 has namespace.name'flag1,flag2'");
     assertTrue(tokenizer.next(TokenKind.IntegerValue));
     assertTrue(tokenizer.next(TokenKind.GreaterThanOperator));
     assertTrue(tokenizer.next(TokenKind.IntegerValue));
@@ -424,6 +424,10 @@ public class UriTokenizerTest {
     assertTrue(tokenizer.next(TokenKind.IntegerValue));
     assertTrue(tokenizer.next(TokenKind.LessThanOperator));
     assertTrue(tokenizer.next(TokenKind.IntegerValue));
+    assertTrue(tokenizer.next(TokenKind.OrOperator));
+    assertTrue(tokenizer.next(TokenKind.IntegerValue));
+    assertTrue(tokenizer.next(TokenKind.HasOperator));
+    assertTrue(tokenizer.next(TokenKind.EnumValue));
     assertTrue(tokenizer.next(TokenKind.EOF));
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/104ecf43/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/queryoption/expression/ExpressionTest.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/queryoption/expression/ExpressionTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/queryoption/expression/ExpressionTest.java
index ec5ce6e..864b17a 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/queryoption/expression/ExpressionTest.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/queryoption/expression/ExpressionTest.java
@@ -31,14 +31,13 @@ import org.apache.olingo.commons.api.edm.EdmAction;
 import org.apache.olingo.commons.api.edm.EdmEntityType;
 import org.apache.olingo.commons.api.edm.EdmEnumType;
 import org.apache.olingo.commons.api.edm.EdmFunction;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
 import org.apache.olingo.server.api.OData;
-import org.apache.olingo.server.api.ODataApplicationException;
 import org.apache.olingo.server.api.edmx.EdmxReference;
 import org.apache.olingo.server.api.uri.UriInfoKind;
 import org.apache.olingo.server.api.uri.UriInfoResource;
 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.ExpressionVisitException;
 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.UriInfoImpl;
@@ -53,7 +52,8 @@ import org.apache.olingo.server.tecsvc.provider.FunctionProvider;
 import org.junit.Test;
 
 public class ExpressionTest {
-  private static final Edm edm = OData.newInstance().createServiceMetadata(
+  private static final OData odata = OData.newInstance();
+  private static final Edm edm = odata.createServiceMetadata(
       new EdmTechProvider(), Collections.<EdmxReference> emptyList()).getEdm();
 
   @Test
@@ -69,7 +69,7 @@ public class ExpressionTest {
   }
 
   @Test
-  public void aliasExpression() throws ExpressionVisitException, ODataApplicationException {
+  public void aliasExpression() throws Exception {
     AliasImpl expression = new AliasImpl("Test");
 
     assertEquals("Test", expression.getParameterName());
@@ -79,47 +79,50 @@ public class ExpressionTest {
   }
 
   @Test
-  public void binaryExpression() throws ExpressionVisitException, ODataApplicationException {
-    Expression expressionLeft = new LiteralImpl("A", null);
-    Expression expressionRight = new LiteralImpl("B", null);
+  public void binaryExpression() throws Exception {
+    Expression expressionLeft = new LiteralImpl("2", odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Byte));
+    Expression expressionRight = new LiteralImpl("-1", odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.SByte));
 
-    BinaryImpl expression = new BinaryImpl(expressionLeft, BinaryOperatorKind.SUB, expressionRight);
+    BinaryImpl expression = new BinaryImpl(expressionLeft, BinaryOperatorKind.SUB, expressionRight,
+        odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Byte));
 
     assertEquals(expressionLeft, expression.getLeftOperand());
     assertEquals(expressionRight, expression.getRightOperand());
     assertEquals(BinaryOperatorKind.SUB, expression.getOperator());
+    assertEquals(odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Byte), expression.getType());
 
     String output = expression.accept(new FilterTreeToText());
-    assertEquals("<<A> sub <B>>", output);
+    assertEquals("<<2> sub <-1>>", output);
   }
 
   @Test
-  public void enumerationExpression() throws ExpressionVisitException, ODataApplicationException {
+  public void enumerationExpression() throws Exception {
     EdmEnumType type = edm.getEnumType(EnumTypeProvider.nameENString);
     assertNotNull(type);
-    EnumerationImpl expression = new EnumerationImpl(type, Arrays.asList("A", "B"));
+    EnumerationImpl expression = new EnumerationImpl(type, Arrays.asList("String1", "String2"));
     assertEquals(type, expression.getType());
-    assertEquals("A", expression.getValues().get(0));
-    assertEquals("B", expression.getValues().get(1));
-    assertEquals("<olingo.odata.test1.ENString<A,B>>", expression.accept(new FilterTreeToText()));
+    assertEquals("String1", expression.getValues().get(0));
+    assertEquals("String2", expression.getValues().get(1));
+    assertEquals("<olingo.odata.test1.ENString<String1,String2>>", expression.accept(new FilterTreeToText()));
   }
 
   @Test
-  public void lambdaRefExpression() throws ExpressionVisitException, ODataApplicationException {
+  public void lambdaRefExpression() throws Exception {
     LambdaRefImpl expression = new LambdaRefImpl("A");
     assertEquals("A", expression.getVariableName());
     assertEquals("<A>", expression.accept(new FilterTreeToText()));
   }
 
   @Test
-  public void literalExpression() throws ExpressionVisitException, ODataApplicationException {
-    LiteralImpl expression = new LiteralImpl("A", null);
-    assertEquals("A", expression.getText());
-    assertEquals("<A>", expression.accept(new FilterTreeToText()));
+  public void literalExpression() throws Exception {
+    LiteralImpl expression = new LiteralImpl("'A'", odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String));
+    assertEquals("'A'", expression.getText());
+    assertEquals("<'A'>", expression.accept(new FilterTreeToText()));
+    assertEquals(odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String), expression.getType());
   }
 
   @Test
-  public void memberExpression() throws ExpressionVisitException, ODataApplicationException {
+  public void memberExpression() throws Exception {
     EdmEntityType entityType = edm.getEntityType(EntityTypeProvider.nameETKeyNav);
 
     // UriResourceImpl
@@ -189,20 +192,21 @@ public class ExpressionTest {
   }
 
   @Test
-  public void methodCallExpression() throws ExpressionVisitException, ODataApplicationException {
-    Expression p0 = new LiteralImpl("A", null);
-    Expression p1 = new LiteralImpl("B", null);
+  public void methodCallExpression() throws Exception {
+    Expression p0 = new LiteralImpl("'A'", odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String));
+    Expression p1 = new LiteralImpl("'B'", odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String));
     MethodImpl expression = new MethodImpl(MethodKind.CONCAT, Arrays.asList(p0, p1));
 
     assertEquals(MethodKind.CONCAT, expression.getMethod());
-    assertEquals("<concat(<A>,<B>)>", expression.accept(new FilterTreeToText()));
+    assertEquals("<concat(<'A'>,<'B'>)>", expression.accept(new FilterTreeToText()));
+    assertEquals(odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String), expression.getType());
 
     assertEquals(p0, expression.getParameters().get(0));
     assertEquals(p1, expression.getParameters().get(1));
   }
 
   @Test
-  public void typeLiteralExpression() throws ExpressionVisitException, ODataApplicationException {
+  public void typeLiteralExpression() throws Exception {
     EdmEntityType entityBaseType = edm.getEntityType(EntityTypeProvider.nameETBaseTwoKeyNav);
     TypeLiteralImpl expression = new TypeLiteralImpl(entityBaseType);
 
@@ -211,13 +215,15 @@ public class ExpressionTest {
   }
 
   @Test
-  public void unaryExpression() throws ExpressionVisitException, ODataApplicationException {
-    Expression operand = new LiteralImpl("A", null);
-    UnaryImpl expression = new UnaryImpl(UnaryOperatorKind.MINUS, operand);
+  public void unaryExpression() throws Exception {
+    Expression operand = new LiteralImpl("1.2", odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Decimal));
+    UnaryImpl expression = new UnaryImpl(UnaryOperatorKind.MINUS, operand,
+        odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Decimal));
 
     assertEquals(UnaryOperatorKind.MINUS, expression.getOperator());
     assertEquals(operand, expression.getOperand());
+    assertEquals(odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Decimal), expression.getType());
 
-    assertEquals("<- <A>>", expression.accept(new FilterTreeToText()));
+    assertEquals("<- <1.2>>", expression.accept(new FilterTreeToText()));
   }
 }