You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ra...@apache.org on 2019/05/14 09:21:36 UTC

[olingo-odata4] branch master updated: [OLINGO-1358] Substringof Method support

This is an automated email from the ASF dual-hosted git repository.

ramyav pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/olingo-odata4.git


The following commit(s) were added to refs/heads/master by this push:
     new 9244d68  [OLINGO-1358] Substringof Method support
9244d68 is described below

commit 9244d682f76596912ba2e25e87dd0aab613b302a
Author: ramya vasanth <ra...@sap.com>
AuthorDate: Tue May 14 14:51:23 2019 +0530

    [OLINGO-1358] Substringof Method support
---
 .../fit/tecsvc/client/FilterSystemQueryITCase.java |  19 +-
 .../olingo/fit/tecsvc/http/BasicHttpITCase.java    |  22 ++
 .../api/uri/queryoption/expression/MethodKind.java |   3 +-
 .../server/core/uri/parser/ExpressionParser.java   |   4 +-
 .../server/core/uri/parser/UriTokenizer.java       |   4 +
 .../uri/queryoption/expression/MethodImpl.java     |   1 +
 .../core/uri/parser/ExpressionParserTest.java      | 402 +++++++++++++++++++++
 .../expression/ExpressionVisitorImpl.java          |   2 +
 .../expression/operation/MethodCallOperator.java   |   9 +
 .../core/uri/parser/ExpressionParserTest.java      |  41 +++
 .../server/core/uri/testutil/FilterValidator.java  |  25 ++
 11 files changed, 529 insertions(+), 3 deletions(-)

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 17308d3..017caba 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
@@ -1071,7 +1071,7 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase {
     }
     return response;
   }
-
+  
   private void fail(final String entitySet, final String filterString, final HttpStatusCode errorCode) {
     try {
       sendRequest(entitySet, filterString);
@@ -1080,4 +1080,21 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase {
       assertEquals(errorCode.getStatusCode(), e.getStatusLine().getStatusCode());
     }
   }
+  
+  @Test
+  public void substringOf() {
+    ODataRetrieveResponse<ClientEntitySet> response =
+        sendRequest(ES_ALL_PRIM, "substringof('Second',PropertyString)");
+    assertEquals(1, response.getBody().getEntities().size());
+    assertEquals("Second Resource - negative values", 
+        response.getBody().getEntities().get(0).getProperty("PropertyString")
+          .getPrimitiveValue().toValue());
+  }
+  
+  @Test
+  public void substringOfWithNegativeValues() {
+    fail(ES_ALL_PRIM, "substringof(123,PropertyString)", HttpStatusCode.BAD_REQUEST);
+    fail(ES_ALL_PRIM, "substringof(PropertyString,123)", HttpStatusCode.BAD_REQUEST);
+    fail(ES_ALL_PRIM, "substringof(123,123)", HttpStatusCode.BAD_REQUEST);
+  }
 }
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/BasicHttpITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/BasicHttpITCase.java
index 7018e36..24f3e28 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/BasicHttpITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/BasicHttpITCase.java
@@ -405,4 +405,26 @@ public class BasicHttpITCase extends AbstractBaseTestITCase {
     assertEquals("3", IOUtils.toString(connection.getInputStream()));
     connection.disconnect();
   }
