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/07 14:08:18 UTC

[1/6] olingo-odata4 git commit: [OLINGO-834] $expand parser in Java + clean-up

Repository: olingo-odata4
Updated Branches:
  refs/heads/OLINGO-834_Filter_Parser 8919d3ef1 -> 8925274c0


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestLexer.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestLexer.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestLexer.java
index 6df6759..6102fd8 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestLexer.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestLexer.java
@@ -18,16 +18,22 @@
  */
 package org.apache.olingo.server.core.uri.antlr;
 
-import org.antlr.v4.runtime.Lexer;
-import org.apache.olingo.server.core.uri.testutil.TokenValidator;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.olingo.server.core.uri.parser.UriTokenizer;
+import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
 import org.junit.Test;
 
+/**
+ * Tests originally written for the ANTLR lexer.
+ */
 public class TestLexer {
 
   private TokenValidator test = null;
 
-  private static final String cPCT_ENCODED = "%45%46%47" + "%22" + "%5C";// last two chars are not in
-  // cPCT_ENCODED_UNESCAPED
+  // The last two chars are not in cPCT_ENCODED_UNESCAPED.
+  private static final String cPCT_ENCODED = "%45%46%47" + "%22" + "%5C";
   private static final String cUNRESERVED = "ABCabc123-._~";
   private static final String cOTHER_DELIMS = "!()*+,;";
   private static final String cSUB_DELIMS = "$&'=" + cOTHER_DELIMS;
@@ -39,265 +45,279 @@ public class TestLexer {
   }
 
   @Test
-  public void test() {
-
-    // test.log(1).run("ESAllPrim?$orderby=PropertyDouble eq 3.5E+38");
+  public void unary() {
+    test.run("-a eq a").has(TokenKind.MinusOperator, TokenKind.ODataIdentifier, TokenKind.EqualsOperator,
+        TokenKind.ODataIdentifier).isInput();
   }
 
-  // ;------------------------------------------------------------------------------
-  // ; 0. URI
-  // ;------------------------------------------------------------------------------
-
   @Test
-  public void testUnary() {
-    test.run("- a eq a").isAllInput();
+  public void uriTokens() {
+//    test.run("#").isType(TokenKind.FRAGMENT).isInput();
+    test.run("$count").has(TokenKind.COUNT).isInput();
+    test.run("$ref").has(TokenKind.REF).isInput();
+    test.run("$value").has(TokenKind.VALUE).isInput();
   }
 
   @Test
-  public void testUriTokens() {
-    test.globalMode(UriLexer.MODE_QUERY);
-    test.run("#").isText("#").isType(UriLexer.FRAGMENT);
-    test.run("$count").isText("$count").isType(UriLexer.COUNT);
-    test.run("$ref").isText("$ref").isType(UriLexer.REF);
-    test.run("$value").isText("$value").isType(UriLexer.VALUE);
+  public void queryOptionsTokens() {
+    test.run("$skip=1").has(TokenKind.SKIP, TokenKind.EQ, TokenKind.IntegerValue).isInput();
+    test.run("$skip=2").has(TokenKind.SKIP, TokenKind.EQ, TokenKind.IntegerValue).isInput();
+    test.run("$skip=123").has(TokenKind.SKIP, TokenKind.EQ, TokenKind.IntegerValue).isInput();
+
+    test.run("$top=1").has(TokenKind.TOP, TokenKind.EQ, TokenKind.IntegerValue).isInput();
+    test.run("$top=2").has(TokenKind.TOP, TokenKind.EQ, TokenKind.IntegerValue).isInput();
+    test.run("$top=123").has(TokenKind.TOP, TokenKind.EQ, TokenKind.IntegerValue).isInput();
+
+    test.run("$levels=1").has(TokenKind.LEVELS, TokenKind.EQ, TokenKind.IntegerValue).isInput();
+    test.run("$levels=2").has(TokenKind.LEVELS, TokenKind.EQ, TokenKind.IntegerValue).isInput();
+    test.run("$levels=123").has(TokenKind.LEVELS, TokenKind.EQ, TokenKind.IntegerValue).isInput();
+    test.run("$levels=max").has(TokenKind.LEVELS, TokenKind.EQ, TokenKind.MAX).isInput();
+
+//    test.run("$format=atom").has(TokenKind.FORMAT, TokenKind.EQ, TokenKind.ODataIdentifier).isInput();
+//    test.run("$format=json").has(TokenKind.FORMAT, TokenKind.EQ, TokenKind.ODataIdentifier).isInput();
+//    test.run("$format=xml").has(TokenKind.FORMAT,, TokenKind.EQ, TokenKind.ODataIdentifier).isInput();
+//    test.run("$format=abc/def").has(TokenKind.FORMAT, TokenKind.EQ,
+//        TokenKind.ODataIdentifier, TokenKind.SLASH, TokenKind.ODataIdentifier).isInput();
+
+//    test.run("$id=123").has(TokenKind.ID, TokenKind.EQ, TokenKind.IntegerValue).isInput();
+//    test.run("$id=ABC").has(TokenKind.ID, TokenKind.EQ, TokenKind.ODataIdentifier).isInput();
+
+//    test.run("$skiptoken=ABC").has(TokenKind.SKIPTOKEN, TokenKind.EQ, TokenKind.ODataIdentifier).isInput();
+//    test.run("$skiptoken=ABC").has(TokenKind.SKIPTOKEN, TokenKind.EQ, TokenKind.ODataIdentifier).isInput();
+
+    test.run("$search=\"ABC\"").has(TokenKind.SEARCH, TokenKind.EQ, TokenKind.Phrase).isInput();
+    test.run("$search=ABC").has(TokenKind.SEARCH, TokenKind.EQ, TokenKind.Word).isInput();
+    test.run("$search=\"A%20B%20C\"").has(TokenKind.SEARCH, TokenKind.EQ, TokenKind.Phrase).isInput();
+    test.run("$search=Test Test").has(TokenKind.SEARCH, TokenKind.EQ, TokenKind.Word,
+        TokenKind.AndOperatorSearch, TokenKind.Word).isInput();
+    test.run("$search=Test&$filter=ABC eq 1").has(TokenKind.SEARCH, TokenKind.EQ, TokenKind.Word);
   }
 
-  // ;------------------------------------------------------------------------------
-  // ; 2. Query Options
-  // ;------------------------------------------------------------------------------
   @Test
-  public void testQueryOptionsTokens() {
-
-    test.globalMode(UriLexer.MODE_QUERY);
-    test.run("$skip=1").isAllText("$skip=1").isType(UriLexer.SKIP_QO);
-    test.run("$skip=2").isAllText("$skip=2").isType(UriLexer.SKIP_QO);
-    test.run("$skip=123").isAllText("$skip=123").isType(UriLexer.SKIP_QO);
-
-    test.run("$top=1").isAllText("$top=1").isType(UriLexer.TOP);
-    test.run("$top=2").isAllText("$top=2").isType(UriLexer.TOP);
-    test.run("$top=123").isAllText("$top=123").isType(UriLexer.TOP);
-
-    test.run("$levels=1").isAllText("$levels=1").isType(UriLexer.LEVELS);
-    test.run("$levels=2").isAllText("$levels=2").isType(UriLexer.LEVELS);
-    test.run("$levels=123").isAllText("$levels=123").isType(UriLexer.LEVELS);
-    test.run("$levels=max").isAllText("$levels=max").isType(UriLexer.LEVELS);
-
-    test.run("$format=atom").isAllText("$format=atom").isType(UriLexer.FORMAT);
-    test.run("$format=json").isAllText("$format=json").isType(UriLexer.FORMAT);
-    test.run("$format=xml").isAllText("$format=xml").isType(UriLexer.FORMAT);
-    test.run("$format=abc/def").isAllText("$format=abc/def").isType(UriLexer.FORMAT);
-
-    test.run("$id=123").isAllText("$id=123").isType(UriLexer.ID);
-    test.run("$id=ABC").isAllText("$id=ABC").isType(UriLexer.ID);
-
-    test.run("$skiptoken=ABC").isAllText("$skiptoken=ABC").isType(UriLexer.SKIPTOKEN);
-    test.run("$skiptoken=ABC").isAllText("$skiptoken=ABC").isType(UriLexer.SKIPTOKEN);
-
-    test.run("$search=\"ABC\"").isAllText("$search=\"ABC\"").isType(UriLexer.SEARCH);
-    test.run("$search=ABC").isAllText("$search=ABC").isType(UriLexer.SEARCH);
-    test.run("$search=\"A%20B%20C\"").isAllText("$search=\"A%20B%20C\"").isType(UriLexer.SEARCH);
-    test.run("$search=Test Test").isAllText("$search=Test Test").isType(UriLexer.SEARCH);
-    test.run("$search=Test&$filter=ABC eq 1").isAllText("$search=Test&$filter=ABC eq 1").isType(UriLexer.SEARCH);
-  }
-  
-  @Test
-  public void testQueryOptionsDefaultMode() {
-    // First set query mode, than use expand(switches to default mode) and use nested system query options
-    test.globalMode(UriLexer.MODE_QUERY);
-    test.run("$expand=ABC($skip=1)").isAllText("$expand=ABC($skip=1)").at(4).isType(UriLexer.SKIP_QO);
-    test.run("$expand=ABC($skip=2)").isAllText("$expand=ABC($skip=2)").at(4).isType(UriLexer.SKIP_QO);
-    test.run("$expand=ABC($skip=123)").isAllText("$expand=ABC($skip=123)").at(4).isType(UriLexer.SKIP_QO);
-    test.run("$expand=ABC($search=abc)").isAllText("$expand=ABC($search=abc)").at(4).isType(UriLexer.SEARCH_INLINE);
-    test.run("$expand=ABC($search=\"123\")").isAllText("$expand=ABC($search=\"123\")")
-                                            .at(4).isType(UriLexer.SEARCH_INLINE)
-                                            .at(6).isType(UriLexer.SEARCHPHRASE);
-    test.run("$expand=ABC($top=1)").isAllText("$expand=ABC($top=1)").at(4).isType(UriLexer.TOP);
-    test.run("$expand=ABC($top=2)").isAllText("$expand=ABC($top=2)").at(4).isType(UriLexer.TOP);
-    test.run("$expand=ABC($top=123)").isAllText("$expand=ABC($top=123)").at(4).isType(UriLexer.TOP);
-    
-    test.run("$expand=ABC($expand=DEF($skip=1))").isAllText("$expand=ABC($expand=DEF($skip=1))")
-                                                 .at(8).isType(UriLexer.SKIP_QO);
-    test.run("$expand=ABC($expand=DEF($skip=2))").isAllText("$expand=ABC($expand=DEF($skip=2))")
-                                                 .at(8).isType(UriLexer.SKIP_QO);
-    test.run("$expand=ABC($expand=DEF($skip=123))").isAllText("$expand=ABC($expand=DEF($skip=123))")
-                                                 .at(8).isType(UriLexer.SKIP_QO);
-    
-    test.run("$expand=ABC($expand=DEF($top=1))").isAllText("$expand=ABC($expand=DEF($top=1))")
-                                                .at(8).isType(UriLexer.TOP);
-    test.run("$expand=ABC($expand=DEF($top=2))").isAllText("$expand=ABC($expand=DEF($top=2))")
-                                                .at(8).isType(UriLexer.TOP);
-    test.run("$expand=ABC($expand=DEF($top=123))").isAllText("$expand=ABC($expand=DEF($top=123))")
-                                                  .at(8).isType(UriLexer.TOP);
-    test.run("$expand=ABC($expand=DEF($search=Test Test))").isAllText("$expand=ABC($expand=DEF($search=Test Test))")
-                                                        .at(8).isType(UriLexer.SEARCH_INLINE)
-                                                        .at(10).isType(UriLexer.SEARCHWORD)
-                                                        .at(12).isType(UriLexer.SEARCHWORD);
+  public void queryOptionsDefaultMode() {
+    test.run("$expand=ABC($skip=1)").has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
+        TokenKind.OPEN, TokenKind.SKIP, TokenKind.EQ, TokenKind.IntegerValue, TokenKind.CLOSE).isInput();
+    test.run("$expand=ABC($skip=123)").has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
+        TokenKind.OPEN, TokenKind.SKIP, TokenKind.EQ, TokenKind.IntegerValue, TokenKind.CLOSE).isInput();
+    test.run("$expand=ABC($search=abc)").has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
+        TokenKind.OPEN, TokenKind.SEARCH, TokenKind.EQ, TokenKind.Word, TokenKind.CLOSE).isInput();
+    test.run("$expand=ABC($search=\"123\")").has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
+        TokenKind.OPEN, TokenKind.SEARCH, TokenKind.EQ, TokenKind.Phrase, TokenKind.CLOSE).isInput();
+    test.run("$expand=ABC($top=1)").has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
+        TokenKind.OPEN, TokenKind.TOP, TokenKind.EQ, TokenKind.IntegerValue, TokenKind.CLOSE).isInput();
+    test.run("$expand=ABC($top=123)").has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
+        TokenKind.OPEN, TokenKind.TOP, TokenKind.EQ, TokenKind.IntegerValue, TokenKind.CLOSE).isInput();
+
+    test.run("$expand=ABC($expand=DEF($skip=1))").has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
+        TokenKind.OPEN, TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
+        TokenKind.OPEN, TokenKind.SKIP, TokenKind.EQ, TokenKind.IntegerValue, TokenKind.CLOSE, TokenKind.CLOSE)
+        .isInput();
+    test.run("$expand=ABC($expand=DEF($skip=123))").has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
+        TokenKind.OPEN, TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
+        TokenKind.OPEN, TokenKind.SKIP, TokenKind.EQ, TokenKind.IntegerValue, TokenKind.CLOSE, TokenKind.CLOSE)
+        .isInput();
+
+    test.run("$expand=ABC($expand=DEF($top=1))").has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
+        TokenKind.OPEN, TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
+        TokenKind.OPEN, TokenKind.TOP, TokenKind.EQ, TokenKind.IntegerValue, TokenKind.CLOSE, TokenKind.CLOSE)
+        .isInput();
+    test.run("$expand=ABC($expand=DEF($top=123))").has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
+        TokenKind.OPEN, TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
+        TokenKind.OPEN, TokenKind.TOP, TokenKind.EQ, TokenKind.IntegerValue, TokenKind.CLOSE, TokenKind.CLOSE)
+        .isInput();
+
+    test.run("$expand=ABC($expand=DEF($search=Test Test))")
+        .has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
+            TokenKind.OPEN, TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
+            TokenKind.OPEN, TokenKind.SEARCH, TokenKind.EQ, TokenKind.Word,
+            TokenKind.AndOperatorSearch, TokenKind.Word, TokenKind.CLOSE, TokenKind.CLOSE)
+        .isInput();
     test.run("$expand=ABC($expand=DEF($search=\"Test\" \"Test\"))")
-                .isAllText("$expand=ABC($expand=DEF($search=\"Test\" \"Test\"))")
-                .at(8).isType(UriLexer.SEARCH_INLINE)
-                .at(10).isType(UriLexer.SEARCHPHRASE)
-                .at(12).isType(UriLexer.SEARCHPHRASE);
+        .has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
+            TokenKind.OPEN, TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
+            TokenKind.OPEN, TokenKind.SEARCH, TokenKind.EQ, TokenKind.Phrase,
+            TokenKind.AndOperatorSearch, TokenKind.Phrase, TokenKind.CLOSE, TokenKind.CLOSE)
+        .isInput();
     test.run("$expand=ABC($expand=DEF($search=\"Test\" \"Test\";$filter=PropertyInt16 eq 0;$orderby=PropertyInt16))")
-                .isAllText("$expand=ABC($expand=DEF($search=\"Test\" \"Test\";$filter=PropertyInt16 " + 
-                           "eq 0;$orderby=PropertyInt16))")
-                .at(8).isType(UriLexer.SEARCH_INLINE)
-                .at(10).isType(UriLexer.SEARCHPHRASE)
-                .at(12).isType(UriLexer.SEARCHPHRASE)
-                .at(13).isType(UriLexer.SEMI)
-                .at(14).isType(UriLexer.FILTER)
-                .at(22).isType(UriLexer.ORDERBY);
+        .has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
+            TokenKind.OPEN, TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
+            TokenKind.OPEN, TokenKind.SEARCH, TokenKind.EQ, TokenKind.Phrase,
+            TokenKind.AndOperatorSearch, TokenKind.Phrase, TokenKind.SEMI,
+            TokenKind.FILTER, TokenKind.EQ, TokenKind.ODataIdentifier, TokenKind.EqualsOperator,
+            TokenKind.IntegerValue, TokenKind.SEMI,
+            TokenKind.ORDERBY, TokenKind.EQ, TokenKind.ODataIdentifier, TokenKind.CLOSE, TokenKind.CLOSE)
+        .isInput();
   }
-  
-  // ;------------------------------------------------------------------------------
-  // ; 4. Expressions
-  // ;------------------------------------------------------------------------------
-  @Test
-  public void testQueryExpressions() {
-    test.globalMode(Lexer.DEFAULT_MODE);
-
-    test.run("$it").isText("$it").isType(UriLexer.IT);
-
-    test.run("$filter=contains(").at(2).isText("contains(").isType(UriLexer.CONTAINS_WORD);
-
-    test.run("$filter=containsabc").at(2).isText("containsabc")
-    .isType(UriLexer.ODATAIDENTIFIER); // test that this is a ODI
-
-    test.run("$filter=startswith(").at(2).isText("startswith(").isType(UriLexer.STARTSWITH_WORD);
-    test.run("$filter=endswith(").at(2).isText("endswith(").isType(UriLexer.ENDSWITH_WORD);
-    test.run("$filter=length(").at(2).isText("length(").isType(UriLexer.LENGTH_WORD);
-    test.run("$filter=indexof(").at(2).isText("indexof(").isType(UriLexer.INDEXOF_WORD);
-    test.run("$filter=substring(").at(2).isText("substring(").isType(UriLexer.SUBSTRING_WORD);
-    test.run("$filter=tolower(").at(2).isText("tolower(").isType(UriLexer.TOLOWER_WORD);
-    test.run("$filter=toupper(").at(2).isText("toupper(").isType(UriLexer.TOUPPER_WORD);
-    test.run("$filter=trim(").at(2).isText("trim(").isType(UriLexer.TRIM_WORD);
-    test.run("$filter=concat(").at(2).isText("concat(").isType(UriLexer.CONCAT_WORD);
 
+  @Test
+  public void queryExpressions() {
+    test.run("$it").has(TokenKind.IT).isText("$it");
+
+    test.run("$filter=contains(").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.ContainsMethod).isText("contains(");
+
+    test.run("$filter=containsabc").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.ODataIdentifier)
+        .isText("containsabc");
+
+    test.run("$filter=startswith(").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.StartswithMethod)
+        .isText("startswith(");
+    test.run("$filter=endswith(").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.EndswithMethod).isText("endswith(");
+    test.run("$filter=length(").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.LengthMethod).isText("length(");
+    test.run("$filter=indexof(").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.IndexofMethod).isText("indexof(");
+    test.run("$filter=substring(").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.SubstringMethod).isText("substring(");
+    test.run("$filter=tolower(").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.TolowerMethod).isText("tolower(");
+    test.run("$filter=toupper(").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.ToupperMethod).isText("toupper(");
+    test.run("$filter=trim(").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.TrimMethod).isText("trim(");
+    test.run("$filter=concat(").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.ConcatMethod).isText("concat(");
   }
 
-  // ;------------------------------------------------------------------------------
-  // ; 7. Literal Data Values
-  // ;------------------------------------------------------------------------------
-
   @Test
-  public void testLiteralDataValues() {
-    test.globalMode(Lexer.DEFAULT_MODE);
+  public void literalDataValues() {
     // null
-    test.run("null").isInput().isType(UriLexer.NULLVALUE);
+    test.run("null").has(TokenKind.NULL).isInput();
 
     // binary
-    test.run("binary'ABCD'").isInput().isType(UriLexer.BINARY);
-    test.run("BiNaRy'ABCD'").isInput().isType(UriLexer.BINARY);
+    test.run("binary'ABCD'").has(TokenKind.BinaryValue).isInput();
+    test.run("BiNaRy'ABCD'").has(TokenKind.BinaryValue).isInput();
 
     // boolean
-    test.run("true").isInput().isType(UriLexer.TRUE);
-    test.run("false").isInput().isType(UriLexer.FALSE);
-    test.run("TrUe").isInput().isType(UriLexer.BOOLEAN);
-    test.run("FaLsE").isInput().isType(UriLexer.BOOLEAN);
+    test.run("true").has(TokenKind.BooleanValue).isInput();
+    test.run("false").has(TokenKind.BooleanValue).isInput();
+    test.run("TrUe").has(TokenKind.BooleanValue).isInput();
+    test.run("FaLsE").has(TokenKind.BooleanValue).isInput();
 
     // Lexer rule INT
-    test.run("123").isInput().isType(UriLexer.INT);
-    test.run("123456789").isInput().isType(UriLexer.INT);
-    test.run("+123").isInput().isType(UriLexer.INT);
-    test.run("+123456789").isInput().isType(UriLexer.INT);
-    test.run("-123").isInput().isType(UriLexer.INT);
-    test.run("-123456789").isInput().isType(UriLexer.INT);
+    test.run("123").has(TokenKind.IntegerValue).isInput();
+    test.run("123456789").has(TokenKind.IntegerValue).isInput();
+    test.run("+123").has(TokenKind.IntegerValue).isInput();
+    test.run("+123456789").has(TokenKind.IntegerValue).isInput();
+    test.run("-123").has(TokenKind.IntegerValue).isInput();
+    test.run("-123456789").has(TokenKind.IntegerValue).isInput();
 
     // Lexer rule DECIMAL
-    test.run("0.1").isInput().isType(UriLexer.DECIMAL);
-    test.run("1.1").isInput().isType(UriLexer.DECIMAL);
-    test.run("+0.1").isInput().isType(UriLexer.DECIMAL);
-    test.run("+1.1").isInput().isType(UriLexer.DECIMAL);
-    test.run("-0.1").isInput().isType(UriLexer.DECIMAL);
-    test.run("-1.1").isInput().isType(UriLexer.DECIMAL);
+    test.run("0.1").has(TokenKind.DecimalValue).isInput();
+    test.run("1.1").has(TokenKind.DecimalValue).isInput();
+    test.run("+0.1").has(TokenKind.DecimalValue).isInput();
+    test.run("+1.1").has(TokenKind.DecimalValue).isInput();
+    test.run("-0.1").has(TokenKind.DecimalValue).isInput();
+    test.run("-1.1").has(TokenKind.DecimalValue).isInput();
 
     // Lexer rule EXP
-    test.run("1.1e+1").isInput().isType(UriLexer.DECIMAL);
-    test.run("1.1e-1").isInput().isType(UriLexer.DECIMAL);
+    test.run("1.1e+1").has(TokenKind.DoubleValue).isInput();
+    test.run("1.1e-1").has(TokenKind.DoubleValue).isInput();
 
-    test.run("NaN").isInput().isType(UriLexer.NANINFINITY);
-    test.run("-INF").isInput().isType(UriLexer.NANINFINITY);
-    test.run("INF").isInput().isType(UriLexer.NANINFINITY);
+    test.run("NaN").has(TokenKind.DoubleValue).isInput();
+    test.run("-INF").has(TokenKind.DoubleValue).isInput();
+    test.run("INF").has(TokenKind.DoubleValue).isInput();
 
     // Lexer rule GUID
-    test.run("1234ABCD-12AB-23CD-45EF-123456780ABC").isInput().isType(UriLexer.GUID);
-    test.run("1234ABCD-12AB-23CD-45EF-123456780ABC").isInput().isType(UriLexer.GUID);
+    test.run("1234ABCD-12AB-23CD-45EF-123456780ABC").has(TokenKind.GuidValue).isInput();
+    test.run("1234ABCD-12AB-23CD-45EF-123456780ABC").has(TokenKind.GuidValue).isInput();
 
     // Lexer rule DATE
-    test.run("2013-11-15").isInput().isType(UriLexer.DATE);
+    test.run("2013-11-15").has(TokenKind.DateValue).isInput();
 
     // Lexer rule DATETIMEOFFSET
-    test.run("2013-11-15T13:35Z").isInput().isType(UriLexer.DATETIMEOFFSET);
-    test.run("2013-11-15T13:35:10Z").isInput().isType(UriLexer.DATETIMEOFFSET);
-    test.run("2013-11-15T13:35:10.1234Z").isInput().isType(UriLexer.DATETIMEOFFSET);
+    test.run("2013-11-15T13:35Z").has(TokenKind.DateTimeOffsetValue).isInput();
+    test.run("2013-11-15T13:35:10Z").has(TokenKind.DateTimeOffsetValue).isInput();
+    test.run("2013-11-15T13:35:10.1234Z").has(TokenKind.DateTimeOffsetValue).isInput();
 
-    test.run("2013-11-15T13:35:10.1234+01:30").isInput().isType(UriLexer.DATETIMEOFFSET);
-    test.run("2013-11-15T13:35:10.1234-01:12").isInput().isType(UriLexer.DATETIMEOFFSET);
+    test.run("2013-11-15T13:35:10.1234+01:30").has(TokenKind.DateTimeOffsetValue).isInput();
+    test.run("2013-11-15T13:35:10.1234-01:12").has(TokenKind.DateTimeOffsetValue).isInput();
 
-    test.run("2013-11-15T13:35Z").isInput().isType(UriLexer.DATETIMEOFFSET);
+    test.run("2013-11-15T13:35Z").has(TokenKind.DateTimeOffsetValue).isInput();
 
     // Lexer rule DURATION
-    test.run("duration'PT67S'").isInput().isType(UriLexer.DURATION);
-    test.run("duration'PT67.89S'").isInput().isType(UriLexer.DURATION);
-
-    test.run("duration'PT5M'").isInput().isType(UriLexer.DURATION);
-    test.run("duration'PT5M67S'").isInput().isType(UriLexer.DURATION);
-    test.run("duration'PT5M67.89S'").isInput().isType(UriLexer.DURATION);
-
-    test.run("duration'PT4H'").isInput().isType(UriLexer.DURATION);
-    test.run("duration'PT4H67S'").isInput().isType(UriLexer.DURATION);
-    test.run("duration'PT4H67.89S'").isInput().isType(UriLexer.DURATION);
-    test.run("duration'PT4H5M'").isInput().isType(UriLexer.DURATION);
-    test.run("duration'PT4H5M67S'").isInput().isType(UriLexer.DURATION);
-    test.run("duration'PT4H5M67.89S'").isInput().isType(UriLexer.DURATION);
-
-    test.run("duration'P3D'");
-    test.run("duration'P3DT67S'").isInput().isType(UriLexer.DURATION);
-    test.run("duration'P3DT67.89S'").isInput().isType(UriLexer.DURATION);
-    test.run("duration'P3DT5M'").isInput().isType(UriLexer.DURATION);
-    test.run("duration'P3DT5M67S'").isInput().isType(UriLexer.DURATION);
-    test.run("duration'P3DT5M67.89S'").isInput().isType(UriLexer.DURATION);
-    test.run("duration'P3DT4H'").isInput().isType(UriLexer.DURATION);
-    test.run("duration'P3DT4H67S'").isInput().isType(UriLexer.DURATION);
-    test.run("duration'P3DT4H67.89S'").isInput().isType(UriLexer.DURATION);
-    test.run("duration'P3DT4H5M'").isInput().isType(UriLexer.DURATION);
-    test.run("duration'P3DT4H5M67S'").isInput().isType(UriLexer.DURATION);
-    test.run("duration'P3DT4H5M67.89S'").isInput().isType(UriLexer.DURATION);
-
-    test.run("DuRaTiOn'P3DT4H5M67.89S'").isInput().isType(UriLexer.DURATION);
-    test.run("DuRaTiOn'-P3DT4H5M67.89S'").isInput().isType(UriLexer.DURATION);
-
-    test.run("20:00").isInput().isType(UriLexer.TIMEOFDAY);
-    test.run("20:15:01").isInput().isType(UriLexer.TIMEOFDAY);
-    test.run("20:15:01.02").isInput().isType(UriLexer.TIMEOFDAY);
-
-    test.run("20:15:01.02").isInput().isType(UriLexer.TIMEOFDAY);
+    test.run("duration'PT67S'").has(TokenKind.DurationValue).isInput();
+    test.run("duration'PT67.89S'").has(TokenKind.DurationValue).isInput();
+
+    test.run("duration'PT5M'").has(TokenKind.DurationValue).isInput();
+    test.run("duration'PT5M67S'").has(TokenKind.DurationValue).isInput();
+    test.run("duration'PT5M67.89S'").has(TokenKind.DurationValue).isInput();
+
+    test.run("duration'PT4H'").has(TokenKind.DurationValue).isInput();
+    test.run("duration'PT4H67S'").has(TokenKind.DurationValue).isInput();
+    test.run("duration'PT4H67.89S'").has(TokenKind.DurationValue).isInput();
+    test.run("duration'PT4H5M'").has(TokenKind.DurationValue).isInput();
+    test.run("duration'PT4H5M67S'").has(TokenKind.DurationValue).isInput();
+    test.run("duration'PT4H5M67.89S'").has(TokenKind.DurationValue).isInput();
+
+    test.run("duration'P3D'").has(TokenKind.DurationValue).isInput();
+    test.run("duration'P3DT67S'").has(TokenKind.DurationValue).isInput();
+    test.run("duration'P3DT67.89S'").has(TokenKind.DurationValue).isInput();
+    test.run("duration'P3DT5M'").has(TokenKind.DurationValue).isInput();
+    test.run("duration'P3DT5M67S'").has(TokenKind.DurationValue).isInput();
+    test.run("duration'P3DT5M67.89S'").has(TokenKind.DurationValue).isInput();
+    test.run("duration'P3DT4H'").has(TokenKind.DurationValue).isInput();
+    test.run("duration'P3DT4H67S'").has(TokenKind.DurationValue).isInput();
+    test.run("duration'P3DT4H67.89S'").has(TokenKind.DurationValue).isInput();
+    test.run("duration'P3DT4H5M'").has(TokenKind.DurationValue).isInput();
+    test.run("duration'P3DT4H5M67S'").has(TokenKind.DurationValue).isInput();
+    test.run("duration'P3DT4H5M67.89S'").has(TokenKind.DurationValue).isInput();
+
+    test.run("DuRaTiOn'P3DT4H5M67.89S'").has(TokenKind.DurationValue).isInput();
+    test.run("DuRaTiOn'-P3DT4H5M67.89S'").has(TokenKind.DurationValue).isInput();
+
+    test.run("20:00").has(TokenKind.TimeOfDayValue).isInput();
+    test.run("20:15:01").has(TokenKind.TimeOfDayValue).isInput();
+    test.run("20:15:01.02").has(TokenKind.TimeOfDayValue).isInput();
+
+    test.run("20:15:01.02").has(TokenKind.TimeOfDayValue).isInput();
 
     // String
-    test.run("'ABC'").isText("'ABC'").isType(UriLexer.STRING);
-    test.run("'A%20C'").isInput().isType(UriLexer.STRING);
-    test.run("'%20%20%20ABC'").isInput().isType(UriLexer.STRING);
-
+    test.run("'ABC'").has(TokenKind.StringValue).isInput();
+    test.run("'A%20C'").has(TokenKind.StringValue).isInput();
+    test.run("'%20%20%20ABC'").has(TokenKind.StringValue).isInput();
   }
 
   @Test
-  public void testDelims() {
-    String reserved = "/";
-    test.globalMode(UriLexer.MODE_QUERY);
+  public void delims() {
+    final String reserved = "/";
     // Test lexer rule UNRESERVED
-    test.run("$format=A/" + cUNRESERVED).isAllInput().isType(UriLexer.FORMAT);
-    test.run("$format=A/" + cUNRESERVED + reserved).isType(UriLexer.FORMAT).at(4).isText(cUNRESERVED);
+//    test.run("$format=A/" + cUNRESERVED).has(TokenKind.FORMAT).isInput();
+//    test.run("$format=A/" + cUNRESERVED + reserved).has(TokenKind.FORMAT).isText(cUNRESERVED);
     // Test lexer rule PCT_ENCODED
-    test.run("$format=A/" + cPCT_ENCODED).isAllInput().isType(UriLexer.FORMAT);
-    test.run("$format=A/" + cPCT_ENCODED + reserved).isType(UriLexer.FORMAT).at(4).isText(cPCT_ENCODED);
+//    test.run("$format=A/" + cPCT_ENCODED).has(TokenKind.FORMAT).isInput();
+//    test.run("$format=A/" + cPCT_ENCODED + reserved).has(TokenKind.FORMAT).isText(cPCT_ENCODED);
     // Test lexer rule SUB_DELIMS
-    test.run("$format=A/" + cSUB_DELIMS).isAllInput().isType(UriLexer.FORMAT);
-    test.run("$format=A/" + cSUB_DELIMS + reserved).isType(UriLexer.FORMAT).at(4).isText("$");
+//    test.run("$format=A/" + cSUB_DELIMS).has(TokenKind.FORMAT).isInput();
+//    test.run("$format=A/" + cSUB_DELIMS + reserved).has(TokenKind.FORMAT).isText("$");
     // Test lexer rule PCHAR rest
-    test.run("$format=A/:@").isAllText("$format=A/:@").isType(UriLexer.FORMAT);
-    test.run("$format=A/:@" + reserved).isType(UriLexer.FORMAT).at(4).isText(":@");
+//    test.run("$format=A/:@").has(TokenKind.FORMAT).isInput();
+//    test.run("$format=A/:@" + reserved).has(TokenKind.FORMAT).isText(":@");
     // Test lexer rule PCHAR all
-    test.run("$format=" + cPCHAR + "/" + cPCHAR).isAllInput().isType(UriLexer.FORMAT);
-
+//    test.run("$format=" + cPCHAR + "/" + cPCHAR).has(TokenKind.FORMAT).isInput();
   }
 
+  public class TokenValidator {
+
+    private String input = null;
+    private UriTokenizer tokenizer = null;
+    private String curText = null;
+
+    public TokenValidator run(final String uri) {
+      input = uri;
+      tokenizer = new UriTokenizer(uri);
+      curText = "";
+      return this;
+    }
+
+    public TokenValidator has(final TokenKind... expected) {
+      for (final TokenKind kind : expected) {
+        assertTrue(tokenizer.next(kind));
+        curText += tokenizer.getText();
+      }
+      return this;
+    }
+
+    public TokenValidator isText(final String expected) {
+      assertEquals(expected, tokenizer.getText());
+      return this;
+    }
+
+    public TokenValidator isInput() {
+      assertEquals(input, curText);
+      assertTrue(tokenizer.next(TokenKind.EOF));
+      return this;
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestUriParserImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestUriParserImpl.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestUriParserImpl.java
index 8790766..dd517f9 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestUriParserImpl.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestUriParserImpl.java
@@ -27,7 +27,7 @@ import org.apache.olingo.server.api.edmx.EdmxReference;
 import org.apache.olingo.server.api.uri.UriInfoKind;
 import org.apache.olingo.server.api.uri.UriResourceKind;
 import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind;
-import org.apache.olingo.server.core.uri.parser.UriParserSyntaxException;
+import org.apache.olingo.server.core.uri.parser.UriParserSemanticException;
 import org.apache.olingo.server.core.uri.testutil.FilterValidator;
 import org.apache.olingo.server.core.uri.testutil.ResourceValidator;
 import org.apache.olingo.server.core.uri.testutil.TestUriValidator;
@@ -67,8 +67,7 @@ public class TestUriParserImpl {
       + "," + PropertyDateTimeOffset + "," + PropertyDuration + "," + PropertyGuid + "," + PropertyTimeOfDay;
 
   @Test
-  public void testBoundFunctionImport_VarParameters() {
-
+  public void boundFunctionImport_VarParameters() {
     // no input
     testRes.run("ESKeyNav(1)/olingo.odata.test1.BFCETKeyNavRTETKeyNav()")
     .at(0).isUriPathInfoKind(UriResourceKind.entitySet)
@@ -89,9 +88,8 @@ public class TestUriParserImpl {
   }
 
   @Test
-  public void testFunctionBound_varReturnType() {
-
-    String esTwoKeyNav = "ESTwoKeyNav(PropertyInt16=1,PropertyString='ABC')";
+  public void functionBound_varReturnType() {
+    final String esTwoKeyNav = "ESTwoKeyNav(PropertyInt16=1,PropertyString='ABC')";
 
     // returning primitive
     testRes.run("ESTwoKeyNav/olingo.odata.test1.BFCESTwoKeyNavRTString()")
@@ -151,8 +149,7 @@ public class TestUriParserImpl {
   }
 
   @Test
-  public void runActionImport_VarReturnType() {
-
+  public void actionImport_VarReturnType() {
     testRes.run(ContainerProvider.AIRT_STRING).isKind(UriInfoKind.resource)
     .first()
     .isActionImport(ContainerProvider.AIRT_STRING)
@@ -189,7 +186,6 @@ public class TestUriParserImpl {
 
   @Test
   public void count() {
-
     // count entity set
     testRes.run("ESAllPrim/$count")
     .at(0)
@@ -216,7 +212,7 @@ public class TestUriParserImpl {
   }
 
   @Test
-  public void runCrossJoin() throws Exception {
+  public void crossJoin() throws Exception {
     testUri.run("$crossjoin(ESAllKey)")
     .isKind(UriInfoKind.crossjoin)
     .isCrossJoinEntityList(Arrays.asList("ESAllKey"));
@@ -226,29 +222,15 @@ public class TestUriParserImpl {
     .isCrossJoinEntityList(Arrays.asList("ESAllKey", "ESTwoPrim"));
   }
 
-  @Test(expected = UriValidationException.class)
-  public void testEntityFailOnValidation1() throws Exception {
+  @Test
+  public void entityFailOnValidation() throws Exception {
     // simple entity set; with qualifiedentityTypeName; with filter
-    testUri.run("$entity/olingo.odata.test1.ETTwoPrim", "$filter=PropertyInt16 eq 123&$id=ESAllKey")
-    .isIdText("ESAllKey")
-    .goFilter().is("<<PropertyInt16> eq <123>>");
-  }
-
-  @Test(expected = UriParserSyntaxException.class)
-  public void testEntityFailOnValidation2() throws Exception {
-    // simple entity set; with qualifiedentityTypeName; with 2xformat(before and after), expand, filter
-    testUri.run("$entity/olingo.odata.test1.ETTwoPrim",
-        "$format=xml&$expand=*&abc=123&$id=ESBase&xyz=987&$filter=PropertyInt16 eq 123&$format=atom&$select=*")
-        .isFormatText("atom")
-        .isCustomParameter(0, "abc", "123")
-        .isIdText("ESBase")
-        .isCustomParameter(1, "xyz", "987")
-        .isSelectItemStar(0);
+    testUri.runEx("$entity/olingo.odata.test1.ETTwoPrim", "$filter=PropertyInt16 eq 123&$id=ESAllKey")
+        .isExValidation(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED);
   }
 
   @Test
-  public void testEntity() throws Exception {
-
+  public void entity() throws Exception {
     // simple entity set
     testUri.run("$entity", "$id=ESAllPrim").isKind(UriInfoKind.entityId)
     .isKind(UriInfoKind.entityId)
@@ -311,14 +293,11 @@ public class TestUriParserImpl {
     .isKind(UriInfoKind.entityId)
     .isEntityType(EntityTypeProvider.nameETBase)
     .isIdText("ESTwoPrim")
-    .isExpandText("*")
     .goExpand().first().isSegmentStar();
-
   }
 
   @Test
   public void entitySet() throws Exception {
-
     // plain entity set
     testRes.run("ESAllPrim")
     .isEntitySet("ESAllPrim")
@@ -342,7 +321,7 @@ public class TestUriParserImpl {
     .isKeyPredicate(1, "PropertyString", "'ABC'");
 
     // with all keys
-    testRes.run("ESAllKey(" + encode(allKeys) + ")")
+    testRes.run("ESAllKey(" + allKeys + ")")
     .isEntitySet("ESAllKey")
     .isKeyPredicate(0, "PropertyString", "'ABC'")
     .isKeyPredicate(1, "PropertyInt16", "1")
@@ -360,10 +339,7 @@ public class TestUriParserImpl {
   }
 
   @Test
-  public void testEntitySet_NavigationProperty() {
-
-    // plain entity set ...
-
+  public void entitySet_NavigationProperty() {
     // with navigation property
     testRes.run("ESKeyNav(1)/NavPropertyETTwoKeyNavOne")
     .at(0)
@@ -467,10 +443,7 @@ public class TestUriParserImpl {
   }
 
   @Test
-  public void testEntitySet_Property() {
-
-    // plain entity set ...
-
+  public void entitySet_Property() {
     // with property
     testRes.run("ESAllPrim(1)/PropertyString")
     .at(0)
@@ -499,8 +472,7 @@ public class TestUriParserImpl {
   }
 
   @Test
-  public void testEntitySet_TypeFilter() {
-
+  public void entitySet_TypeFilter() {
     // filter
     testRes.run("ESTwoPrim/olingo.odata.test1.ETBase")
     .at(0)
@@ -556,57 +528,53 @@ public class TestUriParserImpl {
     .at(1)
     .isPrimitiveProperty("AdditionalPropertyString_5", PropertyProvider.nameString, false)
     .isType(PropertyProvider.nameString);
-
   }
 
   @Test
   public void unary() throws Exception {
-    testFilter.runOnETAllPrim("not PropertyBoolean").isCompr("<not <PropertyBoolean>>");
-    testFilter.runOnETAllPrim("-PropertyInt16 eq PropertyInt16").isCompr("<<- <PropertyInt16>> eq <PropertyInt16>>");
+    testFilter.runOnETAllPrim("not PropertyBoolean").is("<not <PropertyBoolean>>");
+    testFilter.runOnETAllPrim("-PropertyInt16 eq PropertyInt16").is("<<- <PropertyInt16>> eq <PropertyInt16>>");
   }
 
-  // TODO: Use correct types.
   @Test
-  @Ignore
   public void filterComplexMixedPriority() throws Exception {
-    testFilter.runOnETAllPrim("PropertyInt16 or PropertyInt32 and PropertyInt64")
-        .isCompr("<<PropertyInt16> or <<PropertyInt32> and <PropertyInt64>>>");
-    testFilter.runOnETAllPrim("PropertyInt16 or PropertyInt32 and PropertyInt64 eq PropertyByte")
-        .isCompr("<<PropertyInt16> or <<PropertyInt32> and <<PropertyInt64> eq <PropertyByte>>>>");
-    testFilter.runOnETAllPrim("PropertyInt16 or PropertyInt32 eq PropertyInt64 and PropertyByte")
-        .isCompr("<<PropertyInt16> or <<<PropertyInt32> eq <PropertyInt64>> and <PropertyByte>>>");
-    testFilter.runOnETAllPrim("PropertyInt16 or PropertyInt32 eq PropertyInt64 and PropertyByte eq PropertySByte")
-        .isCompr("<<PropertyInt16> or <<<PropertyInt32> eq <PropertyInt64>> "
+    testFilter.runOnETAllPrim("PropertyBoolean or true and false")
+        .is("<<PropertyBoolean> or <<true> and <false>>>");
+    testFilter.runOnETAllPrim("PropertyBoolean or true and PropertyInt64 eq PropertyByte")
+        .is("<<PropertyBoolean> or <<true> and <<PropertyInt64> eq <PropertyByte>>>>");
+    testFilter.runOnETAllPrim("PropertyBoolean or PropertyInt32 eq PropertyInt64 and true")
+        .is("<<PropertyBoolean> or <<<PropertyInt32> eq <PropertyInt64>> and <true>>>");
+    testFilter.runOnETAllPrim("PropertyBoolean or PropertyInt32 eq PropertyInt64 and PropertyByte eq PropertySByte")
+        .is("<<PropertyBoolean> or <<<PropertyInt32> eq <PropertyInt64>> "
             + "and <<PropertyByte> eq <PropertySByte>>>>");
-    testFilter.runOnETAllPrim("PropertyInt16 eq PropertyInt32 or PropertyInt64 and PropertyByte")
-        .isCompr("<<<PropertyInt16> eq <PropertyInt32>> or <<PropertyInt64> and <PropertyByte>>>");
-    testFilter.runOnETAllPrim("PropertyInt16 eq PropertyInt32 or PropertyInt64 and PropertyByte eq PropertySByte")
-        .isCompr("<<<PropertyInt16> eq <PropertyInt32>> "
-            + "or <<PropertyInt64> and <<PropertyByte> eq <PropertySByte>>>>");
-    testFilter.runOnETAllPrim("PropertyInt16 eq PropertyInt32 or PropertyInt64 eq PropertyByte and PropertySByte")
-        .isCompr("<<<PropertyInt16> eq <PropertyInt32>> "
-            + "or <<<PropertyInt64> eq <PropertyByte>> and <PropertySByte>>>");
+    testFilter.runOnETAllPrim("PropertyInt16 eq PropertyInt32 or PropertyBoolean and true")
+        .is("<<<PropertyInt16> eq <PropertyInt32>> or <<PropertyBoolean> and <true>>>");
+    testFilter.runOnETAllPrim("PropertyInt16 eq PropertyInt32 or PropertyBoolean and PropertyByte eq PropertySByte")
+        .is("<<<PropertyInt16> eq <PropertyInt32>> "
+            + "or <<PropertyBoolean> and <<PropertyByte> eq <PropertySByte>>>>");
+    testFilter.runOnETAllPrim("PropertyInt16 eq PropertyInt32 or PropertyInt64 eq PropertyByte and PropertyBoolean")
+        .is("<<<PropertyInt16> eq <PropertyInt32>> "
+            + "or <<<PropertyInt64> eq <PropertyByte>> and <PropertyBoolean>>>");
     testFilter.runOnETAllPrim("PropertyInt16 eq PropertyInt32 or PropertyInt64 eq PropertyByte "
         + "and PropertySByte eq PropertyDecimal")
-        .isCompr("<<<PropertyInt16> eq <PropertyInt32>> or <<<PropertyInt64> eq <PropertyByte>> "
+        .is("<<<PropertyInt16> eq <PropertyInt32>> or <<<PropertyInt64> eq <PropertyByte>> "
             + "and <<PropertySByte> eq <PropertyDecimal>>>>");
   }
 
   @Test
   public void filterSimpleSameBinaryBinaryBinaryPriority() throws Exception {
-    testFilter.runOnETAllPrim("1 add 2 add 3 add 4").isCompr("<<< <1> add   <2>> add  <3>>  add <4>>");
-    testFilter.runOnETAllPrim("1 add 2 add 3 div 4").isCompr("<<  <1> add   <2>> add <<3>   div <4>>>");
-    testFilter.runOnETAllPrim("1 add 2 div 3 add 4").isCompr("<<  <1> add  <<2>  div  <3>>> add <4>>");
-    testFilter.runOnETAllPrim("1 add 2 div 3 div 4").isCompr("<   <1> add <<<2>  div  <3>>  div <4>>>");
-    testFilter.runOnETAllPrim("1 div 2 add 3 add 4").isCompr("<<< <1> div   <2>> add  <3>>  add <4>>");
-    testFilter.runOnETAllPrim("1 div 2 add 3 div 4").isCompr("<<  <1> div   <2>> add <<3>   div <4>>>");
-    testFilter.runOnETAllPrim("1 div 2 div 3 add 4").isCompr("<<< <1> div   <2>> div  <3>>  add <4>>");
-    testFilter.runOnETAllPrim("1 div 2 div 3 div 4").isCompr("<<< <1> div   <2>> div  <3>>  div <4>>");
+    testFilter.runOnETAllPrim("1 add 2 add 3 add 4 ge 0").isCompr("<<<< <1> add   <2>> add  <3>>  add <4>> ge <0>>");
+    testFilter.runOnETAllPrim("1 add 2 add 3 div 4 ge 0").isCompr("<<<  <1> add   <2>> add <<3>   div <4>>> ge <0>>");
+    testFilter.runOnETAllPrim("1 add 2 div 3 add 4 ge 0").isCompr("<<<  <1> add  <<2>  div  <3>>> add <4>> ge <0>>");
+    testFilter.runOnETAllPrim("1 add 2 div 3 div 4 ge 0").isCompr("<<   <1> add <<<2>  div  <3>>  div <4>>> ge <0>>");
+    testFilter.runOnETAllPrim("1 div 2 add 3 add 4 ge 0").isCompr("<<<< <1> div   <2>> add  <3>>  add <4>> ge <0>>");
+    testFilter.runOnETAllPrim("1 div 2 add 3 div 4 ge 0").isCompr("<<<  <1> div   <2>> add <<3>   div <4>>> ge <0>>");
+    testFilter.runOnETAllPrim("1 div 2 div 3 add 4 ge 0").isCompr("<<<< <1> div   <2>> div  <3>>  add <4>> ge <0>>");
+    testFilter.runOnETAllPrim("1 div 2 div 3 div 4 ge 0").isCompr("<<<< <1> div   <2>> div  <3>>  div <4>> ge <0>>");
   }
 
   @Test
-  public void testFunctionImport_VarParameters() {
-
+  public void functionImport_VarParameters() {
     // no input
     testRes.run("FINRTInt16()")
     .isFunctionImport("FINRTInt16")
@@ -627,7 +595,7 @@ public class TestUriParserImpl {
   }
 
   @Test
-  public void testFunctionImport_VarReturning() {
+  public void functionImport_VarReturning() {
     // returning primitive
     testRes.run("FINRTInt16()")
     .isFunctionImport("FINRTInt16")
@@ -666,8 +634,7 @@ public class TestUriParserImpl {
   }
 
   @Test
-  public void testFunctionImportChain() {
-
+  public void functionImportChain() {
     // test chain; returning single complex
     testRes.run("FICRTCTAllPrimTwoParam(ParameterString='ABC',ParameterInt16=1)/PropertyInt16")
     .at(0)
@@ -710,12 +677,10 @@ public class TestUriParserImpl {
     .isKeyPredicate(1, "PropertyString", "'ABC'")
     .at(1)
     .isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
-
   }
 
   @Test
-  public void testMetaData() throws Exception {
-
+  public void metaData() throws Exception {
     // Parsing the fragment may be used if a uri has to be parsed on the consumer side.
     // On the producer side this feature is currently not supported, so the context fragment
     // part is only available as text.
@@ -879,12 +844,12 @@ public class TestUriParserImpl {
   }
 
   @Test
-  public void testRef() throws Exception {
+  public void ref() throws Exception {
     testUri.run("ESKeyNav(1)/NavPropertyETTwoKeyNavOne/$ref");
   }
 
   @Test
-  public void testSingleton() {
+  public void singleton() {
     // plain singleton
     testRes.run("SINav")
     .isSingleton("SINav")
@@ -892,10 +857,7 @@ public class TestUriParserImpl {
   }
 
   @Test
-  public void testNavigationProperty() {
-
-    // plain entity set ...
-
+  public void navigationProperty() {
     // with navigation property
     testRes.run("ESKeyNav(1)/NavPropertyETTwoKeyNavOne")
     .at(0).isEntitySet("ESKeyNav")
@@ -969,10 +931,7 @@ public class TestUriParserImpl {
   }
 
   @Test
-  public void testSingleton_Property() {
-
-    // plain singleton ...
-
+  public void singleton_Property() {
     // with property
     testRes.run("SINav/PropertyInt16")
     .at(0)
@@ -1002,72 +961,51 @@ public class TestUriParserImpl {
   }
 
   @Test
-  public void testValue() throws Exception {
+  public void value() throws Exception {
     testUri.run("ESAllPrim(1)/PropertyString/$value");
   }
 
-  @Test(expected = UriValidationException.class)
-  public void testMemberStartingWithCastFailOnValidation1() throws Exception {
+  @Test
+  public void memberStartingWithCastFailOnValidation1() throws Exception {
     // on EntityType entry
-    testUri.run("ESTwoKeyNav(ParameterInt16=1,PropertyString='ABC')",
+    testUri.runEx("ESTwoKeyNav(Property16=1,PropertyString='ABC')",
         "$filter=olingo.odata.test1.ETBaseTwoKeyNav/PropertyDate")
-        .goFilter().root().isMember()
-        .isMemberStartType(EntityTypeProvider.nameETBaseTwoKeyNav).goPath()
-        // .at(0)
-        // .isUriPathInfoKind(UriResourceKind.startingTypeFilter)
-        // .isType(EntityTypeProvider.nameETTwoKeyNav, false)
-        // .isTypeFilterOnEntry(EntityTypeProvider.nameETBaseTwoKeyNav)
-        .at(0).isType(PropertyProvider.nameDate);
+        .isExValidation(UriValidationException.MessageKeys.INVALID_KEY_PROPERTY);
   }
 
-  @Test(expected = UriValidationException.class)
-  public void testMemberStartingWithCastFailOnValidation2() throws Exception {
-    testUri.run("FICRTCTTwoPrimTwoParam(ParameterInt16=1,ParameterString='2')",
+  @Test
+  public void memberStartingWithCastFailOnValidation2() throws Exception {
+    testUri.runEx("FICRTCTTwoPrimTwoParam(ParameterInt16=1,ParameterString='2')",
         "$filter=olingo.odata.test1.CTBase/AdditionalPropString")
-        .goFilter().root().isMember()
-        .isMemberStartType(ComplexTypeProvider.nameCTBase).goPath()
-        // .at(0)
-        // .isUriPathInfoKind(UriResourceKind.startingTypeFilter)
-        // .isType(ComplexTypeProvider.nameCTTwoPrim, false)
-        // .isTypeFilterOnEntry(ComplexTypeProvider.nameCTBase)
-        .at(0).isType(PropertyProvider.nameString);
+        .isExSemantic(UriParserSemanticException.MessageKeys.TYPES_NOT_COMPATIBLE);
   }
 
   @Test
-  public void testMemberStartingWithCast() throws Exception {
-
+  public void memberStartingWithCast() throws Exception {
     // on EntityType collection
-    testUri.run("ESTwoKeyNav", "$filter=olingo.odata.test1.ETBaseTwoKeyNav/PropertyDate")
-    .goFilter().root().isMember()
+    testFilter.runOnETTwoKeyNav("olingo.odata.test1.ETBaseTwoKeyNav/PropertyDate eq null")
+    .left()
+    .isMember()
     .isMemberStartType(EntityTypeProvider.nameETBaseTwoKeyNav).goPath()
-    // .at(0)
-    // .isUriPathInfoKind(UriResourceKind.startingTypeFilter)
-    // .isType(EntityTypeProvider.nameETTwoKeyNav, true)
-    // .isTypeFilterOnCollection(EntityTypeProvider.nameETBaseTwoKeyNav)
     .at(0).isType(PropertyProvider.nameDate);
 
     // on Complex collection
     testUri.run("FICRTCollCTTwoPrimTwoParam(ParameterInt16=1,ParameterString='2')",
-        "$filter=olingo.odata.test1.CTBase/AdditionalPropString")
-        .goFilter().root().isMember()
+        "$filter=olingo.odata.test1.CTBase/AdditionalPropString eq null")
+        .goFilter().left().isMember()
         .isMemberStartType(ComplexTypeProvider.nameCTBase).goPath()
-        // .at(0)
-        // .isUriPathInfoKind(UriResourceKind.startingTypeFilter)
-        // .isType(ComplexTypeProvider.nameCTTwoPrim, true)
-        // .isTypeFilterOnCollection(ComplexTypeProvider.nameCTBase)
         .at(0).isType(PropertyProvider.nameString);
-
   }
 
   @Test
-  public void testComplexTypeCastFollowingAsCollection() throws Exception {
+  public void complexTypeCastFollowingAsCollection() throws Exception {
     testUri.run("FICRTCollCTTwoPrimTwoParam(ParameterInt16=1,ParameterString='2')/olingo.odata.test1.CTBase");
   }
 
   @Test
-  public void testAlias() throws Exception {
-    testUri.run("ESAllPrim", "$filter=PropertyInt16 eq @p1&@p1=1")
-    .goFilter().is("<<PropertyInt16> eq <@p1>>");
+  public void alias() throws Exception {
+    testFilter.runOnETAllPrim("PropertyInt16 eq @p1&@p1=1")
+        .is("<<PropertyInt16> eq <@p1>>");
   }
 
   @Test
@@ -1087,26 +1025,22 @@ public class TestUriParserImpl {
   @Test
   public void customQueryOption() throws Exception {
     testUri.run("ESTwoKeyNav", "custom")
-    .isCustomParameter(0, "custom", "");
+        .isCustomParameter(0, "custom", "");
     testUri.run("ESTwoKeyNav", "custom=ABC")
-    .isCustomParameter(0, "custom", "ABC");
+        .isCustomParameter(0, "custom", "ABC");
   }
 
   @Test
   @Ignore("Geo types are not supported yet.")
   public void geo() throws Exception {
     testFilter.runOnETAllPrim("geo.distance(PropertySByte,PropertySByte)")
-    .is("<geo.distance(<PropertySByte>,<PropertySByte>)>")
-    .isMethod(MethodKind.GEODISTANCE, 2);
+        .is("<geo.distance(<PropertySByte>,<PropertySByte>)>")
+        .isMethod(MethodKind.GEODISTANCE, 2);
     testFilter.runOnETAllPrim("geo.length(PropertySByte)")
-    .is("<geo.length(<PropertySByte>)>")
-    .isMethod(MethodKind.GEOLENGTH, 1);
+        .is("<geo.length(<PropertySByte>)>")
+        .isMethod(MethodKind.GEOLENGTH, 1);
     testFilter.runOnETAllPrim("geo.intersects(PropertySByte,PropertySByte)")
-    .is("<geo.intersects(<PropertySByte>,<PropertySByte>)>")
-    .isMethod(MethodKind.GEOINTERSECTS, 2);
-  }
-
-  private final String encode(final String uriPart) {
-    return uriPart.replaceAll(":", "%3A");
+        .is("<geo.intersects(<PropertySByte>,<PropertySByte>)>")
+        .isMethod(MethodKind.GEOINTERSECTS, 2);
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/ParserTest.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/ParserTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/ParserTest.java
index f54ad04..af523aa 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/ParserTest.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/ParserTest.java
@@ -18,12 +18,18 @@
  */
 package org.apache.olingo.server.core.uri.parser;
 
+import java.util.Collections;
+
 import org.apache.olingo.commons.api.edm.Edm;
 import org.apache.olingo.commons.api.edm.EdmEntityContainer;
 import org.apache.olingo.commons.api.edm.EdmEntitySet;
 import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmKeyPropertyRef;
 import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
+import org.apache.olingo.commons.api.edm.EdmProperty;
 import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.server.api.OData;
 import org.apache.olingo.server.api.uri.UriInfoKind;
 import org.apache.olingo.server.core.uri.testutil.TestUriValidator;
 import org.junit.Test;
@@ -33,17 +39,57 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
 /**
- * All Tests which involves the <code>Parser</code> implementation
- * (and with that also the <code>UriParseTreeVisitor</code>).
+ * Tests of the <code>Parser</code> implementation that require mocking of the EDM.
  */
 public class ParserTest {
 
+  @Test
+  public void navPropertySameNameAsEntitySet() throws Exception {
+    final String namespace = "namespace";
+    final String entityTypeName = "ETNavProp";
+    final FullQualifiedName nameETNavProp = new FullQualifiedName(namespace, entityTypeName);
+    final String entitySetName = "ESNavProp";
+    final String keyPropertyName = "a";
+    EdmProperty keyProperty = Mockito.mock(EdmProperty.class);
+    Mockito.when(keyProperty.getType())
+        .thenReturn(OData.newInstance().createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Byte));
+    EdmKeyPropertyRef keyPropertyRef = Mockito.mock(EdmKeyPropertyRef.class);
+    Mockito.when(keyPropertyRef.getName()).thenReturn(keyPropertyName);
+    Mockito.when(keyPropertyRef.getProperty()).thenReturn(keyProperty);
+    EdmNavigationProperty navProperty = Mockito.mock(EdmNavigationProperty.class);
+    Mockito.when(navProperty.getName()).thenReturn(entitySetName);
+    Mockito.when(navProperty.isCollection()).thenReturn(true);
+    EdmEntityType entityType = Mockito.mock(EdmEntityType.class);
+    Mockito.when(entityType.getFullQualifiedName()).thenReturn(nameETNavProp);
+    Mockito.when(entityType.getKeyPredicateNames()).thenReturn(Collections.singletonList(keyPropertyName));
+    Mockito.when(entityType.getKeyPropertyRefs()).thenReturn(Collections.singletonList(keyPropertyRef));
+    Mockito.when(entityType.getNavigationProperty(entitySetName)).thenReturn(navProperty);
+    Mockito.when(navProperty.getType()).thenReturn(entityType);
+    EdmEntitySet entitySet = Mockito.mock(EdmEntitySet.class);
+    Mockito.when(entitySet.getName()).thenReturn(entitySetName);
+    Mockito.when(entitySet.getEntityType()).thenReturn(entityType);
+    EdmEntityContainer container = Mockito.mock(EdmEntityContainer.class);
+    Mockito.when(container.getEntitySet(entitySetName)).thenReturn(entitySet);
+    Edm mockedEdm = Mockito.mock(Edm.class);
+    Mockito.when(mockedEdm.getEntityContainer()).thenReturn(container);
+    new TestUriValidator().setEdm(mockedEdm)
+        .run("ESNavProp(1)/ESNavProp(2)/ESNavProp(3)/ESNavProp")
+        .goPath()
+        .at(0).isEntitySet(entitySetName)
+        .at(0).isKeyPredicate(0, keyPropertyName, "1")
+        .at(1).isNavProperty(entitySetName, nameETNavProp, false)
+        .at(1).isKeyPredicate(0, keyPropertyName, "2")
+        .at(2).isNavProperty(entitySetName, nameETNavProp, false)
+        .at(2).isKeyPredicate(0, keyPropertyName, "3")
+        .at(3).isNavProperty(entitySetName, nameETNavProp, true);
+  }
+
   /**
    * Test for EntitySet and NavigationProperty with same name defined in metadata.
    * (related to Olingo issue OLINGO-741)
    */
   @Test
-  public void parseEntitySetAndNavigationPropertyWithSameName() throws Exception {
+  public void expandNavigationPropertyWithSameNameAsEntitySet() throws Exception {
     TestUriValidator testUri = new TestUriValidator();
 
     Edm mockEdm = Mockito.mock(Edm.class);
@@ -60,7 +106,7 @@ public class ParserTest {
     Mockito.when(typeCategory.getNamespace()).thenReturn("NS");
     Mockito.when(esCategory.getEntityType()).thenReturn(typeCategory);
     Mockito.when(productsNavigation.getName()).thenReturn("Products");
-    Mockito.when(typeCategory.getProperty("Products")).thenReturn(productsNavigation);
+    Mockito.when(typeCategory.getNavigationProperty("Products")).thenReturn(productsNavigation);
     Mockito.when(container.getEntitySet("Category")).thenReturn(esCategory);
     Mockito.when(container.getEntitySet("Products")).thenReturn(esProduct);
     Mockito.when(productsType.getFullQualifiedName()).thenReturn(nameProducts);
@@ -122,7 +168,7 @@ public class ParserTest {
           .isType(new FullQualifiedName("NS", "Category"), false);
       fail("Expected exception was not thrown.");
     } catch (final UriParserException e) {
-      assertEquals("NavigationProperty 'Category' not found in type 'NS.Products'", e.getMessage());
+      assertEquals("Navigation Property 'Category' not found in type 'NS.Products'.", e.getMessage());
     }
   }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/ExpandValidator.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/ExpandValidator.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/ExpandValidator.java
index 5638227..35245b4 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/ExpandValidator.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/ExpandValidator.java
@@ -19,6 +19,7 @@
 package org.apache.olingo.server.core.uri.testutil;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -151,12 +152,6 @@ public class ExpandValidator implements TestValidator {
     return this;
   }
 
-  public ExpandValidator isSelectText(final String text) {
-    final QueryOption option = expandItem.getSelectOption();
-    assertEquals(text, option.getText());
-    return this;
-  }
-
   public ExpandValidator isSelectItemStar(final int index) {
     SelectOption select = expandItem.getSelectOption();
     SelectItem item = select.getSelectItems().get(index);
@@ -171,12 +166,6 @@ public class ExpandValidator implements TestValidator {
     return this;
   }
 
-  public ExpandValidator isFilterOptionText(final String text) {
-    QueryOption option = expandItem.getFilterOption();
-    assertEquals(text, option.getText());
-    return this;
-  }
-
   public ExpandValidator isFilterSerialized(final String serialized) {
     FilterOption filter = expandItem.getFilterOption();
 
@@ -201,7 +190,13 @@ public class ExpandValidator implements TestValidator {
   }
 
   public ExpandValidator isExpandStartType(final FullQualifiedName fullName) {
+    assertNotNull(expandItem.getStartTypeFilter());
     assertEquals(fullName, expandItem.getStartTypeFilter().getFullQualifiedName());
     return this;
   }
+
+  public ExpandValidator isSearchSerialized(final String serialized) {
+    assertEquals(serialized, expandItem.getSearchOption().getSearchExpression().toString());
+    return this;
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/FilterValidator.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/FilterValidator.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/FilterValidator.java
index 0800dd0..161299c 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/FilterValidator.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/FilterValidator.java
@@ -46,13 +46,16 @@ 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.core.uri.UriResourceFunctionImpl;
 import org.apache.olingo.server.core.uri.parser.Parser;
 import org.apache.olingo.server.core.uri.parser.UriParserException;
 import org.apache.olingo.server.core.uri.parser.UriParserSemanticException;
 import org.apache.olingo.server.core.uri.parser.UriParserSyntaxException;
+import org.apache.olingo.server.core.uri.queryoption.expression.BinaryImpl;
 import org.apache.olingo.server.core.uri.queryoption.expression.MemberImpl;
 import org.apache.olingo.server.core.uri.queryoption.expression.MethodImpl;
+import org.apache.olingo.server.core.uri.queryoption.expression.UnaryImpl;
 import org.apache.olingo.server.core.uri.validator.UriValidationException;
 
 public class FilterValidator implements TestValidator {
@@ -127,7 +130,7 @@ public class FilterValidator implements TestValidator {
   }
 
   public FilterValidator runOrderByOnETTwoKeyNavEx(final String orderBy) throws UriParserException {
-    return runUriOrderByEx("ESTwoKeyNav", "$orderby=" + orderBy.trim());
+    return runUriEx("ESTwoKeyNav", "$orderby=" + orderBy.trim());
   }
 
   public FilterValidator runOnETTwoKeyNav(final String filter) throws UriParserException, UriValidationException {
@@ -225,19 +228,6 @@ public class FilterValidator implements TestValidator {
     return this;
   }
 
-  public FilterValidator runUriOrderByEx(final String path, final String query) {
-    exception = null;
-    try {
-      new Parser(edm, odata).parseUri(path, query, null);
-      fail("Expected exception not thrown.");
-    } catch (final UriParserException e) {
-      exception = e;
-    } catch (final UriValidationException e) {
-      exception = e;
-    }
-    return this;
-  }
-
   // --- Navigation ---
 
   public ExpandValidator goUpToExpandValidator() {
@@ -274,8 +264,8 @@ public class FilterValidator implements TestValidator {
   // --- Validation ---
 
   /**
-   * Validates the serialized filterTree against a given filterString
-   * The given expected filterString is compressed before to allow better readable code in the unit tests
+   * Validates the serialized filterTree against a given filterString.
+   * The given expected filterString is compressed before to allow better readable code in the unit tests.
    * @param toBeCompr
    * @return {@link FilterValidator}
    */
@@ -310,14 +300,19 @@ public class FilterValidator implements TestValidator {
     EdmType actualType = null;
 
     if (curExpression instanceof Member) {
-      Member member = (Member) curExpression;
-      actualType = member.getType();
+      actualType = ((Member) curExpression).getType();
     } else if (curExpression instanceof TypeLiteral) {
-      TypeLiteral typeLiteral = (TypeLiteral) curExpression;
-      actualType = typeLiteral.getType();
+      actualType = ((TypeLiteral) curExpression).getType();
     } else if (curExpression instanceof Literal) {
-      Literal typeLiteral = (Literal) curExpression;
-      actualType = typeLiteral.getType();
+      actualType = ((Literal) curExpression).getType();
+    } else if (curExpression instanceof Enumeration) {
+      actualType = ((Enumeration) curExpression).getType();
+    } else if (curExpression instanceof Unary) {
+      actualType = ((UnaryImpl) curExpression).getType();
+    } else if (curExpression instanceof Binary) {
+      actualType = ((BinaryImpl) curExpression).getType();
+    } else if (curExpression instanceof Method) {
+      actualType = ((MethodImpl) curExpression).getType();
     }
 
     if (actualType == null) {
@@ -349,7 +344,6 @@ public class FilterValidator implements TestValidator {
 
     curExpression = ((Binary) curExpression).getRightOperand();
     return this;
-
   }
 
   public FilterValidator isLiteral(final String literalText) {
@@ -361,9 +355,9 @@ public class FilterValidator implements TestValidator {
     assertEquals(literalText, actualLiteralText);
     return this;
   }
-  
-  public FilterValidator isLiteralType(EdmType edmType) {
-    if(!(curExpression instanceof Literal)) {
+
+  public FilterValidator isLiteralType(final EdmType edmType) {
+    if (!(curExpression instanceof Literal)) {
       fail("Current expression is not a literal");
     }
     
@@ -374,7 +368,7 @@ public class FilterValidator implements TestValidator {
   }
 
   public FilterValidator isNullLiteralType() {
-    if(!(curExpression instanceof Literal)) {
+    if (!(curExpression instanceof Literal)) {
       fail("Current expression is not a literal");
     }
 
@@ -495,12 +489,4 @@ public class FilterValidator implements TestValidator {
     assertEquals(messageKey, exception.getMessageKey());
     return this;
   }
-
-  public FilterValidator isNull() {
-    return isLiteral("null");
-  }
-
-  public FilterValidator isTrue() {
-    return isLiteral("true");
-  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TestUriValidator.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TestUriValidator.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TestUriValidator.java
index 1dbe62b..fbce5c9 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TestUriValidator.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TestUriValidator.java
@@ -140,6 +140,20 @@ public class TestUriValidator implements TestValidator {
     return this;
   }
 
+  public TestUriValidator isSelectItemStar(final int index) {
+    final SelectOption select = uriInfo.getSelectOption();
+    SelectItem item = select.getSelectItems().get(index);
+    assertTrue(item.isStar());
+    return this;
+  }
+
+  public TestUriValidator isSelectItemAllOp(final int index, final FullQualifiedName fqn) {
+    final SelectOption select = uriInfo.getSelectOption();
+    SelectItem item = select.getSelectItems().get(index);
+    assertEquals(fqn, item.getAllOperationsInSchemaNameSpace());
+    return this;
+  }
+
   // Validation
   public TestUriValidator isKind(final UriInfoKind kind) {
     assertEquals(kind, uriInfo.getKind());
@@ -202,16 +216,6 @@ public class TestUriValidator implements TestValidator {
     return this;
   }
 
-  public TestUriValidator isExpandText(final String text) {
-    assertEquals(text, uriInfo.getExpandOption().getText());
-    return this;
-  }
-
-  public TestUriValidator isSelectText(final String text) {
-    assertEquals(text, uriInfo.getSelectOption().getText());
-    return this;
-  }
-
   public TestUriValidator isFormatText(final String text) {
     assertEquals(text, uriInfo.getFormatOption().getText());
     return this;
@@ -234,18 +238,4 @@ public class TestUriValidator implements TestValidator {
     assertEquals(fullName, uriInfo.getEntityTypeCast().getFullQualifiedName());
     return this;
   }
-
-  public TestUriValidator isSelectItemStar(final int index) {
-    final SelectOption select = uriInfo.getSelectOption();
-    SelectItem item = select.getSelectItems().get(index);
-    assertTrue(item.isStar());
-    return this;
-  }
-
-  public TestUriValidator isSelectItemAllOp(final int index, final FullQualifiedName fqn) {
-    final SelectOption select = uriInfo.getSelectOption();
-    SelectItem item = select.getSelectItems().get(index);
-    assertEquals(fqn, item.getAllOperationsInSchemaNameSpace());
-    return this;
-  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TokenValidator.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TokenValidator.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TokenValidator.java
deleted file mode 100644
index 547c2ea..0000000
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TokenValidator.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * 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.testutil;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.List;
-
-import org.antlr.v4.runtime.ANTLRInputStream;
-import org.antlr.v4.runtime.Token;
-import org.apache.olingo.server.core.uri.antlr.UriLexer;
-
-public class TokenValidator {
-
-  private String input = null;
-  private List<? extends Token> tokens = null;
-  private Token curToken = null;
-  private Exception curException = null;
-
-  private int startMode;
-
-  // --- Execution ---
-
-  public TokenValidator run(final String uri) {
-    input = uri;
-    tokens = parseInput(uri);
-    first();
-    return this;
-  }
-
-  // --- Navigation ---
-
-  // navigate within the tokenlist
-  public TokenValidator first() {
-    try {
-      curToken = tokens.get(0);
-    } catch (IndexOutOfBoundsException ex) {
-      curToken = null;
-    }
-    return this;
-  }
-
-  public TokenValidator last() {
-    curToken = tokens.get(tokens.size() - 1);
-    return this;
-  }
-
-  public TokenValidator at(final int index) {
-    try {
-      curToken = tokens.get(index);
-    } catch (IndexOutOfBoundsException ex) {
-      curToken = null;
-    }
-    return this;
-  }
-
-  // --- Validation ---
-
-  public TokenValidator isText(final String expected) {
-    assertEquals(expected, curToken.getText());
-    return this;
-  }
-
-  public TokenValidator isAllText(final String expected) {
-    String actual = "";
-
-    for (Token curToken : tokens) {
-      actual += curToken.getText();
-    }
-    assertEquals(expected, actual);
-    return this;
-  }
-
-  public TokenValidator isAllInput() {
-    String actual = "";
-
-    for (Token curToken : tokens) {
-      actual += curToken.getText();
-    }
-    assertEquals(input, actual);
-    return this;
-  }
-
-  public TokenValidator isInput() {
-    assertEquals(input, curToken.getText());
-    return this;
-  }
-
-  public TokenValidator isType(final int expected) {
-    assertEquals(UriLexer.VOCABULARY.getDisplayName(expected), UriLexer.VOCABULARY.getDisplayName(curToken.getType()));
-    return this;
-  }
-
-  public TokenValidator isExType(final Class<?> exClass) {
-    assertEquals(exClass, curException.getClass());
-    return this;
-  }
-
-  public void globalMode(final int mode) {
-    startMode = mode;
-  }
-
-  // --- Helper ---
-
-  private List<? extends Token> parseInput(final String input) {
-    ANTLRInputStream inputStream = new ANTLRInputStream(input);
-    UriLexer lexer = new UriLexer(inputStream);
-    lexer.mode(startMode);
-    return lexer.getAllTokens();
-  }
-}


[6/6] olingo-odata4 git commit: [OLINGO-834] $expand parser in Java + clean-up

Posted by ch...@apache.org.
[OLINGO-834] $expand parser in Java + clean-up

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

Branch: refs/heads/OLINGO-834_Filter_Parser
Commit: 8925274c0b5bd6936c3f6c1d3ab55608ced2cf13
Parents: 8919d3e
Author: Klaus Straubinger <kl...@sap.com>
Authored: Thu Jan 7 13:55:34 2016 +0100
Committer: Christian Amend <ch...@sap.com>
Committed: Thu Jan 7 14:02:09 2016 +0100

----------------------------------------------------------------------
 .../tecsvc/client/FilterSystemQueryITCase.java  |    7 +-
 .../tecsvc/client/OrderBySystemQueryITCase.java |    7 +-
 .../server/api/uri/queryoption/ExpandItem.java  |    1 -
 .../server/core/uri/parser/ExpandParser.java    |  282 +++
 .../core/uri/parser/ExpressionParser.java       |  332 +--
 .../server/core/uri/parser/FilterParser.java    |   11 +-
 .../olingo/server/core/uri/parser/Parser.java   |  234 +-
 .../server/core/uri/parser/ParserHelper.java    |   34 +-
 .../core/uri/parser/ResourcePathParser.java     |   62 +-
 .../server/core/uri/parser/SearchParser.java    |  108 +
 .../server/core/uri/parser/UriContext.java      |  115 -
 .../core/uri/parser/UriParseTreeVisitor.java    | 2313 ------------------
 .../server/core/uri/parser/UriTokenizer.java    |  211 +-
 .../uri/queryoption/expression/AliasImpl.java   |    4 +
 .../queryoption/expression/EnumerationImpl.java |    6 +
 .../queryoption/expression/LambdaRefImpl.java   |    5 +
 .../queryoption/expression/TypeLiteralImpl.java |    5 +
 .../server-core-exceptions-i18n.properties      |    8 +-
 .../core/uri/parser/ExpressionParserTest.java   |   14 +-
 .../core/uri/parser/UriTokenizerTest.java       |   82 +-
 .../search/SearchParserAndTokenizerTest.java    |   44 +-
 .../core/uri/antlr/TestFullResourcePath.java    | 1311 +++++-----
 .../olingo/server/core/uri/antlr/TestLexer.java |  450 ++--
 .../core/uri/antlr/TestUriParserImpl.java       |  220 +-
 .../server/core/uri/parser/ParserTest.java      |   56 +-
 .../core/uri/testutil/ExpandValidator.java      |   19 +-
 .../core/uri/testutil/FilterValidator.java      |   56 +-
 .../core/uri/testutil/TestUriValidator.java     |   38 +-
 .../core/uri/testutil/TokenValidator.java       |  127 -
 29 files changed, 1941 insertions(+), 4221 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java
index ca6eb21..6865a65 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java
@@ -35,11 +35,8 @@ import org.apache.olingo.commons.api.edm.FullQualifiedName;
 import org.apache.olingo.commons.api.http.HttpHeader;
 import org.apache.olingo.commons.api.http.HttpStatusCode;
 import org.junit.Assert;
-import org.junit.Ignore;
 import org.junit.Test;
 
-// TODO
-@Ignore
 public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase {
 
   private static final String ES_COMP_ALL_PRIM = "ESCompAllPrim";
@@ -212,7 +209,7 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase {
         sendRequest(ES_TWO_KEY_NAV, "PropertyComp/PropertyComp/PropertyBoolean eq not null");
     assertEquals(0, result.getBody().getEntities().size());
 
-    result = sendRequest(ES_TWO_KEY_NAV, "PropertyComp/PropertyComp/PropertyBoolean eq 0 add -(5 add null)");
+    result = sendRequest(ES_TWO_KEY_NAV, "PropertyComp/PropertyComp/PropertyInt16 eq 0 add -(5 add null)");
     assertEquals(0, result.getBody().getEntities().size());
   }
 
@@ -357,7 +354,7 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase {
 
   @Test
   public void notOperator() {
-    ODataRetrieveResponse<ClientEntitySet> result = sendRequest(ES_TWO_KEY_NAV, "not(PropertyInt16 eq 1)");
+    ODataRetrieveResponse<ClientEntitySet> result = sendRequest(ES_TWO_KEY_NAV, "not (PropertyInt16 eq 1)");
     assertEquals(2, result.getBody().getEntities().size());
 
     ClientEntity clientEntity = result.getBody().getEntities().get(0);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/OrderBySystemQueryITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/OrderBySystemQueryITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/OrderBySystemQueryITCase.java
index 0e31b33..8e16a70 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/OrderBySystemQueryITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/OrderBySystemQueryITCase.java
@@ -30,11 +30,8 @@ import org.apache.olingo.client.api.domain.ClientEntitySet;
 import org.apache.olingo.client.api.domain.ClientValuable;
 import org.apache.olingo.commons.api.http.HttpStatusCode;
 import org.junit.Assert;
-import org.junit.Ignore;
 import org.junit.Test;
 
-// TODO
-@Ignore
 public class OrderBySystemQueryITCase extends AbstractParamTecSvcITCase {
 
   private static final String ES_TWO_PRIM = "ESTwoPrim";
@@ -74,7 +71,7 @@ public class OrderBySystemQueryITCase extends AbstractParamTecSvcITCase {
 
   @Test
   public void multipleOrderBy() {
-    final ODataRetrieveResponse<ClientEntitySet> response = sendRequest(ES_ALL_PRIM, "PropertyByte, PropertyInt16");
+    final ODataRetrieveResponse<ClientEntitySet> response = sendRequest(ES_ALL_PRIM, "PropertyByte,PropertyInt16");
     assertEquals(3, response.getBody().getEntities().size());
 
     ClientEntity clientEntity = response.getBody().getEntities().get(0);
@@ -90,7 +87,7 @@ public class OrderBySystemQueryITCase extends AbstractParamTecSvcITCase {
   @Test
   public void multipleOrderByDescending() {
     final ODataRetrieveResponse<ClientEntitySet> response =
-        sendRequest(ES_ALL_PRIM, "PropertyByte, PropertyInt16 desc");
+        sendRequest(ES_ALL_PRIM, "PropertyByte,PropertyInt16 desc");
     assertEquals(3, response.getBody().getEntities().size());
 
     ClientEntity clientEntity = response.getBody().getEntities().get(0);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/ExpandItem.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/ExpandItem.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/ExpandItem.java
index a16c137..fccf844 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/ExpandItem.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/ExpandItem.java
@@ -38,7 +38,6 @@ public interface ExpandItem {
   FilterOption getFilterOption();
 
   /**
-   * <b>CURRENTLY NOT SUPPORTED. WILL ALWAYS RETURN NULL</b>
    * @return Information of the option $search when used within $expand
    */
   SearchOption getSearchOption();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpandParser.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpandParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpandParser.java
new file mode 100644
index 0000000..d8209d8
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpandParser.java
@@ -0,0 +1,282 @@
+/*
+ * 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.commons.api.edm.Edm;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
+import org.apache.olingo.commons.api.edm.EdmProperty;
+import org.apache.olingo.commons.api.edm.EdmStructuredType;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
+import org.apache.olingo.commons.api.ex.ODataRuntimeException;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.uri.UriInfoKind;
+import org.apache.olingo.server.api.uri.UriResourceNavigation;
+import org.apache.olingo.server.api.uri.UriResourcePartTyped;
+import org.apache.olingo.server.api.uri.queryoption.ExpandItem;
+import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
+import org.apache.olingo.server.api.uri.queryoption.LevelsExpandOption;
+import org.apache.olingo.server.api.uri.queryoption.SystemQueryOption;
+import org.apache.olingo.server.api.uri.queryoption.SystemQueryOptionKind;
+import org.apache.olingo.server.core.uri.UriInfoImpl;
+import org.apache.olingo.server.core.uri.UriResourceComplexPropertyImpl;
+import org.apache.olingo.server.core.uri.UriResourceCountImpl;
+import org.apache.olingo.server.core.uri.UriResourceNavigationPropertyImpl;
+import org.apache.olingo.server.core.uri.UriResourceRefImpl;
+import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
+import org.apache.olingo.server.core.uri.queryoption.CountOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.ExpandItemImpl;
+import org.apache.olingo.server.core.uri.queryoption.ExpandOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.LevelsOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.SkipOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.TopOptionImpl;
+import org.apache.olingo.server.core.uri.validator.UriValidationException;
+
+public class ExpandParser {
+
+  private final Edm edm;
+  private final OData odata;
+
+  public ExpandParser(final Edm edm, final OData odata) {
+    this.edm = edm;
+    this.odata = odata;
+  }
+
+  public ExpandOption parse(UriTokenizer tokenizer, final EdmStructuredType referencedType)
+      throws UriParserException, UriValidationException {
+    ExpandOptionImpl expandOption = new ExpandOptionImpl();
+    do {
+      final ExpandItem item = parseItem(tokenizer, referencedType);
+      expandOption.addExpandItem(item);
+    } while (tokenizer.next(TokenKind.COMMA));
+
+    return expandOption;
+  }
+
+  private ExpandItem parseItem(UriTokenizer tokenizer, final EdmStructuredType referencedType)
+      throws UriParserException, UriValidationException {
+    ExpandItemImpl item = new ExpandItemImpl();
+    if (tokenizer.next(TokenKind.STAR)) {
+      item.setIsStar(true);
+      if (tokenizer.next(TokenKind.SLASH)) {
+        ParserHelper.requireNext(tokenizer, TokenKind.REF);
+        item.setIsRef(true);
+      } else if (tokenizer.next(TokenKind.OPEN)) {
+        ParserHelper.requireNext(tokenizer, TokenKind.LEVELS);
+        ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+        item.setSystemQueryOption((SystemQueryOption) parseLevels(tokenizer));
+        ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
+      }
+
+    } else {
+      final EdmStructuredType typeCast = parseTypeCast(tokenizer, referencedType);
+      if (typeCast != null) {
+        item.setTypeFilter(typeCast);
+        ParserHelper.requireNext(tokenizer, TokenKind.SLASH);
+      }
+
+      UriInfoImpl resource = parseExpandPath(tokenizer, referencedType);
+
+      UriResourcePartTyped lastPart = (UriResourcePartTyped) resource.getLastResourcePart();
+
+      boolean hasSlash = false;
+      if (tokenizer.next(TokenKind.SLASH)) {
+        hasSlash = true;
+        if (lastPart instanceof UriResourceNavigation) {
+          UriResourceNavigationPropertyImpl navigationResource = (UriResourceNavigationPropertyImpl) lastPart;
+          final EdmNavigationProperty navigationProperty = navigationResource.getProperty();
+          final EdmStructuredType typeCastSuffix = parseTypeCast(tokenizer, navigationProperty.getType());
+          if (typeCastSuffix != null) {
+            if (navigationProperty.isCollection()) {
+              navigationResource.setCollectionTypeFilter(typeCastSuffix);
+            } else {
+              navigationResource.setEntryTypeFilter(typeCastSuffix);
+            }
+            hasSlash = false;
+          }
+        }
+      }
+
+      final EdmStructuredType newReferencedType = (EdmStructuredType) lastPart.getType();
+      final boolean newReferencedIsCollection = lastPart.isCollection();
+      if (hasSlash || tokenizer.next(TokenKind.SLASH)) {
+        if (tokenizer.next(TokenKind.REF)) {
+          resource.addResourcePart(new UriResourceRefImpl());
+          parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, true, false);
+        } else {
+          ParserHelper.requireNext(tokenizer, TokenKind.COUNT);
+          resource.addResourcePart(new UriResourceCountImpl());
+          parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, false, true);
+        }
+      } else {
+        parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, false, false);
+      }
+
+      item.setResourcePath(resource);
+    }
+
+    return item;
+  }
+
+  private EdmStructuredType parseTypeCast(UriTokenizer tokenizer, final EdmStructuredType referencedType)
+      throws UriParserException {
+    if (tokenizer.next(TokenKind.QualifiedName)) {
+      final FullQualifiedName qualifiedName = new FullQualifiedName(tokenizer.getText());
+      final EdmStructuredType type = referencedType instanceof EdmEntityType ?
+          edm.getEntityType(qualifiedName) :
+          edm.getComplexType(qualifiedName);
+      if (type == null) {
+        throw new UriParserSemanticException("Type '" + qualifiedName + "' not found.",
+            UriParserSemanticException.MessageKeys.UNKNOWN_PART, qualifiedName.getFullQualifiedNameAsString());
+      } else {
+        if (!type.compatibleTo(referencedType)) {
+          throw new UriParserSemanticException("The type cast '" + qualifiedName + "' is not compatible.",
+              UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER, type.getName());
+        }
+      }
+      return type;
+    }
+    return null;
+  }
+
+  private UriInfoImpl parseExpandPath(UriTokenizer tokenizer, final EdmStructuredType referencedType)
+      throws UriParserException {
+    UriInfoImpl resource = new UriInfoImpl().setKind(UriInfoKind.resource);
+
+    EdmStructuredType type = referencedType;
+    String name = null;
+    while (tokenizer.next(TokenKind.ODataIdentifier)) {
+      name = tokenizer.getText();
+      final EdmProperty property = referencedType.getStructuralProperty(name);
+      if (property != null && property.getType().getKind() == EdmTypeKind.COMPLEX) {
+        type = (EdmStructuredType) property.getType();
+        UriResourceComplexPropertyImpl complexResource = new UriResourceComplexPropertyImpl(property);
+        ParserHelper.requireNext(tokenizer, TokenKind.SLASH);
+        final EdmStructuredType typeCast = parseTypeCast(tokenizer, type);
+        if (typeCast != null) {
+          complexResource.setTypeFilter(typeCast);
+          ParserHelper.requireNext(tokenizer, TokenKind.SLASH);
+          type = typeCast;
+        }
+        resource.addResourcePart(complexResource);
+      }
+    }
+
+    final EdmNavigationProperty navigationProperty = type.getNavigationProperty(name);
+    if (navigationProperty == null) {
+      // TODO: could also have been star after complex property (and maybe type cast)
+      throw new UriParserSemanticException(
+          "Navigation Property '" + name + "' not found in type '" + type.getFullQualifiedName() + "'.",
+          UriParserSemanticException.MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE, type.getName(), name);
+    } else {
+      resource.addResourcePart(new UriResourceNavigationPropertyImpl(navigationProperty));
+    }
+
+    return resource;
+  }
+
+  private void parseOptions(UriTokenizer tokenizer,
+      final EdmStructuredType referencedType, final boolean referencedIsCollection,
+      ExpandItemImpl item,
+      final boolean forRef, final boolean forCount) throws UriParserException, UriValidationException {
+    if (tokenizer.next(TokenKind.OPEN)) {
+      do {
+        SystemQueryOption systemQueryOption;
+        if (!forCount && tokenizer.next(TokenKind.COUNT)) {
+          ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+          ParserHelper.requireNext(tokenizer, TokenKind.BooleanValue);
+          CountOptionImpl countOption = new CountOptionImpl();
+          countOption.setText(tokenizer.getText());
+          countOption.setValue(Boolean.parseBoolean(tokenizer.getText()));
+          systemQueryOption = countOption;
+
+        } else if (!forRef && !forCount && tokenizer.next(TokenKind.EXPAND)) {
+          ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+          systemQueryOption = new ExpandParser(edm, odata).parse(tokenizer, referencedType);
+
+        } else if (tokenizer.next(TokenKind.FILTER)) {
+          ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+          systemQueryOption = new FilterParser(edm, odata).parse(tokenizer, referencedType, null);
+
+        } else if (!forRef && !forCount && tokenizer.next(TokenKind.LEVELS)) {
+          ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+          systemQueryOption = (SystemQueryOption) parseLevels(tokenizer);
+
+        } else if (!forCount && tokenizer.next(TokenKind.ORDERBY)) {
+          ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+          systemQueryOption = new OrderByParser(edm, odata).parse(tokenizer, referencedType, null);
+
+        } else if (tokenizer.next(TokenKind.SEARCH)) {
+          ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+          systemQueryOption = new SearchParser().parse(tokenizer);
+
+        } else if (!forRef && !forCount && tokenizer.next(TokenKind.SELECT)) {
+          ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+          systemQueryOption = new SelectParser(edm).parse(tokenizer, referencedType, referencedIsCollection);
+
+        } else if (!forCount && tokenizer.next(TokenKind.SKIP)) {
+          ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+          ParserHelper.requireNext(tokenizer, TokenKind.IntegerValue);
+          final int value = ParserHelper.parseNonNegativeInteger(SystemQueryOptionKind.SKIP.toString(),
+              tokenizer.getText(), true);
+          SkipOptionImpl skipOption = new SkipOptionImpl();
+          skipOption.setText(tokenizer.getText());
+          skipOption.setValue(value);
+          systemQueryOption = skipOption;
+
+        } else if (!forCount && tokenizer.next(TokenKind.TOP)) {
+          ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+          ParserHelper.requireNext(tokenizer, TokenKind.IntegerValue);
+          final int value = ParserHelper.parseNonNegativeInteger(SystemQueryOptionKind.TOP.toString(),
+              tokenizer.getText(), true);
+          TopOptionImpl topOption = new TopOptionImpl();
+          topOption.setText(tokenizer.getText());
+          topOption.setValue(value);
+          systemQueryOption = topOption;
+
+        } else {
+          throw new UriParserSyntaxException("Allowed query option expected.",
+              UriParserSyntaxException.MessageKeys.SYNTAX);
+        }
+        try {
+          item.setSystemQueryOption(systemQueryOption);
+        } catch (final ODataRuntimeException e) {
+          throw new UriParserSyntaxException("Double system query option '" + systemQueryOption.getName() + "'.", e,
+              UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION, systemQueryOption.getName());
+        }
+      } while (tokenizer.next(TokenKind.SEMI));
+      ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
+    }
+  }
+
+  private LevelsExpandOption parseLevels(UriTokenizer tokenizer) throws UriParserException {
+    final LevelsOptionImpl option = new LevelsOptionImpl();
+    if (tokenizer.next(TokenKind.MAX)) {
+      option.setText(tokenizer.getText());
+      option.setMax();
+    } else {
+      ParserHelper.requireNext(tokenizer, TokenKind.IntegerValue);
+      option.setText(tokenizer.getText());
+      option.setValue(
+          ParserHelper.parseNonNegativeInteger(SystemQueryOptionKind.LEVELS.toString(), tokenizer.getText(), false));
+    }
+    return option;
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/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 049880f..6fa415f 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
@@ -47,9 +47,6 @@ 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.commons.core.edm.primitivetype.EdmByte;
-import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
-import org.apache.olingo.commons.core.edm.primitivetype.EdmSByte;
 import org.apache.olingo.server.api.OData;
 import org.apache.olingo.server.api.uri.UriParameter;
 import org.apache.olingo.server.api.uri.UriResourceFunction;
@@ -161,7 +158,7 @@ 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 */
+    /* Enum and null are not present in the map. These have to be handled differently. */
     Map<TokenKind, EdmPrimitiveTypeKind> temp = new HashMap<TokenKind, EdmPrimitiveTypeKind>();
     temp.put(TokenKind.BooleanValue, EdmPrimitiveTypeKind.Boolean);
     temp.put(TokenKind.StringValue, EdmPrimitiveTypeKind.String);
@@ -242,9 +239,9 @@ public class ExpressionParser {
   }
 
   private Expression parseExprRel() throws UriParserException, UriValidationException {
-    if(tokenizer.next(TokenKind.IsofMethod)) {
-      // The isof method is a terminal. So no further operators are allowed
-      return parseIsOfMethod(TokenKind.IsofMethod);
+    if (tokenizer.next(TokenKind.IsofMethod)) {
+      // The isof method is a terminal.  So no further operators are allowed.
+      return parseIsOfOrCastMethod(MethodKind.ISOF);
     } else {
       Expression left = parseExprAdd();
       TokenKind operatorTokenKind = ParserHelper.next(tokenizer,
@@ -264,30 +261,25 @@ public class ExpressionParser {
     }
   }
 
-  private Expression parseIsOfMethod(final TokenKind lastToken) throws UriParserException, UriValidationException {
-    if(lastToken == TokenKind.IsofMethod) {
-      // The TokenKind 'IsOfMethod' consumes also the opening parenthesis 
-
-      // The first parameter could be an expression or a type literal
-      final List<Expression> parameters = new ArrayList<Expression>();
+  private Expression parseIsOfOrCastMethod(final MethodKind kind) throws UriParserException, UriValidationException {
+    // The TokenKind 'IsOfMethod' consumes also the opening parenthesis.
+    // The first parameter could be an expression or a type literal.
+    List<Expression> parameters = new ArrayList<Expression>();
+    parameters.add(parseExpression());
+    if (!(parameters.get(0) instanceof TypeLiteral)) {
+      // The first parameter is not a type literal, so there must be a second parameter.
+      ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
       parameters.add(parseExpression());
-      if(!(parameters.get(0) instanceof TypeLiteral)) {
-        // The first parameter is not a type literal, so there must be a second parameter
-        ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
-        parameters.add(parseExpression());
-        
-        // The second parameter must be a type literal
-        if(!(parameters.get(1) instanceof TypeLiteral)) {
-          throw new UriParserSemanticException("Type literal extected", 
-              UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER);
-        }
+
+      // The second parameter must be a type literal.
+      if (!(parameters.get(1) instanceof TypeLiteral)) {
+        throw new UriParserSemanticException("Type literal expected.",
+            UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER);
       }
-      
-      ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
-      return new MethodImpl(MethodKind.ISOF, parameters);
-    } else {
-      throw new UriParserSyntaxException("Unexpected token.", UriParserSyntaxException.MessageKeys.SYNTAX);
     }
+
+    ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
+    return new MethodImpl(kind, parameters);
   }
 
   private Expression parseExprAdd() throws UriParserException, UriValidationException {
@@ -296,9 +288,9 @@ public class ExpressionParser {
     // Null for everything other than ADD or SUB
     while (operatorTokenKind != null) {
       final Expression right = parseExprMul();
-      checkAddSubTypes(left, right, operatorTokenKind == TokenKind.AddOperator);
-      left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right,
-          odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Double));
+      final EdmType resultType = getAddSubTypeAndCheckLeftAndRight(left, right,
+          operatorTokenKind == TokenKind.SubOperator);
+      left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right, resultType);
       operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.AddOperator, TokenKind.SubOperator);
     }
     return left;
@@ -328,10 +320,7 @@ public class ExpressionParser {
   }
 
   private Expression parseExprUnary() throws UriParserException, UriValidationException {
-    final TokenKind operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.MINUS, TokenKind.NotOperator, 
-                                                                     TokenKind.CastMethod);
-    
-    if(operatorTokenKind == TokenKind.MINUS) {
+    if (tokenizer.next(TokenKind.MinusOperator)) {
       final Expression expression = parseExprPrimary();
       checkType(expression,
           EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
@@ -339,37 +328,14 @@ public class ExpressionParser {
           EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double,
           EdmPrimitiveTypeKind.Duration);
       return new UnaryImpl(UnaryOperatorKind.MINUS, expression, getType(expression));
-    } else if(operatorTokenKind == TokenKind.NotOperator) {
+    } else if (tokenizer.next(TokenKind.NotOperator)) {
       final Expression expression = parseExprPrimary();
       checkType(expression, EdmPrimitiveTypeKind.Boolean);
       return new UnaryImpl(UnaryOperatorKind.NOT, expression, getType(expression));
-    } else if(operatorTokenKind == TokenKind.CastMethod) {
-      return parseCastMethod(operatorTokenKind);
+    } else if (tokenizer.next(TokenKind.CastMethod)) {
+      return parseIsOfOrCastMethod(MethodKind.CAST);
     } else {
-      final Expression expression = parseExprPrimary();
-      return expression;
-    }
-  }
-
-  private Expression parseCastMethod(final TokenKind lastToken) throws UriParserException, UriValidationException {
-    // The TokenKind 'CastMethod' consumes also the opening parenthesis 
-    if(lastToken == TokenKind.CastMethod) {
-      final List<Expression> parameters = new ArrayList<Expression>();
-      parameters.add(parseExpression());
-      
-      if(!(parameters.get(0) instanceof TypeLiteral)) {
-        ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
-        parameters.add(parseExpression());
-        if(!(parameters.get(1) instanceof TypeLiteral)) {
-          throw new UriParserSemanticException("Type literal extected", 
-              UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER);
-        }
-      }
-      
-      ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
-      return new MethodImpl(MethodKind.CAST, parameters);
-    } else {
-      throw new UriParserSyntaxException("Unexpected token.", UriParserSyntaxException.MessageKeys.SYNTAX);
+      return parseExprPrimary();
     }
   }
 
@@ -433,22 +399,20 @@ public class ExpressionParser {
   private Expression parseMethod(final TokenKind nextMethod) throws UriParserException, UriValidationException {
     // The method token text includes the opening parenthesis so that method calls can be recognized unambiguously.
     // OData identifiers have to be considered after that.
-    
     final MethodKind methodKind = tokenToMethod.get(nextMethod);
     return new MethodImpl(methodKind, parseMethodParameters(methodKind));
   }
 
-  private Expression parsePrimitive(TokenKind nextPrimitive) throws UriParserException {
+  private Expression parsePrimitive(final TokenKind primitiveTokenKind) throws UriParserException {
     final String primitiveValueLiteral = tokenizer.getText();
-    if (nextPrimitive == TokenKind.EnumValue) {
+    if (primitiveTokenKind == TokenKind.EnumValue) {
       return createEnumExpression(primitiveValueLiteral);
     } else {
-      EdmPrimitiveTypeKind primitiveTypeKind = tokenToPrimitiveType.get(nextPrimitive);
-      
-      if(primitiveTypeKind == EdmPrimitiveTypeKind.Int64) {
+      EdmPrimitiveTypeKind primitiveTypeKind = tokenToPrimitiveType.get(primitiveTokenKind);
+      if (primitiveTypeKind == EdmPrimitiveTypeKind.Int64) {
         primitiveTypeKind = determineIntegerType(primitiveValueLiteral);
       }
-      
+
       final EdmPrimitiveType type = primitiveTypeKind == null ?
           // Null handling
           null :
@@ -472,12 +436,11 @@ public class ExpressionParser {
       } else {
         typeKind = EdmPrimitiveTypeKind.Int64;
       }
-    } catch (NumberFormatException e) {
-      // This should never happen. Because the tokenizer has figured out that the literal is an integer
-      throw new UriParserSyntaxException(intValueAsString + " is not an integer", 
+    } catch (final NumberFormatException e) {
+      // This should never happen because the tokenizer has figured out that the literal is an integer.
+      throw new UriParserSyntaxException("'" + intValueAsString + "' is not an integer.", e,
           UriParserSyntaxException.MessageKeys.SYNTAX);
     }
-
     return typeKind;
   }
 
@@ -622,19 +585,19 @@ public class ExpressionParser {
       if (filterType == null) {
         filterType = edm.getComplexType(fullQualifiedName);
       }
-      
-      if(filterType == null) {
-        filterType = getEdmType(fullQualifiedName);
+
+      if (filterType == null) {
+        filterType = getPrimitiveType(fullQualifiedName);
       }
-      
-      if(filterType == null) {
+
+      if (filterType == null) {
         filterType = edm.getEnumType(fullQualifiedName);
       }
-      
-      if(filterType == null) {
+
+      if (filterType == null) {
         filterType = edm.getTypeDefinition(fullQualifiedName);
       }
-      
+
       if (filterType != null) {
         if (tokenizer.next(TokenKind.SLASH)) {
           // Leading type cast
@@ -642,30 +605,29 @@ public class ExpressionParser {
           startTypeFilter = filterType;
 
           final TokenKind tokenKind = ParserHelper.next(tokenizer, TokenKind.QualifiedName, TokenKind.ODataIdentifier);
-          parseMemberExpression(tokenKind, uriInfo, new UriResourceStartingTypeFilterImpl(filterType, false), 
-              false);
+          parseMemberExpression(tokenKind, uriInfo, new UriResourceStartingTypeFilterImpl(filterType, false), false);
         } else {
           // Type literal
           return new TypeLiteralImpl(filterType);
         }
       } else {
-        // Must be bound or unbound function. 
+        // Must be bound or unbound function.
         parseFunction(fullQualifiedName, uriInfo, referringType, true);
       }
     } else if (lastTokenKind == TokenKind.ODataIdentifier) {
       parseFirstMemberODataIdentifier(uriInfo);
     }
-    
+
     return new MemberImpl(uriInfo, startTypeFilter);
   }
 
-  private EdmType getEdmType(final FullQualifiedName fullQualifiedName) {
-    if(!fullQualifiedName.getNamespace().equals(EdmPrimitiveType.EDM_NAMESPACE)) {
+  private EdmType getPrimitiveType(final FullQualifiedName fullQualifiedName) {
+    if (EdmPrimitiveType.EDM_NAMESPACE.equals(fullQualifiedName.getNamespace())) {
+      final EdmPrimitiveTypeKind primitiveTypeKind = EdmPrimitiveTypeKind.valueOf(fullQualifiedName.getName());
+      return primitiveTypeKind == null ? null : odata.createPrimitiveTypeInstance(primitiveTypeKind);
+    } else {
       return null;
     }
-    
-    final EdmPrimitiveTypeKind primitiveTypeKind = EdmPrimitiveTypeKind.valueOfFQN(fullQualifiedName);
-    return primitiveTypeKind == null ? null : EdmPrimitiveTypeFactory.getInstance(primitiveTypeKind);
   }
 
   private void parseDollarRoot(UriInfoImpl uriInfo) throws UriParserException, UriValidationException {
@@ -694,7 +656,7 @@ public class ExpressionParser {
     parseSingleNavigationExpr(uriInfo, resource);
   }
 
-  private void parseDollarIt(UriInfoImpl uriInfo, EdmType referringType) 
+  private void parseDollarIt(UriInfoImpl uriInfo, final EdmType referringType)
       throws UriParserException, UriValidationException {
     UriResourceItImpl itResource = new UriResourceItImpl(referringType, false);
     uriInfo.addResourcePart(itResource);
@@ -759,16 +721,14 @@ public class ExpressionParser {
       if (edmEntityType != null) {
         if (allowTypeFilter) {
           setTypeFilter(lastResource, edmEntityType);
-          
-          if(tokenizer.next(TokenKind.SLASH)) {
-            final TokenKind nextTokenKind = ParserHelper.next(tokenizer, TokenKind.QualifiedName, 
-                                                                         TokenKind.ODataIdentifier);
-            if(nextTokenKind == TokenKind.ODataIdentifier) {
-              parsePropertyPathExpr(uriInfo, lastResource);
-            } else if(nextTokenKind == TokenKind.QualifiedName) {
+
+          if (tokenizer.next(TokenKind.SLASH)) {
+            if (tokenizer.next(TokenKind.QualifiedName)) {
               parseBoundFunction(fullQualifiedName, uriInfo, lastResource);
+            } else if (tokenizer.next(TokenKind.ODataIdentifier)) {
+              parsePropertyPathExpr(uriInfo, lastResource);
             } else {
-              throw new UriParserSyntaxException("Extected OData Identifier or Full Qualified Name", 
+              throw new UriParserSyntaxException("Expected OData Identifier or Full Qualified Name.",
                   UriParserSyntaxException.MessageKeys.SYNTAX);
             }
           }
@@ -814,7 +774,9 @@ public class ExpressionParser {
 
     if (property == null) {
       throw new UriParserSemanticException("Unknown property.",
-          UriParserSemanticException.MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE, oDataIdentifier);
+          UriParserSemanticException.MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE,
+          lastType.getFullQualifiedName().getFullQualifiedNameAsString(),
+          oDataIdentifier);
     }
 
     if (property.getType() instanceof EdmComplexType) {
@@ -823,7 +785,9 @@ public class ExpressionParser {
       uriInfo.addResourcePart(complexResource);
 
       if (property.isCollection()) {
-        parseCollectionPathExpr(uriInfo, complexResource);
+        if (tokenizer.next(TokenKind.SLASH)) {
+          parseCollectionPathExpr(uriInfo, complexResource);
+        }
       } else {
         parseComplexPathExpr(uriInfo, complexResource);
       }
@@ -847,7 +811,9 @@ public class ExpressionParser {
       uriInfo.addResourcePart(primitiveResource);
 
       if (property.isCollection()) {
-        parseCollectionPathExpr(uriInfo, primitiveResource);
+        if (tokenizer.next(TokenKind.SLASH)) {
+          parseCollectionPathExpr(uriInfo, primitiveResource);
+        }
       } else {
         parseSinglePathExpr(uriInfo, primitiveResource);
       }
@@ -856,7 +822,6 @@ public class ExpressionParser {
 
   private void parseSingleNavigationExpr(UriInfoImpl uriInfo, final UriResourcePartTyped lastResource)
       throws UriParserException, UriValidationException {
-    // TODO: Is that correct?
     if (tokenizer.next(TokenKind.SLASH)) {
       final TokenKind tokenKind = ParserHelper.next(tokenizer, TokenKind.QualifiedName, TokenKind.ODataIdentifier);
       parseMemberExpression(tokenKind, uriInfo, lastResource, true);
@@ -865,8 +830,22 @@ public class ExpressionParser {
 
   private void parseCollectionNavigationExpr(UriInfoImpl uriInfo, UriResourcePartTyped lastResource)
       throws UriParserException, UriValidationException {
-    // TODO: Is type cast missing?
-    if (tokenizer.next(TokenKind.OPEN)) {
+    boolean hasSlash = false;
+    if (tokenizer.next(TokenKind.SLASH)) {
+      hasSlash = true;
+      if (tokenizer.next(TokenKind.QualifiedName)) {
+        final FullQualifiedName qualifiedName = new FullQualifiedName(tokenizer.getText());
+        final EdmEntityType edmEntityType = edm.getEntityType(qualifiedName);
+        if (edmEntityType == null) {
+          parseBoundFunction(qualifiedName, uriInfo, lastResource);
+        } else {
+          setTypeFilter(lastResource, edmEntityType);
+        }
+        hasSlash = false;
+      }
+    }
+
+    if (!hasSlash && tokenizer.next(TokenKind.OPEN)) {
       if (lastResource instanceof UriResourceNavigation) {
         ((UriResourceNavigationPropertyImpl) lastResource).setKeyPredicates(
               ParserHelper.parseNavigationKeyPredicate(tokenizer,
@@ -883,7 +862,10 @@ public class ExpressionParser {
       }
       parseSingleNavigationExpr(uriInfo, lastResource);
     }
-    parseCollectionPathExpr(uriInfo, lastResource);
+
+    if (hasSlash || tokenizer.next(TokenKind.SLASH)) {
+      parseCollectionPathExpr(uriInfo, lastResource);
+    }
   }
 
   private void parseSinglePathExpr(UriInfoImpl uriInfo, final UriResourcePartTyped lastResource)
@@ -932,25 +914,23 @@ public class ExpressionParser {
 
   private void parseCollectionPathExpr(UriInfoImpl uriInfo, final UriResourcePartTyped lastResource)
       throws UriParserException, UriValidationException {
-
-    if (tokenizer.next(TokenKind.SLASH)) {
-      if (tokenizer.next(TokenKind.COUNT)) {
-        uriInfo.addResourcePart(new UriResourceCountImpl());
-      } else if (tokenizer.next(TokenKind.ANY)) {
-        uriInfo.addResourcePart(parseLambdaRest(TokenKind.ANY, lastResource));
-      } else if (tokenizer.next(TokenKind.ALL)) {
-        uriInfo.addResourcePart(parseLambdaRest(TokenKind.ALL, lastResource));
-      } else if (tokenizer.next(TokenKind.QualifiedName)) {
-        final FullQualifiedName fullQualifiedName = new FullQualifiedName(tokenizer.getText());
-        parseBoundFunction(fullQualifiedName, uriInfo, lastResource);
-      }
+    // The initial slash (see grammar) must have been checked and consumed by the caller.
+    if (tokenizer.next(TokenKind.COUNT)) {
+      uriInfo.addResourcePart(new UriResourceCountImpl());
+    } else if (tokenizer.next(TokenKind.ANY)) {
+      uriInfo.addResourcePart(parseLambdaRest(TokenKind.ANY, lastResource));
+    } else if (tokenizer.next(TokenKind.ALL)) {
+      uriInfo.addResourcePart(parseLambdaRest(TokenKind.ALL, lastResource));
+    } else if (tokenizer.next(TokenKind.QualifiedName)) {
+      final FullQualifiedName fullQualifiedName = new FullQualifiedName(tokenizer.getText());
+      parseBoundFunction(fullQualifiedName, uriInfo, lastResource);
     }
   }
 
   private void parseFunction(final FullQualifiedName fullQualifiedName, UriInfoImpl uriInfo,
       final EdmType lastType, final boolean lastIsCollection) throws UriParserException, UriValidationException {
 
-    final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, true);
+    final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, edm, referringType, true);
     final List<String> parameterNames = ParserHelper.getParameterNames(parameters);
     final EdmFunction boundFunction = edm.getBoundFunction(fullQualifiedName,
         lastType.getFullQualifiedName(), lastIsCollection, parameterNames);
@@ -966,18 +946,19 @@ public class ExpressionParser {
       return;
     }
 
-    throw new UriParserSemanticException("No function found.",
+    throw new UriParserSemanticException("No function '" + fullQualifiedName + "' found.",
         UriParserSemanticException.MessageKeys.FUNCTION_NOT_FOUND, fullQualifiedName.getFullQualifiedNameAsString());
   }
 
   private void parseBoundFunction(final FullQualifiedName fullQualifiedName, UriInfoImpl uriInfo,
       final UriResourcePartTyped lastResource) throws UriParserException, UriValidationException {
-    final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, true);
+    final EdmType type = lastResource.getType();
+    final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, edm, referringType, true);
     final List<String> parameterNames = ParserHelper.getParameterNames(parameters);
     final EdmFunction boundFunction = edm.getBoundFunction(fullQualifiedName,
-        lastResource.getType().getFullQualifiedName(), lastResource.isCollection(), parameterNames);
+        type.getFullQualifiedName(), lastResource.isCollection(), parameterNames);
     if (boundFunction == null) {
-      throw new UriParserSemanticException("Bound function not found.",
+      throw new UriParserSemanticException("Bound function '" + fullQualifiedName + "' not found.",
           UriParserSemanticException.MessageKeys.FUNCTION_NOT_FOUND, fullQualifiedName.getFullQualifiedNameAsString());
     }
     parseFunctionRest(uriInfo, boundFunction, parameters);
@@ -995,19 +976,23 @@ public class ExpressionParser {
     if (function.isComposable()) {
       if (edmType instanceof EdmEntityType ) {
         if (isCollection) {
-          parseCollectionNavigationExpr(uriInfo, functionResource); 
+          parseCollectionNavigationExpr(uriInfo, functionResource);
         } else {
           parseSingleNavigationExpr(uriInfo, functionResource);
         }
       } else if (edmType instanceof EdmComplexType) {
         if (isCollection) {
-          parseCollectionPathExpr(uriInfo, functionResource);
+          if (tokenizer.next(TokenKind.SLASH)) {
+            parseCollectionPathExpr(uriInfo, functionResource);
+          }
         } else {
           parseComplexPathExpr(uriInfo, functionResource);
         }
       } else if (edmType instanceof EdmPrimitiveType) {
         if (isCollection) {
-          parseCollectionPathExpr(uriInfo, functionResource);
+          if (tokenizer.next(TokenKind.SLASH)) {
+            parseCollectionPathExpr(uriInfo, functionResource);
+          }
         } else {
           parseSinglePathExpr(uriInfo, functionResource);
         }
@@ -1077,7 +1062,7 @@ public class ExpressionParser {
         TokenKind.YearMethod);
   }
 
-  private EdmType getType(final Expression expression) throws UriParserException {
+  protected static EdmType getType(final Expression expression) throws UriParserException {
     EdmType type;
     if (expression instanceof Literal) {
       type = ((Literal) expression).getType();
@@ -1108,13 +1093,12 @@ public class ExpressionParser {
     return type;
   }
 
-  private boolean isType(final Expression expression, final EdmPrimitiveTypeKind... kinds) throws UriParserException {
-    final EdmType expressionType = getType(expression);
-    if (expressionType == null) {
+  private boolean isType(final EdmType type, final EdmPrimitiveTypeKind... kinds) throws UriParserException {
+    if (type == null) {
       return true;
     }
     for (final EdmPrimitiveTypeKind kind : kinds) {
-      if (expressionType.equals(odata.createPrimitiveTypeInstance(kind))) {
+      if (type.equals(odata.createPrimitiveTypeInstance(kind))) {
         return true;
       }
     }
@@ -1122,12 +1106,13 @@ public class ExpressionParser {
   }
 
   private void checkType(final Expression expression, final EdmPrimitiveTypeKind... kinds) throws UriParserException {
-    if (!isType(expression, kinds)) {
+    final EdmType type = getType(expression);
+    if (!isType(type, kinds)) {
       throw new UriParserSemanticException("Incompatible type.",
           UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, // TODO: better message
-          getType(expression) == null ?
+          type == null ?
               "" :
-              getType(expression).getFullQualifiedName().getFullQualifiedNameAsString());
+              type.getFullQualifiedName().getFullQualifiedNameAsString());
     }
   }
 
@@ -1137,21 +1122,21 @@ public class ExpressionParser {
     if (leftType == null || rightType == null || leftType.equals(rightType)) {
       return;
     }
-    
+
     // Numeric promotion for Edm.Byte and Edm.SByte
-    if((leftType instanceof EdmByte || leftType instanceof EdmSByte) 
-        && (rightType instanceof EdmByte || rightType instanceof EdmSByte)) {
+    if (isType(leftType, EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte)
+        && isType(rightType, EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte)) {
       return;
     }
-    
+
     if (leftType.getKind() != EdmTypeKind.PRIMITIVE
         || rightType.getKind() != EdmTypeKind.PRIMITIVE
         || !(((EdmPrimitiveType) leftType).isCompatible((EdmPrimitiveType) rightType)
-        || ((EdmPrimitiveType) rightType).isCompatible((EdmPrimitiveType) leftType)))
-        {
+        || ((EdmPrimitiveType) rightType).isCompatible((EdmPrimitiveType) leftType))) {
       throw new UriParserSemanticException("Incompatible types.",
-          UriParserSemanticException.MessageKeys.TYPES_NOT_COMPATIBLE, leftType.getFullQualifiedName().toString(), 
-                                                                       rightType.getFullQualifiedName().toString());
+          UriParserSemanticException.MessageKeys.TYPES_NOT_COMPATIBLE,
+          leftType.getFullQualifiedName().getFullQualifiedNameAsString(),
+          rightType.getFullQualifiedName().getFullQualifiedNameAsString());
     }
   }
 
@@ -1164,12 +1149,12 @@ public class ExpressionParser {
     }
     return type;
   }
-  
+
   private boolean isEnumType(final Expression expression) throws UriParserException {
     final EdmType expressionType = getType(expression);
     return expressionType == null
         || expressionType.getKind() == EdmTypeKind.ENUM
-        || isType(expression,
+        || isType(expressionType,
             EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
             EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte);
   }
@@ -1208,44 +1193,69 @@ public class ExpressionParser {
         EdmPrimitiveTypeKind.Date, EdmPrimitiveTypeKind.TimeOfDay,
         EdmPrimitiveTypeKind.DateTimeOffset, EdmPrimitiveTypeKind.Duration);
     if (!(((EdmPrimitiveType) leftType).isCompatible((EdmPrimitiveType) rightType)
-    || ((EdmPrimitiveType) rightType).isCompatible((EdmPrimitiveType) leftType))) {
+        || ((EdmPrimitiveType) rightType).isCompatible((EdmPrimitiveType) leftType))) {
       throw new UriParserSemanticException("Incompatible types.",
-          UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, ""); // TODO: better message
+          UriParserSemanticException.MessageKeys.TYPES_NOT_COMPATIBLE,
+          leftType.getFullQualifiedName().getFullQualifiedNameAsString(),
+          rightType.getFullQualifiedName().getFullQualifiedNameAsString());
     }
   }
 
-  private void checkAddSubTypes(final Expression left, final Expression right, final boolean isAdd)
+  private EdmType getAddSubTypeAndCheckLeftAndRight(final Expression left, final Expression right, final boolean isSub)
       throws UriParserException {
     final EdmType leftType = getType(left);
     final EdmType rightType = getType(right);
-    if (leftType == null || rightType == null
-        || isType(left,
+    if (leftType == null || rightType == null) {
+      return null;
+    }
+    if (isType(leftType,
             EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
             EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
             EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double)
-        && isType(right,
+        && isType(rightType,
             EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
             EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
             EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double)) {
-      return;
+      // The result type must be able to handle the overflow,
+      // so we return always a wider type than the types of the operands.
+      if (isType(leftType, EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double)
+          || isType(rightType,
+              EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double)) {
+        return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Double);
+      } else if (isType(leftType, EdmPrimitiveTypeKind.Int64) || isType(rightType, EdmPrimitiveTypeKind.Int64)) {
+        return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Decimal);
+      } else if (isType(leftType, EdmPrimitiveTypeKind.Int32) || isType(rightType, EdmPrimitiveTypeKind.Int32)) {
+        return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Int64);
+      } else if (isType(leftType, EdmPrimitiveTypeKind.Int16) || isType(rightType, EdmPrimitiveTypeKind.Int16)) {
+        return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Int32);
+      } else {
+        return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Int16);
+      }
     }
-    if (isType(left, EdmPrimitiveTypeKind.DateTimeOffset)
-        && (isType(right, EdmPrimitiveTypeKind.Duration)
-        || isType(right, EdmPrimitiveTypeKind.DateTimeOffset) && !isAdd)) {
-      return;
+    if ((isType(leftType, EdmPrimitiveTypeKind.DateTimeOffset)
+        || isType(leftType, EdmPrimitiveTypeKind.Duration))
+        && isType(rightType, EdmPrimitiveTypeKind.Duration)) {
+      return leftType;
     }
-    if (isType(left, EdmPrimitiveTypeKind.Duration) && isType(right, EdmPrimitiveTypeKind.Duration)
-        || isType(left, EdmPrimitiveTypeKind.Date)
-        && (isType(right, EdmPrimitiveTypeKind.Duration) || isType(right, EdmPrimitiveTypeKind.Date) && !isAdd)) {
-      return;
+    if (isType(leftType, EdmPrimitiveTypeKind.Date) && isType(rightType, EdmPrimitiveTypeKind.Duration)) {
+      return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.DateTimeOffset);
+    }
+    if (isSub
+        && (isType(leftType, EdmPrimitiveTypeKind.DateTimeOffset)
+            && isType(rightType, EdmPrimitiveTypeKind.DateTimeOffset)
+            || isType(leftType, EdmPrimitiveTypeKind.Date)
+            && isType(rightType, EdmPrimitiveTypeKind.Date))) {
+      return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Duration);
     }
     throw new UriParserSemanticException("Incompatible types.",
-        UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, ""); // TODO: better message
+        UriParserSemanticException.MessageKeys.TYPES_NOT_COMPATIBLE,
+        leftType.getFullQualifiedName().getFullQualifiedNameAsString(),
+        rightType.getFullQualifiedName().getFullQualifiedNameAsString());
   }
 
   private void checkStructuredTypeFilter(final EdmType type, final EdmType filterType)
       throws UriParserException {
-    if (!(filterType instanceof EdmStructuredType && ((EdmStructuredType)filterType).compatibleTo(type))) {
+    if (!(filterType instanceof EdmStructuredType && ((EdmStructuredType) filterType).compatibleTo(type))) {
       throw new UriParserSemanticException("Incompatible type filter.",
           UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER,
           filterType.getFullQualifiedName().getFullQualifiedNameAsString());

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/FilterParser.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/FilterParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/FilterParser.java
index e2767a1..dd73009 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/FilterParser.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/FilterParser.java
@@ -21,6 +21,7 @@ package org.apache.olingo.server.core.uri.parser;
 import java.util.Collection;
 
 import org.apache.olingo.commons.api.edm.Edm;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
 import org.apache.olingo.commons.api.edm.EdmType;
 import org.apache.olingo.server.api.OData;
 import org.apache.olingo.server.api.uri.queryoption.FilterOption;
@@ -43,7 +44,13 @@ public class FilterParser {
       throws UriParserException, UriValidationException {
     final Expression filterExpression = new ExpressionParser(edm, odata)
         .parse(tokenizer, referencedType, crossjoinEntitySetNames);
-    // TODO: Check that the expression is boolean.
-    return new FilterOptionImpl().setExpression(filterExpression);
+    final EdmType type = ExpressionParser.getType(filterExpression);
+    if (type == null || type.equals(odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean))) {
+      return new FilterOptionImpl().setExpression(filterExpression);
+    } else {
+      throw new UriParserSemanticException("Filter expressions must be boolean.",
+          UriParserSemanticException.MessageKeys.TYPES_NOT_COMPATIBLE,
+          "Edm.Boolean", type.getFullQualifiedName().getFullQualifiedNameAsString());
+    }
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/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 47efda5..3125fa6 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
@@ -18,18 +18,11 @@
  */
 package org.apache.olingo.server.core.uri.parser;
 
+import java.util.ArrayDeque;
+import java.util.Deque;
 import java.util.List;
 
-import org.antlr.v4.runtime.ANTLRInputStream;
-import org.antlr.v4.runtime.BailErrorStrategy;
-import org.antlr.v4.runtime.CommonTokenStream;
-import org.antlr.v4.runtime.Lexer;
-import org.antlr.v4.runtime.ParserRuleContext;
-import org.antlr.v4.runtime.RecognitionException;
-import org.antlr.v4.runtime.atn.PredictionMode;
-import org.antlr.v4.runtime.misc.ParseCancellationException;
 import org.apache.olingo.commons.api.edm.Edm;
-import org.apache.olingo.commons.api.edm.EdmEntityContainer;
 import org.apache.olingo.commons.api.edm.EdmEntitySet;
 import org.apache.olingo.commons.api.edm.EdmStructuredType;
 import org.apache.olingo.commons.api.edm.EdmType;
@@ -52,14 +45,10 @@ import org.apache.olingo.server.api.uri.queryoption.SystemQueryOptionKind;
 import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
 import org.apache.olingo.server.core.uri.UriInfoImpl;
 import org.apache.olingo.server.core.uri.UriResourceStartingTypeFilterImpl;
-import org.apache.olingo.server.core.uri.antlr.UriLexer;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandItemsEOFContext;
 import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
 import org.apache.olingo.server.core.uri.parser.search.SearchParser;
 import org.apache.olingo.server.core.uri.queryoption.AliasQueryOptionImpl;
 import org.apache.olingo.server.core.uri.queryoption.CountOptionImpl;
-import org.apache.olingo.server.core.uri.queryoption.ExpandOptionImpl;
 import org.apache.olingo.server.core.uri.queryoption.FormatOptionImpl;
 import org.apache.olingo.server.core.uri.queryoption.IdOptionImpl;
 import org.apache.olingo.server.core.uri.queryoption.SkipOptionImpl;
@@ -77,8 +66,6 @@ public class Parser {
   private final Edm edm;
   private final OData odata;
 
-  private enum ParserEntryRules { ExpandItems }
-
   public Parser(final Edm edm, final OData odata) {
     this.edm = edm;
     this.odata = odata;
@@ -87,8 +74,9 @@ public class Parser {
   public UriInfo parseUri(final String path, final String query, final String fragment)
       throws UriParserException, UriValidationException {
 
-    UriContext context = new UriContext();
-    UriParseTreeVisitor uriParseTreeVisitor = new UriParseTreeVisitor(edm, context);
+    UriInfoImpl contextUriInfo = new UriInfoImpl();
+    Deque<EdmType> contextTypes = new ArrayDeque<EdmType>();
+    boolean contextIsCollection = false;
 
     final List<String> pathSegmentsDecoded = UriDecoder.splitAndDecodePath(path);
     final int numberOfSegments = pathSegmentsDecoded.size();
@@ -98,49 +86,46 @@ public class Parser {
 
     if (firstSegment.isEmpty()) {
       ensureLastSegment(firstSegment, 0, numberOfSegments);
-      context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.service);
+      contextUriInfo.setKind(UriInfoKind.service);
 
     } else if (firstSegment.equals("$batch")) {
       ensureLastSegment(firstSegment, 1, numberOfSegments);
-      context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.batch);
+      contextUriInfo.setKind(UriInfoKind.batch);
 
     } else if (firstSegment.equals("$metadata")) {
       ensureLastSegment(firstSegment, 1, numberOfSegments);
-      context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.metadata);
-      context.contextUriInfo.setFragment(fragment);
+      contextUriInfo.setKind(UriInfoKind.metadata);
+      contextUriInfo.setFragment(fragment);
 
     } else if (firstSegment.equals("$all")) {
       ensureLastSegment(firstSegment, 1, numberOfSegments);
-      context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.all);
+      contextUriInfo.setKind(UriInfoKind.all);
       // This loads nearly the whole schema, but sooner or later '$all' needs all entity sets anyway.
       for (final EdmEntitySet entitySet : edm.getEntityContainer().getEntitySets()) {
-        context.contextTypes.push(entitySet.getEntityType());
+        contextTypes.push(entitySet.getEntityType());
       }
-      context.isCollection = true;
+      contextIsCollection = true;
 
     } else if (firstSegment.equals("$entity")) {
       if (numberOfSegments > 1) {
         final String typeCastSegment = pathSegmentsDecoded.get(1);
         ensureLastSegment(typeCastSegment, 2, numberOfSegments);
-        context.contextUriInfo = new ResourcePathParser(edm).parseDollarEntityTypeCast(typeCastSegment);
-        context.contextTypes.push(context.contextUriInfo.getEntityTypeCast());
+        contextUriInfo = new ResourcePathParser(edm).parseDollarEntityTypeCast(typeCastSegment);
+        contextTypes.push(contextUriInfo.getEntityTypeCast());
       } else {
-        context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.entityId);
+        contextUriInfo.setKind(UriInfoKind.entityId);
         // The type of the entity is not known until the $id query option has been parsed.
+        // TODO: Set the type (needed for the evaluation of system query options).
       }
-      context.isCollection = false;
+      contextIsCollection = false;
 
     } else if (firstSegment.startsWith("$crossjoin")) {
       ensureLastSegment(firstSegment, 1, numberOfSegments);
-      context.contextUriInfo = new ResourcePathParser(edm).parseCrossjoinSegment(firstSegment);
-      final EdmEntityContainer container = edm.getEntityContainer();
-      for (final String name : context.contextUriInfo.getEntitySetNames()) {
-        context.contextTypes.push(container.getEntitySet(name).getEntityType());
-      }
-      context.isCollection = true;
+      contextUriInfo = new ResourcePathParser(edm).parseCrossjoinSegment(firstSegment);
+      contextIsCollection = true;
 
     } else {
-      context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.resource);
+      contextUriInfo.setKind(UriInfoKind.resource);
       final ResourcePathParser resourcePathParser = new ResourcePathParser(edm);
       int count = 0;
       UriResource lastSegment = null;
@@ -168,7 +153,7 @@ public class Parser {
           } else {
             lastSegment = segment;
           }
-          context.contextUriInfo.addResourcePart(segment);
+          contextUriInfo.addResourcePart(segment);
         }
       }
 
@@ -176,9 +161,9 @@ public class Parser {
         final UriResourcePartTyped typed = (UriResourcePartTyped) lastSegment;
         final EdmType type = ParserHelper.getTypeInformation(typed);
         if (type != null) { // could be null for, e.g., actions without return type
-          context.contextTypes.push(type);
+          contextTypes.push(type);
         }
-        context.isCollection = typed.isCollection();
+        contextIsCollection = typed.isCollection();
       }
     }
 
@@ -191,9 +176,10 @@ public class Parser {
         SystemQueryOption systemOption = null;
         if (optionName.equals(SystemQueryOptionKind.FILTER.toString())) {
           UriTokenizer filterTokenizer = new UriTokenizer(optionValue);
-          // The Referring type could also be a primitive type not only a structured type
-          systemOption = new FilterParser(edm, odata).parse(filterTokenizer, context.contextTypes.peek(),
-                  context.contextUriInfo.getEntitySetNames());
+          // The referring type could be a primitive type or a structured type.
+          systemOption = new FilterParser(edm, odata).parse(filterTokenizer,
+              contextTypes.peek(),
+              contextUriInfo.getEntitySetNames());
           checkOptionEOF(filterTokenizer, optionName, optionValue);
 
         } else if (optionName.equals(SystemQueryOptionKind.FORMAT.toString())) {
@@ -211,19 +197,26 @@ public class Parser {
           systemOption = formatOption;
 
         } else if (optionName.equals(SystemQueryOptionKind.EXPAND.toString())) {
-          try {
-            ExpandItemsEOFContext ctxExpandItems =
-                (ExpandItemsEOFContext) parseRule(optionValue, ParserEntryRules.ExpandItems);
-            systemOption = (ExpandOptionImpl) uriParseTreeVisitor.visitExpandItemsEOF(ctxExpandItems);
-          } catch (final ParseCancellationException e) {
-            throw e.getCause() instanceof UriParserException ?
-                (UriParserException) e.getCause() :
-                new UriParserSyntaxException("Syntax error", e, UriParserSyntaxException.MessageKeys.SYNTAX);
+          if (contextTypes.peek() instanceof EdmStructuredType
+              || !contextUriInfo.getEntitySetNames().isEmpty()
+              || contextUriInfo.getKind() == UriInfoKind.entityId) { // TODO: Remove once the type has been set above.
+            UriTokenizer expandTokenizer = new UriTokenizer(optionValue);
+            systemOption = new ExpandParser(edm, odata).parse(expandTokenizer,
+                contextTypes.peek() instanceof EdmStructuredType ? (EdmStructuredType) contextTypes.peek() : null);
+            checkOptionEOF(expandTokenizer, optionName, optionValue);
+          } else {
+            throw new UriValidationException("Expand is only allowed on structured types!",
+                UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED, optionName);
           }
 
         } else if (optionName.equals(SystemQueryOptionKind.ID.toString())) {
           IdOptionImpl idOption = new IdOptionImpl();
           idOption.setText(optionValue);
+          if (optionValue == null || optionValue.isEmpty()) {
+            throw new UriParserSyntaxException("Illegal value of $id option!",
+                UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
+                optionName, optionValue);
+          }
           idOption.setValue(optionValue);
           systemOption = idOption;
 
@@ -234,10 +227,8 @@ public class Parser {
         } else if (optionName.equals(SystemQueryOptionKind.ORDERBY.toString())) {
           UriTokenizer orderByTokenizer = new UriTokenizer(optionValue);
           systemOption = new OrderByParser(edm, odata).parse(orderByTokenizer,
-              context.contextTypes.peek() instanceof EdmStructuredType ?
-                  (EdmStructuredType) context.contextTypes.peek() :
-                  null,
-                  context.contextUriInfo.getEntitySetNames());
+              contextTypes.peek() instanceof EdmStructuredType ? (EdmStructuredType) contextTypes.peek() : null,
+              contextUriInfo.getEntitySetNames());
           checkOptionEOF(orderByTokenizer, optionName, optionValue);
 
         } else if (optionName.equals(SystemQueryOptionKind.SEARCH.toString())) {
@@ -246,40 +237,31 @@ public class Parser {
         } else if (optionName.equals(SystemQueryOptionKind.SELECT.toString())) {
           UriTokenizer selectTokenizer = new UriTokenizer(optionValue);
           systemOption = new SelectParser(edm).parse(selectTokenizer,
-              context.contextTypes.peek() instanceof EdmStructuredType ?
-                  (EdmStructuredType) context.contextTypes.peek() :
-                  null,
-              context.isCollection);
+              contextTypes.peek() instanceof EdmStructuredType ? (EdmStructuredType) contextTypes.peek() : null,
+              contextIsCollection);
           checkOptionEOF(selectTokenizer, optionName, optionValue);
 
         } else if (optionName.equals(SystemQueryOptionKind.SKIP.toString())) {
           SkipOptionImpl skipOption = new SkipOptionImpl();
           skipOption.setText(optionValue);
-          try {
-            skipOption.setValue(Integer.parseInt(optionValue));
-          } catch (final NumberFormatException e) {
-            throw new UriParserSyntaxException("Illegal value of $skip option!", e,
-                UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
-                optionName, optionValue);
-          }
+          skipOption.setValue(ParserHelper.parseNonNegativeInteger(optionName, optionValue, true));
           systemOption = skipOption;
 
         } else if (optionName.equals(SystemQueryOptionKind.SKIPTOKEN.toString())) {
           SkipTokenOptionImpl skipTokenOption = new SkipTokenOptionImpl();
           skipTokenOption.setText(optionValue);
+          if (optionValue == null || optionValue.isEmpty()) {
+            throw new UriParserSyntaxException("Illegal value of $skiptoken option!",
+                UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
+                optionName, optionValue);
+          }
           skipTokenOption.setValue(optionValue);
           systemOption = skipTokenOption;
 
         } else if (optionName.equals(SystemQueryOptionKind.TOP.toString())) {
           TopOptionImpl topOption = new TopOptionImpl();
           topOption.setText(optionValue);
-          try {
-            topOption.setValue(Integer.parseInt(optionValue));
-          } catch (final NumberFormatException e) {
-            throw new UriParserSyntaxException("Illegal value of $top option!", e,
-                UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
-                optionName, optionValue);
-          }
+          topOption.setValue(ParserHelper.parseNonNegativeInteger(optionName, optionValue, true));
           systemOption = topOption;
 
         } else if (optionName.equals(SystemQueryOptionKind.COUNT.toString())) {
@@ -299,14 +281,14 @@ public class Parser {
               UriParserSyntaxException.MessageKeys.UNKNOWN_SYSTEM_QUERY_OPTION, optionName);
         }
         try {
-          context.contextUriInfo.setSystemQueryOption(systemOption);
+          contextUriInfo.setSystemQueryOption(systemOption);
         } catch (final ODataRuntimeException e) {
           throw new UriParserSyntaxException("Double system query option!", e,
               UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION, optionName);
         }
 
       } else if (optionName.startsWith(AT)) {
-        if (context.contextUriInfo.getAlias(optionName) == null) {
+        if (contextUriInfo.getAlias(optionName) == null) {
           // TODO: Create a proper alias-value parser that can parse also common expressions.
           Expression expression = null;
           UriTokenizer aliasTokenizer = new UriTokenizer(optionValue);
@@ -318,13 +300,13 @@ public class Parser {
           } else {
             UriTokenizer aliasValueTokenizer = new UriTokenizer(optionValue);
             expression = new ExpressionParser(edm, odata).parse(aliasValueTokenizer, null,
-                context.contextUriInfo.getEntitySetNames());
+                contextUriInfo.getEntitySetNames());
             if (!aliasValueTokenizer.next(TokenKind.EOF)) {
               throw new UriParserSyntaxException("Illegal value for alias '" + optionName + "'.",
                   UriParserSyntaxException.MessageKeys.SYNTAX);
             }
           }
-          context.contextUriInfo.addAlias((AliasQueryOption) new AliasQueryOptionImpl()
+          contextUriInfo.addAlias((AliasQueryOption) new AliasQueryOptionImpl()
               .setAliasValue(expression)
               .setName(optionName)
               .setText(NULL.equals(optionValue) ? null : optionValue));
@@ -334,11 +316,11 @@ public class Parser {
         }
 
       } else {
-        context.contextUriInfo.addCustomQueryOption((CustomQueryOption) option);
+        contextUriInfo.addCustomQueryOption((CustomQueryOption) option);
       }
     }
 
-    return context.contextUriInfo;
+    return contextUriInfo;
   }
 
   private void ensureLastSegment(final String segment, final int pos, final int size)
@@ -362,102 +344,4 @@ public class Parser {
           optionName, optionValue);
     }
   }
-
-  private ParserRuleContext parseRule(final String input, final ParserEntryRules entryPoint)
-      throws UriParserSyntaxException {
-    UriParserParser parser = null;
-    UriLexer lexer = null;
-    ParserRuleContext ret = null;
-
-    // Use 2 stage approach to improve performance
-    // see https://github.com/antlr/antlr4/issues/192
-
-    // stage = 1
-    try {
-
-      // create parser
-      lexer = new UriLexer(new ANTLRInputStream(input));
-      parser = new UriParserParser(new CommonTokenStream(lexer));
-
-      // Set error strategy
-      addStage1ErrorStrategy(parser);
-
-      // Set error collector
-      addStage1ErrorListener(parser);
-
-      // user the faster LL parsing
-      parser.getInterpreter().setPredictionMode(PredictionMode.SLL);
-
-      // parse
-      switch (entryPoint) {
-      case ExpandItems:
-        lexer.mode(Lexer.DEFAULT_MODE);
-        ret = parser.expandItemsEOF();
-        break;
-      default:
-        break;
-
-      }
-
-    } catch (ParseCancellationException hardException) {
-      // stage = 2
-      try {
-
-        // create parser
-        lexer = new UriLexer(new ANTLRInputStream(input));
-        parser = new UriParserParser(new CommonTokenStream(lexer));
-
-        // Set error strategy
-        addStage2ErrorStrategy(parser);
-
-        // Set error collector
-        addStage2ErrorListener(parser);
-
-        // Use the slower SLL parsing
-        parser.getInterpreter().setPredictionMode(PredictionMode.LL);
-
-        // parse
-        switch (entryPoint) {
-        case ExpandItems:
-          lexer.mode(Lexer.DEFAULT_MODE);
-          ret = parser.expandItemsEOF();
-          break;
-        default:
-          break;
-        }
-
-      } catch (final RecognitionException weakException) {
-        throw new UriParserSyntaxException("Error in syntax", weakException,
-            UriParserSyntaxException.MessageKeys.SYNTAX);
-
-        // exceptionOnStage = 2;
-      }
-    } catch (final RecognitionException hardException) {
-      throw new UriParserSyntaxException("Error in syntax", hardException,
-          UriParserSyntaxException.MessageKeys.SYNTAX);
-    }
-
-    return ret;
-  }
-
-  protected void addStage1ErrorStrategy(final UriParserParser parser) {
-    // Throw exception at first syntax error
-    parser.setErrorHandler(new BailErrorStrategy());
-
-  }
-
-  protected void addStage2ErrorStrategy(final UriParserParser parser) {
-    // Throw exception at first syntax error
-    parser.setErrorHandler(new BailErrorStrategy());
-  }
-
-  protected void addStage1ErrorListener(final UriParserParser parser) {
-    // No error logging to System.out or System.err, only exceptions used (depending on ErrorStrategy)
-    parser.removeErrorListeners();
-  }
-
-  protected void addStage2ErrorListener(final UriParserParser parser) {
-    // No error logging to System.out or System.err, only exceptions used (depending on ErrorStrategy)
-    parser.removeErrorListeners();
-  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/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
index 65ee461..7f4abf7 100644
--- 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
@@ -23,6 +23,7 @@ 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.EdmEntityType;
 import org.apache.olingo.commons.api.edm.EdmKeyPropertyRef;
 import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
@@ -36,6 +37,8 @@ import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
 import org.apache.olingo.server.api.OData;
 import org.apache.olingo.server.api.uri.UriParameter;
 import org.apache.olingo.server.api.uri.UriResourcePartTyped;
+import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
+import org.apache.olingo.server.api.uri.queryoption.expression.Literal;
 import org.apache.olingo.server.core.ODataImpl;
 import org.apache.olingo.server.core.uri.UriParameterImpl;
 import org.apache.olingo.server.core.uri.UriResourceTypedImpl;
@@ -88,8 +91,9 @@ public class ParserHelper {
         TokenKind.EnumValue);
   }
 
-  protected static List<UriParameter> parseFunctionParameters(UriTokenizer tokenizer, final boolean withComplex)
-      throws UriParserException {
+  protected static List<UriParameter> parseFunctionParameters(UriTokenizer tokenizer,
+      final Edm edm, final EdmType referringType, final boolean withComplex)
+      throws UriParserException, UriValidationException {
     List<UriParameter> parameters = new ArrayList<UriParameter>();
     ParserHelper.requireNext(tokenizer, TokenKind.OPEN);
     if (tokenizer.next(TokenKind.CLOSE)) {
@@ -115,6 +119,13 @@ public class ParserHelper {
           throw new UriParserSemanticException("A JSON array or object is not allowed as parameter value.",
               UriParserSemanticException.MessageKeys.COMPLEX_PARAMETER_IN_RESOURCE_PATH, tokenizer.getText());
         }
+      } else if (withComplex) {
+        final Expression expression = new ExpressionParser(edm, odata).parse(tokenizer, referringType, null);
+        parameters.add(new UriParameterImpl().setName(name)
+            .setText(expression instanceof Literal ?
+                "null".equals(((Literal) expression).getText()) ? null : ((Literal) expression).getText() :
+                null)
+            .setExpression(expression instanceof Literal ? null : expression));
       } else if (nextPrimitiveValue(tokenizer) == null) {
         throw new UriParserSemanticException("Wrong parameter value.",
             UriParserSemanticException.MessageKeys.INVALID_KEY_VALUE, "");
@@ -387,4 +398,23 @@ public class ParserHelper {
 
     return type;
   }
+
+  protected static int parseNonNegativeInteger(final String optionName, final String optionValue,
+      final boolean zeroAllowed) throws UriParserException {
+    int value;
+    try {
+      value = Integer.parseInt(optionValue);
+    } catch (final NumberFormatException e) {
+      throw new UriParserSyntaxException("Illegal value of '" + optionName + "' option!", e,
+          UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
+          optionName, optionValue);
+    }
+    if (value > 0 || value == 0 && zeroAllowed) {
+      return value;
+    } else {
+      throw new UriParserSyntaxException("Illegal value of '" + optionName + "' option!",
+          UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
+          optionName, optionValue);
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/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 1cd4d7a..87cb91a 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
@@ -261,30 +261,33 @@ public class ResourcePathParser {
       throws UriParserException, UriValidationException {
     final FullQualifiedName name = new FullQualifiedName(tokenizer.getText());
     requireTyped(previous, name.getFullQualifiedNameAsString());
-      final UriResourcePartTyped previousTyped = (UriResourcePartTyped) previous;
-      final EdmType previousTypeFilter = getPreviousTypeFilter(previousTyped);
-      final EdmType previousType = previousTypeFilter == null ? previousTyped.getType() : previousTypeFilter;
-      final EdmAction boundAction = edm.getBoundAction(name,
-          previousType.getFullQualifiedName(),
-          previousTyped.isCollection());
-      if (boundAction != null) {
-        ParserHelper.requireTokenEnd(tokenizer);
-        return new UriResourceActionImpl(boundAction);
-      }
-      EdmStructuredType type = edm.getEntityType(name);
-      if (type == null) {
-        type = edm.getComplexType(name);
-      }
-      if (type != null) {
-        return typeCast(name, type, previousTyped);
-      }
-      if (tokenizer.next(TokenKind.EOF)) {
-        throw new UriParserSemanticException("Type '" + name.getFullQualifiedNameAsString() + "' not found.",
-            UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, name.getFullQualifiedNameAsString());
-      }
-      return functionCall(null, name,
-          previousType.getFullQualifiedName(),
-          previousTyped.isCollection());
+    final UriResourcePartTyped previousTyped = (UriResourcePartTyped) previous;
+    final EdmType previousTypeFilter = getPreviousTypeFilter(previousTyped);
+    final EdmType previousType = previousTypeFilter == null ? previousTyped.getType() : previousTypeFilter;
+
+    // We check for bound actions first because they cannot be followed by anything.
+    final EdmAction boundAction =
+        edm.getBoundAction(name, previousType.getFullQualifiedName(), previousTyped.isCollection());
+    if (boundAction != null) {
+      ParserHelper.requireTokenEnd(tokenizer);
+      return new UriResourceActionImpl(boundAction);
+    }
+
+    // Type casts can be syntactically indistinguishable from bound function calls in the case of additional keys.
+    // But normally they are shorter, so they come next.
+    final EdmStructuredType type = previousTyped.getType() instanceof EdmEntityType ?
+        edm.getEntityType(name) :
+        edm.getComplexType(name);
+    if (type != null) {
+      return typeCast(name, type, previousTyped);
+    }
+    if (tokenizer.next(TokenKind.EOF)) {
+      throw new UriParserSemanticException("Type '" + name.getFullQualifiedNameAsString() + "' not found.",
+          UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, name.getFullQualifiedNameAsString());
+    }
+
+    // Now a bound function call is the only remaining option.
+    return functionCall(null, name, previousType.getFullQualifiedName(), previousTyped.isCollection());
   }
 
   private void requireTyped(final UriResource previous, final String forWhat) throws UriParserException {
@@ -317,8 +320,13 @@ public class ResourcePathParser {
           ((UriResourceWithKeysImpl) previousTyped).setEntryTypeFilter(type);
         }
         if (tokenizer.next(TokenKind.OPEN)) {
-          ((UriResourceWithKeysImpl) previousTyped).setKeyPredicates(
-              ParserHelper.parseKeyPredicate(tokenizer, (EdmEntityType) type, null));
+          final List<UriParameter> keys = ParserHelper.parseKeyPredicate(tokenizer, (EdmEntityType) type, null);
+          if (previousTyped.isCollection()) {
+            ((UriResourceWithKeysImpl) previousTyped).setKeyPredicates(keys);
+          } else {
+            throw new UriParserSemanticException("Key not allowed here.",
+                UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED);
+          }
         }
       } else {
         previousTypeFilter = ((UriResourceTypedImpl) previousTyped).getTypeFilter();
@@ -351,7 +359,7 @@ public class ResourcePathParser {
   private UriResource functionCall(final EdmFunctionImport edmFunctionImport,
       final FullQualifiedName boundFunctionName, final FullQualifiedName bindingParameterTypeName,
       final boolean isBindingParameterCollection) throws UriParserException, UriValidationException {
-    final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, false);
+    final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, edm, null, false);
     final List<String> names = ParserHelper.getParameterNames(parameters);
     EdmFunction function = null;
     if (edmFunctionImport != null) {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/SearchParser.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/SearchParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/SearchParser.java
new file mode 100644
index 0000000..a072327
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/SearchParser.java
@@ -0,0 +1,108 @@
+/*
+ * 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.api.uri.queryoption.SearchOption;
+import org.apache.olingo.server.api.uri.queryoption.search.SearchBinaryOperatorKind;
+import org.apache.olingo.server.api.uri.queryoption.search.SearchExpression;
+import org.apache.olingo.server.api.uri.queryoption.search.SearchTerm;
+import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
+import org.apache.olingo.server.core.uri.parser.search.SearchBinaryImpl;
+import org.apache.olingo.server.core.uri.parser.search.SearchParserException;
+import org.apache.olingo.server.core.uri.parser.search.SearchTermImpl;
+import org.apache.olingo.server.core.uri.parser.search.SearchUnaryImpl;
+import org.apache.olingo.server.core.uri.queryoption.SearchOptionImpl;
+
+/**
+ * Parses search expressions according to the following (rewritten) grammar:
+ * <pre>
+ * SearchExpr  ::= ExprOR
+ * ExprOR      ::= ExprAnd ('OR' ExprAnd)*
+ * ExprAnd     ::= Term ('AND'? Term)*
+ * Term        ::= ('NOT'? (Word | Phrase)) | ('(' SearchExpr ')')
+ * </pre> 
+ */
+public class SearchParser {
+
+  public SearchOption parse(UriTokenizer tokenizer) throws SearchParserException {
+    SearchOptionImpl searchOption = new SearchOptionImpl();
+    searchOption.setSearchExpression(processExprOr(tokenizer));
+    return searchOption;
+  }
+
+  private SearchExpression processExprOr(UriTokenizer tokenizer) throws SearchParserException {
+    SearchExpression left = processExprAnd(tokenizer);
+
+    while (tokenizer.next(TokenKind.OrOperatorSearch)) {
+      final SearchExpression right = processExprAnd(tokenizer);
+      left = new SearchBinaryImpl(left, SearchBinaryOperatorKind.OR, right);
+    }
+
+    return left;
+  }
+
+  private SearchExpression processExprAnd(UriTokenizer tokenizer) throws SearchParserException {
+    SearchExpression left = processTerm(tokenizer);
+
+    while (tokenizer.next(TokenKind.AndOperatorSearch)) { // Could be whitespace or whitespace-surrounded 'AND'.
+      final SearchExpression right = processTerm(tokenizer);
+      left = new SearchBinaryImpl(left, SearchBinaryOperatorKind.AND, right);
+    }
+
+    return left;
+  }
+
+  private SearchExpression processTerm(UriTokenizer tokenizer) throws SearchParserException {
+    if (tokenizer.next(TokenKind.OPEN)) {
+      final SearchExpression expr = processExprOr(tokenizer);
+      if (!tokenizer.next(TokenKind.CLOSE)) {
+        throw new SearchParserException("Missing close parenthesis after open parenthesis.",
+            SearchParserException.MessageKeys.MISSING_CLOSE);
+      }
+      return expr;
+    } else if (tokenizer.next(TokenKind.NotOperatorSearch)) {
+      return processNot(tokenizer);
+    } else if (tokenizer.next(TokenKind.Word)) {
+      return new SearchTermImpl(tokenizer.getText());
+    } else if (tokenizer.next(TokenKind.Phrase)) {
+      return processPhrase(tokenizer);
+    } else {
+      throw new SearchParserException("Expected PHRASE or WORD not found.",
+          SearchParserException.MessageKeys.EXPECTED_DIFFERENT_TOKEN, "PHRASE, WORD", "");
+    }
+  }
+
+  private SearchExpression processNot(UriTokenizer tokenizer) throws SearchParserException {
+    if (tokenizer.next(TokenKind.Word)) {
+      return new SearchUnaryImpl(new SearchTermImpl(tokenizer.getText()));
+    } else if (tokenizer.next(TokenKind.Phrase)) {
+      return new SearchUnaryImpl(processPhrase(tokenizer));
+    } else {
+      throw new SearchParserException("NOT must be followed by a term.",
+          SearchParserException.MessageKeys.INVALID_NOT_OPERAND, "");
+    }
+  }
+
+  private SearchTerm processPhrase(UriTokenizer tokenizer) {
+    final String literal = tokenizer.getText();
+    return new SearchTermImpl(literal.substring(1, literal.length() - 1)
+        .replace("\\\"", "\"")
+        .replace("\\\\", "\\"));
+  }
+}


[5/6] olingo-odata4 git commit: [OLINGO-834] $expand parser in Java + clean-up

Posted by ch...@apache.org.
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriContext.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriContext.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriContext.java
deleted file mode 100644
index 36a0887..0000000
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriContext.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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 java.util.ArrayDeque;
-import java.util.Deque;
-
-import org.apache.olingo.commons.api.edm.EdmType;
-import org.apache.olingo.server.core.uri.UriInfoImpl;
-import org.apache.olingo.server.core.uri.queryoption.ExpandItemImpl;
-import org.apache.olingo.server.core.uri.queryoption.SelectItemImpl;
-
-/**
- * UriContext object used for holding information for URI parsing.
- *
- */
-public class UriContext {
-
-  public static class LambdaVariable {
-    public String name;
-    public EdmType type;
-  }
-
-  /**
-   * Hold all currently allowed lambda variables
-   * As lambda functions can be nested there may be more than one allowed lambda variables at a time while parsing a
-   * $filter or $orderby expressions.
-   */
-  public Deque<LambdaVariable> allowedLambdaVariables;
-  /**
-   * Used to stack type information for nested $expand, $filter query options and other cases.
-   */
-  public Deque<EdmType> contextTypes;
-
-  /** Whether the context types are collections. */
-  public boolean isCollection;
-
-  // CHECKSTYLE:OFF (Maven checkstyle)
-  /**
-   * Set within method
-   * {@link org.apache.olingo.server.core.uri.antlr.UriParserBaseVisitor#visitExpandItem(org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandItemContext ctx)}
-   * and
-   * {@link org.apache.olingo.server.core.uri.antlr.UriParserBaseVisitor#visitExpandPathExtension(org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandPathExtensionContext ctx)}
-   * to allow nodes
-   * deeper in the expand tree at
-   * {@link org.apache.olingo.server.core.uri.antlr.UriParserBaseVisitor#visitExpandPathExtension(org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandPathExtensionContext ctx)}
-   * appending path
-   * segments to the currently processed {@link ExpandItemImpl}.
-   */
-  public ExpandItemImpl contextExpandItemPath;
-  // CHECKSTYLE:ON (Maven checkstyle)
-  
-  //CHECKSTYLE:OFF (Maven checkstyle)
-  /**
-   * Set to true in method {@link UriParseTreeVisitor#visitExpandPath} right before 
-   * calling {@link  org.apache.olingo.server.core.uri.parser.UriParseTreeVisitor#readResourcePathSegment}
-   * After reading the path the variable is set back to false
-   * 
-   * readResourcePathSegment handles all navigation properties, it depends on the context if key predicates are allowed or not.
-   * In case of expand 
-   */
-  public boolean contextVisitExpandResourcePath;
-  //CHECKSTYLE:ON (Maven checkstyle)
-  
-  // CHECKSTYLE:OFF (Maven checkstyle)
-  /**
-   * Set within method
-   * {@link org.apache.olingo.server.core.uri.antlr.UriParserBaseVisitor#visitSelectItem(org.apache.olingo.server.core.uri.antlr.UriParserParser.SelectItemContext ctx)}
-   * to allow
-   * nodes
-   * deeper in the expand tree at
-   * {@link org.apache.olingo.server.core.uri.antlr.UriParserBaseVisitor#visitSelectSegment(org.apache.olingo.server.core.uri.antlr.UriParserParser.SelectSegmentContext ctx)}
-   * appending path segments to the
-   * currently processed {@link SelectItemImpl}.
-   */
-  public SelectItemImpl contextSelectItem;
-  // CHECKSTYLE:ON (Maven checkstyle)
-  /**
-   * Stores the currently processed UriInfo objects. There is one URI Info object for the resource path
-   * and one for each new first member access within $filter and $orderBy options.
-   */
-  public UriInfoImpl contextUriInfo;
-  public boolean contextReadingFunctionParameters;
-  
-  /**
-   * Set to true if the parser operates on query part.
-   */
-  public boolean contextReadingQueryPart;
-  
-  public UriContext() {
-
-    contextExpandItemPath = null;
-    contextReadingFunctionParameters = false;
-    contextSelectItem = null;
-    contextTypes = new ArrayDeque<EdmType>();
-    allowedLambdaVariables = new ArrayDeque<UriContext.LambdaVariable>();
-
-  }
-}
\ No newline at end of file


[4/6] olingo-odata4 git commit: [OLINGO-834] $expand parser in Java + clean-up

Posted by ch...@apache.org.
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/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
deleted file mode 100644
index 1bab218..0000000
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java
+++ /dev/null
@@ -1,2313 +0,0 @@
-/*
- * 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 java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import org.antlr.v4.runtime.misc.NotNull;
-import org.antlr.v4.runtime.misc.ParseCancellationException;
-import org.antlr.v4.runtime.tree.ParseTree;
-import org.apache.olingo.commons.api.edm.Edm;
-import org.apache.olingo.commons.api.edm.EdmAction;
-import org.apache.olingo.commons.api.edm.EdmActionImport;
-import org.apache.olingo.commons.api.edm.EdmComplexType;
-import org.apache.olingo.commons.api.edm.EdmElement;
-import org.apache.olingo.commons.api.edm.EdmEntityContainer;
-import org.apache.olingo.commons.api.edm.EdmEntitySet;
-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.EdmFunctionImport;
-import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
-import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
-import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
-import org.apache.olingo.commons.api.edm.EdmProperty;
-import org.apache.olingo.commons.api.edm.EdmReturnType;
-import org.apache.olingo.commons.api.edm.EdmSingleton;
-import org.apache.olingo.commons.api.edm.EdmStructuredType;
-import org.apache.olingo.commons.api.edm.EdmType;
-import org.apache.olingo.commons.api.edm.FullQualifiedName;
-import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
-import org.apache.olingo.commons.api.ex.ODataRuntimeException;
-import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
-import org.apache.olingo.server.api.uri.UriInfoKind;
-import org.apache.olingo.server.api.uri.UriInfoResource;
-import org.apache.olingo.server.api.uri.UriParameter;
-import org.apache.olingo.server.api.uri.UriResource;
-import org.apache.olingo.server.api.uri.UriResourceEntitySet;
-import org.apache.olingo.server.api.uri.UriResourceFunction;
-import org.apache.olingo.server.api.uri.UriResourceNavigation;
-import org.apache.olingo.server.api.uri.UriResourcePartTyped;
-import org.apache.olingo.server.api.uri.UriResourceRoot;
-import org.apache.olingo.server.api.uri.queryoption.SelectItem;
-import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind;
-import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
-import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind;
-import org.apache.olingo.server.api.uri.queryoption.expression.UnaryOperatorKind;
-import org.apache.olingo.server.core.uri.UriInfoImpl;
-import org.apache.olingo.server.core.uri.UriParameterImpl;
-import org.apache.olingo.server.core.uri.UriResourceActionImpl;
-import org.apache.olingo.server.core.uri.UriResourceComplexPropertyImpl;
-import org.apache.olingo.server.core.uri.UriResourceCountImpl;
-import org.apache.olingo.server.core.uri.UriResourceEntitySetImpl;
-import org.apache.olingo.server.core.uri.UriResourceFunctionImpl;
-import org.apache.olingo.server.core.uri.UriResourceImpl;
-import org.apache.olingo.server.core.uri.UriResourceItImpl;
-import org.apache.olingo.server.core.uri.UriResourceLambdaAllImpl;
-import org.apache.olingo.server.core.uri.UriResourceLambdaAnyImpl;
-import org.apache.olingo.server.core.uri.UriResourceLambdaVarImpl;
-import org.apache.olingo.server.core.uri.UriResourceNavigationPropertyImpl;
-import org.apache.olingo.server.core.uri.UriResourcePrimitivePropertyImpl;
-import org.apache.olingo.server.core.uri.UriResourceRefImpl;
-import org.apache.olingo.server.core.uri.UriResourceRootImpl;
-import org.apache.olingo.server.core.uri.UriResourceSingletonImpl;
-import org.apache.olingo.server.core.uri.UriResourceStartingTypeFilterImpl;
-import org.apache.olingo.server.core.uri.UriResourceTypedImpl;
-import org.apache.olingo.server.core.uri.UriResourceValueImpl;
-import org.apache.olingo.server.core.uri.UriResourceWithKeysImpl;
-import org.apache.olingo.server.core.uri.antlr.UriLexer;
-import org.apache.olingo.server.core.uri.antlr.UriParserBaseVisitor;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.AllEOFContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.AllExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.AltAddContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.AltAllContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.AltAndContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.AltAnyContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.AltComparismContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.AltEqualityContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.AltHasContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.AltMultContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.AltOrContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.AnyExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.ArrayOrObjectContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.BinaryLiteralContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.BooleanNonCaseLiteralContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.CastExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.CeilingMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.ConcatMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.ConstSegmentContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.ContainsMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.DateLiteralContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.DateMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.DatetimeoffsetLiteralContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.DayMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.DecimalLiteralContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.DurationLiteralContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.EndsWithMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.EntityEOFContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.EnumLiteralContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandCountOptionContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandItemContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandItemsContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandItemsEOFContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandOptionContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandPathContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandPathExtensionContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandRefOptionContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.FilterContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.FilterExpressionEOFContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.FloorMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.FractionalsecondsMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.GeoDistanceMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.GeoIntersectsMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.GeoLengthMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.GuidLiteralContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.HourMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.IndexOfMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.InlinecountContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.IntLiteralContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.IsofExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.LengthMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.LevelsContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.MaxDateTimeMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.MemberExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.MetadataEOFContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.MinDateTimeMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.MinuteMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.MonthMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.NameValueOptListContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.NameValuePairContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.NamespaceContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.NaninfinityLiteralContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.NowMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.NullruleLiteralContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.OrderByContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.OrderByEOFContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.OrderByItemContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.OrderListContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.PathSegmentContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.PathSegmentsContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.PrimitiveLiteralContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.QueryOptionContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.QueryOptionsContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.RootExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.RoundMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.SearchSpecialTokenContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.SecondMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.SelectContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.SelectEOFContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.SelectItemContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.SelectSegmentContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.SkipContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.SkiptokenContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.StartsWithMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.StringLiteralContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.SubstringMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.TimeMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.TimeofdayLiteralContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.ToLowerMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.ToUpperMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.TopContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.TotalOffsetMinutesMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.TotalsecondsMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.TrimMethodCallExprContext;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.YearMethodCallExprContext;
-import org.apache.olingo.server.core.uri.parser.UriParserSemanticException.MessageKeys;
-import org.apache.olingo.server.core.uri.queryoption.CountOptionImpl;
-import org.apache.olingo.server.core.uri.queryoption.ExpandItemImpl;
-import org.apache.olingo.server.core.uri.queryoption.ExpandOptionImpl;
-import org.apache.olingo.server.core.uri.queryoption.FilterOptionImpl;
-import org.apache.olingo.server.core.uri.queryoption.LevelsOptionImpl;
-import org.apache.olingo.server.core.uri.queryoption.OrderByItemImpl;
-import org.apache.olingo.server.core.uri.queryoption.OrderByOptionImpl;
-import org.apache.olingo.server.core.uri.queryoption.QueryOptionImpl;
-import org.apache.olingo.server.core.uri.queryoption.SelectItemImpl;
-import org.apache.olingo.server.core.uri.queryoption.SelectOptionImpl;
-import org.apache.olingo.server.core.uri.queryoption.SkipOptionImpl;
-import org.apache.olingo.server.core.uri.queryoption.SkipTokenOptionImpl;
-import org.apache.olingo.server.core.uri.queryoption.SystemQueryOptionImpl;
-import org.apache.olingo.server.core.uri.queryoption.TopOptionImpl;
-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.EnumerationImpl;
-import org.apache.olingo.server.core.uri.queryoption.expression.LiteralImpl;
-import org.apache.olingo.server.core.uri.queryoption.expression.MemberImpl;
-import org.apache.olingo.server.core.uri.queryoption.expression.MethodImpl;
-import org.apache.olingo.server.core.uri.queryoption.expression.TypeLiteralImpl;
-import org.apache.olingo.server.core.uri.queryoption.expression.UnaryImpl;
-
-/**
- * Converts the URI parse tree as generated by ANTLR into an internal representation
- * suitable to give to an application.
- * While converting the tree is only validated against the EDM if necessary.
- * <br>
- * Attention:
- * <ul>
- * <li>This UriVisitor is at somes point more lax than the original ABNF.</li>
- * <li>It is more tolerable against additional white spaces.</li>
- * </ul>
- * Currently not supported are
- * <ul>
- * <li>parsing the context of $metadata</li>
- * <li>parsing $search</li>
- * </ul>
- */
-public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
-
-  public UriContext context = null;
-
-  public Edm edm;
-
-  public EdmEntityContainer edmEntityContainer;
-
-  // --- class ---
-  public UriParseTreeVisitor(final Edm edm, final UriContext context) {
-    this.edm = edm;
-    this.context = context;
-    edmEntityContainer = edm.getEntityContainer();
-  }
-
-  @Override
-  protected Object aggregateResult(final Object aggregate, final Object nextResult) {
-    if (aggregate != null) {
-      return aggregate;
-    } else {
-      return nextResult;
-    }
-  }
-
-  private FullQualifiedName getFullNameFromContext(final NamespaceContext vNS, final String odi) {
-    String namespace = vNS.getText();
-    namespace = namespace.substring(0, namespace.length() - 1); // vNS contains a trailing point that has to be removed
-    return new FullQualifiedName(namespace, odi);
-  }
-
-  private UriContext.LambdaVariable getLambdaVar(final String odi) {
-    for (UriContext.LambdaVariable item : context.allowedLambdaVariables) {
-      if (item.name.equals(odi)) {
-        return item;
-      }
-    }
-    return null;
-  }
-
-  public UriResourceTypedImpl readResourcePathSegment(final PathSegmentContext ctx) {
-
-    final boolean checkFirst =
-        context.contextUriInfo.getLastResourcePart() == null
-            || context.contextUriInfo.getLastResourcePart() instanceof UriResourceRootImpl;
-
-    String odi = ctx.vODI.getText();
-
-    boolean searchInContainer = true;
-    // validate if context type and according property is available
-    // otherwise search in container for first element
-    if (checkFirst && ctx.vNS == null && !context.contextTypes.isEmpty()) {
-      EdmType sourceType = context.contextTypes.peek();
-      if (sourceType instanceof EdmStructuredType) {
-        EdmStructuredType str = (EdmStructuredType) sourceType;
-        EdmElement property = str.getProperty(odi);
-        if (property != null) {
-          searchInContainer = false;
-        }
-      }
-    }
-
-    if (searchInContainer) {
-      final List<UriResource> parts = context.contextUriInfo.getUriResourceParts();
-      // check EntitySet
-      EdmEntitySet edmEntitySet = edmEntityContainer.getEntitySet(odi);
-      if (edmEntitySet != null
-          && (parts.isEmpty() || !(parts.get(0) instanceof UriResourcePartTyped)
-              || parts.get(0) instanceof UriResourceRoot)) {
-        ensureNamespaceIsNull(ctx.vNS);
-        context.contextUriInfo.addResourcePart(
-            new UriResourceEntitySetImpl(edmEntitySet));
-        return null;
-      }
-
-      // check Singleton
-      EdmSingleton edmSingleton = edmEntityContainer.getSingleton(odi);
-      if (edmSingleton != null
-          && (parts.isEmpty() || !(parts.get(0) instanceof UriResourcePartTyped)
-              || parts.get(0) instanceof UriResourceRoot)) {
-        ensureNamespaceIsNull(ctx.vNS);
-        context.contextUriInfo.addResourcePart(
-            new UriResourceSingletonImpl(edmSingleton));
-        return null;
-      }
-
-      // check ActionImport
-      EdmActionImport edmActionImport = edmEntityContainer.getActionImport(odi);
-      if (edmActionImport != null
-          && (parts.isEmpty() || !(parts.get(0) instanceof UriResourcePartTyped)
-              || parts.get(0) instanceof UriResourceRoot)) {
-        ensureNamespaceIsNull(ctx.vNS);
-        context.contextUriInfo.addResourcePart(
-            new UriResourceActionImpl(edmActionImport));
-        return null;
-      }
-
-      // check FunctionImport
-      EdmFunctionImport edmFunctionImport = edmEntityContainer.getFunctionImport(odi);
-      
-      if(edmFunctionImport != null && context.contextReadingQueryPart) {
-        throw wrap(new UriParserSemanticException("Function Imports are not allowed in $filter or $orderby", 
-            UriParserSemanticException.MessageKeys.FUNCTION_IMPORT_NOT_ALLOWED, odi));
-      }
-      
-      if (edmFunctionImport != null
-          && (parts.isEmpty() || !(parts.get(0) instanceof UriResourcePartTyped)
-              || parts.get(0) instanceof UriResourceRoot)) {
-
-        // read the URI parameters
-        if (ctx.vlNVO.isEmpty()) {
-          throw wrap(new UriParserSyntaxException(
-              "Function imports must have a (possibly empty) parameter list written in parentheses",
-              UriParserSyntaxException.MessageKeys.SYNTAX));
-        }
-        context.contextReadingFunctionParameters = true;
-        @SuppressWarnings("unchecked")
-        List<UriParameter> parameters = (List<UriParameter>) ctx.vlNVO.get(0).accept(this);
-        context.contextReadingFunctionParameters = false;
-
-        // mark parameters as consumed
-        ctx.vlNVO.remove(0);
-
-        // collect parameter names
-        List<String> names = new ArrayList<String>();
-        for (UriParameter item : parameters) {
-          names.add(item.getName());
-        }
-
-        // get function from function import
-        EdmFunction function = edmFunctionImport.getUnboundFunction(names);
-        if (function == null) {
-          StringBuilder tmp = new StringBuilder();
-          for (String name : names) {
-            tmp.append((tmp.length() != 0 ? "," : "")).append(name);
-          }
-          throw wrap(new UriParserSemanticException("Function of functionimport '" + edmFunctionImport.getName()
-              + "' with parameters [" + tmp.toString() + "] not found",
-              UriParserSemanticException.MessageKeys.FUNCTION_NOT_FOUND, edmFunctionImport.getName(), tmp.toString()));
-        }
-
-        ensureNamespaceIsNull(ctx.vNS);
-        UriResourceFunctionImpl uriResource = new UriResourceFunctionImpl(edmFunctionImport,
-            edmFunctionImport.getUnboundFunction(names),
-            parameters);
-        context.contextUriInfo.addResourcePart(uriResource);
-        return null;
-      }
-    }
-
-    EdmType sourceType;
-    boolean sourceIsCollection = false;
-    final UriResource lastResourcePart = context.contextUriInfo.getLastResourcePart();
-
-    if (lastResourcePart == null) {
-      if (context.contextTypes.isEmpty()) {
-        if (checkFirst && ctx.vNS == null) {
-          throw wrap(new UriParserSemanticException(
-              "Cannot find EntitySet, Singleton, ActionImport or FunctionImport with name '" + odi + "'.",
-              UriParserSemanticException.MessageKeys.RESOURCE_NOT_FOUND, odi));
-        }
-        throw wrap(new UriParserSemanticException(
-            "Resource part '" + odi + "' can only applied on typed resource parts",
-            UriParserSemanticException.MessageKeys.RESOURCE_PART_ONLY_FOR_TYPED_PARTS, odi));
-      }
-      sourceType = context.contextTypes.peek();
-      sourceIsCollection = context.isCollection;
-    } else if (lastResourcePart instanceof UriResourcePartTyped) {
-      sourceType = ParserHelper.getTypeInformation((UriResourcePartTyped) lastResourcePart);
-      sourceIsCollection = ((UriResourcePartTyped) lastResourcePart).isCollection();
-    } else {
-      throw wrap(new UriParserSemanticException(
-          "Resource part '" + odi + "' can only be applied on typed resource parts.",
-          UriParserSemanticException.MessageKeys.RESOURCE_PART_ONLY_FOR_TYPED_PARTS, odi));
-    }
-
-    if (ctx.vNS == null) { // without namespace
-
-      // first check for lambda variable because a newly add property should not shadow a long used lambda variable
-      UriContext.LambdaVariable lVar = getLambdaVar(odi);
-      if (lVar != null) {
-        UriResourceLambdaVarImpl lambdaResource = new UriResourceLambdaVarImpl(lVar.name, lVar.type);
-        context.contextUriInfo.addResourcePart(lambdaResource);
-        return null;
-      }
-
-      if (!(sourceType instanceof EdmStructuredType)) {
-        throw wrap(new UriParserSemanticException(
-            "Cannot parse '" + odi + "'; previous path segment is not a structural type.",
-            UriParserSemanticException.MessageKeys.RESOURCE_PART_MUST_BE_PRECEDED_BY_STRUCTURAL_TYPE, odi));
-      }
-
-      if ((ctx.depth() <= 2 // path evaluation for the resource path
-          || lastResourcePart instanceof UriResourceTypedImpl
-          || lastResourcePart instanceof UriResourceNavigationPropertyImpl)
-          && sourceIsCollection) {
-        throw wrap(new UriParserSemanticException("Property '" + odi + "' is not allowed after collection.",
-            UriParserSemanticException.MessageKeys.PROPERTY_AFTER_COLLECTION, odi));
-      }
-
-      EdmStructuredType structType = (EdmStructuredType) sourceType;
-
-      EdmElement property = structType.getProperty(odi);
-      if (property == null) {
-        throw wrap(new UriParserSemanticException("Property '" + odi + "' not found in type '"
-            + structType.getFullQualifiedName().getFullQualifiedNameAsString() + "'",
-            ctx.depth() > 2 ? // path evaluation inside an expression or for the resource path?
-                UriParserSemanticException.MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE
-                : UriParserSemanticException.MessageKeys.PROPERTY_NOT_IN_TYPE,
-            structType.getFullQualifiedName().getFullQualifiedNameAsString(), odi));
-      }
-
-      if (property instanceof EdmProperty) {
-        if (((EdmProperty) property).isPrimitive()
-            || property.getType().getKind() == EdmTypeKind.ENUM
-            || property.getType().getKind() == EdmTypeKind.DEFINITION) {
-          // create simple property
-          UriResourcePrimitivePropertyImpl simpleResource =
-              new UriResourcePrimitivePropertyImpl((EdmProperty) property);
-          context.contextUriInfo.addResourcePart(simpleResource);
-          return null;
-        } else {
-          // create complex property
-          UriResourceComplexPropertyImpl complexResource = new UriResourceComplexPropertyImpl((EdmProperty) property);
-          context.contextUriInfo.addResourcePart(complexResource);
-          return null;
-        }
-      } else if (property instanceof EdmNavigationProperty) {
-        // create navigation property
-        if (context.contextVisitExpandResourcePath && ctx.vlNVO.size() > 0) {
-          throw wrap(new UriParserSemanticException(
-              "Navigation properties in expand system query options must not be followed by a key.",
-              UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED));
-        }
-
-        UriResourceNavigationPropertyImpl navigationResource =
-            new UriResourceNavigationPropertyImpl((EdmNavigationProperty) property);
-        context.contextUriInfo.addResourcePart(navigationResource);
-        return null;
-      } else {
-        throw wrap(new UriParserSemanticException("Unkown type for property '" + property + "'",
-            UriParserSemanticException.MessageKeys.UNKNOWN_PROPERTY_TYPE, property.getName()));
-      }
-
-    } else { // with namespace
-
-      FullQualifiedName fullFilterName = getFullNameFromContext(ctx.vNS, odi);
-
-      // EdmType lastType = getLastType(lastTyped);
-      if (sourceType instanceof EdmEntityType) {
-
-        EdmEntityType filterEntityType = edm.getEntityType(fullFilterName);
-        if (filterEntityType != null) {
-          // is entity type cast
-          if (!(filterEntityType.compatibleTo(sourceType))) {
-            throw wrap(new UriParserSemanticException(
-                "Entity typefilter not compatible to previous path segment: " + fullFilterName.toString(),
-                UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER, fullFilterName.toString()));
-          }
-
-          if (lastResourcePart == null) {
-            // this may be the case if a member expression within a filter starts with a typeCast
-            UriResourceStartingTypeFilterImpl uriResource = new UriResourceStartingTypeFilterImpl(
-                filterEntityType,
-                sourceIsCollection);
-            if (sourceIsCollection) {
-              uriResource.setCollectionTypeFilter(filterEntityType);
-            } else {
-              uriResource.setEntryTypeFilter(filterEntityType);
-            }
-            context.contextUriInfo.addResourcePart(uriResource);
-            return null;
-          } else {
-
-            // check if last segment may contain key properties
-            if (lastResourcePart instanceof UriResourceWithKeysImpl) {
-              UriResourceWithKeysImpl lastPartWithKeys = (UriResourceWithKeysImpl) lastResourcePart;
-
-              if (!lastPartWithKeys.isCollection()) {
-                if (lastPartWithKeys.getTypeFilterOnEntry() != null) {
-                  throw wrap(new UriParserSemanticException("Entry typefilters are not chainable, used '"
-                      + getName(filterEntityType) + "' behind '"
-                      + getName(lastPartWithKeys.getTypeFilterOnEntry()) + "'",
-                      UriParserSemanticException.MessageKeys.TYPE_FILTER_NOT_CHAINABLE,
-                      getName(lastPartWithKeys.getTypeFilterOnEntry()), getName(filterEntityType)));
-                }
-                lastPartWithKeys.setEntryTypeFilter(filterEntityType);
-                return null;
-              } else {
-                if (lastPartWithKeys.getTypeFilterOnCollection() != null) {
-                  throw wrap(new UriParserSemanticException("Collection typefilters are not chainable, used '"
-                      + getName(filterEntityType) + "' behind '"
-                      + getName(lastPartWithKeys.getTypeFilterOnCollection()) + "'",
-                      UriParserSemanticException.MessageKeys.TYPE_FILTER_NOT_CHAINABLE,
-                      getName(lastPartWithKeys.getTypeFilterOnCollection()), getName(filterEntityType)));
-                }
-                lastPartWithKeys.setCollectionTypeFilter(filterEntityType);
-                return null;
-              }
-            } else if (lastResourcePart instanceof UriResourceTypedImpl) {
-              UriResourceTypedImpl lastPartTyped = (UriResourceTypedImpl) lastResourcePart;
-              if (lastPartTyped.getTypeFilter() != null) {
-                throw wrap(new UriParserSemanticException("Typefilters are not chainable, used '"
-                    + getName(filterEntityType) + "' behind '"
-                    + getName(lastPartTyped.getTypeFilter()) + "'",
-                    UriParserSemanticException.MessageKeys.TYPE_FILTER_NOT_CHAINABLE,
-                    getName(lastPartTyped.getTypeFilter()), getName(filterEntityType)));
-              }
-
-              lastPartTyped.setTypeFilter(filterEntityType);
-              return null;
-            } else {
-              throw wrap(new UriParserSemanticException("Path segment before '" + getName(filterEntityType)
-                  + "' not typed",
-                  UriParserSemanticException.MessageKeys.PREVIOUS_PART_NOT_TYPED, getName(filterEntityType)));
-            }
-          }
-        }
-
-      } else if (sourceType instanceof EdmComplexType) {
-
-        EdmComplexType filterComplexType = edm.getComplexType(fullFilterName);
-
-        if (filterComplexType != null) {
-
-          // is complex type cast
-          if (!(filterComplexType.compatibleTo(sourceType))) {
-            throw wrap(new UriParserSemanticException(
-                "Complex typefilter '" + getName(sourceType) + "'not compatible type of previous path segment '"
-                    + getName(filterComplexType) + "'",
-                UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER, getName(sourceType)));
-          }
-
-          // is simple complex type cast
-          if (lastResourcePart == null) {
-            // this may be the case if a member expression within a filter starts with a typeCast
-            UriResourceStartingTypeFilterImpl uriResource =
-                new UriResourceStartingTypeFilterImpl(filterComplexType, sourceIsCollection);
-
-            if (sourceIsCollection) {
-              uriResource.setCollectionTypeFilter(filterComplexType);
-            } else {
-              uriResource.setEntryTypeFilter(filterComplexType);
-            }
-            context.contextUriInfo.addResourcePart(uriResource);
-            return null;
-          } else {
-            if (lastResourcePart instanceof UriResourceWithKeysImpl) {
-              // e.g. in case of function returning complex data or a list of complex data
-              UriResourceWithKeysImpl lastPartWithKeys = (UriResourceWithKeysImpl) lastResourcePart;
-
-              if (!lastPartWithKeys.isCollection()) {
-                if (lastPartWithKeys.getTypeFilterOnEntry() != null) {
-                  throw wrap(new UriParserSemanticException("Entry typefilters are not chainable, used '"
-                      + getName(filterComplexType) + "' behind '"
-                      + getName(lastPartWithKeys.getTypeFilterOnEntry()) + "'",
-                      UriParserSemanticException.MessageKeys.TYPE_FILTER_NOT_CHAINABLE,
-                      getName(lastPartWithKeys.getTypeFilterOnEntry()), getName(filterComplexType)));
-                }
-                lastPartWithKeys.setEntryTypeFilter(filterComplexType);
-                return null;
-              } else {
-                if (lastPartWithKeys.getTypeFilterOnCollection() != null) {
-                  throw wrap(new UriParserSemanticException("Collection typefilters are not chainable, used '"
-                      + getName(filterComplexType) + "' behind '"
-                      + getName(lastPartWithKeys.getTypeFilterOnCollection()) + "'",
-                      UriParserSemanticException.MessageKeys.TYPE_FILTER_NOT_CHAINABLE,
-                      getName(lastPartWithKeys.getTypeFilterOnCollection()), getName(filterComplexType)));
-                }
-                lastPartWithKeys.setCollectionTypeFilter(filterComplexType);
-                return null;
-              }
-
-            } else if (lastResourcePart instanceof UriResourceTypedImpl) {
-              UriResourceTypedImpl lastPartTyped = (UriResourceTypedImpl) lastResourcePart;
-              if (lastPartTyped.getTypeFilter() != null) {
-                throw wrap(new UriParserSemanticException("Typefilters are not chainable, used '"
-                    + getName(filterComplexType) + "' behind '"
-                    + getName(lastPartTyped.getTypeFilter()) + "'",
-                    UriParserSemanticException.MessageKeys.TYPE_FILTER_NOT_CHAINABLE,
-                    getName(lastPartTyped.getTypeFilter()), getName(filterComplexType)));
-              }
-
-              lastPartTyped.setTypeFilter(filterComplexType);
-              return null;
-            } else {
-              throw wrap(new UriParserSemanticException("Path segment before '" + getName(filterComplexType)
-                  + "' not typed",
-                  UriParserSemanticException.MessageKeys.PREVIOUS_PART_NOT_TYPED, getName(filterComplexType)));
-            }
-          }
-        }
-      }
-
-      FullQualifiedName fullBindingTypeName = sourceType.getFullQualifiedName();
-
-      // check for action
-      EdmAction action = edm.getBoundAction(fullFilterName, fullBindingTypeName, sourceIsCollection);
-      if (action != null) {
-        UriResourceActionImpl pathInfoAction = new UriResourceActionImpl(action);
-        context.contextUriInfo.addResourcePart(pathInfoAction);
-        return null;
-      }
-
-      // do a check for bound functions (which requires a parameter list)
-      if (ctx.vlNVO.size() == 0) {
-        throw wrap(new UriParserSemanticException("Unknown type for type cast " + fullFilterName.toString()
-            + " not found", UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, fullFilterName.toString()));
-      }
-
-      context.contextReadingFunctionParameters = true;
-      @SuppressWarnings("unchecked")
-      List<UriParameter> parameters = (List<UriParameter>) ctx.vlNVO.get(0).accept(this);
-      context.contextReadingFunctionParameters = false;
-
-      // get names of function parameters
-      List<String> names = new ArrayList<String>();
-      for (UriParameter item : parameters) {
-        names.add(item.getName());
-      }
-
-      EdmFunction function = edm.getBoundFunction(fullFilterName, fullBindingTypeName, sourceIsCollection, names);
-
-      if (function != null) {
-        UriResourceFunctionImpl pathInfoFunction = new UriResourceFunctionImpl(null, function, parameters);
-        context.contextUriInfo.addResourcePart(pathInfoFunction);
-
-        // mark parameters as consumed
-        ctx.vlNVO.remove(0);
-        return null;
-      }
-
-      // check for unbound function in the $filter case ( where the previous resource segment is a $it)
-      function = edm.getUnboundFunction(fullFilterName, names);
-
-      if (function != null) {
-        UriResourceFunctionImpl pathInfoFunction = new UriResourceFunctionImpl(null, function, parameters);
-        context.contextUriInfo.addResourcePart(pathInfoFunction);
-
-        // mark parameters as consumed
-        ctx.vlNVO.remove(0);
-        return null;
-      }
-
-      throw wrap(new UriParserSemanticException("Unknown resource path segment:" + fullFilterName.toString(),
-          UriParserSemanticException.MessageKeys.UNKNOWN_PART, fullFilterName.toString()));
-    }
-  }
-
-  /**
-   * Ensures that the namespace of the first resource parts is null
-   * @param vNS namespace or null
-   */
-  private void ensureNamespaceIsNull(final NamespaceContext vNS) {
-    if (vNS != null && context.contextUriInfo.getLastResourcePart() == null) {
-      // First resource part and namespace is not null!
-      throw wrap(new UriParserSemanticException("Namespace is not allowed for EntitySets, Singeltons, "
-          + " Action Imports and Function Imports. Found " + vNS.getText(),
-          UriParserSemanticException.MessageKeys.NAMESPACE_NOT_ALLOWED_AT_FIRST_ELEMENT, vNS.getText()));
-    }
-  }
-
-  private String getName(final EdmType type) {
-    return type.getFullQualifiedName().getFullQualifiedNameAsString();
-  }
-
-  @Override
-  public Object visitAllEOF(final AllEOFContext ctx) {
-    context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.all);
-    return null;
-  }
-
-  @Override
-  public Object visitAllExpr(final AllExprContext ctx) {
-    UriResource obj = context.contextUriInfo.getLastResourcePart();
-    if (!(obj instanceof UriResourcePartTyped)) {
-      throw wrap(new UriParserSemanticException("all only allowed on typed path segments",
-          UriParserSemanticException.MessageKeys.ONLY_FOR_TYPED_PARTS, "all"));
-    } else if (obj instanceof UriResourceNavigation) {
-      if (!((UriResourceNavigation) obj).getKeyPredicates().isEmpty()) {
-        throw wrap(new UriParserSemanticException(
-            "Any lamdba expression must not be following navigation properties with key predicates.",
-            UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED));
-      }
-    }
-
-    UriContext.LambdaVariable var = new UriContext.LambdaVariable();
-    var.name = ctx.vLV.getText();
-    var.type = ParserHelper.getTypeInformation((UriResourcePartTyped) obj);
-
-    context.allowedLambdaVariables.push(var);
-    Expression expression = (Expression) ctx.vLE.accept(this);
-    context.allowedLambdaVariables.pop();
-    return new UriResourceLambdaAllImpl(var.name, expression);
-  }
-
-  @Override
-  public Expression visitAltAdd(final AltAddContext ctx) {
-    int tokenIndex = ctx.vO.getType();
-    return new BinaryImpl(
-        (Expression) ctx.vE1.accept(this),
-        tokenIndex == UriLexer.ADD ? BinaryOperatorKind.ADD : BinaryOperatorKind.SUB,
-        (Expression) ctx.vE2.accept(this),
-        null);
-  }
-
-  @Override
-  public Object visitAltAll(final AltAllContext ctx) {
-
-    UriInfoImpl uriInfoImplpath = new UriInfoImpl().setKind(UriInfoKind.resource);
-
-    uriInfoImplpath.addResourcePart((UriResourceImpl) super.visitAltAll(ctx));
-
-    EdmType startType = removeUriResourceStartingTypeFilterImpl(uriInfoImplpath);
-
-    return new MemberImpl(uriInfoImplpath, startType);
-  }
-
-  private EdmType removeUriResourceStartingTypeFilterImpl(final UriInfoImpl uriInfoImplpath) {
-
-    List<UriResource> segments = uriInfoImplpath.getUriResourceParts();
-    if (segments.size() == 0) {
-      return null;
-    }
-
-    UriResource segment = segments.get(0);
-    if (segment instanceof UriResourceStartingTypeFilterImpl) {
-      UriResourceStartingTypeFilterImpl startingTypeFilter = (UriResourceStartingTypeFilterImpl) segment;
-
-      EdmType type = null;
-      if (startingTypeFilter.getTypeFilterOnEntry() != null) {
-        type = startingTypeFilter.getTypeFilterOnEntry();
-      } else if (startingTypeFilter.getTypeFilterOnCollection() != null) {
-        type = startingTypeFilter.getTypeFilterOnCollection();
-      } else {
-        type = startingTypeFilter.getType();
-      }
-
-      uriInfoImplpath.removeResourcePart(0);
-      return type;
-    }
-
-    return null;
-  }
-
-  @Override
-  public Expression visitAltAnd(final AltAndContext ctx) {
-    return new BinaryImpl(
-        (Expression) ctx.vE1.accept(this),
-        BinaryOperatorKind.AND,
-        (Expression) ctx.vE2.accept(this),
-        EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean));
-  }
-
-  @Override
-  public Object visitAltAny(final AltAnyContext ctx) {
-    UriInfoImpl uriInfoImplpath = new UriInfoImpl().setKind(UriInfoKind.resource);
-
-    uriInfoImplpath.addResourcePart((UriResourceImpl) super.visitAltAny(ctx));
-
-    EdmType startType = removeUriResourceStartingTypeFilterImpl(uriInfoImplpath);
-
-    return new MemberImpl(uriInfoImplpath, startType);
-  }
-
-  @Override
-  public Expression visitAltComparism(final AltComparismContext ctx) {
-    int tokenIndex = ctx.vO.getType();
-    BinaryOperatorKind kind = null;
-    if (tokenIndex == UriLexer.GT) {
-      kind = BinaryOperatorKind.GT;
-    } else if (tokenIndex == UriLexer.GE) {
-      kind = BinaryOperatorKind.GE;
-    } else if (tokenIndex == UriLexer.LT) {
-      kind = BinaryOperatorKind.LT;
-    } else if (tokenIndex == UriLexer.LE) {
-      kind = BinaryOperatorKind.LE;
-    }
-    return new BinaryImpl(
-        (Expression) ctx.vE1.accept(this),
-        kind,
-        (Expression) ctx.vE2.accept(this),
-        EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean));
-  }
-
-  @Override
-  public Object visitEntityEOF(final EntityEOFContext ctx) {
-    String odi = ctx.vODI.getText();
-    FullQualifiedName fullName = getFullNameFromContext(ctx.vNS, odi);
-
-    EdmEntityType type = edm.getEntityType(fullName);
-    if (type == null) {
-      throw wrap(new UriParserSemanticException("Expected EntityTypeName",
-          UriParserSemanticException.MessageKeys.UNKNOWN_ENTITY_TYPE, fullName.toString()));
-    }
-    context.contextUriInfo.setEntityTypeCast(type);
-
-    // contextUriInfo = uriInfo;
-    context.contextTypes.push(context.contextUriInfo.getEntityTypeCast());
-    context.isCollection = true;  // TODO: check!
-
-    return null;
-  }
-
-  @Override
-  public Expression visitAltEquality(final AltEqualityContext ctx) {
-    int tokenIndex = ctx.vO.getType();
-    return new BinaryImpl(
-        (Expression) ctx.vE1.accept(this),
-        tokenIndex == UriLexer.EQ_ALPHA ? BinaryOperatorKind.EQ : BinaryOperatorKind.NE,
-        (Expression) ctx.vE2.accept(this),
-        EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean));
-  }
-
-  @Override
-  public Object visitAltHas(final AltHasContext ctx) {
-    return new BinaryImpl(
-        (Expression) ctx.vE1.accept(this),
-        BinaryOperatorKind.HAS,
-        (Expression) ctx.vE2.accept(this),
-        EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean));
-  }
-
-  @Override
-  public Object visitMetadataEOF(final MetadataEOFContext ctx) {
-
-    context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.metadata);
-    return null;
-  }
-
-  @Override
-  public Expression visitAltMult(final AltMultContext ctx) {
-    int tokenIndex = ctx.vO.getType();
-    BinaryOperatorKind kind;
-    if (tokenIndex == UriLexer.MUL) {
-      kind = BinaryOperatorKind.MUL;
-    } else if (tokenIndex == UriLexer.DIV) {
-      kind = BinaryOperatorKind.DIV;
-    } else {
-      kind = BinaryOperatorKind.MOD;
-    }
-    return new BinaryImpl(
-        (Expression) ctx.vE1.accept(this),
-        kind,
-        (Expression) ctx.vE2.accept(this),
-        null);
-  }
-
-  @Override
-  public Expression visitAltOr(final AltOrContext ctx) {
-    return new BinaryImpl(
-        (Expression) ctx.vE1.accept(this),
-        BinaryOperatorKind.OR,
-        (Expression) ctx.vE2.accept(this),
-        EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean));
-  }
-
-  @Override
-  public Object visitAnyExpr(final AnyExprContext ctx) {
-    if (ctx.vLV != null) {
-      UriResourceImpl lastResourcePart = (UriResourceImpl) context.contextUriInfo.getLastResourcePart();
-      if (!(lastResourcePart instanceof UriResourcePartTyped)) {
-        throw wrap(new UriParserSemanticException("any only allowed on typed path segments",
-            UriParserSemanticException.MessageKeys.ONLY_FOR_TYPED_PARTS, "any"));
-      } else if (lastResourcePart instanceof UriResourceNavigation) {
-        if (!((UriResourceNavigation) lastResourcePart).getKeyPredicates().isEmpty()) {
-          throw wrap(new UriParserSemanticException(
-              "Any lamdba expression must not be following navigation properties with key predicates",
-              UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED));
-        }
-      }
-
-      UriContext.LambdaVariable var = new UriContext.LambdaVariable();
-      var.name = ctx.vLV.getText();
-      var.type = ParserHelper.getTypeInformation((UriResourcePartTyped) lastResourcePart);
-
-      context.allowedLambdaVariables.push(var);
-      Expression expression = (Expression) ctx.vLE.accept(this);
-      context.allowedLambdaVariables.pop();
-      return new UriResourceLambdaAnyImpl(var.name, expression);
-    }
-    return null;
-  }
-
-  @Override
-  public Object visitBooleanNonCaseLiteral(final BooleanNonCaseLiteralContext ctx) {
-    final String text = ctx.getText().toLowerCase();
-    return new LiteralImpl(text.equals("false") ? "false" : "true",
-        EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean));
-  }
-
-  @Override
-  public Expression visitCastExpr(final CastExprContext ctx) {
-    List<Expression> parameters = new ArrayList<Expression>();
-    if (ctx.vE1 != null) {
-      // is optional parameter
-      parameters.add((Expression) ctx.vE1.accept(this));
-    }
-
-    String namespace = ctx.vNS.getText();
-    namespace = namespace.substring(0, namespace.length() - 1);
-
-    FullQualifiedName fullName = new FullQualifiedName(namespace, ctx.vODI.getText());
-    EdmType type = getType(fullName);
-    parameters.add(new TypeLiteralImpl(type));
-    return new MethodImpl(MethodKind.CAST, parameters);
-  }
-
-  private EdmType getType(final FullQualifiedName fullName) {
-    EdmType type = null;
-
-    type = edm.getEntityType(fullName);
-    if (type != null) {
-      return type;
-    }
-
-    type = edm.getComplexType(fullName);
-    if (type != null) {
-      return type;
-    }
-
-    type = edm.getTypeDefinition(fullName);
-    if (type != null) {
-      return type;
-    }
-
-    type = edm.getEnumType(fullName);
-    if (type != null) {
-      return type;
-    }
-
-    if (fullName.getNamespace().equals(EdmPrimitiveType.EDM_NAMESPACE)) {
-      final EdmPrimitiveTypeKind typeKind = EdmPrimitiveTypeKind.valueOf(fullName.getName());
-      type = EdmPrimitiveTypeFactory.getInstance(typeKind);
-      if (type != null) {
-        return type;
-      }
-    }
-
-    return null;
-
-  }
-
-  @Override
-  public Expression visitCeilingMethodCallExpr(final CeilingMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.CEILING, Collections.singletonList((Expression) ctx.vE1.accept(this)));
-  }
-
-  @Override
-  public Expression visitConcatMethodCallExpr(final ConcatMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.CONCAT, Arrays.asList(
-        (Expression) ctx.vE1.accept(this),
-        (Expression) ctx.vE2.accept(this)));
-  }
-
-  @Override
-  public Object visitConstSegment(final ConstSegmentContext ctx) {
-    UriInfoImpl uriInfoResource = context.contextUriInfo;
-    UriResource pathInfo = uriInfoResource.getLastResourcePart();
-
-    if (ctx.vV != null) {
-      if (pathInfo instanceof UriResourcePartTyped) {
-        if (!((UriResourcePartTyped) pathInfo).isCollection()) {
-          context.contextUriInfo.addResourcePart(new UriResourceValueImpl());
-        } else {
-          throw wrap(new UriParserSemanticException("$value only allowed on typed path segments",
-              UriParserSemanticException.MessageKeys.ONLY_FOR_TYPED_PARTS, "$value"));
-        }
-        return null;
-      } else {
-        throw wrap(new UriParserSemanticException("$value only allowed on typed path segments",
-            UriParserSemanticException.MessageKeys.ONLY_FOR_TYPED_PARTS, "$value"));
-      }
-
-    } else if (ctx.vC != null) {
-      if (pathInfo instanceof UriResourcePartTyped) {
-        if (((UriResourcePartTyped) pathInfo).isCollection()) {
-          context.contextUriInfo.addResourcePart(new UriResourceCountImpl());
-        } else {
-          throw wrap(new UriParserSemanticException("$count only allowed on collection properties",
-              UriParserSemanticException.MessageKeys.ONLY_FOR_COLLECTIONS, "$count"));
-        }
-      } else {
-        throw wrap(new UriParserSemanticException("$count only allowed on typed properties",
-            UriParserSemanticException.MessageKeys.ONLY_FOR_TYPED_PARTS, "$count"));
-      }
-    } else if (ctx.vR != null) {
-      if (pathInfo instanceof UriResourcePartTyped) {
-        EdmType type = ((UriResourcePartTyped) pathInfo).getType();
-        if (type instanceof EdmEntityType) {
-          context.contextUriInfo.addResourcePart(new UriResourceRefImpl());
-        } else {
-          throw wrap(new UriParserSemanticException("$ref only allowed on entity types",
-              UriParserSemanticException.MessageKeys.ONLY_FOR_ENTITY_TYPES, "$ref"));
-        }
-      } else {
-        throw wrap(new UriParserSemanticException("$ref only allowed on typed properties",
-            UriParserSemanticException.MessageKeys.ONLY_FOR_TYPED_PROPERTIES, "$ref"));
-      }
-
-    } else if (ctx.vAll != null) {
-      context.contextUriInfo.addResourcePart((UriResourceLambdaAllImpl) ctx.vAll.accept(this));
-    } else if (ctx.vAny != null) {
-      context.contextUriInfo.addResourcePart((UriResourceLambdaAnyImpl) ctx.vAny.accept(this));
-    }
-    return null;
-  }
-
-  @Override
-  public Expression visitContainsMethodCallExpr(final ContainsMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.CONTAINS, Arrays.asList(
-        (Expression) ctx.vE1.accept(this),
-        (Expression) ctx.vE2.accept(this)));
-  }
-
-  @Override
-  public Object visitDateMethodCallExpr(final DateMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.DATE, Collections.singletonList((Expression) ctx.vE1.accept(this)));
-  }
-
-  @Override
-  public Expression visitDayMethodCallExpr(final DayMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.DAY, Collections.singletonList((Expression) ctx.vE1.accept(this)));
-  }
-
-  @Override
-  public Expression visitGeoDistanceMethodCallExpr(final GeoDistanceMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.GEODISTANCE, Arrays.asList(
-        (Expression) ctx.vE1.accept(this),
-        (Expression) ctx.vE2.accept(this)));
-  }
-
-  @Override
-  public Object visitEndsWithMethodCallExpr(final EndsWithMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.ENDSWITH, Arrays.asList(
-        (Expression) ctx.vE1.accept(this),
-        (Expression) ctx.vE2.accept(this)));
-  }
-
-  @Override
-  public Object visitEnumLiteral(final EnumLiteralContext ctx) {
-    // get type
-    final String odi = ctx.vODI.getText();
-
-    final FullQualifiedName fullName = getFullNameFromContext(ctx.vNS, odi);
-    final EdmEnumType edmEnumType = edm.getEnumType(fullName);
-    if (edmEnumType == null) {
-      throw wrap(new UriParserSemanticException(
-          "Enum type '" + fullName.getFullQualifiedNameAsString() + "' not found!",
-          UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, fullName.getFullQualifiedNameAsString()));
-    }
-
-    String valueString = ctx.vValues.getText();
-    valueString = valueString.substring(1, valueString.length() - 1);
-    String[] values = valueString.split(",");
-    return new EnumerationImpl(edmEnumType, Arrays.asList(values));
-  }
-
-  @Override
-  public Object visitExpandItems(final ExpandItemsContext ctx) {
-    ExpandOptionImpl expand = new ExpandOptionImpl();
-    expand.setText(ctx.getText());
-    for (ExpandItemContext eI : ctx.vlEI) {
-      expand.addExpandItem((ExpandItemImpl) eI.accept(this));
-    }
-
-    return expand;
-  }
-
-  @Override
-  public Object visitExpandItem(final ExpandItemContext ctx) {
-
-    ExpandItemImpl expandItem = null;
-    if (ctx.vS != null) {
-      expandItem = new ExpandItemImpl().setIsStar(true);
-      if (ctx.vR != null) {
-        expandItem.setIsRef(true);
-      } else if (ctx.vM != null) {
-        LevelsOptionImpl levels = new LevelsOptionImpl().setMax();
-        levels.setText(ctx.vM.getText());
-        try {
-          expandItem.setSystemQueryOption(levels);
-        } catch (ODataRuntimeException e) {
-          // Thrown if duplicated system query options are detected
-          throw wrap(new UriParserSyntaxException("Double system query option!", e,
-              UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION, e.getMessage()));
-        }
-      } else if (ctx.vL != null) {
-        LevelsOptionImpl levels = new LevelsOptionImpl();
-        String text = ctx.vL.getText();
-        levels.setText(text);
-        levels.setValue(Integer.parseInt(text));
-        try {
-          expandItem.setSystemQueryOption(levels);
-        } catch (ODataRuntimeException e) {
-          // Thrown if duplicated system query options are detected
-          throw wrap(new UriParserSyntaxException("Double system query option!", e,
-              UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION, e.getMessage()));
-        }
-      }
-
-    } else if (ctx.vEP != null) {
-      expandItem = (ExpandItemImpl) ctx.vEP.accept(this);
-
-      if (ctx.vEPE != null) {
-        ExpandItemImpl contextExpandItemPathBU = context.contextExpandItemPath;
-        context.contextExpandItemPath = expandItem;
-
-        @SuppressWarnings("unchecked")
-        List<SystemQueryOptionImpl> list = (List<SystemQueryOptionImpl>) ctx.vEPE.accept(this);
-        try {
-          for (SystemQueryOptionImpl option : list) {
-            expandItem.setSystemQueryOption(option);
-          }
-        } catch (ODataRuntimeException e) {
-          // Thrown if duplicated system query options are detected
-          throw wrap(new UriParserSyntaxException("Double system query option!", e,
-              UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION, e.getMessage()));
-        }
-        context.contextExpandItemPath = contextExpandItemPathBU;
-      }
-    }
-
-    return expandItem;
-
-  }
-
-  @Override
-  public Object visitExpandPath(final ExpandPathContext ctx) {
-    ExpandItemImpl expandItem = new ExpandItemImpl();
-
-    // save context
-    ExpandItemImpl contextExpandItemPathBU = context.contextExpandItemPath;
-    UriInfoImpl uriInfoResourceBU = context.contextUriInfo;
-
-    // set tmp context
-    context.contextExpandItemPath = expandItem;
-    context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.resource);
-
-    context.contextVisitExpandResourcePath = true;
-    super.visitExpandPath(ctx);
-    context.contextVisitExpandResourcePath = false;
-
-    EdmType startType = removeUriResourceStartingTypeFilterImpl(context.contextUriInfo);
-    expandItem.setResourcePath(context.contextUriInfo);
-    if (startType != null) {
-      expandItem.setTypeFilter(startType);
-    }
-
-    // reset context
-    context.contextUriInfo = uriInfoResourceBU;
-    context.contextExpandItemPath = contextExpandItemPathBU;
-
-    // test
-    validate(uriInfoResourceBU.asUriInfoResource(), expandItem);
-    //
-
-    return expandItem;
-  }
-
-  private void validate(UriInfoResource uriInfoResource, ExpandItemImpl expandItem) {
-    if (uriInfoResource != null) {
-      EdmEntityType type = getEntityType(uriInfoResource);
-      EdmEntityType name = getEntityType(expandItem.getResourcePath());
-
-      if (name != null && type != null) {
-        EdmElement property = type.getProperty(name.getName());
-        if (!(property instanceof EdmNavigationProperty)) {
-          throw wrap(new UriParserSemanticException(
-              "NavigationProperty '" + name.getName() + "' not found in type '"
-                  + type.getFullQualifiedName().getFullQualifiedNameAsString() + "'",
-              UriParserSemanticException.MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE,
-              name.getFullQualifiedName().getFullQualifiedNameAsString(),
-              type.getFullQualifiedName().getFullQualifiedNameAsString()));
-        }
-      }
-    }
-  }
-
-  private EdmEntityType getEntityType(UriInfoResource test) {
-    List<UriResource> parts = test.getUriResourceParts();
-    if (!parts.isEmpty()) {
-      UriResource lastPart = parts.get(parts.size() - 1);
-      if (lastPart instanceof UriResourceEntitySet) {
-        UriResourceEntitySet entitySet = (UriResourceEntitySet) lastPart;
-        return entitySet.getEntityType();
-      }
-    }
-    return null;
-  }
-
-  @Override
-  public Object visitExpandPathExtension(final ExpandPathExtensionContext ctx) {
-    List<SystemQueryOptionImpl> list = new ArrayList<SystemQueryOptionImpl>();
-
-    EdmType targetType = null;
-    boolean isColl = false;
-    if (context.contextExpandItemPath == null) {
-      // use the type of the last resource path segement
-      UriResourceTypedImpl lastSegment = (UriResourceTypedImpl) context.contextUriInfo.getLastResourcePart();
-      targetType = ParserHelper.getTypeInformation(lastSegment);
-      isColl = lastSegment.isCollection();
-    } else {
-      if (context.contextExpandItemPath.getResourcePath() == null) {
-        // use the type of the last resource path segement
-        UriResourceTypedImpl lastSegment = (UriResourceTypedImpl) context.contextUriInfo.getLastResourcePart();
-        targetType = ParserHelper.getTypeInformation(lastSegment);
-        isColl = lastSegment.isCollection();
-      } else {
-        // use the type of the last ''expand'' path segement
-        UriInfoImpl info = (UriInfoImpl) context.contextExpandItemPath.getResourcePath();
-        targetType = ParserHelper.getTypeInformation((UriResourcePartTyped) info.getLastResourcePart());
-        isColl = ((UriResourcePartTyped) info.getLastResourcePart()).isCollection();
-      }
-    }
-
-    context.contextTypes.push(targetType);
-    context.isCollection = isColl;
-
-    if (ctx.vC != null) {
-      UriInfoImpl resourcePath = (UriInfoImpl) context.contextExpandItemPath.getResourcePath();
-      resourcePath.addResourcePart(new UriResourceCountImpl());
-
-      for (ExpandCountOptionContext s : ctx.vlEOC) {
-        list.add((SystemQueryOptionImpl) s.accept(this));
-      }
-    } else if (ctx.vR != null) {
-      UriInfoImpl resourcePath = (UriInfoImpl) context.contextExpandItemPath.getResourcePath();
-      resourcePath.addResourcePart(new UriResourceRefImpl());
-
-      for (ExpandRefOptionContext s : ctx.vlEOR) {
-        list.add((SystemQueryOptionImpl) s.accept(this));
-      }
-    } else {
-      for (ExpandOptionContext s : ctx.vlEO) {
-        list.add((SystemQueryOptionImpl) s.accept(this));
-      }
-    }
-
-    context.contextTypes.pop();
-    return list;
-
-  }
-
-  @Override
-  public Object visitFilter(final FilterContext ctx) {
-    context.contextReadingQueryPart = true;
-    final FilterOptionImpl result = new FilterOptionImpl().setExpression((Expression) ctx.children.get(2)
-                                                          .accept(this));
-    context.contextReadingQueryPart = false;
-
-    return result;
-  }
-
-  @Override
-  public Object visitFilterExpressionEOF(final FilterExpressionEOFContext ctx) {
-    context.contextReadingQueryPart = true;
-    final FilterOptionImpl result = new FilterOptionImpl().setExpression((Expression) ctx.children.get(0)
-                                                          .accept(this));
-    context.contextReadingQueryPart = false;
-
-    return result;
-  }
-
-  @Override
-  public Expression visitFloorMethodCallExpr(final FloorMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.FLOOR, Collections.singletonList((Expression) ctx.vE1.accept(this)));
-  }
-
-  @Override
-  public Expression visitFractionalsecondsMethodCallExpr(final FractionalsecondsMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.FRACTIONALSECONDS, Collections.singletonList((Expression) ctx.vE1.accept(this)));
-  }
-
-  @Override
-  public Expression visitGeoLengthMethodCallExpr(final GeoLengthMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.GEOLENGTH, Collections.singletonList((Expression) ctx.vE1.accept(this)));
-  }
-
-  @Override
-  public Expression visitHourMethodCallExpr(final HourMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.HOUR, Collections.singletonList((Expression) ctx.vE1.accept(this)));
-  }
-
-  @Override
-  public Expression visitIndexOfMethodCallExpr(final IndexOfMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.INDEXOF, Arrays.asList(
-        (Expression) ctx.vE1.accept(this),
-        (Expression) ctx.vE2.accept(this)));
-  }
-
-  @Override
-  public Object visitInlinecount(final InlinecountContext ctx) {
-    final String text = ctx.children.get(2).getText();
-    return new CountOptionImpl()
-        .setValue(text.equalsIgnoreCase("true") ? true : false)
-        .setText(text);
-  }
-
-  @Override
-  public Expression visitGeoIntersectsMethodCallExpr(final GeoIntersectsMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.GEOINTERSECTS, Arrays.asList(
-        (Expression) ctx.vE1.accept(this),
-        (Expression) ctx.vE2.accept(this)));
-  }
-
-  @Override
-  public Expression visitIsofExpr(final IsofExprContext ctx) {
-    List<Expression> parameters = new ArrayList<Expression>();
-    if (ctx.vE1 != null) {
-      parameters.add((Expression) ctx.vE1.accept(this));
-    }
-
-    String namespace = ctx.vNS.getText();
-    namespace = namespace.substring(0, namespace.length() - 1);
-
-    FullQualifiedName fullName = new FullQualifiedName(namespace, ctx.vODI.getText());
-    EdmType type = getType(fullName);
-    parameters.add(new TypeLiteralImpl(type));
-
-    return new MethodImpl(MethodKind.ISOF, parameters);
-  }
-
-  @Override
-  public Expression visitLengthMethodCallExpr(final LengthMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.LENGTH, Collections.singletonList((Expression) ctx.vE1.accept(this)));
-  }
-
-  @Override
-  public Object visitLevels(final LevelsContext ctx) {
-
-    LevelsOptionImpl levels = new LevelsOptionImpl();
-
-    String text = ctx.children.get(2).getText();
-
-    if (text.equals("max")) {
-      levels.setMax();
-    } else {
-      levels.setValue(Integer.parseInt(text));
-    }
-    levels.setText(text);
-
-    return levels;
-
-  }
-
-  @Override
-  public Expression visitMaxDateTimeMethodCallExpr(final MaxDateTimeMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.MAXDATETIME, null);
-  }
-
-  @Override
-  public Object visitMemberExpr(final MemberExprContext ctx) {
-
-    UriInfoImpl uriInfoImplpath = new UriInfoImpl().setKind(UriInfoKind.resource);
-
-    if (context.contextTypes.isEmpty()) {
-      throw wrap(new UriParserSemanticException("Expression '" + ctx.getText() + "' is not allowed as key value.",
-          UriParserSemanticException.MessageKeys.INVALID_KEY_VALUE, ctx.getText()));
-    }
-
-    if (ctx.vIt != null || ctx.vIts != null) {
-      uriInfoImplpath.addResourcePart(new UriResourceItImpl(context.contextTypes.peek(), context.isCollection));
-    }
-
-    if (ctx.vPs != null) {
-      // save the context
-      UriInfoImpl backupUriInfoPath = context.contextUriInfo;
-
-      // set temporary uriInfoPath
-      context.contextUriInfo = uriInfoImplpath;
-
-      ctx.vPs.accept(this);
-
-      // reset context
-      context.contextUriInfo = backupUriInfoPath;
-    }
-
-    if (ctx.vALL != null) {
-      uriInfoImplpath.addResourcePart((UriResourceImpl) ctx.vALL.accept(this));
-    }
-    if (ctx.vANY != null) {
-      uriInfoImplpath.addResourcePart((UriResourceImpl) ctx.vANY.accept(this));
-    }
-
-    EdmType startType = removeUriResourceStartingTypeFilterImpl(uriInfoImplpath);
-
-    return new MemberImpl(uriInfoImplpath, startType);
-  }
-
-  @Override
-  public Expression visitMinDateTimeMethodCallExpr(final MinDateTimeMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.MINDATETIME, null);
-  }
-
-  @Override
-  public Expression visitMinuteMethodCallExpr(final MinuteMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.MINUTE, Collections.singletonList((Expression) ctx.vE1.accept(this)));
-  }
-
-  @Override
-  public Expression visitMonthMethodCallExpr(final MonthMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.MONTH, Collections.singletonList((Expression) ctx.vE1.accept(this)));
-  }
-
-  @Override
-  public Object visitNameValueOptList(final NameValueOptListContext ctx) {
-    if (ctx.vVO != null) {
-      // This branch is chosen if the key predicate is a common expression e.g. EntitySet(0)
-
-      // is single key predicate without a name
-      String valueText = ctx.vVO.getText();
-      Expression expression = null;
-      try {
-        expression = (Expression) ctx.vVO.accept(this);
-      } catch (final RuntimeException e) {
-        throw wrap(new UriParserSemanticException("Invalid key value: " + valueText, e,
-            UriParserSemanticException.MessageKeys.INVALID_KEY_VALUE, valueText));
-      }
-
-      // get type of last resource part
-      UriResource last = context.contextUriInfo.getLastResourcePart();
-      if (!(last instanceof UriResourcePartTyped)) {
-        throw wrap(new UriParserSemanticException("Parameters list on untyped resource path segment not allowed",
-            UriParserSemanticException.MessageKeys.PARAMETERS_LIST_ONLY_FOR_TYPED_PARTS));
-
-      } else if (last instanceof UriResourceFunction) {
-        // Handle functions
-        final UriResourceFunction uriResourceFunction =
-            (UriResourceFunction) context.contextUriInfo.getLastResourcePart();
-        final EdmReturnType returnType = uriResourceFunction.getFunction().getReturnType();
-
-        if (returnType.getType().getKind() != EdmTypeKind.ENTITY || !returnType.isCollection()) {
-          throw wrap(new UriParserSemanticException("No keys allowed",
-              UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED));
-        } else {
-          // The functions returns a collection of entities
-          // Get the EDM Type and determine how many key predicates are needed. In this case only one
-          // key predicate is allowed. If the entity type needs more than one key predicate, the client
-          // has to use the key value syntax e.g. EntitySet(ID=1,Order=2)
-          final EdmEntityType entityType = (EdmEntityType) uriResourceFunction.getFunction().getReturnType().getType();
-          final List<String> lastKeyPredicates = entityType.getKeyPredicateNames();
-
-          if (lastKeyPredicates.size() == 1) {
-            return Collections.singletonList(new UriParameterImpl()
-                .setName(lastKeyPredicates.get(0))
-                .setText(valueText)
-                .setExpression(expression));
-          } else {
-            throw wrap(new UriParserSemanticException("Wrong number of key properties.",
-                UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES,
-                Integer.toString(lastKeyPredicates.size()), "1"));
-          }
-        }
-      } else {
-        // Handle EntitySets
-        EdmEntityType lastType = (EdmEntityType) ((UriResourcePartTyped) last).getType();
-
-        // get list of keys for lastType
-        List<String> lastKeyPredicates = lastType.getKeyPredicateNames();
-
-        // If there is exactly one key defined in the EDM, then this key is the key written in the URI,
-        // so fill the keylist with this key and return.
-        if (lastKeyPredicates.size() == 1) {
-          return Collections.singletonList(new UriParameterImpl()
-              .setName(lastKeyPredicates.get(0))
-              .setText(valueText)
-              .setExpression(expression));
-        }
-
-        // There are more keys defined in the EDM, but only one is written in the URI. This is allowed only if
-        // referential constraints are defined on this navigation property which can be used to fill up all
-        // required keys.
-        // For using referential constraints the last resource part must be a navigation property.
-        if (!(context.contextUriInfo.getLastResourcePart() instanceof UriResourceNavigationPropertyImpl)) {
-          throw wrap(new UriParserSemanticException("Wrong number of key properties.",
-              UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES,
-              Integer.toString(lastKeyPredicates.size()), "1"));
-        }
-        UriResourceNavigationPropertyImpl lastNav = (UriResourceNavigationPropertyImpl) last;
-
-        // get the partner of the navigation property
-        EdmNavigationProperty partner = lastNav.getProperty().getPartner();
-        if (partner == null) {
-          throw wrap(new UriParserSemanticException("Wrong number of key properties.",
-              UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES,
-              Integer.toString(lastKeyPredicates.size()), "1"));
-        }
-
-        // create the keylist
-        List<UriParameterImpl> list = new ArrayList<UriParameterImpl>();
-
-        // Find the keys not filled by referential constraints
-        // and collect the other keys filled by referential constraints.
-        String missedKey = null;
-        for (String item : lastKeyPredicates) {
-          String property = partner.getReferencingPropertyName(item);
-          if (property != null) {
-            list.add(new UriParameterImpl().setName(item).setReferencedProperty(property));
-          } else {
-            if (missedKey == null) {
-              missedKey = item;
-            } else {
-              // two of more keys are missing
-              throw wrap(new UriParserSemanticException("Not enough referential constraints defined",
-                  UriParserSemanticException.MessageKeys.NOT_ENOUGH_REFERENTIAL_CONSTRAINTS));
-            }
-          }
-        }
-
-        // the missing key is the one which is defined in the URI
-        list.add(new UriParameterImpl().setName(missedKey).setText(valueText).setExpression(expression));
-
-        return list;
-      }
-    } else if (ctx.vNVL != null) {
-      // The client provided a list of key values pairs e.g. EntitySet(ID=1,Order=2)
-      List<UriParameterImpl> list = new ArrayList<UriParameterImpl>();
-
-      for (ParseTree c : ctx.vNVL.vlNVP) {
-        list.add((UriParameterImpl) c.accept(this));
-      }
-
-      if (context.contextReadingFunctionParameters) {
-        return list;
-      }
-
-      UriResource last = context.contextUriInfo.getLastResourcePart();
-
-      // get type of last resource part
-      if (!(last instanceof UriResourcePartTyped)) {
-        throw wrap(new UriParserSemanticException("Parameters list on untyped resource path segment not allowed",
-            UriParserSemanticException.MessageKeys.PARAMETERS_LIST_ONLY_FOR_TYPED_PARTS));
-      }
-      if (last instanceof UriResourceFunction) {
-        final UriResourceFunction uriResourceFunction = (UriResourceFunction) context.contextUriInfo
-            .getLastResourcePart();
-        final EdmReturnType returnType = uriResourceFunction.getFunction().getReturnType();
-
-        if (returnType.getType().getKind() != EdmTypeKind.ENTITY || !returnType.isCollection()) {
-          throw wrap(new UriParserSemanticException("No keys allowed",
-              UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED));
-        } else {
-          // The functions returns a collection of entities
-          // Get the EDM Type and determine how many key predicates are needed.
-          // In case of functions all key predicates must be provided by the client.
-          final EdmEntityType entityType = (EdmEntityType) uriResourceFunction.getFunction().getReturnType().getType();
-          final List<String> lastKeyPredicates = entityType.getKeyPredicateNames();
-
-          if (lastKeyPredicates.size() == list.size()) {
-            return list;
-          } else {
-            throw wrap(new UriParserSemanticException("Wrong number of key properties.",
-                UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES,
-                Integer.toString(lastKeyPredicates.size()), "1"));
-          }
-        }
-      } else {
-        // Handle entity sets
-        EdmEntityType lastType = (EdmEntityType) ((UriResourcePartTyped) last).getType();
-
-        // get list of keys for lastType
-        List<String> lastKeyPredicates = lastType.getKeyPredicateNames();
-
-        // check if all key are filled from the URI
-        if (list.size() == lastKeyPredicates.size()) {
-          return list;
-        }
-
-        // if not, check if the missing key predicates can be satisfied with help of the defined
-        // referential constraints
-        // for using referential constraints the last resource part must be a navigation property
-        if (!(context.contextUriInfo.getLastResourcePart() instanceof UriResourceNavigationPropertyImpl)) {
-          throw wrap(new UriParserSemanticException("Wrong number of key properties.",
-              UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES,
-              Integer.toString(lastKeyPredicates.size()), Integer.toString(list.size())));
-        }
-        UriResourceNavigationPropertyImpl lastNav = (UriResourceNavigationPropertyImpl) last;
-
-        // get the partner of the navigation property
-        EdmNavigationProperty partner = lastNav.getProperty().getPartner();
-        if (partner == null) {
-          throw wrap(new UriParserSemanticException("Wrong number of key properties.",
-              UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES,
-              Integer.toString(lastKeyPredicates.size()), Integer.toString(list.size())));
-        }
-
-        // fill missing keys from referential constraints
-        for (String key : lastKeyPredicates) {
-          boolean found = false;
-          for (UriParameterImpl item : list) {
-            if (item.getName().equals(key)) {
-              found = true;
-              break;
-            }
-          }
-
-          if (!found) {
-            String property = partner.getReferencingPropertyName(key);
-            if (property != null) {
-              // store the key name as referenced property
-              list.add(0, new UriParameterImpl().setName(key).setReferencedProperty(property));
-            }
-          }
-        }
-
-        // check again if all key predicates are filled from the URI
-        if (list.size() == lastKeyPredicates.size()) {
-          return list;
-        } else {
-          throw wrap(new UriParserSemanticException("Wrong number of key properties.",
-              UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES,
-              Integer.toString(lastKeyPredicates.size()), Integer.toString(list.size())));
-        }
-      }
-    } else {
-      // No key predicates are provided by the client
-
-      if (context.contextReadingFunctionParameters) {
-        return Collections.emptyList();
-      } else {
-        final UriResource last = context.contextUriInfo.getLastResourcePart();
-        final int number = last instanceof UriResourcePartTyped ? ((EdmEntityType) ((UriResourcePartTyped) last)
-            .getType()).getKeyPredicateNames().size() : 0;
-        throw wrap(new UriParserSemanticException("Wrong number of key properties.",
-            UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES, Integer.toString(number), "0"));
-      }
-    }
-  }
-
-  @Override
-  public UriParameterImpl visitNameValuePair(final NameValuePairContext ctx) {
-    UriParameterImpl uriParameter = new UriParameterImpl();
-    uriParameter.setName(ctx.vODI.getText());
-
-    if (ctx.vCOM != null) {
-      final String text = ctx.vCOM.getText();
-      uriParameter.setText("null".equals(text) ? null : text);
-      uriParameter.setExpression((Expression) ctx.vCOM.accept(this));
-    } else {
-      uriParameter.setAlias("@" + ctx.vALI.getText());
-    }
-
-    return uriParameter;
-  }
-
-  @Override
-  public Object visitNaninfinityLiteral(final NaninfinityLiteralContext ctx) {
-    return new LiteralImpl(ctx.getText(),
-        EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal));
-  }
-
-  @Override
-  public Expression visitNowMethodCallExpr(final NowMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.NOW, null);
-  }
-
-  @Override
-  public Object visitNullruleLiteral(final NullruleLiteralContext ctx) {
-    return new LiteralImpl("null", null);
-  }
-
-  @Override
-  public Object visitOrderBy(final OrderByContext ctx) {
-
-    OrderByOptionImpl orderBy = new OrderByOptionImpl();
-
-    for (OrderByItemContext item : ((OrderListContext) ctx.getChild(2)).vlOI) {
-      OrderByItemImpl oItem = (OrderByItemImpl) item.accept(this);
-      orderBy.addOrder(oItem);
-    }
-
-    return orderBy;
-  }
-
-  @Override
-  public Object visitOrderByEOF(final OrderByEOFContext ctx) {
-    context.contextReadingQueryPart = true;
-    
-    OrderByOptionImpl orderBy = new OrderByOptionImpl();
-
-    for (OrderByItemContext item : ((OrderListContext) ctx.getChild(0)).vlOI) {
-      OrderByItemImpl oItem = (OrderByItemImpl) item.accept(this);
-      orderBy.addOrder(oItem);
-    }
-
-    context.contextReadingFunctionParameters = false;
-    return orderBy;
-  }
-
-  @Override
-  public Object visitOrderByItem(final OrderByItemContext ctx) {
-    OrderByItemImpl oItem = new OrderByItemImpl();
-    if (ctx.vD != null) {
-      oItem.setDescending(true);
-    }
-
-    oItem.setExpression((Expression) ctx.vC.accept(this));
-    return oItem;
-  }
-
-  @Override
-  public Object visitPathSegment(final PathSegmentContext ctx) {
-    readResourcePathSegment(ctx);
-    /*
-     * if (contextUriInfo.getLastResourcePart() == null ||
-     * contextUriInfo.getLastResourcePart() instanceof UriResourceRootImpl) {
-     * 
-     * } else {
-     * readNextPathInfoSegment(ctx);
-     * }
-     */
-    UriResourceImpl pathInfoSegment = (UriResourceImpl) context.contextUriInfo.getLastResourcePart();
-
-    if (ctx.vlNVO.size() > 0) {
-      // check for keyPredicates
-      if (pathInfoSegment instanceof UriResourceWithKeysImpl) {
-        if (ctx.vlNVO.size() > 1) {
-          throw wrap(new UriParserSemanticException("More than one key predicates found",
-              UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES, "1",
-              Integer.toString(ctx.vlNVO.size())));
-        }
-
-        @SuppressWarnings("unchecked")
-        List<UriParameter> list = (List<UriParameter>) ctx.vlNVO.get(0).accept(this);
-        ((UriResourceWithKeysImpl) pathInfoSegment)
-            .setKeyPredicates(list);
-      } else {
-        throw wrap(new UriParserSemanticException("Key properties not allowed",
-            UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED));
-      }
-    }
-
-    return pathInfoSegment;
-  }
-
-  @Override
-  public Object visitPathSegments(final PathSegmentsContext ctx) {
-    // path segment
-    for (PathSegmentContext it : ctx.vlPS) {
-      it.accept(this);
-    }
-
-    // const segment
-    if (ctx.vCS != null) {
-      ctx.vCS.accept(this);
-    }
-    return null;
-  }
-
-  @Override
-  public Object visitPrimitiveLiteral(final PrimitiveLiteralContext ctx) {
-    ParseTree child1 = ctx.children.get(0);
-
-    if (child1 instanceof EnumLiteralContext
-        || child1 instanceof BooleanNonCaseLiteralContext
-        || child1 instanceof NullruleLiteralContext
-        || child1 instanceof NaninfinityLiteralContext
-        || child1 instanceof StringLiteralContext
-        || child1 instanceof IntLiteralContext
-        || child1 instanceof BinaryLiteralContext
-        || child1 instanceof DateLiteralContext
-        || child1 instanceof DatetimeoffsetLiteralContext
-        || child1 instanceof DurationLiteralContext
-        || child1 instanceof GuidLiteralContext
-        || child1 instanceof TimeofdayLiteralContext
-        || child1 instanceof DecimalLiteralContext
-        || child1 instanceof BinaryLiteralContext) {
-      return child1.accept(this);
-    }
-
-    // TODO Implement geography types and set a proper type
-    return new LiteralImpl(ctx.getText(), null);
-  }
-
-  @Override
-  public Object visitBinaryLiteral(BinaryLiteralContext ctx) {
-    return new LiteralImpl(ctx.getText(),
-        EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Binary));
-  }
-
-  @Override
-  public Object visitStringLiteral(final StringLiteralContext ctx) {
-    return new LiteralImpl(ctx.getText(),
-        EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.String));
-  }
-
-  @Override
-  public Object visitDecimalLiteral(final DecimalLiteralContext ctx) {
-    final EdmType type = EdmPrimitiveTypeFactory.getInstance(
-        ctx.getText().contains("e") || ctx.getText().contains("E") ?
-            EdmPrimitiveTypeKind.Double :
-            EdmPrimitiveTypeKind.Decimal);
-
-    return new LiteralImpl(ctx.getText(), type);
-  }
-
-  @Override
-  public Object visitIntLiteral(final IntLiteralContext ctx) {
-    EdmPrimitiveTypeKind typeKind = null;
-    try {
-      final long value = Long.parseLong(ctx.getText());
-      if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
-        typeKind = EdmPrimitiveTypeKind.SByte;
-      } else if (value >= 0 && value <= 255) {
-        typeKind = EdmPrimitiveTypeKind.Byte;
-      } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
-        typeKind = EdmPrimitiveTypeKind.Int16;
-      } else if (value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE) {
-        typeKind = EdmPrimitiveTypeKind.Int32;
-      } else {
-        typeKind = EdmPrimitiveTypeKind.Int64;
-      }
-    } catch (NumberFormatException e) {
-      return new LiteralImpl(ctx.getText(),
-          EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal));
-    }
-    return new LiteralImpl(ctx.getText(),
-        EdmPrimitiveTypeFactory.getInstance(typeKind));
-  }
-
-  @Override
-  public Object visitDateLiteral(final DateLiteralContext ctx) {
-    return new LiteralImpl(ctx.getText(),
-        EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Date));
-  }
-
-  @Override
-  public Object visitDatetimeoffsetLiteral(final DatetimeoffsetLiteralContext ctx) {
-    return new LiteralImpl(ctx.getText(),
-        EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.DateTimeOffset));
-  }
-
-  @Override
-  public Object visitDurationLiteral(final DurationLiteralContext ctx) {
-    return new LiteralImpl(ctx.getText(),
-        EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Duration));
-  }
-
-  @Override
-  public Object visitGuidLiteral(final GuidLiteralContext ctx) {
-    return new LiteralImpl(ctx.getText(),
-        EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Guid));
-  }
-
-  @Override
-  public Object visitTimeofdayLiteral(final TimeofdayLiteralContext ctx) {
-    return new LiteralImpl(ctx.getText(),
-        EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.TimeOfDay));
-  }
-
-  @Override
-  public Object visitQueryOptions(final QueryOptionsContext ctx) {
-
-    List<QueryOptionImpl> qpList = new ArrayList<QueryOptionImpl>();
-    for (QueryOptionContext entityOption : ctx.vlQO) {
-      qpList.add((QueryOptionImpl) entityOption.accept(this));
-    }
-
-    return qpList;
-  }
-
-  @Override
-  public Object visitRootExpr(final RootExprContext ctx) {
-
-    UriResource lastResource = context.contextUriInfo.getLastResourcePart();
-
-    if (!(lastResource instanceof UriResourcePartTyped)) {
-      throw wrap(new UriParserSemanticException("Resource path not typed",
-          UriParserSemanticException.MessageKeys.RESOURCE_PATH_NOT_TYPED));
-    }
-
-    UriResourcePartTyped lastType = (UriResourcePartTyped) lastResource;
-
-    UriResourceRootImpl pathInfoRoot = new UriResourceRootImpl(
-        ParserHelper.getTypeInformation(lastType),
-        lastType.isCollection());
-
-    UriInfoImpl uriInfoImplpath = new UriInfoImpl().setKind(UriInfoKind.resource);
-    uriInfoImplpath.addResourcePart(pathInfoRoot);
-
-    if (ctx.vPs != null) {
-      // store the context uriInfoPath
-      UriInfoImpl backupUriInfoPath = context.contextUriInfo;
-
-      // set temporary uriInfoPath to collect the path information of the memberExpression
-      context.contextUriInfo = uriInfoImplpath;
-
-      ctx.vPs.accept(this);
-
-      context.contextUriInfo = backupUriInfoPath;
-
-    }
-    return new MemberImpl(uriInfoImplpath, null);
-
-  }
-
-  @Override
-  public Expression visitRoundMethodCallExpr(final RoundMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.ROUND, Collections.singletonList((Expression) ctx.vE1.accept(this)));
-  }
-
-  @Override
-  public Expression visitSecondMethodCallExpr(final SecondMethodCallExprContext ctx) {
-    return new MethodImpl(MethodKind.SECOND, Collections.singletonList((Expression) ctx.vE1.accept(this)));
-  }
-
-  @Override
-  public Object visitSelect(final SelectContext ctx) {
-    List<SelectItem> selectItems = new ArrayList<SelectItem>();
-    for (SelectItemContext si : ctx.vlSI) {
-      selectItems.add((SelectItem) si.accept(this));
-    }
-
-    return new SelectOptionImpl().setSelectItems(selectItems).setText(ctx.children.get(2).getText());
-  }
-
-  @Override
-  public Object visitSelectEOF(final SelectEOFContext ctx) {
-    context.contextReadingQueryPart = true;
-    List<SelectItem> selectItems = new ArrayList<SelectItem>();
-    for (SelectItemContext si : ctx.vlSI) {
-      selectItems.add((SelectItem) si.accept(this));
-    }
-
-    final QueryOptionImpl result = new SelectOptionImpl().setSelectItems(selectItems).setText(ctx.getText());
-    context.contextReadingQueryPart = false;
-    
-    return result;
-  }
-
-  @Override
-  public Object visitSelectItem(final SelectItemContext ctx) {
-    SelectItemImpl selectItem = new SelectItemImpl();
-
-    context.contextSelectItem = selectItem;
-    for (SelectSegmentContext si : ctx.vlSS) {
-      si.accept(this);
-    }
-    context.contextSelectItem = null;
-
-    return selectItem;
-  }
-
-  @Override
-  public Object visitSelectSegment(final SelectSegmentContext ctx) {
-
-    if (ctx.vS != null) {
-      if (ctx.vNS != null) {
-        String namespace = ctx.vNS.getText();
-        namespace = namespace.substring(0, namespace.length() - 1);
-        FullQualifiedName fullName = new FullQualifiedName(namespace, "*");
-        context.contextSelectItem.addAllOperationsInSchema(fullName);
-      } else {
-        context.contextSelectItem.setStar(true);
-      }
-      return null;
-    }
-
-    String odi = ctx.vODI.getText();
-    if (ctx.vNS == null) {
-
-      EdmType prevType = null;
-      if (context.contextSelectItem.getResourcePath() == null) {
-        prevType = context.contextTypes.peek();
-      } else {
-        UriInfoImpl uriInfo = (UriInfoImpl) context.contextSelectItem.getResourcePath();
-        UriResource last = uriInfo.getLastResourcePart();
-
-        prevType = ParserHelper.getTypeInformation((UriResourcePartTyped) last);
-        if (prevType == null) {
-          throw wrap(new UriParserSemanticException("prev segment not typed",
-              UriParserSemanticException.MessageKeys.ONLY_FOR_TYPED_PARTS, "select"));
-        }
-      }
-
-      if (!(prevType instanceof EdmStructuredType)) {
-        throw wrap(new UriParserSemanticException("Previous select item is not a structural type",
-            UriParserSemanticException.MessageKeys.ONLY_FOR_STRUCTURAL_TYPES, "select"));

<TRUNCATED>

[2/6] olingo-odata4 git commit: [OLINGO-834] $expand parser in Java + clean-up

Posted by ch...@apache.org.
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java
index c27c960..a94026f 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java
@@ -18,29 +18,19 @@
  */
 package org.apache.olingo.server.core.uri.antlr;
 
-import java.io.UnsupportedEncodingException;
 import java.util.Arrays;
 import java.util.Collections;
 
 import org.apache.olingo.commons.api.edm.Edm;
-import org.apache.olingo.commons.api.edm.EdmEntityContainer;
-import org.apache.olingo.commons.api.edm.EdmEntitySet;
-import org.apache.olingo.commons.api.edm.EdmEntityType;
-import org.apache.olingo.commons.api.edm.EdmKeyPropertyRef;
-import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
 import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
-import org.apache.olingo.commons.api.edm.EdmProperty;
 import org.apache.olingo.commons.api.edm.FullQualifiedName;
 import org.apache.olingo.commons.api.format.ContentType;
-import org.apache.olingo.commons.core.Encoder;
-import org.apache.olingo.commons.core.edm.primitivetype.EdmString;
 import org.apache.olingo.server.api.OData;
 import org.apache.olingo.server.api.edmx.EdmxReference;
 import org.apache.olingo.server.api.uri.UriInfoKind;
 import org.apache.olingo.server.api.uri.UriResourceKind;
 import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind;
 import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind;
-import org.apache.olingo.server.core.uri.parser.UriParserException;
 import org.apache.olingo.server.core.uri.parser.UriParserSemanticException.MessageKeys;
 import org.apache.olingo.server.core.uri.parser.UriParserSyntaxException;
 import org.apache.olingo.server.core.uri.parser.search.SearchParserException;
@@ -56,19 +46,17 @@ import org.apache.olingo.server.tecsvc.provider.EnumTypeProvider;
 import org.apache.olingo.server.tecsvc.provider.FunctionProvider;
 import org.apache.olingo.server.tecsvc.provider.PropertyProvider;
 import org.apache.olingo.server.tecsvc.provider.TypeDefinitionProvider;
-import org.junit.Ignore;
 import org.junit.Test;
-import org.mockito.Mockito;
 
 public class TestFullResourcePath {
 
   private static final OData oData = OData.newInstance();
+  private static final Edm edm = oData.createServiceMetadata(
+      new EdmTechProvider(), Collections.<EdmxReference> emptyList()).getEdm();
   private final TestUriValidator testUri;
   private final FilterValidator testFilter;
 
   public TestFullResourcePath() {
-    final Edm edm = oData.createServiceMetadata(new EdmTechProvider(), Collections.<EdmxReference> emptyList())
-        .getEdm();
     testUri = new TestUriValidator().setEdm(edm);
     testFilter = new FilterValidator().setEdm(edm);
   }
@@ -1033,16 +1021,10 @@ public class TestFullResourcePath {
   public void resourcePathWithApostrophe() throws Exception {
     testUri.runEx("ESAllPrim'").isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
     testUri.runEx("ESAllPrim'InvalidStuff").isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
-
-    testFilter.runOnETKeyNavEx("PropertyInt16' eq 0")
-        .isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION);
-
-    testFilter.runOnETKeyNavEx("PropertyInt16 eq' 0")
-        .isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION);
-
+    testFilter.runOnETKeyNavEx("PropertyInt16' eq 0").isExSemantic(MessageKeys.TYPES_NOT_COMPATIBLE);
+    testFilter.runOnETKeyNavEx("PropertyInt16 eq' 0").isExSemantic(MessageKeys.TYPES_NOT_COMPATIBLE);
     testFilter.runOnETKeyNavEx("PropertyInt16 eq 0'")
         .isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION);
-
     testFilter.runOnETKeyNavEx("PropertyInt16 eq 'dsd''")
         .isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
   }
@@ -1306,13 +1288,13 @@ public class TestFullResourcePath {
 
   @Test
   public void runEsNameParaKeys() throws Exception {
-    testUri.run(encode("ESAllKey(PropertyString='O''Neil',PropertyBoolean=true,PropertyByte=255,"
+    testUri.run("ESAllKey(PropertyString='O''Neil',PropertyBoolean=true,PropertyByte=255,"
         + "PropertySByte=-128,PropertyInt16=-32768,PropertyInt32=-2147483648,"
         + "PropertyInt64=-9223372036854775808,PropertyDecimal=1,PropertyDate=2013-09-25,"
         + "PropertyDateTimeOffset=2002-10-10T12:00:00-05:00,"
         + "PropertyDuration=duration'P50903316DT2H25M4S',"
         + "PropertyGuid=12345678-1234-1234-1234-123456789012,"
-        + "PropertyTimeOfDay=12:34:55)"))
+        + "PropertyTimeOfDay=12:34:55)")
         .isKind(UriInfoKind.resource).goPath()
         .first()
         .isEntitySet("ESAllKey")
@@ -1380,12 +1362,11 @@ public class TestFullResourcePath {
         .isType(EntityTypeProvider.nameETTwoPrim)
         .isTypeFilterOnCollection(EntityTypeProvider.nameETTwoBase);
 
-    // TODO: Keys cannot be specified twice.
-    //testUri.runEx("ESTwoPrim(1)/olingo.odata.test1.ETBase(1)")
-    //    .isExSemantic(MessageKeys.INCOMPATIBLE_TYPE_FILTER);
-
-    //testUri.runEx("ESTwoPrim/olingo.odata.test1.ETBase(1)/olingo.odata.test1.ETTwoBase(1)")
-    //    .isExSemantic(MessageKeys.TYPE_FILTER_NOT_CHAINABLE);
+    // Keys cannot be specified twice.
+    testUri.runEx("ESTwoPrim(1)/olingo.odata.test1.ETBase(1)")
+        .isExSemantic(MessageKeys.KEY_NOT_ALLOWED);
+    testUri.runEx("ESTwoPrim/olingo.odata.test1.ETBase(1)/olingo.odata.test1.ETTwoBase(1)")
+        .isExSemantic(MessageKeys.KEY_NOT_ALLOWED);
 
     testUri.runEx("ESBase/olingo.odata.test1.ETTwoPrim(1)").isExSemantic(MessageKeys.INCOMPATIBLE_TYPE_FILTER);
   }
@@ -2321,8 +2302,7 @@ public class TestFullResourcePath {
   }
 
   @Test
-  public void runExpand() throws Exception {
-
+  public void expandStar() throws Exception {
     testUri.run("ESKeyNav(1)", "$expand=*")
         .isKind(UriInfoKind.resource).goPath().goExpand()
         .first()
@@ -2353,7 +2333,10 @@ public class TestFullResourcePath {
         .first()
         .isSegmentStar()
         .isLevelText("max");
+  }
 
+  @Test
+  public void expandNavigationRef() throws Exception {
     testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany/$ref")
         .isKind(UriInfoKind.resource).goPath().goExpand()
         .first()
@@ -2441,7 +2424,10 @@ public class TestFullResourcePath {
         .goUpExpandValidator()
         .isSkipText("1")
         .isTopText("3");
+  }
 
+  @Test
+  public void expandNavigationCount() throws Exception {
     testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany/$count")
         .isKind(UriInfoKind.resource).goPath().goExpand()
         .first()
@@ -2467,7 +2453,10 @@ public class TestFullResourcePath {
         .n().isCount()
         .goUpExpandValidator()
         .isFilterSerialized("<<PropertyInt16> gt <1>>");
+  }
 
+  @Test
+  public void expandNavigationOptions() throws Exception {
     testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany($filter=PropertyInt16 eq 1)")
         .isKind(UriInfoKind.resource).goPath().goExpand()
         .first()
@@ -2521,7 +2510,6 @@ public class TestFullResourcePath {
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
         .isType(EntityTypeProvider.nameETKeyNav, true)
         .goUpExpandValidator()
-        .isSelectText("PropertyString")
         .goSelectItem(0).isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
 
     testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany($expand=NavPropertyETTwoKeyNavOne)")
@@ -2562,7 +2550,6 @@ public class TestFullResourcePath {
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
         .isType(EntityTypeProvider.nameETKeyNav, true)
         .goUpExpandValidator()
-        .isSelectText("PropertyString")
         .goSelectItem(0).isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
 
     testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavOne($levels=max)")
@@ -2594,6 +2581,12 @@ public class TestFullResourcePath {
         .isSkipText("1")
         .isTopText("2");
 
+    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany($search=Country AND Western)")
+        .isKind(UriInfoKind.resource).goPath().goExpand()
+        .first().goPath().first().isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
+        .goUpExpandValidator()
+        .isSearchSerialized("{'Country' AND 'Western'}");
+
     testUri.run("ESTwoKeyNav(PropertyInt16=1,PropertyString='Hugo')", "$expand=NavPropertyETKeyNavMany")
         .isKind(UriInfoKind.resource).goPath()
         .first()
@@ -2604,7 +2597,10 @@ public class TestFullResourcePath {
         .goPath().first()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
         .isType(EntityTypeProvider.nameETKeyNav, true);
+  }
 
+  @Test
+  public void expandTypeCasts() throws Exception {
     testUri.run("ESTwoKeyNav", "$expand=olingo.odata.test1.ETBaseTwoKeyNav/NavPropertyETKeyNavMany")
         .isKind(UriInfoKind.resource).goPath().first()
         .goExpand().first()
@@ -2635,15 +2631,13 @@ public class TestFullResourcePath {
         .isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true);
 
     testUri.run("ESTwoKeyNav(PropertyInt16=1,PropertyString='2')",
-        "$expand=olingo.odata.test1.ETBaseTwoKeyNav"
-            + "/NavPropertyETTwoKeyNavMany/olingo.odata.test1.ETTwoBaseTwoKeyNav")
+        "$expand=olingo.odata.test1.ETBaseTwoKeyNav/NavPropertyETTwoKeyNavMany/olingo.odata.test1.ETTwoBaseTwoKeyNav")
         .isKind(UriInfoKind.resource).goPath().first()
         .isKeyPredicate(0, "PropertyInt16", "1")
         .isKeyPredicate(1, "PropertyString", "'2'")
         .goExpand().first()
         .isExpandStartType(EntityTypeProvider.nameETBaseTwoKeyNav)
         .goPath().first()
-        .isType(EntityTypeProvider.nameETTwoKeyNav)
         .isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true)
         .isTypeFilterOnCollection(EntityTypeProvider.nameETTwoBaseTwoKeyNav);
 
@@ -2724,7 +2718,6 @@ public class TestFullResourcePath {
         .isNavProperty("NavPropertyETKeyNavOne", EntityTypeProvider.nameETKeyNav, false)
         .isType(EntityTypeProvider.nameETKeyNav)
         .goUpExpandValidator()
-        .isSelectText("PropertyInt16")
         .goSelectItem(0).isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
 
     testUri.run("ESKeyNav", "$expand=NavPropertyETKeyNavOne($select=PropertyCompNav/PropertyInt16)")
@@ -2735,7 +2728,9 @@ public class TestFullResourcePath {
         .isNavProperty("NavPropertyETKeyNavOne", EntityTypeProvider.nameETKeyNav, false)
         .isType(EntityTypeProvider.nameETKeyNav)
         .goUpExpandValidator()
-        .isSelectText("PropertyCompNav/PropertyInt16");
+        .goSelectItem(0)
+        .first().isComplexProperty("PropertyCompNav", ComplexTypeProvider.nameCTNavFiveProp, false)
+        .n().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
 
     testUri.runEx("ESKeyNav", "$expand=undefined")
         .isExSemantic(MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE);
@@ -2744,7 +2739,7 @@ public class TestFullResourcePath {
   }
 
   @Test
-  public void runDuplicatedSystemQueryOptionsInExpand() throws UriParserException, UriValidationException {
+  public void duplicatedSystemQueryOptionsInExpand() throws Exception {
     testUri.runEx("ESKeyNav", "$expand=NavPropertyETKeyNavOne($select=PropertyInt16;$select=PropertyInt16)")
         .isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
 
@@ -2768,11 +2763,7 @@ public class TestFullResourcePath {
 
     testUri.runEx("ESKeyNav", "$expand=NavPropertyETKeyNavOne($skip=2;$skip=2)")
         .isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
-  }
 
-  @Test
-  @Ignore("$search in expand currently not implemented")
-  public void duplicatedSearchExpand() throws Exception {
     testUri.runEx("ESKeyNav", "$expand=NavPropertyETKeyNavOne($search=Test;$search=Test)")
         .isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
   }
@@ -2906,8 +2897,7 @@ public class TestFullResourcePath {
   }
 
   @Test
-  public void runTop() throws Exception {
-    // top
+  public void top() throws Exception {
     testUri.run("ESKeyNav", "$top=1")
         .isKind(UriInfoKind.resource).goPath()
         .isEntitySet("ESKeyNav")
@@ -2918,20 +2908,16 @@ public class TestFullResourcePath {
         .isEntitySet("ESKeyNav")
         .isTopText("0");
 
-    testUri.run("ESKeyNav", "$top=-3")
-        .isKind(UriInfoKind.resource).goPath()
-        .isEntitySet("ESKeyNav")
-        .isTopText("-3");
-
     testUri.runEx("ESKeyNav", "$top=undefined")
         .isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION);
     testUri.runEx("ESKeyNav", "$top=")
         .isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION);
+    testUri.runEx("ESKeyNav", "$top=-3")
+        .isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION);
   }
 
   @Test
-  public void runFormat() throws Exception {
-    // format
+  public void format() throws Exception {
     testUri.run("ESKeyNav(1)", "$format=atom")
         .isKind(UriInfoKind.resource).goPath()
         .isFormatText("atom");
@@ -2963,8 +2949,7 @@ public class TestFullResourcePath {
   }
 
   @Test
-  public void runCount() throws Exception {
-    // count
+  public void count() throws Exception {
     testUri.run("ESAllPrim", "$count=true")
         .isKind(UriInfoKind.resource).goPath()
         .isInlineCountText("true");
@@ -2979,20 +2964,19 @@ public class TestFullResourcePath {
 
   @Test
   public void skip() throws Exception {
-    // skip
     testUri.run("ESAllPrim", "$skip=3")
         .isKind(UriInfoKind.resource).goPath()
         .isSkipText("3");
     testUri.run("ESAllPrim", "$skip=0")
         .isKind(UriInfoKind.resource).goPath()
         .isSkipText("0");
-    testUri.run("ESAllPrim", "$skip=-3")
-        .isKind(UriInfoKind.resource).goPath()
-        .isSkipText("-3");
+
     testUri.runEx("ESAllPrim", "$skip=F")
         .isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION);
     testUri.runEx("ESAllPrim", "$skip=")
         .isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION);
+    testUri.runEx("ESAllPrim", "$skip=-3")
+        .isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION);
   }
 
   @Test
@@ -3000,6 +2984,9 @@ public class TestFullResourcePath {
     testUri.run("ESAllPrim", "$skiptoken=foo")
         .isKind(UriInfoKind.resource).goPath()
         .isSkipTokenText("foo");
+
+    testUri.runEx("ESAllPrim", "$skiptoken=")
+        .isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION);
   }
 
   @Test
@@ -3010,7 +2997,6 @@ public class TestFullResourcePath {
 
   @Test
   public void misc() throws Exception {
-
     testUri.run("")
         .isKind(UriInfoKind.service);
     testUri.run("/")
@@ -3227,56 +3213,47 @@ public class TestFullResourcePath {
         .isType(EntityTypeProvider.nameETTwoKeyNav)
         .isTypeFilterOnEntry(EntityTypeProvider.nameETBaseTwoKeyNav)
         .n().isValue();
-
   }
 
-  // TODO
   @Test
   public void filter() throws Exception {
+    testFilter.runOnETAllPrim("PropertyBoolean")
+        .is("<PropertyBoolean>")
+        .isType(PropertyProvider.nameBoolean);
 
-    testFilter.runOnETTwoKeyNav("PropertyString")
-        .is("<PropertyString>")
-        .isType(PropertyProvider.nameString);
-
-    testFilter.runOnETTwoKeyNav("PropertyComp/PropertyInt16")
-        .is("<PropertyComp/PropertyInt16>")
-        .isType(PropertyProvider.nameInt16);
+    testFilter.runOnETTwoKeyNav("PropertyComp/PropertyInt16 gt 0")
+        .is("<<PropertyComp/PropertyInt16> gt <0>>")
+        .left().isType(PropertyProvider.nameInt16);
 
-    testFilter.runOnETTwoKeyNav("PropertyComp/PropertyComp/PropertyDate")
-        .is("<PropertyComp/PropertyComp/PropertyDate>")
-        .isType(PropertyProvider.nameDate);
+    testFilter.runOnETTwoKeyNav("PropertyComp/PropertyComp/PropertyDate ne null")
+        .is("<<PropertyComp/PropertyComp/PropertyDate> ne <null>>")
+        .left().isType(PropertyProvider.nameDate);
 
-    testFilter.runOnETTwoKeyNav("NavPropertyETTwoKeyNavOne")
-        .is("<NavPropertyETTwoKeyNavOne>")
-        .isType(EntityTypeProvider.nameETTwoKeyNav);
+    testFilter.runOnETTwoKeyNav("NavPropertyETTwoKeyNavOne eq null")
+        .is("<<NavPropertyETTwoKeyNavOne> eq <null>>")
+        .left().isType(EntityTypeProvider.nameETTwoKeyNav);
 
-    testFilter.runOnETTwoKeyNav("NavPropertyETTwoKeyNavOne/PropertyString")
-        .is("<NavPropertyETTwoKeyNavOne/PropertyString>")
-        .isType(PropertyProvider.nameString);
-
-    testFilter.runOnETTwoKeyNav("NavPropertyETTwoKeyNavOne/PropertyComp")
-        .is("<NavPropertyETTwoKeyNavOne/PropertyComp>")
-        .isType(ComplexTypeProvider.nameCTPrimComp);
+    testFilter.runOnETTwoKeyNav("NavPropertyETTwoKeyNavOne/PropertyString eq ''")
+        .is("<<NavPropertyETTwoKeyNavOne/PropertyString> eq <''>>")
+        .left().isType(PropertyProvider.nameString);
 
-    testFilter.runOnETTwoKeyNav("NavPropertyETTwoKeyNavOne/PropertyComp/PropertyComp")
-        .is("<NavPropertyETTwoKeyNavOne/PropertyComp/PropertyComp>")
-        .isType(ComplexTypeProvider.nameCTAllPrim);
+    testFilter.runOnETTwoKeyNav("NavPropertyETTwoKeyNavOne/PropertyComp eq null")
+        .is("<<NavPropertyETTwoKeyNavOne/PropertyComp> eq <null>>")
+        .left().isType(ComplexTypeProvider.nameCTPrimComp);
 
-    testFilter.runOnETTwoKeyNav("NavPropertyETTwoKeyNavOne/PropertyComp/PropertyInt16")
-        .is("<NavPropertyETTwoKeyNavOne/PropertyComp/PropertyInt16>")
-        .isType(PropertyProvider.nameInt16);
+    testFilter.runOnETTwoKeyNav("NavPropertyETTwoKeyNavOne/PropertyComp/PropertyComp eq null")
+        .is("<<NavPropertyETTwoKeyNavOne/PropertyComp/PropertyComp> eq <null>>")
+        .left().isType(ComplexTypeProvider.nameCTAllPrim);
 
     testFilter.runOnETTwoKeyNav("NavPropertyETTwoKeyNavOne/PropertyComp/PropertyInt16 eq 1")
         .is("<<NavPropertyETTwoKeyNavOne/PropertyComp/PropertyInt16> eq <1>>")
-        .root().left()
-        .isType(PropertyProvider.nameInt16)
-        .root().right()
-        .isLiteral("1");
+        .left().isType(PropertyProvider.nameInt16)
+        .root().right().isLiteral("1");
 
     testFilter.runOnETTwoKeyNav("NavPropertyETKeyNavMany(1)/NavPropertyETTwoKeyNavMany(PropertyString='2')/"
         + "PropertyString eq 'SomeString'")
         .is("<<NavPropertyETKeyNavMany/NavPropertyETTwoKeyNavMany/PropertyString> eq <'SomeString'>>")
-        .root().left()
+        .left()
         .isType(PropertyProvider.nameString)
         .isMember().goPath()
         .first()
@@ -3293,7 +3270,7 @@ public class TestFullResourcePath {
 
     testFilter.runOnETTwoKeyNav("olingo.odata.test1.ETBaseTwoKeyNav/PropertyDate eq 2013-11-12")
         .is("<<PropertyDate> eq <2013-11-12>>")
-        .root().left()
+        .left()
         .isType(PropertyProvider.nameDate)
         .isMember().isMemberStartType(EntityTypeProvider.nameETBaseTwoKeyNav).goPath()
         .first().isPrimitiveProperty("PropertyDate", PropertyProvider.nameDate, false)
@@ -3303,7 +3280,7 @@ public class TestFullResourcePath {
 
     testFilter.runOnCTTwoPrim("olingo.odata.test1.CTBase/AdditionalPropString eq 'SomeString'")
         .is("<<AdditionalPropString> eq <'SomeString'>>")
-        .root().left()
+        .left()
         .isType(PropertyProvider.nameString)
         .isMember().isMemberStartType(ComplexTypeProvider.nameCTBase).goPath()
         .first().isPrimitiveProperty("AdditionalPropString", PropertyProvider.nameString, false)
@@ -3314,7 +3291,7 @@ public class TestFullResourcePath {
     testFilter
         .runOnETTwoKeyNav("NavPropertyETTwoKeyNavOne/olingo.odata.test1.ETBaseTwoKeyNav/PropertyDate eq 2013-11-12")
         .is("<<NavPropertyETTwoKeyNavOne/olingo.odata.test1.ETBaseTwoKeyNav/PropertyDate> eq <2013-11-12>>")
-        .root().left()
+        .left()
         .isType(PropertyProvider.nameDate)
         .root().right()
         .isLiteral("2013-11-12");
@@ -3322,20 +3299,15 @@ public class TestFullResourcePath {
     testFilter
         .runOnETTwoKeyNav("PropertyCompTwoPrim/olingo.odata.test1.CTTwoBase/AdditionalPropString eq 'SomeString'")
         .is("<<PropertyCompTwoPrim/olingo.odata.test1.CTTwoBase/AdditionalPropString> eq <'SomeString'>>")
-        .root().left()
+        .left()
         .isType(PropertyProvider.nameString)
         .root().right()
         .isLiteral("'SomeString'");
 
-    testFilter.runOnETTwoKeyNavEx("invalid")
-        .isExSemantic(MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE);
-    // TODO: This should throw an exception because the top node of the filter tree must be boolean.
-    // testFilter.runOnETTwoKeyNavEx("PropertyComp")
-    //     .isExSemantic(MessageKeys.UNKNOWN_TYPE);
-    testFilter.runOnETTwoKeyNavEx("PropertyComp/invalid")
-        .isExSemantic(MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE);
-    testFilter.runOnETTwoKeyNavEx("concat('a','b')/invalid")
-        .isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION);
+    testFilter.runOnETTwoKeyNavEx("invalid").isExSemantic(MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE);
+    testFilter.runOnETTwoKeyNavEx("PropertyComp").isExSemantic(MessageKeys.TYPES_NOT_COMPATIBLE);
+    testFilter.runOnETTwoKeyNavEx("PropertyComp/invalid").isExSemantic(MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE);
+    testFilter.runOnETTwoKeyNavEx("concat('a','b')/invalid").isExSemantic(MessageKeys.TYPES_NOT_COMPATIBLE);
     testFilter.runOnETTwoKeyNavEx("PropertyComp/concat('a','b')")
         .isExSemantic(MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE);
     testFilter.runOnETTwoKeyNavEx("PropertyComp/PropertyInt16 eq '1'")
@@ -3347,437 +3319,307 @@ public class TestFullResourcePath {
     testFilter.runOnETTwoKeyNavEx("PropertyComp/PropertyInt64 eq 1")
         .isExSemantic(MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE);
     testFilter.runOnETTwoKeyNavEx("NavPropertyETKeyNavMany/PropertyInt16 gt 42")
-        .isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION);
+        .isExSemantic(MessageKeys.TYPES_NOT_COMPATIBLE);
     testFilter.runOnETTwoKeyNavEx("NavPropertyETKeyNavMany/NavPropertyETTwoKeyNavOne eq null")
-        .isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION);
+        .isExSemantic(MessageKeys.TYPES_NOT_COMPATIBLE);
+  }
 
+  @Test
+  public void filterBinaryOperators() throws Exception {
     testFilter.runOnETAllPrim("PropertySByte eq PropertySByte")
         .is("<<PropertySByte> eq <PropertySByte>>")
         .isBinary(BinaryOperatorKind.EQ)
-        .root().left()
-        .isType(PropertyProvider.nameSByte)
-        .root().right()
-        .isType(PropertyProvider.nameSByte);
+        .left().isType(PropertyProvider.nameSByte)
+        .root().right().isType(PropertyProvider.nameSByte);
 
     testFilter.runOnETAllPrim("PropertySByte ne PropertySByte")
         .is("<<PropertySByte> ne <PropertySByte>>")
         .isBinary(BinaryOperatorKind.NE)
-        .root().left()
-        .isType(PropertyProvider.nameSByte)
-        .root().right()
-        .isType(PropertyProvider.nameSByte);
-
-    testFilter.runOnETAllPrim("PropertySByte add PropertySByte")
-        .is("<<PropertySByte> add <PropertySByte>>")
-        .root().left()
-        .isType(PropertyProvider.nameSByte)
-        .root().right()
-        .isType(PropertyProvider.nameSByte);
-
-    testFilter.runOnETAllPrim("PropertyByte add PropertyByte")
-        .is("<<PropertyByte> add <PropertyByte>>")
-        .root().left()
-        .isType(PropertyProvider.nameByte)
-        .root().right()
-        .isType(PropertyProvider.nameByte);
-    testFilter.runOnETAllPrim("PropertyInt16 add PropertyInt16")
-        .is("<<PropertyInt16> add <PropertyInt16>>")
-        .root().left()
-        .isType(PropertyProvider.nameInt16)
-        .root().right()
-        .isType(PropertyProvider.nameInt16);
-    testFilter.runOnETAllPrim("PropertyInt32 add PropertyInt32")
-        .is("<<PropertyInt32> add <PropertyInt32>>")
-        .root().left()
-        .isType(PropertyProvider.nameInt32)
-        .root().right()
-        .isType(PropertyProvider.nameInt32);
-
-    testFilter.runOnETAllPrim("PropertyInt64 add PropertyInt64")
-        .is("<<PropertyInt64> add <PropertyInt64>>")
-        .root().left()
-        .isType(PropertyProvider.nameInt64)
-        .root().right()
-        .isType(PropertyProvider.nameInt64);
-    testFilter.runOnETAllPrim("PropertySingle add PropertySingle")
-        .is("<<PropertySingle> add <PropertySingle>>")
-        .root().left()
-        .isType(PropertyProvider.nameSingle)
-        .root().right()
-        .isType(PropertyProvider.nameSingle);
-    testFilter.runOnETAllPrim("PropertyDouble add PropertyDouble")
-        .is("<<PropertyDouble> add <PropertyDouble>>")
-        .root().left()
-        .isType(PropertyProvider.nameDouble)
-        .root().right()
-        .isType(PropertyProvider.nameDouble);
-    testFilter.runOnETAllPrim("PropertyDecimal add PropertyDecimal")
-        .is("<<PropertyDecimal> add <PropertyDecimal>>")
-        .root().left()
-        .isType(PropertyProvider.nameDecimal)
-        .root().right()
-        .isType(PropertyProvider.nameDecimal);
-    testFilter.runOnETAllPrim("PropertySByte add PropertyDecimal")
-        .is("<<PropertySByte> add <PropertyDecimal>>")
-        .root().left()
-        .isType(PropertyProvider.nameSByte)
-        .root().right()
-        .isType(PropertyProvider.nameDecimal);
-    testFilter.runOnETAllPrim("PropertySByte add PropertyInt32")
-        .is("<<PropertySByte> add <PropertyInt32>>")
-        .root().left()
-        .isType(PropertyProvider.nameSByte)
-        .root().right()
-        .isType(PropertyProvider.nameInt32);
-    testFilter.runOnETAllPrim("PropertySByte add PropertyInt64")
-        .is("<<PropertySByte> add <PropertyInt64>>")
-        .root().left()
-        .isType(PropertyProvider.nameSByte)
-        .root().right()
-        .isType(PropertyProvider.nameInt64);
-    testFilter.runOnETAllPrim("PropertyDateTimeOffset add PropertyDuration")
-        .is("<<PropertyDateTimeOffset> add <PropertyDuration>>")
-        .root().left()
-        .isType(PropertyProvider.nameDateTimeOffset)
-        .root().right()
-        .isType(PropertyProvider.nameDuration);
-    testFilter.runOnETAllPrim("PropertyDuration add PropertyDuration")
-        .is("<<PropertyDuration> add <PropertyDuration>>")
-        .root().left()
-        .isType(PropertyProvider.nameDuration)
-        .root().right()
-        .isType(PropertyProvider.nameDuration);
-    testFilter.runOnETAllPrim("PropertyDate add PropertyDuration")
-        .is("<<PropertyDate> add <PropertyDuration>>")
-        .root().left()
-        .isType(PropertyProvider.nameDate)
-        .root().right()
-        .isType(PropertyProvider.nameDuration);
-    testFilter.runOnETAllPrim("PropertySByte sub PropertySByte")
-        .is("<<PropertySByte> sub <PropertySByte>>")
-        .root().left()
-        .isType(PropertyProvider.nameSByte)
-        .root().right()
-        .isType(PropertyProvider.nameSByte);
-    testFilter.runOnETAllPrim("PropertyByte sub PropertyByte")
-        .is("<<PropertyByte> sub <PropertyByte>>")
-        .root().left()
-        .isType(PropertyProvider.nameByte)
-        .root().right()
-        .isType(PropertyProvider.nameByte);
-    testFilter.runOnETAllPrim("PropertyInt16 sub PropertyInt16")
-        .is("<<PropertyInt16> sub <PropertyInt16>>")
-        .root().left()
-        .isType(PropertyProvider.nameInt16)
-        .root().right()
-        .isType(PropertyProvider.nameInt16);
-    testFilter.runOnETAllPrim("PropertyInt32 sub PropertyInt32")
-        .is("<<PropertyInt32> sub <PropertyInt32>>")
-        .root().left()
-        .isType(PropertyProvider.nameInt32)
-        .root().right()
-        .isType(PropertyProvider.nameInt32);
-    testFilter.runOnETAllPrim("PropertyInt64 sub PropertyInt64")
-        .is("<<PropertyInt64> sub <PropertyInt64>>")
-        .root().left()
-        .isType(PropertyProvider.nameInt64)
-        .root().right()
-        .isType(PropertyProvider.nameInt64);
-    testFilter.runOnETAllPrim("PropertySingle sub PropertySingle")
-        .is("<<PropertySingle> sub <PropertySingle>>")
-        .root().left()
-        .isType(PropertyProvider.nameSingle)
-        .root().right()
-        .isType(PropertyProvider.nameSingle);
-    testFilter.runOnETAllPrim("PropertyDouble sub PropertyDouble")
-        .is("<<PropertyDouble> sub <PropertyDouble>>")
-        .root().left()
-        .isType(PropertyProvider.nameDouble)
-        .root().right()
-        .isType(PropertyProvider.nameDouble);
-    testFilter.runOnETAllPrim("PropertyDecimal sub PropertyDecimal")
-        .is("<<PropertyDecimal> sub <PropertyDecimal>>")
-        .root().left()
-        .isType(PropertyProvider.nameDecimal)
-        .root().right()
-        .isType(PropertyProvider.nameDecimal);
-    testFilter.runOnETAllPrim("PropertyDecimal sub PropertyInt32")
-        .is("<<PropertyDecimal> sub <PropertyInt32>>")
-        .root().left()
-        .isType(PropertyProvider.nameDecimal)
-        .root().right()
-        .isType(PropertyProvider.nameInt32);
-    testFilter.runOnETAllPrim("PropertyDecimal sub PropertyInt64")
-        .is("<<PropertyDecimal> sub <PropertyInt64>>")
-        .root().left()
-        .isType(PropertyProvider.nameDecimal)
-        .root().right()
-        .isType(PropertyProvider.nameInt64);
-    testFilter.runOnETAllPrim("PropertyDecimal sub PropertyByte")
-        .is("<<PropertyDecimal> sub <PropertyByte>>")
-        .root().left()
-        .isType(PropertyProvider.nameDecimal)
-        .root().right()
-        .isType(PropertyProvider.nameByte);
-    testFilter.runOnETAllPrim("PropertyDateTimeOffset sub PropertyDuration")
-        .is("<<PropertyDateTimeOffset> sub <PropertyDuration>>")
-        .root().left()
-        .isType(PropertyProvider.nameDateTimeOffset)
-        .root().right()
-        .isType(PropertyProvider.nameDuration);
-    testFilter.runOnETAllPrim("PropertyDuration sub PropertyDuration")
-        .is("<<PropertyDuration> sub <PropertyDuration>>")
-        .root().left()
-        .isType(PropertyProvider.nameDuration)
-        .root().right()
-        .isType(PropertyProvider.nameDuration);
-    testFilter.runOnETAllPrim("PropertyDateTimeOffset sub PropertyDateTimeOffset")
-        .is("<<PropertyDateTimeOffset> sub <PropertyDateTimeOffset>>")
-        .root().left()
-        .isType(PropertyProvider.nameDateTimeOffset)
-        .root().right()
-        .isType(PropertyProvider.nameDateTimeOffset);
-    testFilter.runOnETAllPrim("PropertyDate sub PropertyDuration")
-        .is("<<PropertyDate> sub <PropertyDuration>>")
-        .root().left()
-        .isType(PropertyProvider.nameDate)
-        .root().right()
-        .isType(PropertyProvider.nameDuration);
-    testFilter.runOnETAllPrim("PropertyDate sub PropertyDate")
-        .is("<<PropertyDate> sub <PropertyDate>>")
-        .root().left()
-        .isType(PropertyProvider.nameDate)
-        .root().right()
-        .isType(PropertyProvider.nameDate);
-    testFilter.runOnETAllPrim("PropertySByte mul PropertySByte")
-        .is("<<PropertySByte> mul <PropertySByte>>")
-        .root().left()
-        .isType(PropertyProvider.nameSByte)
-        .root().right()
-        .isType(PropertyProvider.nameSByte);
-    testFilter.runOnETAllPrim("PropertyByte mul PropertyByte")
-        .is("<<PropertyByte> mul <PropertyByte>>")
-        .root().left()
-        .isType(PropertyProvider.nameByte)
-        .root().right()
-        .isType(PropertyProvider.nameByte);
-    testFilter.runOnETAllPrim("PropertyInt16 mul PropertyInt16")
-        .is("<<PropertyInt16> mul <PropertyInt16>>")
-        .root().left()
-        .isType(PropertyProvider.nameInt16)
-        .root().right()
-        .isType(PropertyProvider.nameInt16);
-    testFilter.runOnETAllPrim("PropertyInt32 mul PropertyInt32")
-        .is("<<PropertyInt32> mul <PropertyInt32>>")
-        .root().left()
-        .isType(PropertyProvider.nameInt32)
-        .root().right()
-        .isType(PropertyProvider.nameInt32);
-    testFilter.runOnETAllPrim("PropertyInt64 mul PropertyInt64")
-        .is("<<PropertyInt64> mul <PropertyInt64>>")
-        .root().left()
-        .isType(PropertyProvider.nameInt64)
-        .root().right()
-        .isType(PropertyProvider.nameInt64);
-    testFilter.runOnETAllPrim("PropertySingle mul PropertySingle")
-        .is("<<PropertySingle> mul <PropertySingle>>")
-        .root().left()
-        .isType(PropertyProvider.nameSingle)
-        .root().right()
-        .isType(PropertyProvider.nameSingle);
-    testFilter.runOnETAllPrim("PropertyDouble mul PropertyDouble")
-        .is("<<PropertyDouble> mul <PropertyDouble>>")
-        .root().left()
-        .isType(PropertyProvider.nameDouble)
-        .root().right()
-        .isType(PropertyProvider.nameDouble);
-    testFilter.runOnETAllPrim("PropertyDecimal mul PropertyDecimal")
-        .is("<<PropertyDecimal> mul <PropertyDecimal>>")
-        .root().left()
-        .isType(PropertyProvider.nameDecimal)
-        .root().right()
-        .isType(PropertyProvider.nameDecimal);
-    testFilter.runOnETAllPrim("PropertyInt64 mul PropertyInt32")
-        .is("<<PropertyInt64> mul <PropertyInt32>>")
-        .root().left()
-        .isType(PropertyProvider.nameInt64)
-        .root().right()
-        .isType(PropertyProvider.nameInt32);
-    testFilter.runOnETAllPrim("PropertyInt64 mul PropertySByte")
-        .is("<<PropertyInt64> mul <PropertySByte>>")
-        .root().left()
-        .isType(PropertyProvider.nameInt64)
-        .root().right()
-        .isType(PropertyProvider.nameSByte);
-    testFilter.runOnETAllPrim("PropertyInt64 mul PropertyDecimal")
-        .is("<<PropertyInt64> mul <PropertyDecimal>>")
-        .root().left()
-        .isType(PropertyProvider.nameInt64)
-        .root().right()
-        .isType(PropertyProvider.nameDecimal);
-    testFilter.runOnETAllPrim("PropertySByte div PropertySByte")
-        .is("<<PropertySByte> div <PropertySByte>>")
-        .root().left()
-        .isType(PropertyProvider.nameSByte)
-        .root().right()
-        .isType(PropertyProvider.nameSByte);
-    testFilter.runOnETAllPrim("PropertyByte div PropertyByte")
-        .is("<<PropertyByte> div <PropertyByte>>")
-        .root().left()
-        .isType(PropertyProvider.nameByte)
-        .root().right()
-        .isType(PropertyProvider.nameByte);
-    testFilter.runOnETAllPrim("PropertyInt16 div PropertyInt16")
-        .is("<<PropertyInt16> div <PropertyInt16>>")
-        .root().left()
-        .isType(PropertyProvider.nameInt16)
-        .root().right()
-        .isType(PropertyProvider.nameInt16);
-    testFilter.runOnETAllPrim("PropertyInt32 div PropertyInt32")
-        .is("<<PropertyInt32> div <PropertyInt32>>")
-        .root().left()
-        .isType(PropertyProvider.nameInt32)
-        .root().right()
-        .isType(PropertyProvider.nameInt32);
-    testFilter.runOnETAllPrim("PropertyInt64 div PropertyInt64")
-        .is("<<PropertyInt64> div <PropertyInt64>>")
-        .root().left()
-        .isType(PropertyProvider.nameInt64)
-        .root().right()
-        .isType(PropertyProvider.nameInt64);
-    testFilter.runOnETAllPrim("PropertySingle div PropertySingle")
-        .is("<<PropertySingle> div <PropertySingle>>")
-        .root().left()
-        .isType(PropertyProvider.nameSingle)
-        .root().right()
-        .isType(PropertyProvider.nameSingle);
-    testFilter.runOnETAllPrim("PropertyDouble div PropertyDouble")
-        .is("<<PropertyDouble> div <PropertyDouble>>")
-        .root().left()
-        .isType(PropertyProvider.nameDouble)
-        .root().right()
-        .isType(PropertyProvider.nameDouble);
-    testFilter.runOnETAllPrim("PropertyDecimal div PropertyDecimal")
-        .is("<<PropertyDecimal> div <PropertyDecimal>>")
-        .root().left()
-        .isType(PropertyProvider.nameDecimal)
-        .root().right()
-        .isType(PropertyProvider.nameDecimal);
-    testFilter.runOnETAllPrim("PropertyByte div PropertyInt32")
-        .is("<<PropertyByte> div <PropertyInt32>>")
-        .root().left()
-        .isType(PropertyProvider.nameByte)
-        .root().right()
-        .isType(PropertyProvider.nameInt32);
-    testFilter.runOnETAllPrim("PropertyByte div PropertyDecimal")
-        .is("<<PropertyByte> div <PropertyDecimal>>")
-        .root().left()
-        .isType(PropertyProvider.nameByte)
-        .root().right()
-        .isType(PropertyProvider.nameDecimal);
-    testFilter.runOnETAllPrim("PropertyByte div PropertySByte")
-        .is("<<PropertyByte> div <PropertySByte>>")
-        .root().left()
-        .isType(PropertyProvider.nameByte)
-        .root().right()
-        .isType(PropertyProvider.nameSByte);
-
-    testFilter.runOnETAllPrim("PropertyByte div 0")
-        .is("<<PropertyByte> div <0>>");
-
-    testFilter.runOnETAllPrim("0 div 0")
-        .is("<<0> div <0>>");
-
-    testFilter.runOnETAllPrim("PropertySByte mod PropertySByte")
-        .is("<<PropertySByte> mod <PropertySByte>>")
-        .root().left()
-        .isType(PropertyProvider.nameSByte)
-        .root().right()
-        .isType(PropertyProvider.nameSByte);
-    testFilter.runOnETAllPrim("PropertyByte mod PropertyByte")
-        .is("<<PropertyByte> mod <PropertyByte>>")
-        .root().left()
-        .isType(PropertyProvider.nameByte)
-        .root().right()
-        .isType(PropertyProvider.nameByte);
-    testFilter.runOnETAllPrim("PropertyInt16 mod PropertyInt16")
-        .is("<<PropertyInt16> mod <PropertyInt16>>")
-        .root().left()
-        .isType(PropertyProvider.nameInt16)
-        .root().right()
-        .isType(PropertyProvider.nameInt16);
-    testFilter.runOnETAllPrim("PropertyInt32 mod PropertyInt32")
-        .is("<<PropertyInt32> mod <PropertyInt32>>")
-        .root().left()
-        .isType(PropertyProvider.nameInt32)
-        .root().right()
-        .isType(PropertyProvider.nameInt32);
-    testFilter.runOnETAllPrim("PropertyInt64 mod PropertyInt64")
-        .is("<<PropertyInt64> mod <PropertyInt64>>")
-        .root().left()
-        .isType(PropertyProvider.nameInt64)
-        .root().right()
-        .isType(PropertyProvider.nameInt64);
-    testFilter.runOnETAllPrim("PropertySingle mod PropertySingle")
-        .is("<<PropertySingle> mod <PropertySingle>>")
-        .root().left()
-        .isType(PropertyProvider.nameSingle)
-        .root().right()
-        .isType(PropertyProvider.nameSingle);
-    testFilter.runOnETAllPrim("PropertyDouble mod PropertyDouble")
-        .is("<<PropertyDouble> mod <PropertyDouble>>")
-        .root().left()
-        .isType(PropertyProvider.nameDouble)
-        .root().right()
-        .isType(PropertyProvider.nameDouble);
-    testFilter.runOnETAllPrim("PropertyDecimal mod PropertyDecimal")
-        .is("<<PropertyDecimal> mod <PropertyDecimal>>")
-        .root().left()
-        .isType(PropertyProvider.nameDecimal)
-        .root().right()
-        .isType(PropertyProvider.nameDecimal);
+        .left().isType(PropertyProvider.nameSByte)
+        .root().right().isType(PropertyProvider.nameSByte);
+
+    testFilter.runOnETAllPrim("PropertySByte add PropertySByte gt 0")
+        .is("<<<PropertySByte> add <PropertySByte>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameSByte)
+        .root().left().right().isType(PropertyProvider.nameSByte);
+
+    testFilter.runOnETAllPrim("PropertyByte add PropertyByte gt 0")
+        .is("<<<PropertyByte> add <PropertyByte>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameByte)
+        .root().left().right().isType(PropertyProvider.nameByte);
+    testFilter.runOnETAllPrim("PropertyInt16 add PropertyInt16 gt 0")
+        .is("<<<PropertyInt16> add <PropertyInt16>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameInt16)
+        .root().left().right().isType(PropertyProvider.nameInt16);
+    testFilter.runOnETAllPrim("PropertyInt32 add PropertyInt32 gt 0")
+        .is("<<<PropertyInt32> add <PropertyInt32>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameInt32)
+        .root().left().right().isType(PropertyProvider.nameInt32);
+    testFilter.runOnETAllPrim("PropertyInt64 add PropertyInt64 gt 0")
+        .is("<<<PropertyInt64> add <PropertyInt64>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameInt64)
+        .root().left().right().isType(PropertyProvider.nameInt64);
+    testFilter.runOnETAllPrim("PropertySingle add PropertySingle gt 0")
+        .is("<<<PropertySingle> add <PropertySingle>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameSingle)
+        .root().left().right().isType(PropertyProvider.nameSingle);
+    testFilter.runOnETAllPrim("PropertyDouble add PropertyDouble gt 0")
+        .is("<<<PropertyDouble> add <PropertyDouble>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameDouble)
+        .root().left().right().isType(PropertyProvider.nameDouble);
+    testFilter.runOnETAllPrim("PropertyDecimal add PropertyDecimal gt 0")
+        .is("<<<PropertyDecimal> add <PropertyDecimal>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameDecimal)
+        .root().left().right().isType(PropertyProvider.nameDecimal);
+    testFilter.runOnETAllPrim("PropertySByte add PropertyDecimal gt 0")
+        .is("<<<PropertySByte> add <PropertyDecimal>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameSByte)
+        .root().left().right().isType(PropertyProvider.nameDecimal);
+    testFilter.runOnETAllPrim("PropertySByte add PropertyInt32 gt 0")
+        .is("<<<PropertySByte> add <PropertyInt32>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameSByte)
+        .root().left().right().isType(PropertyProvider.nameInt32);
+    testFilter.runOnETAllPrim("PropertySByte add PropertyInt64 gt 0")
+        .is("<<<PropertySByte> add <PropertyInt64>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameSByte)
+        .root().left().right().isType(PropertyProvider.nameInt64);
+    testFilter.runOnETAllPrim("PropertyDateTimeOffset add PropertyDuration ne null")
+        .is("<<<PropertyDateTimeOffset> add <PropertyDuration>> ne <null>>")
+        .left().left().isType(PropertyProvider.nameDateTimeOffset)
+        .root().left().right().isType(PropertyProvider.nameDuration);
+    testFilter.runOnETAllPrim("PropertyDuration add PropertyDuration ne null")
+        .is("<<<PropertyDuration> add <PropertyDuration>> ne <null>>")
+        .left().left().isType(PropertyProvider.nameDuration)
+        .root().left().right().isType(PropertyProvider.nameDuration);
+    testFilter.runOnETAllPrim("PropertyDate add PropertyDuration ne null")
+        .is("<<<PropertyDate> add <PropertyDuration>> ne <null>>")
+        .left().left().isType(PropertyProvider.nameDate)
+        .root().left().right().isType(PropertyProvider.nameDuration);
+    testFilter.runOnETAllPrim("PropertySByte sub PropertySByte gt 0")
+        .is("<<<PropertySByte> sub <PropertySByte>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameSByte)
+        .root().left().right().isType(PropertyProvider.nameSByte);
+    testFilter.runOnETAllPrim("PropertyByte sub PropertyByte gt 0")
+        .is("<<<PropertyByte> sub <PropertyByte>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameByte)
+        .root().left().right().isType(PropertyProvider.nameByte);
+    testFilter.runOnETAllPrim("PropertyInt16 sub PropertyInt16 gt 0")
+        .is("<<<PropertyInt16> sub <PropertyInt16>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameInt16)
+        .root().left().right().isType(PropertyProvider.nameInt16);
+    testFilter.runOnETAllPrim("PropertyInt32 sub PropertyInt32 gt 0")
+        .is("<<<PropertyInt32> sub <PropertyInt32>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameInt32)
+        .root().left().right().isType(PropertyProvider.nameInt32);
+    testFilter.runOnETAllPrim("PropertyInt64 sub PropertyInt64 gt 0")
+        .is("<<<PropertyInt64> sub <PropertyInt64>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameInt64)
+        .root().left().right().isType(PropertyProvider.nameInt64);
+    testFilter.runOnETAllPrim("PropertySingle sub PropertySingle gt 0")
+        .is("<<<PropertySingle> sub <PropertySingle>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameSingle)
+        .root().left().right().isType(PropertyProvider.nameSingle);
+    testFilter.runOnETAllPrim("PropertyDouble sub PropertyDouble gt 0")
+        .is("<<<PropertyDouble> sub <PropertyDouble>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameDouble)
+        .root().left().right().isType(PropertyProvider.nameDouble);
+    testFilter.runOnETAllPrim("PropertyDecimal sub PropertyDecimal gt 0")
+        .is("<<<PropertyDecimal> sub <PropertyDecimal>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameDecimal)
+        .root().left().right().isType(PropertyProvider.nameDecimal);
+    testFilter.runOnETAllPrim("PropertyDecimal sub PropertyInt32 gt 0")
+        .is("<<<PropertyDecimal> sub <PropertyInt32>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameDecimal)
+        .root().left().right().isType(PropertyProvider.nameInt32);
+    testFilter.runOnETAllPrim("PropertyDecimal sub PropertyInt64 gt 0")
+        .is("<<<PropertyDecimal> sub <PropertyInt64>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameDecimal)
+        .root().left().right().isType(PropertyProvider.nameInt64);
+    testFilter.runOnETAllPrim("PropertyDecimal sub PropertyByte gt 0")
+        .is("<<<PropertyDecimal> sub <PropertyByte>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameDecimal)
+        .root().left().right().isType(PropertyProvider.nameByte);
+    testFilter.runOnETAllPrim("PropertyDateTimeOffset sub PropertyDuration ne null")
+        .is("<<<PropertyDateTimeOffset> sub <PropertyDuration>> ne <null>>")
+        .left().left().isType(PropertyProvider.nameDateTimeOffset)
+        .root().left().right().isType(PropertyProvider.nameDuration);
+    testFilter.runOnETAllPrim("PropertyDuration sub PropertyDuration ne null")
+        .is("<<<PropertyDuration> sub <PropertyDuration>> ne <null>>")
+        .left().left().isType(PropertyProvider.nameDuration)
+        .root().left().right().isType(PropertyProvider.nameDuration);
+    testFilter.runOnETAllPrim("PropertyDateTimeOffset sub PropertyDateTimeOffset ne null")
+        .is("<<<PropertyDateTimeOffset> sub <PropertyDateTimeOffset>> ne <null>>")
+        .left().left().isType(PropertyProvider.nameDateTimeOffset)
+        .root().left().right().isType(PropertyProvider.nameDateTimeOffset);
+    testFilter.runOnETAllPrim("PropertyDate sub PropertyDuration ne null")
+        .is("<<<PropertyDate> sub <PropertyDuration>> ne <null>>")
+        .left().left().isType(PropertyProvider.nameDate)
+        .root().left().right().isType(PropertyProvider.nameDuration);
+    testFilter.runOnETAllPrim("PropertyDate sub PropertyDate ne null")
+        .is("<<<PropertyDate> sub <PropertyDate>> ne <null>>")
+        .left().left().isType(PropertyProvider.nameDate)
+        .root().left().right().isType(PropertyProvider.nameDate);
+    testFilter.runOnETAllPrim("PropertySByte mul PropertySByte gt 0")
+        .is("<<<PropertySByte> mul <PropertySByte>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameSByte)
+        .root().left().right().isType(PropertyProvider.nameSByte);
+    testFilter.runOnETAllPrim("PropertyByte mul PropertyByte gt 0")
+        .is("<<<PropertyByte> mul <PropertyByte>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameByte)
+        .root().left().right().isType(PropertyProvider.nameByte);
+    testFilter.runOnETAllPrim("PropertyInt16 mul PropertyInt16 gt 0")
+        .is("<<<PropertyInt16> mul <PropertyInt16>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameInt16)
+        .root().left().right().isType(PropertyProvider.nameInt16);
+    testFilter.runOnETAllPrim("PropertyInt32 mul PropertyInt32 gt 0")
+        .is("<<<PropertyInt32> mul <PropertyInt32>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameInt32)
+        .root().left().right().isType(PropertyProvider.nameInt32);
+    testFilter.runOnETAllPrim("PropertyInt64 mul PropertyInt64 gt 0")
+        .is("<<<PropertyInt64> mul <PropertyInt64>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameInt64)
+        .root().left().right().isType(PropertyProvider.nameInt64);
+    testFilter.runOnETAllPrim("PropertySingle mul PropertySingle gt 0")
+        .is("<<<PropertySingle> mul <PropertySingle>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameSingle)
+        .root().left().right().isType(PropertyProvider.nameSingle);
+    testFilter.runOnETAllPrim("PropertyDouble mul PropertyDouble gt 0")
+        .is("<<<PropertyDouble> mul <PropertyDouble>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameDouble)
+        .root().left().right().isType(PropertyProvider.nameDouble);
+    testFilter.runOnETAllPrim("PropertyDecimal mul PropertyDecimal gt 0")
+        .is("<<<PropertyDecimal> mul <PropertyDecimal>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameDecimal)
+        .root().left().right().isType(PropertyProvider.nameDecimal);
+    testFilter.runOnETAllPrim("PropertyInt64 mul PropertyInt32 gt 0")
+        .is("<<<PropertyInt64> mul <PropertyInt32>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameInt64)
+        .root().left().right().isType(PropertyProvider.nameInt32);
+    testFilter.runOnETAllPrim("PropertyInt64 mul PropertySByte gt 0")
+        .is("<<<PropertyInt64> mul <PropertySByte>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameInt64)
+        .root().left().right().isType(PropertyProvider.nameSByte);
+    testFilter.runOnETAllPrim("PropertyInt64 mul PropertyDecimal gt 0")
+        .is("<<<PropertyInt64> mul <PropertyDecimal>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameInt64)
+        .root().left().right().isType(PropertyProvider.nameDecimal);
+    testFilter.runOnETAllPrim("PropertySByte div PropertySByte gt 0")
+        .is("<<<PropertySByte> div <PropertySByte>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameSByte)
+        .root().left().right().isType(PropertyProvider.nameSByte);
+    testFilter.runOnETAllPrim("PropertyByte div PropertyByte gt 0")
+        .is("<<<PropertyByte> div <PropertyByte>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameByte)
+        .root().left().right().isType(PropertyProvider.nameByte);
+    testFilter.runOnETAllPrim("PropertyInt16 div PropertyInt16 gt 0")
+        .is("<<<PropertyInt16> div <PropertyInt16>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameInt16)
+        .root().left().right().isType(PropertyProvider.nameInt16);
+    testFilter.runOnETAllPrim("PropertyInt32 div PropertyInt32 gt 0")
+        .is("<<<PropertyInt32> div <PropertyInt32>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameInt32)
+        .root().left().right().isType(PropertyProvider.nameInt32);
+    testFilter.runOnETAllPrim("PropertyInt64 div PropertyInt64 gt 0")
+        .is("<<<PropertyInt64> div <PropertyInt64>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameInt64)
+        .root().left().right().isType(PropertyProvider.nameInt64);
+    testFilter.runOnETAllPrim("PropertySingle div PropertySingle gt 0")
+        .is("<<<PropertySingle> div <PropertySingle>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameSingle)
+        .root().left().right().isType(PropertyProvider.nameSingle);
+    testFilter.runOnETAllPrim("PropertyDouble div PropertyDouble gt 0")
+        .is("<<<PropertyDouble> div <PropertyDouble>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameDouble)
+        .root().left().right().isType(PropertyProvider.nameDouble);
+    testFilter.runOnETAllPrim("PropertyDecimal div PropertyDecimal gt 0")
+        .is("<<<PropertyDecimal> div <PropertyDecimal>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameDecimal)
+        .root().left().right().isType(PropertyProvider.nameDecimal);
+    testFilter.runOnETAllPrim("PropertyByte div PropertyInt32 gt 0")
+        .is("<<<PropertyByte> div <PropertyInt32>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameByte)
+        .root().left().right().isType(PropertyProvider.nameInt32);
+    testFilter.runOnETAllPrim("PropertyByte div PropertyDecimal gt 0")
+        .is("<<<PropertyByte> div <PropertyDecimal>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameByte)
+        .root().left().right().isType(PropertyProvider.nameDecimal);
+    testFilter.runOnETAllPrim("PropertyByte div PropertySByte gt 0")
+        .is("<<<PropertyByte> div <PropertySByte>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameByte)
+        .root().left().right().isType(PropertyProvider.nameSByte);
+
+    testFilter.runOnETAllPrim("PropertyByte div 0 gt 0")
+        .is("<<<PropertyByte> div <0>> gt <0>>");
+
+    testFilter.runOnETAllPrim("0 div 0 gt 0")
+        .is("<<<0> div <0>> gt <0>>");
+
+    testFilter.runOnETAllPrim("PropertySByte mod PropertySByte gt 0")
+        .is("<<<PropertySByte> mod <PropertySByte>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameSByte)
+        .root().left().right().isType(PropertyProvider.nameSByte);
+    testFilter.runOnETAllPrim("PropertyByte mod PropertyByte gt 0")
+        .is("<<<PropertyByte> mod <PropertyByte>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameByte)
+        .root().left().right().isType(PropertyProvider.nameByte);
+    testFilter.runOnETAllPrim("PropertyInt16 mod PropertyInt16 gt 0")
+        .is("<<<PropertyInt16> mod <PropertyInt16>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameInt16)
+        .root().left().right().isType(PropertyProvider.nameInt16);
+    testFilter.runOnETAllPrim("PropertyInt32 mod PropertyInt32 gt 0")
+        .is("<<<PropertyInt32> mod <PropertyInt32>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameInt32)
+        .root().left().right().isType(PropertyProvider.nameInt32);
+    testFilter.runOnETAllPrim("PropertyInt64 mod PropertyInt64 gt 0")
+        .is("<<<PropertyInt64> mod <PropertyInt64>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameInt64)
+        .root().left().right().isType(PropertyProvider.nameInt64);
+    testFilter.runOnETAllPrim("PropertySingle mod PropertySingle gt 0")
+        .is("<<<PropertySingle> mod <PropertySingle>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameSingle)
+        .root().left().right().isType(PropertyProvider.nameSingle);
+    testFilter.runOnETAllPrim("PropertyDouble mod PropertyDouble gt 0")
+        .is("<<<PropertyDouble> mod <PropertyDouble>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameDouble)
+        .root().left().right().isType(PropertyProvider.nameDouble);
+    testFilter.runOnETAllPrim("PropertyDecimal mod PropertyDecimal gt 0")
+        .is("<<<PropertyDecimal> mod <PropertyDecimal>> gt <0>>")
+        .left().left().isType(PropertyProvider.nameDecimal)
+        .root().left().right().isType(PropertyProvider.nameDecimal);
 
     testFilter.runOnETAllPrim("PropertyDecimal ge PropertyDecimal")
         .is("<<PropertyDecimal> ge <PropertyDecimal>>")
         .isBinary(BinaryOperatorKind.GE)
-        .root().left()
-        .isType(PropertyProvider.nameDecimal)
-        .root().right()
-        .isType(PropertyProvider.nameDecimal);
+        .left().isType(PropertyProvider.nameDecimal)
+        .root().right().isType(PropertyProvider.nameDecimal);
     testFilter.runOnETAllPrim("PropertyDecimal lt PropertyDecimal")
         .is("<<PropertyDecimal> lt <PropertyDecimal>>")
         .isBinary(BinaryOperatorKind.LT)
-        .root().left()
-        .isType(PropertyProvider.nameDecimal)
-        .root().right()
-        .isType(PropertyProvider.nameDecimal);
+        .left().isType(PropertyProvider.nameDecimal)
+        .root().right().isType(PropertyProvider.nameDecimal);
     testFilter.runOnETAllPrim("PropertyDecimal le PropertyDecimal")
         .is("<<PropertyDecimal> le <PropertyDecimal>>")
         .isBinary(BinaryOperatorKind.LE)
-        .root().left()
-        .isType(PropertyProvider.nameDecimal)
-        .root().right()
-        .isType(PropertyProvider.nameDecimal);
+        .left().isType(PropertyProvider.nameDecimal)
+        .root().right().isType(PropertyProvider.nameDecimal);
 
-    // Numeric promotion: Double is considered the widest type
-    testFilter.runOnETAllPrim("PropertyDecimal sub NaN")
+    // Numeric promotion: Double is considered the widest type.
+    testFilter.runOnETAllPrim("PropertyDecimal ne NaN")
         .right().isLiteral("NaN").isType(PropertyProvider.nameDouble);
-    testFilter.runOnETAllPrim("PropertyDecimal sub -INF")
+    testFilter.runOnETAllPrim("PropertyDecimal gt -INF")
         .right().isLiteral("-INF").isType(PropertyProvider.nameDouble);
-    testFilter.runOnETAllPrim("PropertyDecimal sub INF")
+    testFilter.runOnETAllPrim("PropertyDecimal lt INF")
         .right().isLiteral("INF").isType(PropertyProvider.nameDouble);
   }
 
-  // TODO
   @Test
-  @Ignore
   public void filterProperties() throws Exception {
     testFilter.runOnETAllPrim("PropertyBoolean eq true")
         .is("<<PropertyBoolean> eq <true>>")
         .isBinary(BinaryOperatorKind.EQ)
         .root().left().goPath().isPrimitiveProperty("PropertyBoolean", PropertyProvider.nameBoolean, false)
         .goUpFilterValidator()
-        .root().right().isTrue();
+        .root().right().isLiteral("true");
 
     testFilter.runOnETAllPrim("PropertyDecimal eq 1.25")
         .is("<<PropertyDecimal> eq <1.25>>")
@@ -3917,37 +3759,43 @@ public class TestFullResourcePath {
         .first().isComplex("PropertyCompMixedEnumDef")
         .n().isPrimitiveProperty("PropertyEnumString", EnumTypeProvider.nameENString, false);
 
-    testFilter.runOnETAllPrim("PropertyByte mod 0")
-        .is("<<PropertyByte> mod <0>>");
+    testFilter.runOnETAllPrim("PropertyByte mod 0 gt 0")
+        .is("<<<PropertyByte> mod <0>> gt <0>>");
+  }
 
-    testFilter.runOnETAllPrim("olingo.odata.test1.UFCRTETTwoKeyNavParamCTTwoPrim(ParameterCTTwoPrim=@ParamAlias)")
-        .is("<UFCRTETTwoKeyNavParamCTTwoPrim>")
-        .goPath()
+  @Test
+  public void filterFunctions() throws Exception {
+    testFilter.runOnETAllPrim(
+        "olingo.odata.test1.UFCRTETTwoKeyNavParamCTTwoPrim(ParameterCTTwoPrim=@ParamAlias) eq null")
+        .is("<<UFCRTETTwoKeyNavParamCTTwoPrim> eq <null>>")
+        .left().goPath()
         .first()
         .isFunction("UFCRTETTwoKeyNavParamCTTwoPrim")
         .isParameterAlias(0, "ParameterCTTwoPrim", "@ParamAlias");
 
-    testFilter.runOnETTwoKeyNav("PropertyComp/olingo.odata.test1.BFCCTPrimCompRTESTwoKeyNavParam"
-        + "(ParameterString=PropertyComp/PropertyComp/PropertyString)(PropertyInt16=1,PropertyString='2')"
-        + "/PropertyString eq 'SomeString'")
-        .is("<<PropertyComp/BFCCTPrimCompRTESTwoKeyNavParam/PropertyString> eq <'SomeString'>>")
+    testFilter.runOnETTwoKeyNav("PropertyComp/olingo.odata.test1.BFCCTPrimCompRTETTwoKeyNavParam"
+        + "(ParameterString=PropertyComp/PropertyComp/PropertyString)/PropertyString eq 'SomeString'")
+        .is("<<PropertyComp/BFCCTPrimCompRTETTwoKeyNavParam/PropertyString> eq <'SomeString'>>")
         .root().left().goPath()
-        .first()
-        .isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false)
-        .n()
-        .isFunction("BFCCTPrimCompRTESTwoKeyNavParam")
-        .isParameter(0, "ParameterString", "PropertyComp/PropertyComp/PropertyString")
-        .goParameter(0)
-        .isMember()
+        .first().isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false)
+        .n().isFunction("BFCCTPrimCompRTETTwoKeyNavParam")
+        .goParameter(0).isMember()
         .goPath()
-        .first().isComplex("PropertyComp")
-        .n().isComplex("PropertyComp")
-        .n().isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false).goUpFilterValidator()
-        .goUpToResourceValidator()
+        .first().isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false)
+        .n().isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTAllPrim, false)
+        .n().isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false)
+        .goUpFilterValidator().goUpToResourceValidator()
+        .n().isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
+
+    testFilter.runOnETTwoKeyNav("PropertyComp/olingo.odata.test1.BFCCTPrimCompRTESTwoKeyNav()"
+        + "(PropertyInt16=1,PropertyString='2')/PropertyString eq 'SomeString'")
+        .is("<<PropertyComp/BFCCTPrimCompRTESTwoKeyNav/PropertyString> eq <'SomeString'>>")
+        .root().left().goPath()
+        .first().isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false)
+        .n().isFunction("BFCCTPrimCompRTESTwoKeyNav")
         .isKeyPredicate(0, "PropertyInt16", "1")
         .isKeyPredicate(1, "PropertyString", "'2'")
-        .n()
-        .isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
+        .n().isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
 
     testFilter.runOnETTwoKeyNav("PropertyComp/olingo.odata.test1.BFCCTPrimCompRTETTwoKeyNavParam"
         + "(ParameterString=null)/PropertyString eq 'SomeString'")
@@ -3957,9 +3805,7 @@ public class TestFullResourcePath {
         .isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false)
         .n()
         .isFunction("BFCCTPrimCompRTETTwoKeyNavParam")
-        .goParameter(0)
-        .isNull()
-        .goUpToResourceValidator()
+        .isParameter(0, "ParameterString", null)
         .n()
         .isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
 
@@ -3969,59 +3815,43 @@ public class TestFullResourcePath {
         .root().left().goPath()
         .first()
         .isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true)
-        .n()
-        .isFunction("BFCESTwoKeyNavRTString");
+        .n().isFunction("BFCESTwoKeyNavRTString");
 
-    testFilter.runOnETTwoKeyNav("$it/olingo.odata.test1.BFESTwoKeyNavRTESTwoKeyNav()/PropertyString eq 'SomeString'")
-        .is("<<$it/BFESTwoKeyNavRTESTwoKeyNav/PropertyString> eq <'SomeString'>>")
+    testFilter.runOnETKeyNav("$it/olingo.odata.test1.BFCETKeyNavRTETKeyNav()/PropertyString eq 'SomeString'")
+        .is("<<$it/BFCETKeyNavRTETKeyNav/PropertyString> eq <'SomeString'>>")
         .root().left().goPath()
-        .first()
-        .isIt()
-        .n()
-        .isFunction("BFESTwoKeyNavRTESTwoKeyNav")
-        .n()
-        .isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
+        .first().isIt()
+        .n().isFunction("BFCETKeyNavRTETKeyNav")
+        .n().isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
 
-    testFilter.runOnETTwoKeyNav("olingo.odata.test1.BFESTwoKeyNavRTESTwoKeyNav()/PropertyString eq 'SomeString'")
-        .is("<<BFESTwoKeyNavRTESTwoKeyNav/PropertyString> eq <'SomeString'>>")
+    testFilter.runOnETTwoKeyNav("olingo.odata.test1.BFCESTwoKeyNavRTCTTwoPrim()/PropertyString eq 'SomeString'")
+        .is("<<BFCESTwoKeyNavRTCTTwoPrim/PropertyString> eq <'SomeString'>>")
         .root().left().goPath()
-        .first()
-        .isFunction("BFESTwoKeyNavRTESTwoKeyNav")
-        .n()
-        .isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
+        .first().isFunction("BFCESTwoKeyNavRTCTTwoPrim")
+        .n().isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
 
     testFilter.runOnETTwoKeyNav("NavPropertyETTwoKeyNavOne/olingo.odata.test1.BFCETTwoKeyNavRTETTwoKeyNav()"
         + "/PropertyComp/PropertyComp/PropertyString eq 'Walldorf'")
         .is("<<NavPropertyETTwoKeyNavOne/BFCETTwoKeyNavRTETTwoKeyNav/PropertyComp/PropertyComp/PropertyString> "
             + "eq <'Walldorf'>>")
         .root().left().goPath()
-        .first()
-        .isNavProperty("NavPropertyETTwoKeyNavOne", EntityTypeProvider.nameETTwoKeyNav, false)
-        .n()
-        .isFunction("BFCETTwoKeyNavRTETTwoKeyNav")
-        .n()
-        .isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false)
-        .n()
-        .isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTAllPrim, false)
-        .n()
-        .isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
+        .first().isNavProperty("NavPropertyETTwoKeyNavOne", EntityTypeProvider.nameETTwoKeyNav, false)
+        .n().isFunction("BFCETTwoKeyNavRTETTwoKeyNav")
+        .n().isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false)
+        .n().isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTAllPrim, false)
+        .n().isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
 
-    testFilter.runOnETTwoKeyNav("PropertyComp/olingo.odata.test1.BFCCTPrimCompRTESTwoKeyNavParam"
-        + "(ParameterString='1')"
-        + "/olingo.odata.test1.ETBaseTwoKeyNav(PropertyInt16=2,PropertyString='3')"
-        + "/PropertyString eq 'SomeString'")
-        .is("<<PropertyComp/BFCCTPrimCompRTESTwoKeyNavParam/olingo.odata.test1.ETBaseTwoKeyNav/PropertyString> "
+    testFilter.runOnETTwoKeyNav("PropertyComp/olingo.odata.test1.BFCCTPrimCompRTESTwoKeyNav()"
+        + "/olingo.odata.test1.ETBaseTwoKeyNav(PropertyInt16=2,PropertyString='3')/PropertyString eq 'SomeString'")
+        .is("<<PropertyComp/BFCCTPrimCompRTESTwoKeyNav/olingo.odata.test1.ETBaseTwoKeyNav/PropertyString> "
             + "eq <'SomeString'>>")
         .root().left().goPath()
-        .first()
-        .isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false)
-        .n()
-        .isFunction("BFCCTPrimCompRTESTwoKeyNavParam")
-        .isTypeFilterOnCollection(EntityTypeProvider.nameETBaseTwoKeyNav)
+        .first().isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false)
+        .n().isFunction("BFCCTPrimCompRTESTwoKeyNav")
+        .isTypeFilterOnEntry(EntityTypeProvider.nameETBaseTwoKeyNav)
         .isKeyPredicate(0, "PropertyInt16", "2")
         .isKeyPredicate(1, "PropertyString", "'3'")
-        .n()
-        .isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
+        .n().isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
 
     testFilter.runOnETTwoKeyNavSingle("$it/olingo.odata.test1.BFCETTwoKeyNavRTCTTwoPrim()/olingo.odata.test1.CTBase"
         + "/PropertyString eq 'SomeString'")
@@ -4071,59 +3901,58 @@ public class TestFullResourcePath {
         .root().left().goPath()
         .first()
         .isFunction("UFCRTETTwoKeyNavParam")
-        .isParameter(0, "ParameterInt16", "PropertyInt16")
-        .n()
-        .isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false)
-        .n()
-        .isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTAllPrim, false)
-        .n()
-        .isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
+        .goParameter(0)
+        .isMember().goPath().first().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false)
+        .goUpFilterValidator().goUpToResourceValidator()
+        .n().isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false)
+        .n().isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTAllPrim, false)
+        .n().isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
   }
 
   @Test
   public void methods() throws Exception {
     testFilter.runOnETKeyNav("indexof(PropertyString,'47') eq 5")
         .is("<<indexof(<PropertyString>,<'47'>)> eq <5>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.INDEXOF, 2)
         .isParameterText(0, "<PropertyString>")
         .isParameterText(1, "<'47'>");
 
     testFilter.runOnETKeyNav("tolower(PropertyString) eq 'foo'")
         .is("<<tolower(<PropertyString>)> eq <'foo'>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.TOLOWER, 1)
         .isParameterText(0, "<PropertyString>");
 
     testFilter.runOnETKeyNav("toupper(PropertyString) eq 'FOO'")
         .is("<<toupper(<PropertyString>)> eq <'FOO'>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.TOUPPER, 1)
         .isParameterText(0, "<PropertyString>");
 
     testFilter.runOnETKeyNav("trim(PropertyString) eq 'fooba'")
         .is("<<trim(<PropertyString>)> eq <'fooba'>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.TRIM, 1)
         .isParameterText(0, "<PropertyString>");
 
     testFilter.runOnETKeyNav("substring(PropertyString,4) eq 'foo'")
         .is("<<substring(<PropertyString>,<4>)> eq <'foo'>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.SUBSTRING, 2)
         .isParameterText(0, "<PropertyString>")
         .isParameterText(1, "<4>");
 
     testFilter.runOnETKeyNav("substring(PropertyString,4) eq 'foo'")
         .is("<<substring(<PropertyString>,<4>)> eq <'foo'>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.SUBSTRING, 2)
         .isParameterText(0, "<PropertyString>")
         .isParameterText(1, "<4>");
 
     testFilter.runOnETKeyNav("substring(PropertyString,2,4) eq 'foo'")
         .is("<<substring(<PropertyString>,<2>,<4>)> eq <'foo'>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.SUBSTRING, 3)
         .isParameterText(0, "<PropertyString>")
         .isParameterText(1, "<2>")
@@ -4131,355 +3960,355 @@ public class TestFullResourcePath {
 
     testFilter.runOnETKeyNav("concat(PropertyString,PropertyCompTwoPrim/PropertyString) eq 'foo'")
         .is("<<concat(<PropertyString>,<PropertyCompTwoPrim/PropertyString>)> eq <'foo'>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.CONCAT, 2)
         .isParameterText(0, "<PropertyString>")
         .isParameterText(1, "<PropertyCompTwoPrim/PropertyString>");
 
     testFilter.runOnETKeyNav("concat(PropertyString,'bar') eq 'foobar'")
         .is("<<concat(<PropertyString>,<'bar'>)> eq <'foobar'>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.CONCAT, 2)
         .isParameterText(0, "<PropertyString>")
         .isParameterText(1, "<'bar'>");
 
     testFilter.runOnETKeyNav("concat(PropertyString,'bar') eq 'foobar'")
         .is("<<concat(<PropertyString>,<'bar'>)> eq <'foobar'>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.CONCAT, 2)
         .isParameterText(0, "<PropertyString>")
         .isParameterText(1, "<'bar'>");
 
     testFilter.runOnETKeyNav("length(PropertyString) eq 32")
         .is("<<length(<PropertyString>)> eq <32>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.LENGTH, 1)
         .isParameterText(0, "<PropertyString>");
 
     testFilter.runOnETAllPrim("year(PropertyDate) eq 2013")
         .is("<<year(<PropertyDate>)> eq <2013>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.YEAR, 1)
         .isParameterText(0, "<PropertyDate>");
 
     testFilter.runOnETAllPrim("year(2013-09-25) eq 2013")
         .is("<<year(<2013-09-25>)> eq <2013>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.YEAR, 1)
         .isParameterText(0, "<2013-09-25>");
 
     testFilter.runOnETAllPrim("year(PropertyDateTimeOffset) eq 2013")
         .is("<<year(<PropertyDateTimeOffset>)> eq <2013>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.YEAR, 1)
         .isParameterText(0, "<PropertyDateTimeOffset>");
 
     testFilter.runOnETAllPrim("year(2013-09-25T12:34:56.123456789012-10:24) eq 2013")
         .is("<<year(<2013-09-25T12:34:56.123456789012-10:24>)> eq <2013>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.YEAR, 1)
         .isParameterText(0, "<2013-09-25T12:34:56.123456789012-10:24>");
 
     testFilter.runOnETAllPrim("month(PropertyDate) eq 9")
         .is("<<month(<PropertyDate>)> eq <9>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.MONTH, 1)
         .isParameterText(0, "<PropertyDate>");
 
     testFilter.runOnETAllPrim("month(2013-09-25) eq 9")
         .is("<<month(<2013-09-25>)> eq <9>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.MONTH, 1)
         .isParameterText(0, "<2013-09-25>");
 
     testFilter.runOnETAllPrim("month(PropertyDateTimeOffset) eq 9")
         .is("<<month(<PropertyDateTimeOffset>)> eq <9>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.MONTH, 1)
         .isParameterText(0, "<PropertyDateTimeOffset>");
 
     testFilter.runOnETAllPrim("month(2013-09-25T12:34:56.123456789012-10:24) eq 9")
         .is("<<month(<2013-09-25T12:34:56.123456789012-10:24>)> eq <9>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.MONTH, 1)
         .isParameterText(0, "<2013-09-25T12:34:56.123456789012-10:24>");
 
     testFilter.runOnETAllPrim("day(PropertyDate) eq 25")
         .is("<<day(<PropertyDate>)> eq <25>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.DAY, 1)
         .isParameterText(0, "<PropertyDate>");
 
     testFilter.runOnETAllPrim("day(2013-09-25) eq 25")
         .is("<<day(<2013-09-25>)> eq <25>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.DAY, 1)
         .isParameterText(0, "<2013-09-25>");
 
     testFilter.runOnETAllPrim("day(PropertyDateTimeOffset) eq 25")
         .is("<<day(<PropertyDateTimeOffset>)> eq <25>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.DAY, 1)
         .isParameterText(0, "<PropertyDateTimeOffset>");
 
     testFilter.runOnETAllPrim("day(2013-09-25T12:34:56.123456789012-10:24) eq 25")
         .is("<<day(<2013-09-25T12:34:56.123456789012-10:24>)> eq <25>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.DAY, 1)
         .isParameterText(0, "<2013-09-25T12:34:56.123456789012-10:24>");
 
     testFilter.runOnETAllPrim("hour(PropertyDateTimeOffset) eq 2")
         .is("<<hour(<PropertyDateTimeOffset>)> eq <2>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.HOUR, 1)
         .isParameterText(0, "<PropertyDateTimeOffset>");
 
     testFilter.runOnETAllPrim("hour(PropertyDateTimeOffset) eq 2")
         .is("<<hour(<PropertyDateTimeOffset>)> eq <2>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.HOUR, 1)
         .isParameterText(0, "<PropertyDateTimeOffset>");
 
     testFilter.runOnETAllPrim("hour(2013-09-25T12:34:56.123456789012-10:24) eq 2")
         .is("<<hour(<2013-09-25T12:34:56.123456789012-10:24>)> eq <2>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.HOUR, 1)
         .isParameterText(0, "<2013-09-25T12:34:56.123456789012-10:24>");
 
     testFilter.runOnETAllPrim("hour(PropertyTimeOfDay) eq 2")
         .is("<<hour(<PropertyTimeOfDay>)> eq <2>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.HOUR, 1)
         .isParameterText(0, "<PropertyTimeOfDay>");
 
     testFilter.runOnETAllPrim("hour(12:34:55.123456789012) eq 12")
         .is("<<hour(<12:34:55.123456789012>)> eq <12>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.HOUR, 1)
         .isParameterText(0, "<12:34:55.123456789012>");
 
     testFilter.runOnETAllPrim("minute(PropertyDateTimeOffset) eq 34")
         .is("<<minute(<PropertyDateTimeOffset>)> eq <34>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.MINUTE, 1)
         .isParameterText(0, "<PropertyDateTimeOffset>");
 
     testFilter.runOnETAllPrim("minute(2013-09-25T12:34:56.123456789012-10:24) eq 34")
         .is("<<minute(<2013-09-25T12:34:56.123456789012-10:24>)> eq <34>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.MINUTE, 1)
         .isParameterText(0, "<2013-09-25T12:34:56.123456789012-10:24>");
 
     testFilter.runOnETAllPrim("minute(PropertyTimeOfDay) eq 34")
         .is("<<minute(<PropertyTimeOfDay>)> eq <34>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.MINUTE, 1)
         .isParameterText(0, "<PropertyTimeOfDay>");
 
     testFilter.runOnETAllPrim("minute(12:34:55.123456789012) eq 34")
         .is("<<minute(<12:34:55.123456789012>)> eq <34>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.MINUTE, 1)
         .isParameterText(0, "<12:34:55.123456789012>");
 
     testFilter.runOnETAllPrim("second(PropertyDateTimeOffset) eq 56")
         .is("<<second(<PropertyDateTimeOffset>)> eq <56>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.SECOND, 1)
         .isParameterText(0, "<PropertyDateTimeOffset>");
 
     testFilter.runOnETAllPrim("second(2013-09-25T12:34:56.123456789012-10:24) eq 56")
         .is("<<second(<2013-09-25T12:34:56.123456789012-10:24>)> eq <56>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.SECOND, 1)
         .isParameterText(0, "<2013-09-25T12:34:56.123456789012-10:24>");
 
     testFilter.runOnETAllPrim("second(PropertyTimeOfDay) eq 56")
         .is("<<second(<PropertyTimeOfDay>)> eq <56>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.SECOND, 1)
         .isParameterText(0, "<PropertyTimeOfDay>");
 
     testFilter.runOnETAllPrim("second(12:34:55.123456789012) eq 56")
         .is("<<second(<12:34:55.123456789012>)> eq <56>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.SECOND, 1)
         .isParameterText(0, "<12:34:55.123456789012>");
 
     testFilter.runOnETAllPrim("fractionalseconds(PropertyDateTimeOffset) eq 123456789012")
         .is("<<fractionalseconds(<PropertyDateTimeOffset>)> eq <123456789012>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.FRACTIONALSECONDS, 1)
         .isParameterText(0, "<PropertyDateTimeOffset>");
 
     testFilter.runOnETAllPrim("fractionalseconds(2013-09-25T12:34:56.123456789012-10:24) eq 123456789012")
         .is("<<fractionalseconds(<2013-09-25T12:34:56.123456789012-10:24>)> eq <123456789012>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.FRACTIONALSECONDS, 1)
         .isParameterText(0, "<2013-09-25T12:34:56.123456789012-10:24>");
 
     testFilter.runOnETAllPrim("fractionalseconds(PropertyTimeOfDay) eq 123456789012")
         .is("<<fractionalseconds(<PropertyTimeOfDay>)> eq <123456789012>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.FRACTIONALSECONDS, 1)
         .isParameterText(0, "<PropertyTimeOfDay>");
 
     testFilter.runOnETAllPrim("fractionalseconds(12:34:55.123456789012) eq 123456789012")
         .is("<<fractionalseconds(<12:34:55.123456789012>)> eq <123456789012>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.FRACTIONALSECONDS, 1)
         .isParameterText(0, "<12:34:55.123456789012>");
 
     testFilter.runOnETAllPrim("totalseconds(PropertyDuration) eq 4711")
         .is("<<totalseconds(<PropertyDuration>)> eq <4711>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.TOTALSECONDS, 1)
         .isParameterText(0, "<PropertyDuration>");
 
     testFilter.runOnETAllPrim("totalseconds(duration'P10DT5H34M21.123456789012S') eq 4711")
         .is("<<totalseconds(<duration'P10DT5H34M21.123456789012S'>)> eq <4711>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.TOTALSECONDS, 1)
         .isParameterText(0, "<duration'P10DT5H34M21.123456789012S'>");
 
     testFilter.runOnETAllPrim("date(PropertyDateTimeOffset) eq 2013-09-25")
         .is("<<date(<PropertyDateTimeOffset>)> eq <2013-09-25>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.DATE, 1)
         .isParameterText(0, "<PropertyDateTimeOffset>");
 
     testFilter.runOnETAllPrim("date(2013-09-25T12:34:56.123456789012-10:24) eq 2013-09-25")
         .is("<<date(<2013-09-25T12:34:56.123456789012-10:24>)> eq <2013-09-25>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.DATE, 1)
         .isParameterText(0, "<2013-09-25T12:34:56.123456789012-10:24>");
 
     testFilter.runOnETAllPrim("time(PropertyDateTimeOffset) eq 12:34:55.123456789012")
         .is("<<time(<PropertyDateTimeOffset>)> eq <12:34:55.123456789012>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.TIME, 1)
         .isParameterText(0, "<PropertyDateTimeOffset>");
 
     testFilter.runOnETAllPrim("time(2013-09-25T12:34:56.123456789012-10:24) eq 12:34:55.123456789012")
         .is("<<time(<2013-09-25T12:34:56.123456789012-10:24>)> eq <12:34:55.123456789012>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.TIME, 1)
         .isParameterText(0, "<2013-09-25T12:34:56.123456789012-10:24>");
 
     testFilter.runOnETAllPrim("round(PropertyDouble) eq 17")
         .is("<<round(<PropertyDouble>)> eq <17>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.ROUND, 1)
         .isParameterText(0, "<PropertyDouble>");
 
     testFilter.runOnETAllPrim("round(17.45e1) eq 17")
         .is("<<round(<17.45e1>)> eq <17>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.ROUND, 1)
         .isParameterText(0, "<17.45e1>");
 
     testFilter.runOnETAllPrim("round(PropertyDecimal) eq 17")
         .is("<<round(<PropertyDecimal>)> eq <17>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.ROUND, 1)
         .isParameterText(0, "<PropertyDecimal>");
 
     testFilter.runOnETAllPrim("round(17.45) eq 17")
         .is("<<round(<17.45>)> eq <17>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.ROUND, 1)
         .isParameterText(0, "<17.45>");
 
     testFilter.runOnETAllPrim("floor(PropertyDouble) eq 17")
         .is("<<floor(<PropertyDouble>)> eq <17>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.FLOOR, 1)
         .isParameterText(0, "<PropertyDouble>");
 
     testFilter.runOnETAllPrim("floor(17.45e1) eq 17")
         .is("<<floor(<17.45e1>)> eq <17>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.FLOOR, 1)
         .isParameterText(0, "<17.45e1>");
 
     testFilter.runOnETAllPrim("floor(PropertyDecimal) eq 17")
         .is("<<floor(<PropertyDecimal>)> eq <17>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.FLOOR, 1)
         .isParameterText(0, "<PropertyDecimal>");
 
     testFilter.runOnETAllPrim("floor(17.45) eq 17")
         .is("<<floor(<17.45>)> eq <17>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.FLOOR, 1)
         .isParameterText(0, "<17.45>");
 
     testFilter.runOnETAllPrim("ceiling(PropertyDouble) eq 18")
         .is("<<ceiling(<PropertyDouble>)> eq <18>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.CEILING, 1)
         .isParameterText(0, "<PropertyDouble>");
 
     testFilter.runOnETAllPrim("ceiling(17.55e1) eq 18")
         .is("<<ceiling(<17.55e1>)> eq <18>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.CEILING, 1)
         .isParameterText(0, "<17.55e1>");
 
     testFilter.runOnETAllPrim("ceiling(PropertyDecimal) eq 18")
         .is("<<ceiling(<PropertyDecimal>)> eq <18>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.CEILING, 1)
         .isParameterText(0, "<PropertyDecimal>");
 
     testFilter.runOnETAllPrim("ceiling(17.55) eq 18")
         .is("<<ceiling(<17.55>)> eq <18>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.CEILING, 1)
         .isParameterText(0, "<17.55>");
 
     testFilter.runOnETAllPrim("totaloffsetminutes(PropertyDateTimeOffset) eq 4711")
         .is("<<totaloffsetminutes(<PropertyDateTimeOffset>)> eq <4711>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.TOTALOFFSETMINUTES, 1)
         .isParameterText(0, "<PropertyDateTimeOffset>");
 
     testFilter.runOnETAllPrim("totaloffsetminutes(2013-09-25T12:34:56.123456789012-10:24) eq 4711")
         .is("<<totaloffsetminutes(<2013-09-25T12:34:56.123456789012-10:24>)> eq <4711>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.TOTALOFFSETMINUTES, 1)
         .isParameterText(0, "<2013-09-25T12:34:56.123456789012-10:24>");
 
-    testFilter.runOnETAllPrim("mindatetime()")
-        .is("<mindatetime()>")
-        .isMethod(MethodKind.MINDATETIME, 0);
+    testFilter.runOnETAllPrim("mindatetime() ne null")
+        .is("<<mindatetime()> ne <null>>")
+        .left().isMethod(MethodKind.MINDATETIME, 0);
 
     testFilter.runOnETAllPrim("mindatetime() eq 2013-09-25T12:34:56.123456789012-10:24")
         .is("<<mindatetime()> eq <2013-09-25T12:34:56.123456789012-10:24>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.MINDATETIME, 0);
 
-    testFilter.runOnETAllPrim("maxdatetime()")
-        .is("<maxdatetime()>")
-        .isMethod(MethodKind.MAXDATETIME, 0);
+    testFilter.runOnETAllPrim("maxdatetime() ne null")
+        .is("<<maxdatetime()> ne <null>>")
+        .left().isMethod(MethodKind.MAXDATETIME, 0);
 
     testFilter.runOnETAllPrim("maxdatetime() eq 2013-09-25T12:34:56.123456789012-10:24")
         .is("<<maxdatetime()> eq <2013-09-25T12:34:56.123456789012-10:24>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.MAXDATETIME, 0);
 
-    testFilter.runOnETAllPrim("now()")
-        .is("<now()>")
-        .isMethod(MethodKind.NOW, 0);
+    testFilter.runOnETAllPrim("now() ne null")
+        .is("<<now()> ne <null>>")
+        .left().isMethod(MethodKind.NOW, 0);
 
     testFilter.runOnETAllPrim("now() eq 2013-09-25T12:34:56.123456789012-10:24")
         .is("<<now()> eq <2013-09-25T12:34:56.123456789012-10:24>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.NOW, 0);
 
     testFilter.runOnETTwoKeyNav("$it/PropertyString eq 'SomeString'")
         .is("<<$it/PropertyString> eq <'SomeString'>>")
-        .root().left()
+        .left()
         .goPath()
         .first().isUriPathInfoKind(UriResourceKind.it)
         .isType(EntityTypeProvider.nameETTwoKeyNav, false)
@@ -4487,7 +4316,7 @@ public class TestFullResourcePath {
 
     testFilter.runOnCTTwoPrim("$it/PropertyString eq 'SomeString'")
         .is("<<$it/PropertyString> eq <'SomeString'>>")
-        .root().left()
+        .left()
         .goPath()
         .first().isUriPathInfoKind(UriResourceKind.it)
         .isType(ComplexTypeProvider.nameCTTwoPrim, false)
@@ -4495,7 +4324,7 @@ public class TestFullResourcePath {
 
     testFilter.runOnString("$it eq 'Walldorf'")
         .is("<<$it> eq <'Walldorf'>>")
-        .root().left()
+        .left()
         .goPath()
         .first().isUriPathInfoKind(UriResourceKind.it)
         .isType(PropertyProvider.nameString, false);
@@ -4512,7 +4341,7 @@ public class TestFullResourcePath {
 
     testFilter.runOnString("endswith($it,'sap.com') eq false")
         .is("<<endswith(<$it>,<'sap.com'>)> eq <false>>")
-        .root().left()
+        .left()
         .isMethod(MethodKind.ENDSWITH, 2)
         .isParameterText(0, "<$it>")
         .isParameterText(1, "<'sap.com'>")
@@ -4644,44 +4473,42 @@ public class TestFullResourcePath {
         .n().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
   }
 
-  // TODO: Implement cast method.
   @Test
   public void castMethod() throws Exception {
-    testFilter.runOnETKeyNav("cast(olingo.odata.test1.ETBaseTwoKeyNav)")
-        .is("<cast(<olingo.odata.test1.ETBaseTwoKeyNav>)>")
-        .root()
+    testFilter.runOnETKeyNav("cast(olingo.odata.test1.ETBaseTwoKeyNav) ne null")
+        .is("<<cast(<olingo.odata.test1.ETBaseTwoKeyNav>)> ne <n

<TRUNCATED>

[3/6] olingo-odata4 git commit: [OLINGO-834] $expand parser in Java + clean-up

Posted by ch...@apache.org.
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/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 f505a21..3b673a6 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
@@ -40,6 +40,15 @@ public class UriTokenizer {
     ROOT,
     IT,
 
+    EXPAND,
+    FILTER,
+    LEVELS,
+    ORDERBY,
+    SEARCH,
+    SELECT,
+    SKIP,
+    TOP,
+
     ANY,
     ALL,
 
@@ -53,8 +62,9 @@ public class UriTokenizer {
     EQ,
     STAR,
     PLUS,
-    MINUS,
+
     NULL,
+    MAX,
 
     // variable-value tokens (convention: mixed case)
     ODataIdentifier,
@@ -76,6 +86,13 @@ public class UriTokenizer {
 
     jsonArrayOrObject,
 
+    Word,
+    Phrase,
+
+    OrOperatorSearch,
+    AndOperatorSearch,
+    NotOperatorSearch,
+
     OrOperator,
     AndOperator,
     EqualsOperator,
@@ -90,6 +107,7 @@ public class UriTokenizer {
     MulOperator,
     DivOperator,
     ModOperator,
+    MinusOperator,
     NotOperator,
 
     CastMethod,
@@ -161,6 +179,10 @@ public class UriTokenizer {
     boolean found = false;
     final int previousIndex = index;
     switch (allowedTokenKind) {
+    case EOF:
+      found = index >= parseString.length();
+      break;
+
     // Constants
     case REF:
       found = nextConstant("$ref");
@@ -181,6 +203,31 @@ public class UriTokenizer {
       found = nextConstant("$it");
       break;
 
+    case EXPAND:
+      found = nextConstant("$expand");
+      break;
+    case FILTER:
+      found = nextConstant("$filter");
+      break;
+    case LEVELS:
+      found = nextConstant("$levels");
+      break;
+    case ORDERBY:
+      found = nextConstant("$orderby");
+      break;
+    case SEARCH:
+      found = nextConstant("$search");
+      break;
+    case SELECT:
+      found = nextConstant("$select");
+      break;
+    case SKIP:
+      found = nextConstant("$skip");
+      break;
+    case TOP:
+      found = nextConstant("$top");
+      break;
+
     case ANY:
       found = nextConstant("any");
       break;
@@ -218,14 +265,12 @@ public class UriTokenizer {
     case PLUS:
       found = nextCharacter('+');
       break;
-    case MINUS:
-      found = nextMinus();
-      break;
+
     case NULL:
       found = nextConstant("null");
       break;
-    case EOF:
-      found = index >= parseString.length();
+    case MAX:
+      found = nextConstant("max");
       break;
 
     // Identifiers
@@ -282,6 +327,25 @@ public class UriTokenizer {
       found = nextJsonArrayOrObject();
       break;
 
+    // Search
+    case Word:
+      found = nextWord();
+      break;
+    case Phrase:
+      found = nextPhrase();
+      break;
+
+    // Operators in Search Expressions
+    case OrOperatorSearch:
+      found = nextBinaryOperator("OR");
+      break;
+    case AndOperatorSearch:
+      found = nextAndOperatorSearch();
+      break;
+    case NotOperatorSearch:
+      found = nextUnaryOperator("NOT");
+      break;
+
     // Operators
     case OrOperator:
       found = nextBinaryOperator("or");
@@ -325,8 +389,12 @@ public class UriTokenizer {
     case ModOperator:
       found = nextBinaryOperator("mod");
       break;
+    case MinusOperator:
+      // To avoid unnecessary minus operators for negative numbers, we have to check what follows the minus sign.
+      found = nextCharacter('-') && !nextDigit() && !nextConstant("INF");
+      break;
     case NotOperator:
-      found = nextConstant("not") && nextWhitespace();
+      found = nextUnaryOperator("not");
       break;
 
     // Methods
@@ -444,27 +512,6 @@ public class UriTokenizer {
     return found;
   }
 
-  private boolean nextMinus() {
-    if(parseString.startsWith("-", index)) {
-      final int lastGoodIndex = index;
-      
-      if(nextDoubleValue()) {
-        index = lastGoodIndex;
-        return false;
-      } else if(nextDecimalValue()) {
-        index = lastGoodIndex;
-        return false;
-      } else if(nextIntegerValue(true)) {
-        index = lastGoodIndex;
-        return false;
-      } else {
-        index++;
-        return true;
-      }
-    }
-    return false;
-  }
-
   /**
    * Moves past the given string constant if found; otherwise leaves the index unchanged.
    * @return whether the constant has been found at the current index
@@ -569,34 +616,6 @@ public class UriTokenizer {
     }
     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 (required) whitespace and the given suffix name if found;
-   * otherwise leaves the index unchanged.
-   * @return whether the suffix has been found at the current index
-   */
-  private boolean nextSuffix(final String suffixName) {
-    return nextWhitespace() && nextConstant(suffixName);
-  }
-
   /**
    * Moves past an OData identifier if found; otherwise leaves the index unchanged.
    * @return whether an OData identifier has been found at the current index
@@ -650,6 +669,38 @@ public class UriTokenizer {
     }
   }
 
+  /**
+   * Moves past the given whitespace-surrounded operator constant if found.
+   * @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 whitespace-suffixed operator constant if found.
+   * @return whether the operator has been found at the current index
+   */
+  private boolean nextUnaryOperator(final String operator) {
+    return nextConstant(operator) && nextWhitespace();
+  }
+
+  /**
+   * Moves past the given method name and its immediately following opening parenthesis if found.
+   * @return whether the method has been found at the current index
+   */
+  private boolean nextMethod(final String methodName) {
+    return nextConstant(methodName) && nextCharacter('(');
+  }
+
+  /**
+   * Moves past (required) whitespace and the given suffix name if found.
+   * @return whether the suffix has been found at the current index
+   */
+  private boolean nextSuffix(final String suffixName) {
+    return nextWhitespace() && nextConstant(suffixName);
+  }
+
   private boolean nextParameterAliasName() {
     return nextCharacter('@') && nextODataIdentifier();
   }
@@ -978,4 +1029,52 @@ public class UriTokenizer {
       return false;
     }
   }
+
+  private boolean nextAndOperatorSearch() {
+    if (nextWhitespace()) {
+      final int lastGoodIndex = index;
+      if (nextUnaryOperator("OR")) {
+        return false;
+      } else if (!(nextUnaryOperator("AND"))) {
+        index = lastGoodIndex;
+      }
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  private boolean nextWord() {
+    int count = 0;
+    while (index < parseString.length()) {
+      final int code = parseString.codePointAt(index);
+      if (Character.isUnicodeIdentifierStart(code)) {
+        count++;
+        // Unicode characters outside of the Basic Multilingual Plane are represented as two Java characters.
+        index += Character.isSupplementaryCodePoint(code) ? 2 : 1;
+      } else {
+        break;
+      }
+    }
+    final String word = parseString.substring(index - count, index);
+    return count > 0 && !("OR".equals(word) || "AND".equals(word) || "NOT".equals(word));
+  }
+
+  private boolean nextPhrase() {
+    if (nextCharacter('"')) {
+      do {
+        if (nextCharacter('\\')) {
+          if (!(nextCharacter('\\') || nextCharacter('"'))) {
+            return false;
+          }
+        } else if (nextCharacter('"')) {
+          return true;
+        } else {
+          index++;
+        }
+      } while (index < parseString.length());
+      return false;
+    }
+    return false;
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/AliasImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/AliasImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/AliasImpl.java
index c7d7c20..8c8dab9 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/AliasImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/AliasImpl.java
@@ -41,4 +41,8 @@ public class AliasImpl implements Alias {
     return visitor.visitAlias(parameterName);
   }
 
+  @Override
+  public String toString() {
+    return parameterName;
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/EnumerationImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/EnumerationImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/EnumerationImpl.java
index 256b8d1..a238104 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/EnumerationImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/EnumerationImpl.java
@@ -53,4 +53,10 @@ public class EnumerationImpl implements Enumeration {
   public <T> T accept(final ExpressionVisitor<T> visitor) throws ExpressionVisitException, ODataApplicationException {
     return visitor.visitEnum(type, values);
   }
+
+  @Override
+  public String toString() {
+    return type == null ? null :
+      type.getFullQualifiedName().getFullQualifiedNameAsString() + getValues();
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/LambdaRefImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/LambdaRefImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/LambdaRefImpl.java
index 824943a..16232b8 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/LambdaRefImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/LambdaRefImpl.java
@@ -40,4 +40,9 @@ public class LambdaRefImpl implements LambdaRef {
   public <T> T accept(final ExpressionVisitor<T> visitor) throws ExpressionVisitException, ODataApplicationException {
     return visitor.visitLambdaReference(variableText);
   }
+
+  @Override
+  public String toString() {
+    return variableText;
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/TypeLiteralImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/TypeLiteralImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/TypeLiteralImpl.java
index 336c203..6a2a1c6 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/TypeLiteralImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/TypeLiteralImpl.java
@@ -41,4 +41,9 @@ public class TypeLiteralImpl implements TypeLiteral {
   public <T> T accept(final ExpressionVisitor<T> visitor) throws ExpressionVisitException, ODataApplicationException {
     return visitor.visitTypeLiteral(type);
   }
+
+  @Override
+  public String toString() {
+    return type == null ? null : type.getFullQualifiedName().getFullQualifiedNameAsString();
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties b/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties
index 0b43f70..e178fed 100644
--- a/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties
+++ b/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties
@@ -81,11 +81,11 @@ UriParserSemanticException.COMPLEX_PROPERTY_OF_ENTITY_TYPE_EXPECTED=A complex pr
 UriParserSemanticException.NOT_FOR_ENTITY_TYPE=Not allowed for entity type.
 UriParserSemanticException.PREVIOUS_PART_TYPED=The previous part is typed.
 UriParserSemanticException.RESOURCE_NOT_FOUND=Cannot find EntitySet, Singleton, ActionImport or FunctionImport with name '%1$s'.
-UriParserSemanticException.NOT_IMPLEMENTED=%1$s is not implemented!
-UriParserSemanticException.NAMESPACE_NOT_ALLOWED_AT_FIRST_ELEMENT=Namespace is not allowed for Entity Sets, Singeltons, Action Imports and Function Imports. Found '%1$s'.
-UriParserSemanticException.COMPLEX_PARAMETER_IN_RESOURCE_PATH=Complex parameters must not appear in resource path segments. Found: '%1$s'.
+UriParserSemanticException.NOT_IMPLEMENTED='%1$s' is not implemented!
+UriParserSemanticException.NAMESPACE_NOT_ALLOWED_AT_FIRST_ELEMENT=Namespace is not allowed for Entity Sets, Singletons, Action Imports and Function Imports; found '%1$s'.
+UriParserSemanticException.COMPLEX_PARAMETER_IN_RESOURCE_PATH=Complex parameters must not appear in resource path segments; found: '%1$s'.
 UriParserSemanticException.FUNCTION_IMPORT_NOT_ALLOWED=Function Imports are not allowed in $filter or $orderby. Found: '%1$s'.
-UriParserSemanticException.TYPES_NOT_COMPATIBLE=Types are not compatible. Left type: '%1$s', right type: '%1$s'.
+UriParserSemanticException.TYPES_NOT_COMPATIBLE=The types '%1$s' and '%2$s' are not compatible.
 
 UriValidationException.UNSUPPORTED_QUERY_OPTION=The query option '%1$s' is not supported.
 UriValidationException.UNSUPPORTED_URI_KIND=The URI kind '%1$s' is not supported.

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/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 4ab7fce..94d5373 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
@@ -22,9 +22,11 @@ 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 static org.mockito.Mockito.mock;
 
 import java.util.Locale;
 
+import org.apache.olingo.commons.api.edm.Edm;
 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;
@@ -260,9 +262,17 @@ public class ExpressionParserTest {
     expression = parseMethod(TokenKind.SubstringMethod, "'abc'", "1");
     assertEquals("{substring ['abc', 1]}", expression.toString());
 
+    assertEquals("{cast [Edm.SByte]}", parseMethod(TokenKind.CastMethod, "Edm.SByte").toString());
+    assertEquals("{cast [42, Edm.SByte]}", parseMethod(TokenKind.CastMethod, "42", "Edm.SByte").toString());
+
+    assertEquals("{isof [Edm.SByte]}", parseMethod(TokenKind.IsofMethod, "Edm.SByte").toString());
+    assertEquals("{isof [42, Edm.SByte]}", parseMethod(TokenKind.IsofMethod, "42", "Edm.SByte").toString());
+
     wrongExpression("substring('abc')");
     wrongExpression("substring('abc',1,2,3)");
     wrongExpression("substring(1,2)");
+    wrongExpression("cast(1,2)");
+    wrongExpression("isof(Edm.Int16,2)");
   }
 
   private Expression parseMethod(TokenKind kind, String... parameters)
@@ -288,7 +298,7 @@ public class ExpressionParserTest {
   private Expression parseExpression(final String expressionString)
       throws UriParserException, UriValidationException {
     UriTokenizer tokenizer = new UriTokenizer(expressionString);
-    Expression expression = new ExpressionParser(null, odata).parse(tokenizer, null, null);
+    Expression expression = new ExpressionParser(mock(Edm.class), odata).parse(tokenizer, null, null);
     assertNotNull(expression);
     assertTrue(tokenizer.next(TokenKind.EOF));
     return expression;
@@ -296,7 +306,7 @@ public class ExpressionParserTest {
 
   private void wrongExpression(final String expressionString) {
     try {
-      new ExpressionParser(null, odata).parse(new UriTokenizer(expressionString), null, null);
+      new ExpressionParser(mock(Edm.class), odata).parse(new UriTokenizer(expressionString), null, null);
       fail("Expected exception not thrown.");
     } catch (final UriParserException e) {
       assertNotNull(e);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/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 af45e80..e130457 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
@@ -76,7 +76,7 @@ public class UriTokenizerTest {
     assertTrue(tokenizer.next(TokenKind.STAR));
     assertTrue(tokenizer.next(TokenKind.SLASH));
     assertTrue(tokenizer.next(TokenKind.PLUS));
-    assertTrue(tokenizer.next(TokenKind.MINUS));
+    assertTrue(tokenizer.next(TokenKind.MinusOperator));
     assertTrue(tokenizer.next(TokenKind.EOF));
 
     tokenizer = new UriTokenizer("any(a:true) or all(b:false)");
@@ -97,6 +97,45 @@ public class UriTokenizerTest {
   }
 
   @Test
+  public void systemQueryOptions() {
+    UriTokenizer tokenizer = new UriTokenizer("$expand=*;$filter=true;$levels=max;$orderby=false");
+    assertTrue(tokenizer.next(TokenKind.EXPAND));
+    assertTrue(tokenizer.next(TokenKind.EQ));
+    assertTrue(tokenizer.next(TokenKind.STAR));
+    assertTrue(tokenizer.next(TokenKind.SEMI));
+    assertTrue(tokenizer.next(TokenKind.FILTER));
+    assertTrue(tokenizer.next(TokenKind.EQ));
+    assertTrue(tokenizer.next(TokenKind.BooleanValue));
+    assertTrue(tokenizer.next(TokenKind.SEMI));
+    assertTrue(tokenizer.next(TokenKind.LEVELS));
+    assertTrue(tokenizer.next(TokenKind.EQ));
+    assertTrue(tokenizer.next(TokenKind.MAX));
+    assertTrue(tokenizer.next(TokenKind.SEMI));
+    assertTrue(tokenizer.next(TokenKind.ORDERBY));
+    assertTrue(tokenizer.next(TokenKind.EQ));
+    assertTrue(tokenizer.next(TokenKind.BooleanValue));
+    assertTrue(tokenizer.next(TokenKind.EOF));
+
+    tokenizer = new UriTokenizer("$search=A;$select=*;$skip=1;$top=2");
+    assertTrue(tokenizer.next(TokenKind.SEARCH));
+    assertTrue(tokenizer.next(TokenKind.EQ));
+    assertTrue(tokenizer.next(TokenKind.ODataIdentifier));
+    assertTrue(tokenizer.next(TokenKind.SEMI));
+    assertTrue(tokenizer.next(TokenKind.SELECT));
+    assertTrue(tokenizer.next(TokenKind.EQ));
+    assertTrue(tokenizer.next(TokenKind.STAR));
+    assertTrue(tokenizer.next(TokenKind.SEMI));
+    assertTrue(tokenizer.next(TokenKind.SKIP));
+    assertTrue(tokenizer.next(TokenKind.EQ));
+    assertTrue(tokenizer.next(TokenKind.IntegerValue));
+    assertTrue(tokenizer.next(TokenKind.SEMI));
+    assertTrue(tokenizer.next(TokenKind.TOP));
+    assertTrue(tokenizer.next(TokenKind.EQ));
+    assertTrue(tokenizer.next(TokenKind.IntegerValue));
+    assertTrue(tokenizer.next(TokenKind.EOF));
+  }
+
+  @Test
   public void identifier() {
     assertTrue(new UriTokenizer("name").next(TokenKind.ODataIdentifier));
     assertTrue(new UriTokenizer("_name").next(TokenKind.ODataIdentifier));
@@ -390,11 +429,11 @@ public class UriTokenizerTest {
     assertTrue(tokenizer.next(TokenKind.IntegerValue));
     assertTrue(tokenizer.next(TokenKind.EOF));
 
-    tokenizer = new UriTokenizer("1ne 2");
+    tokenizer = new UriTokenizer("-1ne 2");
     assertTrue(tokenizer.next(TokenKind.IntegerValue));
     assertFalse(tokenizer.next(TokenKind.NotEqualsOperator));
 
-    tokenizer = new UriTokenizer("1 ne2");
+    tokenizer = new UriTokenizer("1 ne-2");
     assertTrue(tokenizer.next(TokenKind.IntegerValue));
     assertFalse(tokenizer.next(TokenKind.NotEqualsOperator));
 
@@ -404,6 +443,11 @@ public class UriTokenizerTest {
     assertTrue(tokenizer.next(TokenKind.IntegerValue));
     assertTrue(tokenizer.next(TokenKind.EOF));
 
+    assertTrue(new UriTokenizer("-x").next(TokenKind.MinusOperator));
+    assertFalse(new UriTokenizer("-1").next(TokenKind.MinusOperator));
+    assertFalse(new UriTokenizer("-INF").next(TokenKind.MinusOperator));
+    assertFalse(new UriTokenizer("+").next(TokenKind.MinusOperator));
+
     assertFalse(new UriTokenizer("nottrue").next(TokenKind.NotOperator));
     assertFalse(new UriTokenizer("no true").next(TokenKind.NotOperator));
 
@@ -484,6 +528,38 @@ public class UriTokenizerTest {
     wrongToken(TokenKind.DescSuffix, " desc", 'D');
   }
 
+  @Test
+  public void search() {
+    UriTokenizer tokenizer = new UriTokenizer("a AND b OR NOT \"c\" d");
+    assertTrue(tokenizer.next(TokenKind.Word));
+    assertTrue(tokenizer.next(TokenKind.AndOperatorSearch));
+    assertTrue(tokenizer.next(TokenKind.Word));
+    assertFalse(tokenizer.next(TokenKind.AndOperatorSearch));
+    assertTrue(tokenizer.next(TokenKind.OrOperatorSearch));
+    assertTrue(tokenizer.next(TokenKind.NotOperatorSearch));
+    assertTrue(tokenizer.next(TokenKind.Phrase));
+    assertTrue(tokenizer.next(TokenKind.AndOperatorSearch));
+    assertTrue(tokenizer.next(TokenKind.Word));
+    assertFalse(tokenizer.next(TokenKind.AndOperatorSearch));
+    assertFalse(tokenizer.next(TokenKind.Word));
+    assertFalse(tokenizer.next(TokenKind.Phrase));
+    assertTrue(tokenizer.next(TokenKind.EOF));
+
+    assertTrue(new UriTokenizer("\"a\\\\x\\\"\"").next(TokenKind.Phrase));
+    assertFalse(new UriTokenizer("\"a\\\"").next(TokenKind.Phrase));
+    assertFalse(new UriTokenizer("\"a\\x\"").next(TokenKind.Phrase));
+    wrongToken(TokenKind.Phrase, "\"a\"", '\\');
+
+    final String outsideBmpLetter = String.valueOf(Character.toChars(0x10330));
+    assertTrue(new UriTokenizer("\"" + outsideBmpLetter + "\"").next(TokenKind.Phrase));
+
+    assertTrue(new UriTokenizer(outsideBmpLetter).next(TokenKind.Word));
+    assertFalse(new UriTokenizer("1").next(TokenKind.Word));
+    assertFalse(new UriTokenizer("AND").next(TokenKind.Word));
+    assertFalse(new UriTokenizer("OR").next(TokenKind.Word));
+    assertFalse(new UriTokenizer("NOT").next(TokenKind.Word));
+  }
+
   private void wrongToken(final TokenKind kind, final String value, final char disturbCharacter) {
     assertFalse(new UriTokenizer(disturbCharacter + value).next(kind));
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchParserAndTokenizerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchParserAndTokenizerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchParserAndTokenizerTest.java
index e028cfe..f3e50a2 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchParserAndTokenizerTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchParserAndTokenizerTest.java
@@ -35,7 +35,7 @@ public class SearchParserAndTokenizerTest {
     assertQuery("a AND b AND c").resultsIn("{{'a' AND 'b'} AND 'c'}");
     assertQuery("a OR b").resultsIn("{'a' OR 'b'}");
     assertQuery("a OR b OR c").resultsIn("{{'a' OR 'b'} OR 'c'}");
-    
+
     assertQuery("NOT a NOT b").resultsIn("{{NOT 'a'} AND {NOT 'b'}}");
     assertQuery("NOT a AND NOT b").resultsIn("{{NOT 'a'} AND {NOT 'b'}}");
     assertQuery("NOT a OR NOT b").resultsIn("{{NOT 'a'} OR {NOT 'b'}}");
@@ -59,16 +59,16 @@ public class SearchParserAndTokenizerTest {
     assertQuery("a AND (b OR c)").resultsIn("{'a' AND {'b' OR 'c'}}");
     assertQuery("(a OR b) AND NOT c").resultsIn("{{'a' OR 'b'} AND {NOT 'c'}}");
     assertQuery("(a OR B) AND (c OR d AND NOT e OR (f))")
-            .resultsIn("{{'a' OR 'B'} AND {{'c' OR {'d' AND {NOT 'e'}}} OR 'f'}}");
+        .resultsIn("{{'a' OR 'B'} AND {{'c' OR {'d' AND {NOT 'e'}}} OR 'f'}}");
     assertQuery("(a OR B) (c OR d NOT e OR (f))")
-      .resultsIn("{{'a' OR 'B'} AND {{'c' OR {'d' AND {NOT 'e'}}} OR 'f'}}");
+        .resultsIn("{{'a' OR 'B'} AND {{'c' OR {'d' AND {NOT 'e'}}} OR 'f'}}");
     assertQuery("((((a))))").resultsIn("'a'");
     assertQuery("((((a)))) ((((a))))").resultsIn("{'a' AND 'a'}");
     assertQuery("((((a)))) OR ((((a))))").resultsIn("{'a' OR 'a'}");
     assertQuery("((((((a)))) ((((c))) OR (((C)))) ((((a))))))").resultsIn("{{'a' AND {'c' OR 'C'}} AND 'a'}");
     assertQuery("((((\"a\")))) OR ((((\"a\"))))").resultsIn("{'a' OR 'a'}");
   }
-  
+
   @Test
   public void parseImplicitAnd() throws Exception {
     assertQuery("a b").resultsIn("{'a' AND 'b'}");
@@ -103,7 +103,7 @@ public class SearchParserAndTokenizerTest {
     assertQuery("((a AND b OR c)").resultsIn(SearchParserException.MessageKeys.MISSING_CLOSE);
     assertQuery("a AND (b OR c").resultsIn(SearchParserException.MessageKeys.MISSING_CLOSE);
     assertQuery("(a AND ((b OR c)").resultsIn(SearchParserException.MessageKeys.MISSING_CLOSE);
-    
+
     assertQuery("NOT NOT a").resultsIn(SearchParserException.MessageKeys.INVALID_NOT_OPERAND);
     assertQuery("NOT (a)").resultsIn(SearchParserException.MessageKeys.TOKENIZER_EXCEPTION);
   }
@@ -186,7 +186,6 @@ public class SearchParserAndTokenizerTest {
     //    <Input>http://serviceRoot/Products?$search=blue</Input>
     assertQuery("blue").resultsIn("'blue'");
 
-
     // below cases can not be tested here
     //    <TestCase Name="5.1.7 Search - on entity container" Rule="odataUri">
     //    <Input>http://serviceRoot/Model.Container/$all?$search=blue</Input>
@@ -194,68 +193,47 @@ public class SearchParserAndTokenizerTest {
     //    <Input>http://serviceRoot/$all?$search=blue</Input>
   }
 
-
   private static Validator assertQuery(String searchQuery) {
-    return Validator.init(searchQuery);
+    return new Validator(searchQuery);
   }
 
   private static class Validator {
-    private boolean log;
     private final String searchQuery;
 
     private Validator(String searchQuery) {
       this.searchQuery = searchQuery;
     }
 
-    private static Validator init(String searchQuery) {
-      return new Validator(searchQuery);
-    }
-
-    @SuppressWarnings("unused")
-    private Validator withLogging() {
-      log = true;
-      return this;
-    }
-
-    private void resultsIn(SearchParserException.MessageKey key)
-            throws SearchTokenizerException {
+    private void resultsIn(SearchParserException.MessageKey key) throws SearchTokenizerException {
       try {
         resultsIn(searchQuery);
       } catch (SearchParserException e) {
         Assert.assertEquals("SearchParserException with unexpected message '" + e.getMessage() +
             "' was thrown.", key, e.getMessageKey());
-        if(log) {
-          System.out.println("Caught SearchParserException with message key " +
-              e.getMessageKey() + " and message " + e.getMessage());
-        }
         return;
       }
       Assert.fail("SearchParserException with message key " + key.getKey() + " was not thrown.");
     }
-    
+
     public void resultsInExpectedTerm(final String actualToken) throws SearchTokenizerException {
       try {
         resultsIn(searchQuery);
-      } catch(SearchParserException e) {
+      } catch (SearchParserException e) {
         Assert.assertEquals(SearchParserException.MessageKeys.EXPECTED_DIFFERENT_TOKEN, e.getMessageKey());
         Assert.assertEquals("Expected PHRASE||WORD found: " + actualToken, e.getMessage());
       }
     }
-    
+
     private void resultsIn(String expectedSearchExpression) throws SearchTokenizerException, SearchParserException {
       final SearchExpression searchExpression = getSearchExpression();
       Assert.assertEquals(expectedSearchExpression, searchExpression.toString());
     }
 
     private SearchExpression getSearchExpression() throws SearchParserException, SearchTokenizerException {
-      SearchParser tokenizer = new SearchParser();
-      SearchOption result = tokenizer.parse(searchQuery);
+      SearchOption result = new SearchParser().parse(searchQuery);
       Assert.assertNotNull(result);
       final SearchExpression searchExpression = result.getSearchExpression();
       Assert.assertNotNull(searchExpression);
-      if (log) {
-        System.out.println(searchExpression);
-      }
       return searchExpression;
     }
   }