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

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

[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("\\\\", "\\"));
+  }
+}