+  
+  @Test
+  public void testSubstringOfWithParameterAlias() throws Exception {
+    URL url = new URL(SERVICE_URI + "ESAllPrim?$filter=substringof(@word,PropertyString)&@word='Second'");
+
+    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+    connection.setRequestMethod(HttpMethod.GET.name());
+    connection.setRequestProperty(HttpHeader.ACCEPT, "application/json");
+    connection.connect();
+
+    assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode());
+    String content = IOUtils.toString(connection.getInputStream());
+    assertNotNull(content);
+    assertTrue(content.contains("\"value\":[{\"PropertyInt16\":-32768,"
+        + "\"PropertyString\":\"Second Resource - negative values\","
+        + "\"PropertyBoolean\":false,\"PropertyByte\":0,\"PropertySByte\":-128,\"PropertyInt32\":-2147483648,"
+        + "\"PropertyInt64\":-9223372036854775808,\"PropertySingle\":-1.79E8,\"PropertyDouble\":-179000.0,"
+        + "\"PropertyDecimal\":-34,\"PropertyBinary\":\"ASNFZ4mrze8=\",\"PropertyDate\":\"2015-11-05\","
+        + "\"PropertyDateTimeOffset\":\"2005-12-03T07:17:08Z\",\"PropertyDuration\":\"PT9S\","
+        + "\"PropertyGuid\":\"76543201-23ab-cdef-0123-456789dddfff\",\"PropertyTimeOfDay\":\"23:49:14\"}]}"));
+    connection.disconnect();
+  }
 }
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/expression/MethodKind.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/expression/MethodKind.java
index 6433325..527fddc 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/expression/MethodKind.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/expression/MethodKind.java
@@ -52,7 +52,8 @@ public enum MethodKind {
   GEOLENGTH("geo.length"),
   GEOINTERSECTS("geo.intersects"),
   CAST("cast"),
-  ISOF("isof");
+  ISOF("isof"),
+  SUBSTRINGOF("substringof");
 
   private String syntax;
 
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 ed0af1c..6be186d 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
@@ -153,6 +153,7 @@ public class ExpressionParser {
     temp.put(TokenKind.ToupperMethod, MethodKind.TOUPPER);
     temp.put(TokenKind.TrimMethod, MethodKind.TRIM);
     temp.put(TokenKind.YearMethod, MethodKind.YEAR);
+    temp.put(TokenKind.SubstringofMethod, MethodKind.SUBSTRINGOF);
 
     tokenToMethod = Collections.unmodifiableMap(temp);
   }
@@ -386,7 +387,7 @@ public class ExpressionParser {
     if (tokenizer.next(TokenKind.ODataIdentifier)) {
       return parseFirstMemberExpr(TokenKind.ODataIdentifier);
     }
-
+    
     throw new UriParserSyntaxException("Unexpected token.", UriParserSyntaxException.MessageKeys.SYNTAX);
   }
 
@@ -527,6 +528,7 @@ public class ExpressionParser {
     case STARTSWITH:
     case INDEXOF:
     case CONCAT:
+    case SUBSTRINGOF:
       ParserHelper.bws(tokenizer);
       final Expression stringParameter1 = parseExpression();
       checkType(stringParameter1, EdmPrimitiveTypeKind.String);
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 2363254..50c9060 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
@@ -170,6 +170,7 @@ public class UriTokenizer {
     ToupperMethod,
     TrimMethod,
     YearMethod,
+    SubstringofMethod,
 
     IsDefinedMethod, // for the aggregation extension
 
@@ -638,6 +639,9 @@ public class UriTokenizer {
     case YearMethod:
       found = nextMethod("year");
       break;
+    case SubstringofMethod:
+      found = nextMethod("substringof");
+      break;
 
     // Method for the aggregation extension
     case IsDefinedMethod:
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/MethodImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/MethodImpl.java
index 126a6b2..b9f8542 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/MethodImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/MethodImpl.java
@@ -54,6 +54,7 @@ public class MethodImpl implements Method {
     case CONTAINS:
     case STARTSWITH:
     case ENDSWITH:
+    case SUBSTRINGOF:
       kind = EdmPrimitiveTypeKind.Boolean;
       break;
     case LENGTH:
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 5646a4e..92c09da 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
@@ -25,14 +25,30 @@ import static org.junit.Assert.fail;
 import static org.mockito.Mockito.mock;
 
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.Locale;
+import java.util.Map;
 
 import org.apache.olingo.commons.api.edm.Edm;
+import org.apache.olingo.commons.api.edm.EdmComplexType;
+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.EdmType;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
 import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.uri.queryoption.AliasQueryOption;
 import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
 import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
+import org.apache.olingo.server.core.uri.queryoption.AliasQueryOptionImpl;
 import org.apache.olingo.server.core.uri.validator.UriValidationException;
 import org.junit.Test;
+import org.mockito.Mockito;
 
 public class ExpressionParserTest {
 
@@ -51,9 +67,28 @@ public class ExpressionParserTest {
     assertEquals("{null EQ null}", parseExpression("null eq null").toString());
 
     wrongExpression("5 eq '5'");
+    
   }
 
   @Test
+  public void testIntegerTypes() throws Exception {
+    Expression expression = parseExpression("5 ne 545678979");
+    assertEquals("{5 NE 545678979}", expression.toString());
+    
+    expression = parseExpression("5456 eq 5456");
+    assertEquals("{5456 EQ 5456}", expression.toString());
+    
+    expression = parseExpression("null ne 54561234567");
+    assertEquals("{null NE 54561234567}", expression.toString());
+    
+    expression = parseExpression("null ne 255");
+    assertEquals("{null NE 255}", expression.toString());
+    
+    expression = parseExpression("123 le 2551234567890000999999");
+    assertEquals("{123 LE 2551234567890000999999}", expression.toString());
+  }
+  
+  @Test
   public void relational() throws Exception {
     Expression expression = parseExpression("5 gt 5");
     assertEquals("{5 GT 5}", expression.toString());
@@ -217,6 +252,12 @@ public class ExpressionParserTest {
     wrongExpression("endswith('a',1)");
     
     assertEquals("{concat ['a', 'b']}", parseExpression("concat( 'a' ,\t'b' )").toString());
+    
+    parseMethod(TokenKind.SubstringofMethod, "'a'", "'b'");
+    parseMethod(TokenKind.SubstringofMethod, "' '", "'b'");
+    parseMethod(TokenKind.SubstringofMethod, "' '", "' '");
+    parseMethod(TokenKind.SubstringofMethod, null, "'a'");
+    wrongExpression("substringof('a',1)");
 }
 
   @Test
@@ -239,6 +280,62 @@ public class ExpressionParserTest {
     assertEquals("{cast [42, Edm.SByte]}", parseExpression("cast( 42\t, Edm.SByte        )").toString());
   }
 
+  @Test
+  public void twoParameterAliasMethods() throws Exception {
+    parseMethodWithParametersWithAlias(TokenKind.SubstringofMethod, "'a'", "'b'");
+    parseMethodWithParametersWithoutAlias(TokenKind.SubstringofMethod, "'a'", "'b'");
+  }
+  
+  private void parseMethodWithParametersWithoutAlias(TokenKind kind, String... parameters) 
+      throws UriParserException, UriValidationException {
+    final String methodName = kind.name().substring(0, kind.name().indexOf("Method")).toLowerCase(Locale.ROOT)
+        .replace("geo", "geo.");
+    String expressionString = methodName + '(';
+    expressionString += "@word1";
+    expressionString += ',';
+    expressionString += parameters[1];
+    expressionString += ')';
+    expressionString += "&@word1=" + parameters[0];
+
+    Map<String, AliasQueryOption> alias = new HashMap<String, AliasQueryOption>();
+    AliasQueryOptionImpl aliasQueryOption = new AliasQueryOptionImpl();
+    aliasQueryOption.setName("@word");
+    aliasQueryOption.setText("\'a\'");
+    alias.put("@word", aliasQueryOption);
+    UriTokenizer tokenizer = new UriTokenizer(expressionString);
+    final Expression expression = new ExpressionParser(mock(Edm.class), odata).parse(tokenizer, null, null, alias);
+    assertNotNull(expression);
+    
+    assertEquals('{' + methodName + ' ' + "[@word1, " + parameters[1] + ']' + '}',
+        expression.toString());
+    
+  }
+
+  private void parseMethodWithParametersWithAlias(TokenKind kind, 
+      String... parameters) throws UriParserException, UriValidationException {
+    final String methodName = kind.name().substring(0, kind.name().indexOf("Method")).toLowerCase(Locale.ROOT)
+        .replace("geo", "geo.");
+    String expressionString = methodName + '(';
+    expressionString += "@word";
+    expressionString += ',';
+    expressionString += parameters[1];
+    expressionString += ')';
+    expressionString += "&@word=" + parameters[0];
+
+    Map<String, AliasQueryOption> alias = new HashMap<String, AliasQueryOption>();
+    AliasQueryOptionImpl aliasQueryOption = new AliasQueryOptionImpl();
+    aliasQueryOption.setName("@word");
+    aliasQueryOption.setText("\'a\'");
+    alias.put("@word", aliasQueryOption);
+    UriTokenizer tokenizer = new UriTokenizer(expressionString);
+    final Expression expression = new ExpressionParser(mock(Edm.class), odata).parse(tokenizer, null, null, alias);
+    assertNotNull(expression);
+    
+    assertEquals('{' + methodName + ' ' + "[@word, " + parameters[1] + ']' + '}',
+        expression.toString());
+    
+  }
+
   private void parseMethod(TokenKind kind, String... parameters) throws UriParserException, UriValidationException {
     final String methodName = kind.name().substring(0, kind.name().indexOf("Method")).toLowerCase(Locale.ROOT)
         .replace("geo", "geo.");
@@ -278,4 +375,309 @@ public class ExpressionParserTest {
       assertNotNull(e);
     }
   }
+  
+  @Test
+  public void testPropertyPathExp() throws Exception {
+    final String entitySetName = "ESName";
+    final String keyPropertyName = "a";
+    EdmProperty keyProperty = mockProperty(keyPropertyName,
+        OData.newInstance().createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String));
+    EdmKeyPropertyRef keyPropertyRef = mockKeyPropertyRef(keyPropertyName, keyProperty);
+    EdmEntityType entityType = mockEntityType(keyPropertyName, keyPropertyRef);
+    Mockito.when(entityType.getPropertyNames()).thenReturn(Collections.singletonList(keyPropertyName));
+    Mockito.when(entityType.getProperty(keyPropertyName)).thenReturn(keyProperty);
+    EdmEntitySet entitySet = mockEntitySet(entitySetName, entityType);
+    EdmEntityContainer container = mockContainer(entitySetName, entitySet);
+    Edm mockedEdm = Mockito.mock(Edm.class);
+    Mockito.when(mockedEdm.getEntityContainer()).thenReturn(container);
+    
+    UriTokenizer tokenizer = new UriTokenizer("a eq \'abc\'");
+    final Expression expression = new ExpressionParser(mockedEdm, odata).parse(tokenizer, 
+        entityType, null, null);
+    assertNotNull(expression);
+    assertEquals("{[a] EQ \'abc\'}", expression.toString());
+  }
+
+  /**
+   * @param keyPropertyName
+   * @param keyPropertyRef
+   * @return
+   */
+  private EdmEntityType mockEntityType(final String keyPropertyName, EdmKeyPropertyRef keyPropertyRef) {
+    EdmEntityType entityType = Mockito.mock(EdmEntityType.class);
+    Mockito.when(entityType.getKeyPredicateNames()).thenReturn(Collections.singletonList(keyPropertyName));
+    Mockito.when(entityType.getKeyPropertyRefs()).thenReturn(Collections.singletonList(keyPropertyRef));
+    return entityType;
+  }
+
+  /**
+   * @param keyPropertyName
+   * @param keyProperty
+   * @return
+   */
+  private EdmKeyPropertyRef mockKeyPropertyRef(final String keyPropertyName, EdmProperty keyProperty) {
+    EdmKeyPropertyRef keyPropertyRef = Mockito.mock(EdmKeyPropertyRef.class);
+    Mockito.when(keyPropertyRef.getName()).thenReturn(keyPropertyName);
+    Mockito.when(keyPropertyRef.getProperty()).thenReturn(keyProperty);
+    return keyPropertyRef;
+  }
+
+  /**
+   * @param propertyName
+   * @return
+   */
+  private EdmProperty mockProperty(final String propertyName, final EdmType type) {
+    EdmProperty keyProperty = Mockito.mock(EdmProperty.class);
+    Mockito.when(keyProperty.getType()).thenReturn(type);
+    Mockito.when(keyProperty.getDefaultValue()).thenReturn("");
+    Mockito.when(keyProperty.getName()).thenReturn(propertyName);
+    return keyProperty;
+  }
+  
+  @Test(expected = UriParserSemanticException.class)
+  public void testPropertyPathExpWithoutType() throws Exception {
+    final String entitySetName = "ESName";
+    final String keyPropertyName = "a";
+    EdmProperty keyProperty = mockProperty(keyPropertyName,
+        OData.newInstance().createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String));
+    EdmKeyPropertyRef keyPropertyRef = mockKeyPropertyRef(keyPropertyName, keyProperty);
+    EdmEntityType entityType = mockEntityType(keyPropertyName, keyPropertyRef);
+    Mockito.when(entityType.getPropertyNames()).thenReturn(Collections.singletonList(keyPropertyName));
+    Mockito.when(entityType.getProperty(keyPropertyName)).thenReturn(keyProperty);
+    EdmEntitySet entitySet = mockEntitySet(entitySetName, entityType);
+    EdmEntityContainer container = mockContainer(entitySetName, entitySet);
+    Edm mockedEdm = Mockito.mock(Edm.class);
+    Mockito.when(mockedEdm.getEntityContainer()).thenReturn(container);
+    
+    UriTokenizer tokenizer = new UriTokenizer("a eq \'abc\'");
+    new ExpressionParser(mockedEdm, odata).parse(tokenizer, null, null, null);
+  }
+  
+  @Test(expected = UriParserSemanticException.class)
+  public void testPropertyPathExpWithoutProperty() throws Exception {
+    final String entitySetName = "ESName";
+    final String keyPropertyName = "a";
+    EdmProperty keyProperty = mockProperty(keyPropertyName, 
+        OData.newInstance().createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String));
+    EdmKeyPropertyRef keyPropertyRef = mockKeyPropertyRef(keyPropertyName, keyProperty);
+    EdmEntityType entityType = mockEntityType(keyPropertyName, keyPropertyRef);
+    Mockito.when(entityType.getFullQualifiedName()).thenReturn(new FullQualifiedName("test.ETName"));
+    EdmEntitySet entitySet = mockEntitySet(entitySetName, entityType);
+    EdmEntityContainer container = mockContainer(entitySetName, entitySet);
+    Edm mockedEdm = Mockito.mock(Edm.class);
+    Mockito.when(mockedEdm.getEntityContainer()).thenReturn(container);
+    
+    UriTokenizer tokenizer = new UriTokenizer("a eq \'abc\'");
+    new ExpressionParser(mockedEdm, odata).parse(tokenizer, entityType, null, null);
+  }
+
+  /**
+   * @param entitySetName
+   * @param entitySet
+   * @return
+   */
+  private EdmEntityContainer mockContainer(final String entitySetName, EdmEntitySet entitySet) {
+    EdmEntityContainer container = Mockito.mock(EdmEntityContainer.class);
+    Mockito.when(container.getEntitySet(entitySetName)).thenReturn(entitySet);
+    return container;
+  }
+
+  /**
+   * @param entitySetName
+   * @param entityType
+   * @return
+   */
+  private EdmEntitySet mockEntitySet(final String entitySetName, EdmEntityType entityType) {
+    EdmEntitySet entitySet = Mockito.mock(EdmEntitySet.class);
+    Mockito.when(entitySet.getName()).thenReturn(entitySetName);
+    Mockito.when(entitySet.getEntityType()).thenReturn(entityType);
+    return entitySet;
+  }
+  
+  @Test
+  public void testComplexPropertyPathExp() throws Exception {
+    final String entitySetName = "ESName";
+    final String keyPropertyName = "a";
+    final String complexPropertyName = "comp";
+    final String propertyName = "prop";
+    EdmProperty keyProperty = mockProperty(keyPropertyName, 
+        OData.newInstance().createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String));
+    EdmKeyPropertyRef keyPropertyRef = mockKeyPropertyRef(keyPropertyName, keyProperty);
+    EdmProperty property = mockProperty(propertyName, 
+        OData.newInstance().createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String));
+    
+    EdmComplexType complexType = mockComplexType(propertyName, property);
+    EdmProperty complexProperty = mockProperty(complexPropertyName, complexType);
+    
+    EdmEntityType entityType = mockEntityType(keyPropertyName, keyPropertyRef);
+    Mockito.when(entityType.getPropertyNames()).thenReturn(Arrays.asList(keyPropertyName, complexPropertyName));
+    Mockito.when(entityType.getProperty(keyPropertyName)).thenReturn(keyProperty);
+    Mockito.when(entityType.getProperty(complexPropertyName)).thenReturn(complexProperty);
+    EdmEntitySet entitySet = mockEntitySet(entitySetName, entityType);
+    EdmEntityContainer container = mockContainer(entitySetName, entitySet);
+    Edm mockedEdm = Mockito.mock(Edm.class);
+    Mockito.when(mockedEdm.getEntityContainer()).thenReturn(container);
+    
+    UriTokenizer tokenizer = new UriTokenizer("comp/prop eq \'abc\'");
+    final Expression expression = new ExpressionParser(mockedEdm, odata).parse(tokenizer, 
+        entityType, null, null);
+    assertNotNull(expression);
+    assertEquals("{[comp, prop] EQ \'abc\'}", expression.toString());
+  }
+
+  /**
+   * @param propertyName
+   * @param property
+   * @return
+   */
+  private EdmComplexType mockComplexType(final String propertyName, EdmProperty property) {
+    EdmComplexType complexType = Mockito.mock(EdmComplexType.class);
+    Mockito.when(complexType.getPropertyNames()).thenReturn(Collections.singletonList(propertyName));
+    Mockito.when(complexType.getProperty(propertyName)).thenReturn(property);
+    return complexType;
+  }
+  
+  /**
+   * @param propertyName
+   * @param property
+   * @return
+   */
+  private EdmComplexType mockComplexType(final String propertyName, EdmNavigationProperty property) {
+    EdmComplexType complexType = Mockito.mock(EdmComplexType.class);
+    Mockito.when(complexType.getPropertyNames()).thenReturn(Collections.singletonList(propertyName));
+    Mockito.when(complexType.getProperty(propertyName)).thenReturn(property);
+    return complexType;
+  }
+  
+  @Test
+  public void testLambdaPropertyPathExp() throws Exception {
+    final String entitySetName = "ESName";
+    final String keyPropertyName = "a";
+    final String complexPropertyName = "comp";
+    final String propertyName = "prop";
+    EdmProperty keyProperty = mockProperty(keyPropertyName, 
+        OData.newInstance().createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String));
+    EdmKeyPropertyRef keyPropertyRef = mockKeyPropertyRef(keyPropertyName, keyProperty);
+    EdmProperty property = mockProperty(propertyName, 
+        OData.newInstance().createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String));
+    
+    EdmComplexType complexType = mockComplexType(propertyName, property);
+    EdmProperty complexProperty = mockProperty(complexPropertyName, complexType);
+    Mockito.when(complexProperty.isCollection()).thenReturn(true);
+    
+    EdmEntityType entityType = mockEntityType(keyPropertyName, keyPropertyRef);
+    Mockito.when(entityType.getPropertyNames()).thenReturn(Arrays.asList(keyPropertyName, complexPropertyName));
+    Mockito.when(entityType.getProperty(keyPropertyName)).thenReturn(keyProperty);
+    Mockito.when(entityType.getProperty(complexPropertyName)).thenReturn(complexProperty);
+    Mockito.when(entityType.getFullQualifiedName()).thenReturn(new FullQualifiedName("test.ET"));
+    EdmEntitySet entitySet = mockEntitySet(entitySetName, entityType);
+    EdmEntityContainer container = mockContainer(entitySetName, entitySet);
+    Edm mockedEdm = Mockito.mock(Edm.class);
+    Mockito.when(mockedEdm.getEntityContainer()).thenReturn(container);
+    
+    UriTokenizer tokenizer = new UriTokenizer("comp/any(d:d/prop eq \'abc\')");
+    Expression expression = new ExpressionParser(mockedEdm, odata).parse(tokenizer, 
+        entityType, null, null);
+    assertNotNull(expression);
+    assertEquals("[comp, any]", expression.toString());
+    
+    tokenizer = new UriTokenizer("comp/all(d:d/prop eq \'abc\')");
+    expression = new ExpressionParser(mockedEdm, odata).parse(tokenizer, 
+        entityType, null, null);
+    assertNotNull(expression);
+    assertEquals("[comp, all]", expression.toString());
+  }
+  
+  @Test
+  public void testNavigationPropertyPathExp() throws Exception {
+    final String entitySetName = "ESName";
+    final String keyPropertyName = "a";
+    final String complexPropertyName = "comp";
+    final String propertyName = "navProp";
+    EdmProperty keyProperty = mockProperty(keyPropertyName, 
+        OData.newInstance().createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String));
+    EdmKeyPropertyRef keyPropertyRef = mockKeyPropertyRef(keyPropertyName, keyProperty);
+    
+    EdmEntityType targetEntityType = mockEntityType(keyPropertyName, keyPropertyRef);
+    Mockito.when(targetEntityType.getFullQualifiedName()).thenReturn(new FullQualifiedName("test.TargetET"));
+    EdmNavigationProperty navProperty = mockNavigationProperty(propertyName, targetEntityType);
+    
+    EdmComplexType complexType = mockComplexType(propertyName, navProperty);
+    EdmProperty complexProperty = mockProperty(complexPropertyName, complexType);
+    
+    EdmEntityType startEntityType = mockEntityType(keyPropertyName, keyPropertyRef);
+    Mockito.when(startEntityType.getFullQualifiedName()).thenReturn(new FullQualifiedName("test.StartET"));
+    Mockito.when(startEntityType.getPropertyNames()).thenReturn(
+        Arrays.asList(keyPropertyName, complexPropertyName));
+    Mockito.when(startEntityType.getProperty(keyPropertyName)).thenReturn(keyProperty);
+    Mockito.when(startEntityType.getProperty(complexPropertyName)).thenReturn(complexProperty);
+    EdmEntitySet entitySet = mockEntitySet(entitySetName, startEntityType);
+    EdmEntityContainer container = mockContainer(entitySetName, entitySet);
+    Edm mockedEdm = Mockito.mock(Edm.class);
+    Mockito.when(mockedEdm.getEntityContainer()).thenReturn(container);
+    
+    UriTokenizer tokenizer = new UriTokenizer("comp/navProp");
+    final Expression expression = new ExpressionParser(mockedEdm, odata).parse(tokenizer, 
+        startEntityType, null, null);
+    assertNotNull(expression);
+    assertEquals("[comp, navProp]", expression.toString());
+  }
+
+  /**
+   * @param propertyName
+   * @param entityType
+   * @return
+   */
+  private EdmNavigationProperty mockNavigationProperty(final String propertyName, EdmEntityType entityType) {
+    EdmNavigationProperty navProperty = Mockito.mock(EdmNavigationProperty.class);
+    Mockito.when(navProperty.getName()).thenReturn(propertyName);
+    Mockito.when(navProperty.getType()).thenReturn(entityType);
+    return navProperty;
+  }
+  
+  @Test
+  public void testDerivedPathExp() throws Exception {
+    final String derivedEntitySetName = "ESName";
+    final String keyPropertyName = "a";
+    final String propertyName = "navProp";
+    EdmProperty keyProperty = mockProperty(keyPropertyName, 
+        OData.newInstance().createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String));
+    EdmKeyPropertyRef keyPropertyRef = mockKeyPropertyRef(keyPropertyName, keyProperty);
+    
+    EdmEntityType navEntityType = mockEntityType(keyPropertyName, keyPropertyRef);
+    Mockito.when(navEntityType.getFullQualifiedName()).thenReturn(new FullQualifiedName("test.navET"));
+    Mockito.when(navEntityType.getNamespace()).thenReturn("test");
+    Mockito.when(navEntityType.getPropertyNames()).thenReturn(
+        Arrays.asList(keyPropertyName));
+    Mockito.when(navEntityType.getProperty(keyPropertyName)).thenReturn(keyProperty);
+    
+    EdmEntityType baseEntityType = mockEntityType(keyPropertyName, keyPropertyRef);
+    Mockito.when(baseEntityType.getFullQualifiedName()).thenReturn(new FullQualifiedName("test.baseET"));
+    Mockito.when(baseEntityType.getNamespace()).thenReturn("test");
+    Mockito.when(baseEntityType.getPropertyNames()).thenReturn(
+        Arrays.asList(keyPropertyName));
+    Mockito.when(baseEntityType.getProperty(keyPropertyName)).thenReturn(keyProperty);
+    
+    Mockito.when(navEntityType.getBaseType()).thenReturn(baseEntityType);
+    Mockito.when(baseEntityType.compatibleTo(navEntityType)).thenReturn(true);
+    
+    EdmEntityType entityType = mockEntityType(keyPropertyName, keyPropertyRef);
+    Mockito.when(entityType.getFullQualifiedName()).thenReturn(new FullQualifiedName("test.derivedET"));
+    Mockito.when(entityType.getNamespace()).thenReturn("test");
+    Mockito.when(entityType.getPropertyNames()).thenReturn(Arrays.asList(keyPropertyName, propertyName));
+    EdmNavigationProperty navProperty = mockNavigationProperty(propertyName, navEntityType);
+    Mockito.when(entityType.getProperty(propertyName)).thenReturn(navProperty);
+    
+    EdmEntitySet entitySet = mockEntitySet(derivedEntitySetName, entityType);
+    EdmEntityContainer container = mockContainer(derivedEntitySetName, entitySet);
+    Edm mockedEdm = Mockito.mock(Edm.class);
+    Mockito.when(mockedEdm.getEntityContainer()).thenReturn(container);
+    Mockito.when(mockedEdm.getEntityType(new FullQualifiedName("test.baseET"))).thenReturn(baseEntityType);
+    
+    UriTokenizer tokenizer = new UriTokenizer("navProp/test.baseET");
+    Expression expression = new ExpressionParser(mockedEdm, odata).parse(tokenizer, 
+        entityType, null, null);
+    assertNotNull(expression);
+    assertEquals("[navProp]", expression.toString());
+  }
 }
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/ExpressionVisitorImpl.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/ExpressionVisitorImpl.java
index 253dbde..98b3f7f 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/ExpressionVisitorImpl.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/ExpressionVisitorImpl.java
@@ -182,6 +182,8 @@ public class ExpressionVisitorImpl implements ExpressionVisitor<VisitorOperand>
       return methodCallOperation.floor();
     case CEILING:
       return methodCallOperation.ceiling();
