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

[13/30] olingo-odata4 git commit: [OLINGO-834] ExpressionParser uses UriTokenizer

[OLINGO-834] ExpressionParser uses UriTokenizer

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/2f3bc286
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/2f3bc286
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/2f3bc286

Branch: refs/heads/master
Commit: 2f3bc2866befb2f8f75a81ecb4952950dc3913df
Parents: 208f26c
Author: Klaus Straubinger <kl...@sap.com>
Authored: Fri Dec 11 16:48:38 2015 +0100
Committer: Christian Amend <ch...@sap.com>
Committed: Mon Dec 14 10:12:22 2015 +0100

----------------------------------------------------------------------
 .../core/uri/parser/ExpressionParser.java       | 499 +++++++------------
 .../olingo/server/core/uri/parser/Parser.java   |   8 +
 .../server/core/uri/parser/ParserHelper.java    |  65 +++
 .../core/uri/parser/ResourcePathParser.java     | 130 +++--
 .../server/core/uri/parser/SelectParser.java    |  15 +-
 .../server/core/uri/parser/UriTokenizer.java    | 291 ++++++++++-
 .../uri/queryoption/expression/UnaryImpl.java   |   2 +-
 .../core/uri/parser/ExpressionParserTest.java   | 174 +++----
 .../core/uri/parser/UriTokenizerTest.java       | 313 +++++++-----
 9 files changed, 841 insertions(+), 656 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2f3bc286/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 854536d..3b04089 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
@@ -32,6 +32,7 @@ import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
 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.UnaryOperatorKind;
+import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
 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.LiteralImpl;
@@ -39,75 +40,73 @@ import org.apache.olingo.server.core.uri.queryoption.expression.MethodImpl;
 import org.apache.olingo.server.core.uri.queryoption.expression.UnaryImpl;
 
 public class ExpressionParser {
-  private Tokenizer tokenizer;
-
   private static final Map<TokenKind, BinaryOperatorKind> tokenToBinaryOperator;
   static {
-    Map<TokenKind, BinaryOperatorKind> temp = new HashMap<ExpressionParser.TokenKind, BinaryOperatorKind>();
-    temp.put(TokenKind.OR_OP, BinaryOperatorKind.OR);
-    temp.put(TokenKind.AND_OP, BinaryOperatorKind.AND);
+    Map<TokenKind, BinaryOperatorKind> temp = new HashMap<TokenKind, BinaryOperatorKind>();
+    temp.put(TokenKind.OrOperator, BinaryOperatorKind.OR);
+    temp.put(TokenKind.AndOperator, BinaryOperatorKind.AND);
 
-    temp.put(TokenKind.EQ_OP, BinaryOperatorKind.EQ);
-    temp.put(TokenKind.NE_OP, BinaryOperatorKind.NE);
+    temp.put(TokenKind.EqualsOperator, BinaryOperatorKind.EQ);
+    temp.put(TokenKind.NotEqualsOperator, 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.GreaterThanOperator, BinaryOperatorKind.GT);
+    temp.put(TokenKind.GreaterThanOrEqualsOperator, BinaryOperatorKind.GE);
+    temp.put(TokenKind.LessThanOperator, BinaryOperatorKind.LT);
+    temp.put(TokenKind.LessThanOrEqualsOperator, BinaryOperatorKind.LE);
 
-    temp.put(TokenKind.ADD_OP, BinaryOperatorKind.ADD);
-    temp.put(TokenKind.SUB_OP, BinaryOperatorKind.SUB);
+    temp.put(TokenKind.AddOperator, BinaryOperatorKind.ADD);
+    temp.put(TokenKind.SubOperator, BinaryOperatorKind.SUB);
 
-    temp.put(TokenKind.MUL_OP, BinaryOperatorKind.MUL);
-    temp.put(TokenKind.DIV_OP, BinaryOperatorKind.DIV);
-    temp.put(TokenKind.MOD_OP, BinaryOperatorKind.MOD);
+    temp.put(TokenKind.MulOperator, BinaryOperatorKind.MUL);
+    temp.put(TokenKind.DivOperator, BinaryOperatorKind.DIV);
+    temp.put(TokenKind.ModOperator, BinaryOperatorKind.MOD);
 
     tokenToBinaryOperator = Collections.unmodifiableMap(temp);
   }
 
   private static final Map<TokenKind, UnaryOperatorKind> tokenToUnaryOperator;
   static {
-    Map<TokenKind, UnaryOperatorKind> temp = new HashMap<ExpressionParser.TokenKind, UnaryOperatorKind>();
+    Map<TokenKind, UnaryOperatorKind> temp = new HashMap<TokenKind, UnaryOperatorKind>();
     temp.put(TokenKind.MINUS, UnaryOperatorKind.MINUS);
-    temp.put(TokenKind.NOT, UnaryOperatorKind.NOT);
+    temp.put(TokenKind.NotOperator, UnaryOperatorKind.NOT);
     tokenToUnaryOperator = Collections.unmodifiableMap(temp);
   }
 
   private static final Map<TokenKind, MethodKind> tokenToMethod;
   static {
-    Map<TokenKind, MethodKind> temp = new HashMap<ExpressionParser.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);
+    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);
+    temp.put(TokenKind.DateMethod, MethodKind.DATE);
+    temp.put(TokenKind.DayMethod, MethodKind.DAY);
+    temp.put(TokenKind.EndswithMethod, MethodKind.ENDSWITH);
+    temp.put(TokenKind.FloorMethod, MethodKind.FLOOR);
+    temp.put(TokenKind.FractionalsecondsMethod, MethodKind.FRACTIONALSECONDS);
+    temp.put(TokenKind.GeoDistanceMethod, MethodKind.GEODISTANCE);
+    temp.put(TokenKind.GeoIntersectsMethod, MethodKind.GEOINTERSECTS);
+    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);
+    temp.put(TokenKind.MinuteMethod, MethodKind.MINUTE);
+    temp.put(TokenKind.MonthMethod, MethodKind.MONTH);
+    temp.put(TokenKind.NowMethod, MethodKind.NOW);
+    temp.put(TokenKind.RoundMethod, MethodKind.ROUND);
+    temp.put(TokenKind.SecondMethod, MethodKind.SECOND);
+    temp.put(TokenKind.StartswithMethod, MethodKind.STARTSWITH);
+    temp.put(TokenKind.SubstringMethod, MethodKind.SUBSTRING);
+    temp.put(TokenKind.TimeMethod, MethodKind.TIME);
+    temp.put(TokenKind.TolowerMethod, MethodKind.TOLOWER);
+    temp.put(TokenKind.TotaloffsetminutesMethod, MethodKind.TOTALOFFSETMINUTES);
+    temp.put(TokenKind.TotalsecondsMethod, MethodKind.TOTALSECONDS);
+    temp.put(TokenKind.ToupperMethod, MethodKind.TOUPPER);
+    temp.put(TokenKind.TrimMethod, MethodKind.TRIM);
+    temp.put(TokenKind.YearMethod, MethodKind.YEAR);
 
     tokenToMethod = Collections.unmodifiableMap(temp);
   }