+    case SUBSTRINGOF:
+      return methodCallOperation.substringof();
 
     default:
       return throwNotImplemented();
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/operation/MethodCallOperator.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/operation/MethodCallOperator.java
index 77185af..70b8172 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/operation/MethodCallOperator.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/operation/MethodCallOperator.java
@@ -108,6 +108,15 @@ public class MethodCallOperator {
       }
     }, primBoolean);
   }
+  
+  public VisitorOperand substringof() throws ODataApplicationException {
+    return stringFunction(new StringFunction() {
+      @Override
+      public Object perform(final List<String> params) {
+        return params.get(1).contains(params.get(0));
+      }
+    }, primBoolean);
+  }
 
   public VisitorOperand toLower() throws ODataApplicationException {
     return stringFunction(new StringFunction() {
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java
index 6985e51..63d7487 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java
@@ -50,6 +50,29 @@ public class ExpressionParserTest {
 
   @Test
   public void filter() throws Exception {
+    testFilter.runOnESCompCollComp("PropertyComp/CollPropertyComp/any"
+        + "(f:f/olingo.odata.test1.CTBase/AdditionalPropString eq 'ADD TEST')")
+    .is("<PropertyComp/CollPropertyComp/<ANY;<<f/olingo.odata.test1.CTBase/AdditionalPropString> eq <'ADD TEST'>>>>")
+    .isMember().goPath()
+    .first()
+    .isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTCompCollComp, false)
+    .n()
+    .isComplexProperty("CollPropertyComp", ComplexTypeProvider.nameCTTwoPrim, true)
+    .n()
+    .isUriPathInfoKind(UriResourceKind.lambdaAny)
+    .goLambdaExpression()
+    .isBinary(BinaryOperatorKind.EQ)
+    .left().goPath()
+    .first().isUriPathInfoKind(UriResourceKind.lambdaVariable)
+    .isType(ComplexTypeProvider.nameCTTwoPrim, false)
+    .n()
+    .isPrimitiveProperty("AdditionalPropString", PropertyProvider.nameString, false)
+    .goUpFilterValidator()
+    .root().right()
+    .isLiteral("'ADD TEST'");
+    
+    testFilter.runOnESCompCollCompNeg("PropertyComp/CollPropertyComp/olingo.odata.test1.BFCCTPrimCompRTESTwoKeyNav()");
+    
     testFilter.runOnETAllPrim("PropertyBoolean")
         .is("<PropertyBoolean>")
         .isType(PropertyProvider.nameBoolean);
@@ -1928,6 +1951,24 @@ public class ExpressionParserTest {
         .n().isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false)
         .n().isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTAllPrim, false)
         .n().isPrimitiveProperty("PropertyDate", PropertyProvider.nameDate, false);
+    
+    testFilter.runOrderByOnETTwoKeyNav("PropertyComp/PropertyComp/PropertyDate eq "
+        + "$root/SINav/PropertyComp/PropertyComp/PropertyDate")
+        .isSortOrder(0, false)
+        .goOrder(0).isBinary(BinaryOperatorKind.EQ).left().goPath()
+        .first().isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false)
+        .n().isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTAllPrim, false)
+        .n().isPrimitiveProperty("PropertyDate", PropertyProvider.nameDate, false)
+        .goUpFilterValidator()
+        .goOrder(0).right().goPath()
+        .first().isUriPathInfoKind(UriResourceKind.root)
+        .n().isSingleton("SINav")
+        .n().isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false)
+        .n().isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTAllPrim, false)
+        .n().isPrimitiveProperty("PropertyDate", PropertyProvider.nameDate, false);
+    
+    testFilter.runOrderByOnETTwoKeyNavNeg("PropertyComp/PropertyComp/PropertyDate eq "
+        + "$root/SIType/PropertyComp/PropertyComp/PropertyDate");
 
     testFilter.runOrderByOnETTwoKeyNav("PropertyComp/PropertyComp/PropertyDate eq 2013-11-12 desc,"
         + "PropertyString eq 'SomeString' desc")
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 246a408..c68ecde 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
@@ -51,6 +51,7 @@ 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.queryoption.expression.BinaryImpl;
 import org.apache.olingo.server.core.uri.queryoption.expression.MemberImpl;
 import org.apache.olingo.server.core.uri.queryoption.expression.MethodImpl;
@@ -102,6 +103,16 @@ public class FilterValidator implements TestValidator {
       throws UriParserException, UriValidationException {
     return runUriOrderBy("ESTwoKeyNav", "$orderby=" + orderBy);
   }
+  
+  public FilterValidator runOrderByOnETTwoKeyNavNeg(final String orderBy) 
+      throws UriParserException, UriValidationException {
+    try {
+      runUriOrderBy("ESTwoKeyNav", "$orderby=" + orderBy);
+    } catch (UriParserSemanticException e) {
+      assertEquals("EntitySet or singleton expected.", e.getMessage());
+    }
+    return this;
+  }
 
   public FilterValidator runOrderByOnETMixEnumDefCollComp(final String orderBy)
       throws UriParserException, UriValidationException {
@@ -133,6 +144,20 @@ public class FilterValidator implements TestValidator {
   public FilterValidator runOnETAllPrim(final String filter) throws UriParserException, UriValidationException {
     return runUri("ESAllPrim(1)", "$filter=" + filter);
   }
+  
+  public FilterValidator runOnESCompCollComp(final String filter) throws UriParserException, UriValidationException {
+    return runUri("ESCompCollComp", "$filter=" + filter);
+  }
+  
+  public FilterValidator runOnESCompCollCompNeg(final String filter) 
+      throws UriParserException, UriValidationException {
+    try {
+      runUri("ESCompCollComp", "$filter=" + filter);
+    } catch (UriParserSemanticException e) {
+      assertEquals("Bound function 'olingo.odata.test1.BFCCTPrimCompRTESTwoKeyNav' not found.", e.getMessage());
+    }
+    return this;
+  }
 
   public FilterValidator runOnETKeyNav(final String filter) throws UriParserException, UriValidationException {
     return runUri("ESKeyNav(1)", "$filter=" + filter);