@@ -115,24 +114,26 @@ public class ExpressionParser {
   private static final Map<TokenKind, EdmPrimitiveTypeKind> tokenToPrimitiveType;
   static {
     /* Enum and null are not present in the map. These have to be handled differently */
-    Map<TokenKind, EdmPrimitiveTypeKind> temp = new HashMap<ExpressionParser.TokenKind, EdmPrimitiveTypeKind>();
-    temp.put(TokenKind.PrimitiveBooleanValue, EdmPrimitiveTypeKind.Boolean);
-    temp.put(TokenKind.PrimitiveStringValue, EdmPrimitiveTypeKind.String);
+    Map<TokenKind, EdmPrimitiveTypeKind> temp = new HashMap<TokenKind, EdmPrimitiveTypeKind>();
+    temp.put(TokenKind.BooleanValue, EdmPrimitiveTypeKind.Boolean);
+    temp.put(TokenKind.StringValue, 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);
+    temp.put(TokenKind.IntegerValue, EdmPrimitiveTypeKind.Int64);
+    temp.put(TokenKind.GuidValue, EdmPrimitiveTypeKind.Guid);
+    temp.put(TokenKind.DateValue, EdmPrimitiveTypeKind.Date);
+    temp.put(TokenKind.DateTimeOffsetValue, EdmPrimitiveTypeKind.DateTimeOffset);
+    temp.put(TokenKind.TimeOfDayValue, EdmPrimitiveTypeKind.TimeOfDay);
+    temp.put(TokenKind.DecimalValue, EdmPrimitiveTypeKind.Decimal);
+    temp.put(TokenKind.DoubleValue, EdmPrimitiveTypeKind.Double);
+    temp.put(TokenKind.DurationValue, EdmPrimitiveTypeKind.Duration);
+    temp.put(TokenKind.BinaryValue, EdmPrimitiveTypeKind.Binary);
 
     tokenToPrimitiveType = Collections.unmodifiableMap(temp);
   }
 
-  public Expression parse(Tokenizer tokenizer) throws UriParserException {
+  private UriTokenizer tokenizer;
+
+  public Expression parse(UriTokenizer tokenizer) throws UriParserException {
     // Initialize tokenizer.
     this.tokenizer = tokenizer;
 
@@ -141,23 +142,17 @@ public class ExpressionParser {
 
   private Expression parseExpression() throws UriParserException {
     Expression left = parseAnd();
-
-    while (is(TokenKind.OR_OP) != null) {
-      tokenizer.getText();
-
-      Expression right = parseAnd();
+    while (tokenizer.next(TokenKind.OrOperator)) {
+      final Expression right = parseAnd();
       left = new BinaryImpl(left, BinaryOperatorKind.OR, right);
     }
-
     return left;
   }
 
   private Expression parseAnd() throws UriParserException {
     Expression left = parseExprEquality();
-    while (is(TokenKind.AND_OP) != null) {
-      tokenizer.getText();
-
-      Expression right = parseExprEquality();
+    while (tokenizer.next(TokenKind.AndOperator)) {
+      final Expression right = parseExprEquality();
       left = new BinaryImpl(left, BinaryOperatorKind.AND, right);
     }
     return left;
@@ -165,110 +160,107 @@ public class ExpressionParser {
 
   private Expression parseExprEquality() throws UriParserException {
     Expression left = parseExprRel();
-
-    TokenKind nextTokenKind = is(TokenKind.EQ_OP, TokenKind.NE_OP);
+    TokenKind operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.EqualsOperator, TokenKind.NotEqualsOperator);
     // 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);
+    while (operatorTokenKind != null) {
+      final Expression right = parseExprEquality();
+      left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right);
+      operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.EqualsOperator, TokenKind.NotEqualsOperator);
     }
-
     return left;
   }
 
   private Expression parseExprRel() throws UriParserException {
     Expression left = parseExprAdd();
-
-    TokenKind nextTokenKind = is(TokenKind.GT_OP, TokenKind.GE_OP, TokenKind.LT_OP, TokenKind.LE_OP);
+    TokenKind operatorTokenKind = ParserHelper.next(tokenizer,
+        TokenKind.GreaterThanOperator, TokenKind.GreaterThanOrEqualsOperator,
+        TokenKind.LessThanOperator, TokenKind.LessThanOrEqualsOperator);
     // 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);
+    while (operatorTokenKind != null) {
+      final Expression right = parseExprAdd();
+      left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right);
+      operatorTokenKind = ParserHelper.next(tokenizer,
+          TokenKind.GreaterThanOperator, TokenKind.GreaterThanOrEqualsOperator,
+          TokenKind.LessThanOperator, TokenKind.LessThanOrEqualsOperator);
     }
-
     return left;
   }
 
   private Expression parseExprAdd() throws UriParserException {
     Expression left = parseExprMul();
-
-    TokenKind nextTokenKind = is(TokenKind.ADD_OP, TokenKind.SUB_OP);
+    TokenKind operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.AddOperator, TokenKind.SubOperator);
     // 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);
+    while (operatorTokenKind != null) {
+      final Expression right = parseExprMul();
+      left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right);
+      operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.AddOperator, TokenKind.SubOperator);
     }
-
     return left;
   }
 
   private Expression parseExprMul() throws UriParserException {
     Expression left = parseExprUnary();
-
-    TokenKind nextTokenKind = is(TokenKind.MUL_OP, TokenKind.DIV_OP, TokenKind.MOD_OP);
+    TokenKind operatorTokenKind = ParserHelper.next(tokenizer,
+        TokenKind.MulOperator, TokenKind.DivOperator, TokenKind.ModOperator);
     // 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);
+    while (operatorTokenKind != null) {
+      final Expression right = parseExprUnary();
+      left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right);
+      operatorTokenKind = ParserHelper.next(tokenizer,
+          TokenKind.MulOperator, TokenKind.DivOperator, TokenKind.ModOperator);
     }
-
     return left;
   }
 
   private Expression parseExprUnary() throws UriParserException {
     Expression left = null;
-    TokenKind nextTokenKind = is(TokenKind.MINUS, TokenKind.NOT);
+    TokenKind operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.MINUS, TokenKind.NotOperator);
     // 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);
+    while (operatorTokenKind != null) {
+      final Expression expression = parseExprValue();
+      left = new UnaryImpl(tokenToUnaryOperator.get(operatorTokenKind), expression);
+      operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.MINUS, TokenKind.NotOperator);
     }
-
     if (left == null) {
       left = parseExprValue();
     }
-
     return left;
   }
 
   private Expression parseExprValue() throws UriParserException {
-    if (is(TokenKind.OPEN) != null) {
-      tokenizer.getText();
-      Expression exp = parseExpression();
-      require(TokenKind.CLOSE);
-      return exp;
+    if (tokenizer.next(TokenKind.OPEN)) {
+      final Expression expression = parseExpression();
+      ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
+      return expression;
     }
 
-    if (is(TokenKind.ParameterAlias) != null) {
+    if (tokenizer.next(TokenKind.ParameterAliasName)) {
       return new AliasImpl(tokenizer.getText());
     }
 
-    if (is(TokenKind.RootExpr) != null) {
-      tokenizer.getText();
-      // TODO: Consume $root Expression.
+    if (tokenizer.next(TokenKind.jsonArrayOrObject)) {
+      // TODO: Can the type be determined?
+      return new LiteralImpl(tokenizer.getText(), null);
     }
 
-    TokenKind nextPrimitive = isPrimitive();
+    if (tokenizer.next(TokenKind.ROOT)) {
+      // TODO: Consume $root expression.
+    }
+
+    if (tokenizer.next(TokenKind.IT)) {
+      // TODO: Consume $it expression.
+    }
+
+    if (tokenizer.next(TokenKind.QualifiedName)) {
+      // TODO: Consume typecast or bound-function expression.
+    }
+
+    TokenKind nextPrimitive = ParserHelper.nextPrimitive(tokenizer);
     if (nextPrimitive != null) {
-      EdmPrimitiveTypeKind primitiveTypeKind = tokenToPrimitiveType.get(nextPrimitive);
+      final EdmPrimitiveTypeKind primitiveTypeKind = tokenToPrimitiveType.get(nextPrimitive);
       EdmPrimitiveType type;
       if (primitiveTypeKind == null) {
-        if (nextPrimitive == TokenKind.PrimitiveEnumValue) {
+        if (nextPrimitive == TokenKind.EnumValue) {
           // TODO: Get enum type.
           type = null;
         } else {
@@ -281,22 +273,16 @@ public class ExpressionParser {
       return new LiteralImpl(tokenizer.getText(), type);
     }
 
-    TokenKind nextMethod = isMethod();
+    TokenKind nextMethod = nextMethod();
     if (nextMethod != null) {
       MethodKind methodKind = tokenToMethod.get(nextMethod);
       List<Expression> parameters = new ArrayList<Expression>();
-      // Consume Method name.
-      tokenizer.getText();
-      if (is(TokenKind.CLOSE) != null) {
-        // Consume closing parenthesis.
-        tokenizer.getText();
-      } else {
-        parameters.add(parseExpression());
-        while (is(TokenKind.COMMA) != null) {
-          tokenizer.getText();
+      // The method token text includes the opening parenthesis!
+      if (!tokenizer.next(TokenKind.CLOSE)) {
+        do {
           parameters.add(parseExpression());
-        }
-        require(TokenKind.CLOSE);
+        } while (tokenizer.next(TokenKind.COMMA));
+        ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
       }
 
       MethodImpl methodImpl = new MethodImpl(methodKind, parameters);
@@ -305,6 +291,10 @@ public class ExpressionParser {
       return methodImpl;
     }
 
+    if (tokenizer.next(TokenKind.ODataIdentifier)) {
+      // TODO: Consume property-path or lambda-variable expression.
+    }
+
     throw new UriParserSyntaxException("Unexpected token", UriParserSyntaxException.MessageKeys.SYNTAX);
   }
 
@@ -356,7 +346,7 @@ public class ExpressionParser {
     case NOW:
     case MAXDATETIME:
     case MINDATETIME:
-      if (size != 0) {
+      if (size > 0) {
         throw new UriParserSemanticException("The method '" + method.getMethod() + "' must have no parameters.",
             null); // TODO: message key
       }
@@ -384,192 +374,39 @@ public class ExpressionParser {
     }
   }
 
-  private String require(TokenKind required) throws UriParserException {
-    if (is(required) == null) {
-      throw new UriParserSyntaxException("Required token: " + required,
-          UriParserSyntaxException.MessageKeys.SYNTAX);
-    }
-    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;
-    }
+  private TokenKind nextMethod() {
+    return ParserHelper.next(tokenizer,
+        TokenKind.CastMethod,
+        TokenKind.CeilingMethod,
+        TokenKind.ConcatMethod,
+        TokenKind.ContainsMethod,
+        TokenKind.DateMethod,
+        TokenKind.DayMethod,
+        TokenKind.EndswithMethod,
+        TokenKind.FloorMethod,
+        TokenKind.FractionalsecondsMethod,
+        TokenKind.GeoDistanceMethod,
+        TokenKind.GeoIntersectsMethod,
+        TokenKind.GeoLengthMethod,
+        TokenKind.HourMethod,
+        TokenKind.IndexofMethod,
+        TokenKind.IsofMethod,
+        TokenKind.LengthMethod,
+        TokenKind.MaxdatetimeMethod,
+        TokenKind.MindatetimeMethod,
+        TokenKind.MinuteMethod,
+        TokenKind.MonthMethod,
+        TokenKind.NowMethod,
+        TokenKind.RoundMethod,
+        TokenKind.SecondMethod,
+        TokenKind.StartswithMethod,
+        TokenKind.SubstringMethod,
+        TokenKind.TimeMethod,
+        TokenKind.TolowerMethod,
+        TokenKind.TotaloffsetminutesMethod,
+        TokenKind.TotalsecondsMethod,
+        TokenKind.ToupperMethod,
+        TokenKind.TrimMethod,
+        TokenKind.YearMethod);
   }
-
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2f3bc286/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java
index 0b53e69..2a994d3 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java
@@ -208,6 +208,14 @@ public class Parser {
                 (UriParserException) e.getCause() :
                 new UriParserSyntaxException("Syntax error", e, UriParserSyntaxException.MessageKeys.SYNTAX);
           }
+//          UriTokenizer filterTokenizer = new UriTokenizer(optionValue);
+//          systemOption = new FilterOptionImpl().setExpression(
+//              new ExpressionParser().parse(filterTokenizer));
+//          if (!filterTokenizer.next(TokenKind.EOF)) {
+//            throw new UriParserSyntaxException("Illegal value of $filter option!",
+//                UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
+//                optionName, optionValue);
+//          }
 
         } else if (optionName.equals(SystemQueryOptionKind.FORMAT.toString())) {
           FormatOptionImpl formatOption = new FormatOptionImpl();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2f3bc286/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ParserHelper.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ParserHelper.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ParserHelper.java
new file mode 100644
index 0000000..e811575
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ParserHelper.java
@@ -0,0 +1,65 @@
+/*
+ * 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.parser;
+
+import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
+
+public class ParserHelper {
+
+  public static void requireNext(UriTokenizer tokenizer, final TokenKind required) throws UriParserException {
+    if (!tokenizer.next(required)) {
+      throw new UriParserSyntaxException("Expected token '" + required.toString() + "' not found.",
+          UriParserSyntaxException.MessageKeys.SYNTAX);
+    }
+  }
+
+  public static void requireTokenEnd(UriTokenizer tokenizer) throws UriParserException {
+    requireNext(tokenizer, TokenKind.EOF);
+  }
+
+  public static TokenKind next(UriTokenizer tokenizer, final TokenKind... kind) {
+    for (int i = 0; i < kind.length; i++) {
+      if (tokenizer.next(kind[i])) {
+        return kind[i];
+      }
+    }
+    return null;
+  }
+
+  public static TokenKind nextPrimitive(UriTokenizer tokenizer) {
+    return next(tokenizer,
+        TokenKind.NULL,
+        TokenKind.BooleanValue,
+        TokenKind.StringValue,
+
+        // 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.DoubleValue,
+        TokenKind.DecimalValue,
+        TokenKind.GuidValue,
+        TokenKind.DateTimeOffsetValue,
+        TokenKind.DateValue,
+        TokenKind.TimeOfDayValue,
+        TokenKind.IntegerValue,
+
+        TokenKind.DurationValue,
+        TokenKind.BinaryValue,
+        TokenKind.EnumValue);
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2f3bc286/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.java
index 9852011..5d2fbde 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.java
@@ -115,9 +115,9 @@ public class ResourcePathParser {
   public UriInfoImpl parseDollarEntityTypeCast(final String pathSegment) throws UriParserException {
     UriInfoImpl uriInfo = new UriInfoImpl().setKind(UriInfoKind.entityId);
     tokenizer = new UriTokenizer(pathSegment);
-    requireNext(TokenKind.QualifiedName);
+    ParserHelper.requireNext(tokenizer, TokenKind.QualifiedName);
     final String name = tokenizer.getText();
-    requireTokenEnd();
+    ParserHelper.requireTokenEnd(tokenizer);
     final EdmEntityType type = edm.getEntityType(new FullQualifiedName(name));
     if (type == null) {
       throw new UriParserSemanticException("Type '" + name + "' not found.",
@@ -131,11 +131,11 @@ public class ResourcePathParser {
   public UriInfoImpl parseCrossjoinSegment(final String pathSegment) throws UriParserException {
     UriInfoImpl uriInfo = new UriInfoImpl().setKind(UriInfoKind.crossjoin);
     tokenizer = new UriTokenizer(pathSegment);
-    requireNext(TokenKind.CROSSJOIN);
-    requireNext(TokenKind.OPEN);
+    ParserHelper.requireNext(tokenizer, TokenKind.CROSSJOIN);
+    ParserHelper.requireNext(tokenizer, TokenKind.OPEN);
     // At least one entity-set name is mandatory.  Try to fetch all.
     do {
-      requireNext(TokenKind.ODataIdentifier);
+      ParserHelper.requireNext(tokenizer, TokenKind.ODataIdentifier);
       final String name = tokenizer.getText();
       final EdmEntitySet edmEntitySet = edmEntityContainer.getEntitySet(name);
       if (edmEntitySet == null) {
@@ -145,13 +145,13 @@ public class ResourcePathParser {
         uriInfo.addEntitySetName(name);
       }
     } while (tokenizer.next(TokenKind.COMMA));
-    requireNext(TokenKind.CLOSE);
-    requireTokenEnd();
+    ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
+    ParserHelper.requireTokenEnd(tokenizer);
     return uriInfo;
   }
 
   private UriResource ref(final UriResource previous) throws UriParserException {
-    requireTokenEnd();
+    ParserHelper.requireTokenEnd(tokenizer);
     requireTyped(previous, "$ref");
     if (((UriResourcePartTyped) previous).getType() instanceof EdmEntityType) {
       return new UriResourceRefImpl();
@@ -162,7 +162,7 @@ public class ResourcePathParser {
   }
 
   private UriResource value(final UriResource previous) throws UriParserException {
-    requireTokenEnd();
+    ParserHelper.requireTokenEnd(tokenizer);
     requireTyped(previous, "$value");
     if (!((UriResourcePartTyped) previous).isCollection()) {
       return new UriResourceValueImpl();
@@ -173,7 +173,7 @@ public class ResourcePathParser {
   }
 
   private UriResource count(final UriResource previous) throws UriParserException {
-    requireTokenEnd();
+    ParserHelper.requireTokenEnd(tokenizer);
     requireTyped(previous, "$count");
     if (((UriResourcePartTyped) previous).isCollection()) {
       return new UriResourceCountImpl();
@@ -195,19 +195,19 @@ public class ResourcePathParser {
         entitySetResource.setKeyPredicates(keyPredicates);
       }
 
-      requireTokenEnd();
+      ParserHelper.requireTokenEnd(tokenizer);
       return entitySetResource;
     }
 
     final EdmSingleton edmSingleton = edmEntityContainer.getSingleton(oDataIdentifier);
     if (edmSingleton != null) {
-      requireTokenEnd();
+      ParserHelper.requireTokenEnd(tokenizer);
       return new UriResourceSingletonImpl().setSingleton(edmSingleton);
     }
 
     final EdmActionImport edmActionImport = edmEntityContainer.getActionImport(oDataIdentifier);
     if (edmActionImport != null) {
-      requireTokenEnd();
+      ParserHelper.requireTokenEnd(tokenizer);
       return new UriResourceActionImpl().setActionImport(edmActionImport);
     }
 
@@ -271,7 +271,7 @@ public class ResourcePathParser {
             UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED);
       }
     }
-    requireTokenEnd();
+    ParserHelper.requireTokenEnd(tokenizer);
     return new UriResourceNavigationPropertyImpl()
         .setNavigationProperty(navigationProperty)
         .setKeyPredicates(keyPredicate);
@@ -288,7 +288,7 @@ public class ResourcePathParser {
           previousType.getFullQualifiedName(),
           previousTyped.isCollection());
       if (boundAction != null) {
-        requireTokenEnd();
+        ParserHelper.requireTokenEnd(tokenizer);
         return new UriResourceActionImpl().setAction(boundAction);
       }
       EdmStructuredType type = edm.getEntityType(name);
@@ -386,7 +386,7 @@ public class ResourcePathParser {
         edmProperty == null ? null : (EdmPrimitiveType) edmProperty.getType(),
         edmProperty == null ? false : edmProperty.isNullable())) {
       final String literalValue = tokenizer.getText();
-      requireNext(TokenKind.CLOSE);
+      ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
       return createUriParameter(edmProperty, edmKeyPropertyRef.getName(), literalValue);
     } else {
       throw new UriParserSemanticException("The key value is not valid.",
@@ -424,10 +424,10 @@ public class ResourcePathParser {
       parameterNames.add(keyPredicateName);
       hasComma = tokenizer.next(TokenKind.COMMA);
       if (hasComma) {
-        requireNext(TokenKind.ODataIdentifier);
+        ParserHelper.requireNext(tokenizer, TokenKind.ODataIdentifier);
       }
     } while (hasComma);
-    requireNext(TokenKind.CLOSE);
+    ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
 
     return parameters;
   }
@@ -440,7 +440,7 @@ public class ResourcePathParser {
       throw new UriValidationException(keyPredicateName + " is not a valid key property name.",
           UriValidationException.MessageKeys.INVALID_KEY_PROPERTY, keyPredicateName);
     }
-    requireNext(TokenKind.EQ);
+    ParserHelper.requireNext(tokenizer, TokenKind.EQ);
     if (tokenizer.next(TokenKind.COMMA) || tokenizer.next(TokenKind.CLOSE) || tokenizer.next(TokenKind.EOF)) {
       throw new UriParserSyntaxException("Key value expected.", UriParserSyntaxException.MessageKeys.SYNTAX);
     }
@@ -512,7 +512,7 @@ public class ResourcePathParser {
         }
         ((UriResourceTypedImpl) previousTyped).setTypeFilter(type);
       }
-      requireTokenEnd();
+      ParserHelper.requireTokenEnd(tokenizer);
       return null;
     } else {
       throw new UriParserSemanticException(
@@ -572,53 +572,42 @@ public class ResourcePathParser {
             UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED);
       }
     }
-    requireTokenEnd();
+    ParserHelper.requireTokenEnd(tokenizer);
     return resource;
   }
 
   private List<UriParameter> functionParameters() throws UriParserException {
     List<UriParameter> parameters = new ArrayList<UriParameter>();
-    requireNext(TokenKind.OPEN);
+    ParserHelper.requireNext(tokenizer, TokenKind.OPEN);
     if (tokenizer.next(TokenKind.CLOSE)) {
       return parameters;
     }
     do {
-      requireNext(TokenKind.ODataIdentifier);
+      ParserHelper.requireNext(tokenizer, TokenKind.ODataIdentifier);
       final String name = tokenizer.getText();
       if (parameters.contains(name)) {
         throw new UriParserSemanticException("Duplicated function parameter " + name,
             UriParserSemanticException.MessageKeys.INVALID_KEY_VALUE, name);
       }
-      requireNext(TokenKind.EQ);
+      ParserHelper.requireNext(tokenizer, TokenKind.EQ);
       if (tokenizer.next(TokenKind.COMMA) || tokenizer.next(TokenKind.CLOSE) || tokenizer.next(TokenKind.EOF)) {
         throw new UriParserSyntaxException("Parameter value expected.", UriParserSyntaxException.MessageKeys.SYNTAX);
       }
-      if (nextPrimitiveValue()) {
+      if (tokenizer.next(TokenKind.ParameterAliasName)) {
+        parameters.add(new UriParameterImpl().setName(name).setAlias(tokenizer.getText()));
+      } else if (nextPrimitiveValue()) {
         final String literalValue = tokenizer.getText();
-        UriParameterImpl parameter = new UriParameterImpl().setName(name);
-        parameters.add(literalValue.startsWith("@") ?
-            parameter.setAlias(literalValue) :
-            parameter.setText("null".equals(literalValue) ? null : literalValue));
+        parameters.add(new UriParameterImpl().setName(name)
+            .setText("null".equals(literalValue) ? null : literalValue));
       } else {
         throw new UriParserSemanticException("Wrong parameter value.",
             UriParserSemanticException.MessageKeys.INVALID_KEY_VALUE, "");
       }
     } while (tokenizer.next(TokenKind.COMMA));
-    requireNext(TokenKind.CLOSE);
+    ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
     return parameters;
   }
 
-  private void requireNext(final TokenKind kind) throws UriParserException {
-    if (!tokenizer.next(kind)) {
-      throw new UriParserSyntaxException("Expected token '" + kind.toString() + "' not found.",
-          UriParserSyntaxException.MessageKeys.SYNTAX);
-    }
-  }
-
-  private void requireTokenEnd() throws UriParserException {
-    requireNext(TokenKind.EOF);
-  }
-
   private boolean nextPrimitiveTypeValue(final EdmPrimitiveType primitiveType, final boolean nullable) {
     final EdmPrimitiveType type = primitiveType instanceof EdmTypeDefinition ?
         ((EdmTypeDefinition) primitiveType).getUnderlyingType() :
@@ -629,64 +618,63 @@ public class ResourcePathParser {
       return true;
 
     } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean).equals(type)) {
-      return tokenizer.next(TokenKind.PrimitiveBooleanValue);
+      return tokenizer.next(TokenKind.BooleanValue);
     } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String).equals(type)) {
-      return tokenizer.next(TokenKind.PrimitiveStringValue);
+      return tokenizer.next(TokenKind.StringValue);
     } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.SByte).equals(type)
         || odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Byte).equals(type)
         || odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Int16).equals(type)
         || odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Int32).equals(type)
         || odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Int64).equals(type)) {
-      return tokenizer.next(TokenKind.PrimitiveIntegerValue);
+      return tokenizer.next(TokenKind.IntegerValue);
     } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Guid).equals(type)) {
-      return tokenizer.next(TokenKind.PrimitiveGuidValue);
+      return tokenizer.next(TokenKind.GuidValue);
     } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Date).equals(type)) {
-      return tokenizer.next(TokenKind.PrimitiveDateValue);
+      return tokenizer.next(TokenKind.DateValue);
     } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.DateTimeOffset).equals(type)) {
-      return tokenizer.next(TokenKind.PrimitiveDateTimeOffsetValue);
+      return tokenizer.next(TokenKind.DateTimeOffsetValue);
     } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.TimeOfDay).equals(type)) {
-      return tokenizer.next(TokenKind.PrimitiveTimeOfDayValue);
+      return tokenizer.next(TokenKind.TimeOfDayValue);
     } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Decimal).equals(type)) {
       // The order is important.
       // A decimal value should not be parsed as integer and let the tokenizer stop at the decimal point.
-      return tokenizer.next(TokenKind.PrimitiveDecimalValue)
-          || tokenizer.next(TokenKind.PrimitiveIntegerValue);
+      return tokenizer.next(TokenKind.DecimalValue)
+          || tokenizer.next(TokenKind.IntegerValue);
     } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Double).equals(type)) {
       // The order is important.
       // A floating-point value should not be parsed as decimal and let the tokenizer stop at 'E'.
       // A decimal value should not be parsed as integer and let the tokenizer stop at the decimal point.
-      return tokenizer.next(TokenKind.PrimitiveDoubleValue)
-          || tokenizer.next(TokenKind.PrimitiveDecimalValue)
-          || tokenizer.next(TokenKind.PrimitiveIntegerValue);
+      return tokenizer.next(TokenKind.DoubleValue)
+          || tokenizer.next(TokenKind.DecimalValue)
+          || tokenizer.next(TokenKind.IntegerValue);
     } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Duration).equals(type)) {
-      return tokenizer.next(TokenKind.PrimitiveDurationValue);
+      return tokenizer.next(TokenKind.DurationValue);
     } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Binary).equals(type)) {
-      return tokenizer.next(TokenKind.PrimitiveBinaryValue);
+      return tokenizer.next(TokenKind.BinaryValue);
     } else if (type.getKind() == EdmTypeKind.ENUM) {
-      return tokenizer.next(TokenKind.PrimitiveEnumValue);
+      return tokenizer.next(TokenKind.EnumValue);
     } else {
       return false;
     }
   }
 
   private boolean nextPrimitiveValue() {
-    return tokenizer.next(TokenKind.ParameterAliasName)
-        || tokenizer.next(TokenKind.NULL)
-        || tokenizer.next(TokenKind.PrimitiveBooleanValue)
-        || tokenizer.next(TokenKind.PrimitiveStringValue)
+    return tokenizer.next(TokenKind.NULL)
+        || tokenizer.next(TokenKind.BooleanValue)
+        || tokenizer.next(TokenKind.StringValue)
 
         // 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 ...).
-        || tokenizer.next(TokenKind.PrimitiveDoubleValue)
-        || tokenizer.next(TokenKind.PrimitiveDecimalValue)
-        || tokenizer.next(TokenKind.PrimitiveGuidValue)
-        || tokenizer.next(TokenKind.PrimitiveDateTimeOffsetValue)
-        || tokenizer.next(TokenKind.PrimitiveDateValue)
-        || tokenizer.next(TokenKind.PrimitiveTimeOfDayValue)
-        || tokenizer.next(TokenKind.PrimitiveIntegerValue)
-
-        || tokenizer.next(TokenKind.PrimitiveDurationValue)
-        || tokenizer.next(TokenKind.PrimitiveBinaryValue)
-        || tokenizer.next(TokenKind.PrimitiveEnumValue);
+        || tokenizer.next(TokenKind.DoubleValue)
+        || tokenizer.next(TokenKind.DecimalValue)
+        || tokenizer.next(TokenKind.GuidValue)
+        || tokenizer.next(TokenKind.DateTimeOffsetValue)
+        || tokenizer.next(TokenKind.DateValue)
+        || tokenizer.next(TokenKind.TimeOfDayValue)
+        || tokenizer.next(TokenKind.IntegerValue)
+
+        || tokenizer.next(TokenKind.DurationValue)
+        || tokenizer.next(TokenKind.BinaryValue)
+        || tokenizer.next(TokenKind.EnumValue);
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2f3bc286/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/SelectParser.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/SelectParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/SelectParser.java
index 3d933d2..b257e68 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/SelectParser.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/SelectParser.java
@@ -92,7 +92,7 @@ public class SelectParser {
           if (type.compatibleTo(referencedType)) {
             item.setTypeFilter(type);
             if (tokenizer.next(TokenKind.SLASH)) {
-              requireNext(tokenizer, TokenKind.ODataIdentifier);
+              ParserHelper.requireNext(tokenizer, TokenKind.ODataIdentifier);
               UriInfoImpl resource = new UriInfoImpl().setKind(UriInfoKind.resource);
               addSelectPath(tokenizer, type, resource);
               item.setResourcePath(resource);
@@ -105,7 +105,7 @@ public class SelectParser {
       }
 
     } else {
-      requireNext(tokenizer, TokenKind.ODataIdentifier);
+      ParserHelper.requireNext(tokenizer, TokenKind.ODataIdentifier);
       // The namespace or its alias could be a single OData identifier.
       final FullQualifiedName allOperationsInSchema = parseAllOperationsInSchema(tokenizer);
       if (allOperationsInSchema != null) {
@@ -167,10 +167,10 @@ public class SelectParser {
     List<String> names = new ArrayList<String>();
     if (tokenizer.next(TokenKind.OPEN)) {
       do {
-        requireNext(tokenizer, TokenKind.ODataIdentifier);
+        ParserHelper.requireNext(tokenizer, TokenKind.ODataIdentifier);
         names.add(tokenizer.getText());
       } while (tokenizer.next(TokenKind.COMMA));
-      requireNext(tokenizer, TokenKind.CLOSE);
+      ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
     }
     return names;
   }
@@ -231,11 +231,4 @@ public class SelectParser {
       }
     }
   }
-
-  private void requireNext(UriTokenizer tokenizer, final TokenKind kind) throws UriParserSyntaxException {
-    if (!tokenizer.next(kind)) {
-      throw new UriParserSyntaxException("Illegal $select expression.",
-          UriParserSyntaxException.MessageKeys.SYNTAX);
-    }
-  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2f3bc286/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 8051573..a40f4ec 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
@@ -24,6 +24,8 @@ package org.apache.olingo.server.core.uri.parser;
  * Since only the index is "moved", backing out while parsing a token is easy and used throughout.
  * There is intentionally no method to push back tokens (although it would be easy to add such a method)
  * because this tokenizer should behave like a classical token-consuming tokenizer.</p>
+ * <p>Whitespace is not an extra token but consumed with the tokens that require whitespace.
+ * Optional whitespace is not supported.</p>
  */
 public class UriTokenizer {
 
@@ -35,6 +37,9 @@ public class UriTokenizer {
     VALUE,
     COUNT,
     CROSSJOIN,
+    ROOT,
+    IT,
+
     OPEN,
     CLOSE,
     COMMA,
@@ -44,6 +49,7 @@ public class UriTokenizer {
     EQ,
     STAR,
     PLUS,
+    MINUS,
     NULL,
 
     // variable-value tokens (convention: mixed case)
@@ -51,20 +57,68 @@ public class UriTokenizer {
     QualifiedName,
     ParameterAliasName,
 
-    PrimitiveBooleanValue,
-    PrimitiveStringValue,
-    PrimitiveIntegerValue,
-    PrimitiveGuidValue,
-    PrimitiveDateValue,
-    PrimitiveDateTimeOffsetValue,
-    PrimitiveTimeOfDayValue,
-    PrimitiveDecimalValue,
-    PrimitiveDoubleValue,
-    PrimitiveDurationValue,
-    PrimitiveBinaryValue,
-    PrimitiveEnumValue,
-
-    jsonArrayOrObject
+    BooleanValue,
+    StringValue,
+    IntegerValue,
+    GuidValue,
+    DateValue,
+    DateTimeOffsetValue,
+    TimeOfDayValue,
+    DecimalValue,
+    DoubleValue,
+    DurationValue,
+    BinaryValue,
+    EnumValue,
+
+    jsonArrayOrObject,
+
+    OrOperator,
+    AndOperator,
+    EqualsOperator,
+    NotEqualsOperator,
+    GreaterThanOperator,
+    GreaterThanOrEqualsOperator,
+    LessThanOperator,
+    LessThanOrEqualsOperator,
+    AddOperator,
+    SubOperator,
+    MulOperator,
+    DivOperator,
+    ModOperator,
+    NotOperator,
+
+    CastMethod,
+    CeilingMethod,
+    ConcatMethod,
+    ContainsMethod,
+    DateMethod,
+    DayMethod,
+    EndswithMethod,
+    FloorMethod,
+    FractionalsecondsMethod,
+    GeoDistanceMethod,
+    GeoIntersectsMethod,
+    GeoLengthMethod,
+    HourMethod,
+    IndexofMethod,
+    IsofMethod,
+    LengthMethod,
+    MaxdatetimeMethod,
+    MindatetimeMethod,
+    MinuteMethod,
+    MonthMethod,
+    NowMethod,
+    RoundMethod,
+    SecondMethod,
+    StartswithMethod,
+    SubstringMethod,
+    TimeMethod,
+    TolowerMethod,
+    TotaloffsetminutesMethod,
+    TotalsecondsMethod,
+    ToupperMethod,
+    TrimMethod,
+    YearMethod
   }
 
   private final String parseString;
@@ -111,6 +165,13 @@ public class UriTokenizer {
     case CROSSJOIN:
       found = nextConstant("$crossjoin");
       break;
+    case ROOT:
+      found = nextConstant("$root");
+      break;
+    case IT:
+      found = nextConstant("$it");
+      break;
+
     case OPEN:
       found = nextCharacter('(');
       break;
@@ -138,6 +199,9 @@ public class UriTokenizer {
     case PLUS:
       found = nextCharacter('+');
       break;
+    case MINUS:
+      found = nextCharacter('-');
+      break;
     case NULL:
       found = nextConstant("null");
       break;
@@ -157,47 +221,189 @@ public class UriTokenizer {
       break;
 
     // Primitive Values
-    case PrimitiveBooleanValue:
+    case BooleanValue:
       found = nextBooleanValue();
       break;
-    case PrimitiveStringValue:
+    case StringValue:
       found = nextStringValue();
       break;
-    case PrimitiveIntegerValue:
+    case IntegerValue:
       found = nextIntegerValue(true);
       break;
-    case PrimitiveGuidValue:
+    case GuidValue:
       found = nextGuidValue();
       break;
-    case PrimitiveDateValue:
+    case DateValue:
       found = nextDateValue();
       break;
-    case PrimitiveDateTimeOffsetValue:
+    case DateTimeOffsetValue:
       found = nextDateTimeOffsetValue();
       break;
-    case PrimitiveTimeOfDayValue:
+    case TimeOfDayValue:
       found = nextTimeOfDayValue();
       break;
-    case PrimitiveDecimalValue:
+    case DecimalValue:
       found = nextDecimalValue();
       break;
-    case PrimitiveDoubleValue:
+    case DoubleValue:
       found = nextDoubleValue();
       break;
-    case PrimitiveDurationValue:
+    case DurationValue:
       found = nextDurationValue();
       break;
-    case PrimitiveBinaryValue:
+    case BinaryValue:
       found = nextBinaryValue();
       break;
-    case PrimitiveEnumValue:
+    case EnumValue:
       found = nextEnumValue();
       break;
 
-    // Primitive Values
+    // Complex or Collection Value
     case jsonArrayOrObject:
       found = nextJsonArrayOrObject();
       break;
+
+    // Operators
+    case OrOperator:
+      found = nextBinaryOperator("or");
+      break;
+    case AndOperator:
+      found = nextBinaryOperator("and");
+      break;
+    case EqualsOperator:
+      found = nextBinaryOperator("eq");
+      break;
+    case NotEqualsOperator:
+      found = nextBinaryOperator("ne");
+      break;
+    case GreaterThanOperator:
+      found = nextBinaryOperator("gt");
+      break;
+    case GreaterThanOrEqualsOperator:
+      found = nextBinaryOperator("ge");
+      break;
+    case LessThanOperator:
+      found = nextBinaryOperator("lt");
+      break;
+    case LessThanOrEqualsOperator:
+      found = nextBinaryOperator("le");
+      break;
+    case AddOperator:
+      found = nextBinaryOperator("add");
+      break;
+    case SubOperator:
+      found = nextBinaryOperator("sub");
+      break;
+    case MulOperator:
+      found = nextBinaryOperator("mul");
+      break;
+    case DivOperator:
+      found = nextBinaryOperator("div");
+      break;
+    case ModOperator:
+      found = nextBinaryOperator("mod");
+      break;
+    case NotOperator:
+      found = nextConstant("not") && nextWhitespace();
+      break;
+
+    // Methods
+    case CastMethod:
+      found = nextMethod("cast");
+      break;
+    case CeilingMethod:
+      found = nextMethod("ceiling");
+      break;
+    case ConcatMethod:
+      found = nextMethod("concat");
+      break;
+    case ContainsMethod:
+      found = nextMethod("contains");
+      break;
+    case DateMethod:
+      found = nextMethod("date");
+      break;
+    case DayMethod:
+      found = nextMethod("day");
+      break;
+    case EndswithMethod:
+      found = nextMethod("endswith");
+      break;
+    case FloorMethod:
+      found = nextMethod("floor");
+      break;
+    case FractionalsecondsMethod:
+      found = nextMethod("fractionalseconds");
+      break;
+    case GeoDistanceMethod:
+      found = nextMethod("geo.distance");
+      break;
+    case GeoIntersectsMethod:
+      found = nextMethod("geo.intersects");
+      break;
+    case GeoLengthMethod:
+      found = nextMethod("geo.length");
+      break;
+    case HourMethod:
+      found = nextMethod("hour");
+      break;
+    case IndexofMethod:
+      found = nextMethod("indexof");
+      break;
+    case IsofMethod:
+      found = nextMethod("isof");
+      break;
+    case LengthMethod:
+      found = nextMethod("length");
+      break;
+    case MaxdatetimeMethod:
+      found = nextMethod("maxdatetime");
+      break;
+    case MindatetimeMethod:
+      found = nextMethod("mindatetime");
+      break;
+    case MinuteMethod:
+      found = nextMethod("minute");
+      break;
+    case MonthMethod:
+      found = nextMethod("month");
+      break;
+    case NowMethod:
+      found = nextMethod("now");
+      break;
+    case RoundMethod:
+      found = nextMethod("round");
+      break;
+    case SecondMethod:
+      found = nextMethod("second");
+      break;
+    case StartswithMethod:
+      found = nextMethod("startswith");
+      break;
+    case SubstringMethod:
+      found = nextMethod("substring");
+      break;
+    case TimeMethod:
+      found = nextMethod("time");
+      break;
+    case TolowerMethod:
+      found = nextMethod("tolower");
+      break;
+    case TotaloffsetminutesMethod:
+      found = nextMethod("totaloffsetminutes");
+      break;
+    case TotalsecondsMethod:
+      found = nextMethod("totalseconds");
+      break;
+    case ToupperMethod:
+      found = nextMethod("toupper");
+      break;
+    case TrimMethod:
+      found = nextMethod("trim");
+      break;
+    case YearMethod:
+      found = nextMethod("year");
+      break;
     }
 
     if (found) {
@@ -301,6 +507,37 @@ public class UriTokenizer {
   }
 
   /**
+   * Moves past whitespace (space or horizontal tabulator) characters if found;
+   * otherwise leaves the index unchanged.
+   * @return whether whitespace characters have been found at the current index
+   */
+  private boolean nextWhitespace() {
+    int count = 0;
+    while (nextCharacter(' ') || nextCharacter('\t')) {
+      count++;
+    }
+    return count > 0;
+  }
+
+  /**
+   * Moves past the given whitespace-surrounded operator constant if found;
+   * otherwise leaves the index unchanged.
+   * @return whether the operator has been found at the current index
+   */
+  private boolean nextBinaryOperator(final String operator) {
+    return nextWhitespace() && nextConstant(operator) && nextWhitespace();
+  }
+
+  /**
+   * Moves past the given method name and its immediately following opening parenthesis if found;
+   * otherwise leaves the index unchanged.
+   * @return whether the method has been found at the current index
+   */
+  private boolean nextMethod(final String methodName) {
+    return nextConstant(methodName) && nextCharacter('(');
+  }
+
+  /**
    * Moves past an OData identifier if found; otherwise leaves the index unchanged.
    * @return whether an OData identifier has been found at the current index
    */

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2f3bc286/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 2438d27..910997e 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
@@ -53,6 +53,6 @@ public class UnaryImpl implements Unary {
 
   @Override
   public String toString() {
-    return "{" + operator + " " + expression + '}';
+    return "{" + operator.name() + " " + expression + '}';
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2f3bc286/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 7da823e..58f2a1f 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
@@ -20,191 +20,157 @@ 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 java.util.ArrayList;
+import java.util.Locale;
 
 import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
-import org.apache.olingo.server.core.uri.parser.ExpressionParser.Token;
-import org.apache.olingo.server.core.uri.parser.ExpressionParser.TokenKind;
-import org.apache.olingo.server.core.uri.parser.ExpressionParser.Tokenizer;
+import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
 import org.junit.Test;
 
 public class ExpressionParserTest {
 
   @Test
   public void equality() throws Exception {
-    Expression expression = parseExpression(TokenKind.EQ_OP);
+    Expression expression = parseExpression("5 eq 5");
     assertEquals("{5 EQ 5}", expression.toString());
 
-    expression = parseExpression(TokenKind.NE_OP);
+    expression = parseExpression("5 ne 5");
     assertEquals("{5 NE 5}", expression.toString());
   }
 
   @Test
   public void relational() throws Exception {
-    Expression expression = parseExpression(TokenKind.GT_OP);
+    Expression expression = parseExpression("5 gt 5");
     assertEquals("{5 GT 5}", expression.toString());
 
-    expression = parseExpression(TokenKind.GE_OP);
+    expression = parseExpression("5 ge 5");
     assertEquals("{5 GE 5}", expression.toString());
 
-    expression = parseExpression(TokenKind.LT_OP);
+    expression = parseExpression("5 lt 5");
     assertEquals("{5 LT 5}", expression.toString());
 
-    expression = parseExpression(TokenKind.LE_OP);
+    expression = parseExpression("5 le 5");
     assertEquals("{5 LE 5}", expression.toString());
   }
 
   @Test
   public void additive() throws Exception {
-    Expression expression = parseExpression(TokenKind.ADD_OP);
+    Expression expression = parseExpression("5 add 5");
     assertEquals("{5 ADD 5}", expression.toString());
 
-    expression = parseExpression(TokenKind.SUB_OP);
+    expression = parseExpression("5 sub 5");
     assertEquals("{5 SUB 5}", expression.toString());
   }
 
   @Test
   public void multiplicative() throws Exception {
-    Expression expression = parseExpression(TokenKind.MUL_OP);
+    Expression expression = parseExpression("5 mul 5");
     assertEquals("{5 MUL 5}", expression.toString());
 
-    expression = parseExpression(TokenKind.DIV_OP);
+    expression = parseExpression("5 div 5");
     assertEquals("{5 DIV 5}", expression.toString());
 
-    expression = parseExpression(TokenKind.MOD_OP);
+    expression = parseExpression("5 mod 5");
     assertEquals("{5 MOD 5}", expression.toString());
   }
 
   @Test
   public void unary() throws Exception {
-    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 ExpressionParser().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 ExpressionParser().parse(tokenizer);
-    assertEquals("{not 5}", expression.toString());
+    Expression expression = parseExpression("-5");
+    assertEquals("{MINUS 5}", expression.toString());
+
+    assertEquals("{MINUS -1}", parseExpression("--1").toString());
+
+    expression = parseExpression("not 5");
+    assertEquals("{NOT 5}", expression.toString());
   }
 
   @Test
   public void grouping() throws Exception {
-    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 ExpressionParser().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 ExpressionParser().parse(tokenizer);
-    assertEquals("{- {5 ADD 5}}", expression.toString());
+    Expression expression = parseExpression("-5 add 5");
+    assertEquals("{{MINUS 5} ADD 5}", expression.toString());
+
+    expression = parseExpression("-(5 add 5)");
+    assertEquals("{MINUS {5 ADD 5}}", expression.toString());
+  }
+
+  @Test
+  public void precedence() throws Exception {
+    assertEquals("{{MINUS 1} ADD {2 DIV 3}}", parseExpression("-1 add 2 div 3").toString());
+    assertEquals("{true OR {{NOT false} AND true}}", parseExpression("true or not false and true").toString());
   }
 
   @Test
   public void noParameterMethods() throws Exception {
-    Expression expression = parseMethod(TokenKind.Now);
+    Expression expression = parseMethod(TokenKind.NowMethod);
     assertEquals("{now []}", expression.toString());
 
-    expression = parseMethod(TokenKind.Maxdatetime);
+    expression = parseMethod(TokenKind.MaxdatetimeMethod);
     assertEquals("{maxdatetime []}", expression.toString());
 
-    expression = parseMethod(TokenKind.Mindatetime);
+    expression = parseMethod(TokenKind.MindatetimeMethod);
     assertEquals("{mindatetime []}", expression.toString());
   }
 
   @Test
   public void oneParameterMethods() throws Exception {
-    Expression expression = parseMethod(TokenKind.Length, TokenKind.PrimitiveStringValue);
-    assertEquals("{length [String1]}", expression.toString());
-
-    expression = parseMethod(TokenKind.Tolower, TokenKind.PrimitiveStringValue);
-    assertEquals("{tolower [String1]}", expression.toString());
+    final String stringValue = "'abc'";
+    final String dateValue = "1234-12-25";
+    final String dateTimeOffsetValue = "1234-12-25T11:12:13.456Z";
 
-    expression = parseMethod(TokenKind.Toupper, TokenKind.PrimitiveStringValue);
-    assertEquals("{toupper [String1]}", expression.toString());
+    Expression expression = parseMethod(TokenKind.LengthMethod, stringValue);
+    assertEquals("{length [" + stringValue + "]}", expression.toString());
 
-    expression = parseMethod(TokenKind.Trim, TokenKind.PrimitiveStringValue);
-    assertEquals("{trim [String1]}", expression.toString());
+    expression = parseMethod(TokenKind.TolowerMethod, stringValue);
+    assertEquals("{tolower [" + stringValue + "]}", expression.toString());
 
-    expression = parseMethod(TokenKind.Year, TokenKind.PrimitiveDateValue);
-    assertEquals("{year [Date1]}", expression.toString());
+    expression = parseMethod(TokenKind.ToupperMethod, stringValue);
+    assertEquals("{toupper [" + stringValue + "]}", expression.toString());
 
-    expression = parseMethod(TokenKind.Month, TokenKind.PrimitiveDateValue);
-    assertEquals("{month [Date1]}", expression.toString());
+    expression = parseMethod(TokenKind.TrimMethod, stringValue);
+    assertEquals("{trim [" + stringValue + "]}", expression.toString());
 
-    expression = parseMethod(TokenKind.Day, TokenKind.PrimitiveDateValue);
-    assertEquals("{day [Date1]}", expression.toString());
+    expression = parseMethod(TokenKind.YearMethod, dateValue);
+    assertEquals("{year [" + dateValue + "]}", expression.toString());
 
-    expression = parseMethod(TokenKind.Hour, TokenKind.PrimitiveDateTimeOffsetValue);
-    assertEquals("{hour [DateTimeOffset1]}", expression.toString());
+    expression = parseMethod(TokenKind.MonthMethod, dateValue);
+    assertEquals("{month [" + dateValue + "]}", expression.toString());
 
-    expression = parseMethod(TokenKind.Minute, TokenKind.PrimitiveDateTimeOffsetValue);
-    assertEquals("{minute [DateTimeOffset1]}", expression.toString());
+    expression = parseMethod(TokenKind.DayMethod, dateValue);
+    assertEquals("{day [" + dateValue + "]}", expression.toString());
 
-    expression = parseMethod(TokenKind.Second, TokenKind.PrimitiveDateTimeOffsetValue);
-    assertEquals("{second [DateTimeOffset1]}", expression.toString());
-  }
+    expression = parseMethod(TokenKind.HourMethod, dateTimeOffsetValue);
+    assertEquals("{hour [" + dateTimeOffsetValue + "]}", expression.toString());
 
-  @Test
-  public void twoParameterMethods() {
+    expression = parseMethod(TokenKind.MinuteMethod, dateTimeOffsetValue);
+    assertEquals("{minute [" + dateTimeOffsetValue + "]}", expression.toString());
 
+    expression = parseMethod(TokenKind.SecondMethod, dateTimeOffsetValue);
+    assertEquals("{second [" + dateTimeOffsetValue + "]}", expression.toString());
   }
 
-  private Expression parseMethod(TokenKind... kind) throws UriParserException {
-    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;
+  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) {
+        expressionString += ',';
       }
-      tokens.add(new Token(kind[i], text));
+      expressionString += parameters[i];
     }
+    expressionString += ')';
 
-    tokens.add(new Token(TokenKind.CLOSE, ""));
-    Tokenizer tokenizer = new Tokenizer(tokens);
-    Expression expression = new ExpressionParser().parse(tokenizer);
+    Expression expression = parseExpression(expressionString);
     assertNotNull(expression);
     return expression;
   }
 
-  private Expression parseExpression(TokenKind operator) throws UriParserException {
-    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);
-
+  private Expression parseExpression(final String expressionString) throws UriParserException {
+    UriTokenizer tokenizer = new UriTokenizer(expressionString);
     Expression expression = new ExpressionParser().parse(tokenizer);
     assertNotNull(expression);
+    assertTrue(tokenizer.next(TokenKind.EOF));
     return expression;
   }
 }