olingo-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From chri...@apache.org
Subject [2/7] olingo-odata4 git commit: [OLINGO-834] better alias support in URI parser
Date Thu, 04 Feb 2016 13:02:19 GMT
[OLINGO-834] better alias support in URI parser

Signed-off-by: Christian Amend <christian.amend@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/110c7b0e
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/110c7b0e
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/110c7b0e

Branch: refs/heads/master
Commit: 110c7b0e5afa1f9f717d95ca48bd5c695821db06
Parents: b317b90
Author: Klaus Straubinger <klaus.straubinger@sap.com>
Authored: Thu Feb 4 12:24:38 2016 +0100
Committer: Christian Amend <christian.amend@sap.com>
Committed: Thu Feb 4 12:38:10 2016 +0100

----------------------------------------------------------------------
 .../uri/queryoption/SystemQueryOptionKind.java  |  22 +-
 .../apache/olingo/server/core/ODataHandler.java |   4 +-
 .../server/core/ODataHttpHandlerImpl.java       |  19 +-
 .../olingo/server/core/uri/UriInfoImpl.java     |  37 +-
 .../server/core/uri/parser/ExpandParser.java    |  30 +-
 .../core/uri/parser/ExpressionParser.java       |  85 ++-
 .../server/core/uri/parser/FilterParser.java    |   6 +-
 .../server/core/uri/parser/OrderByParser.java   |   6 +-
 .../olingo/server/core/uri/parser/Parser.java   | 354 +++++++------
 .../server/core/uri/parser/ParserHelper.java    | 168 ++++--
 .../core/uri/parser/ResourcePathParser.java     |  38 +-
 .../server/core/uri/parser/SelectParser.java    |  12 +-
 .../server/core/uri/parser/UriTokenizer.java    |  12 +-
 .../uri/queryoption/expression/AliasImpl.java   |   9 +-
 .../uri/validator/UriValidationException.java   |   4 +-
 .../server/core/uri/validator/UriValidator.java | 513 +++++--------------
 .../server-core-exceptions-i18n.properties      |   1 +
 .../olingo/server/core/uri/UriInfoImplTest.java |  50 +-
 .../core/uri/parser/ExpressionParserTest.java   |   2 +-
 .../core/uri/parser/UriTokenizerTest.java       |   3 +
 .../olingo/server/tecsvc/data/DataProvider.java |  12 +-
 .../SystemQueryOptionsRuntimeException.java     |   3 +-
 .../tecsvc/provider/ContainerProvider.java      |   6 +-
 .../core/uri/parser/TestFullResourcePath.java   | 112 ++--
 .../core/uri/queryoption/QueryOptionTest.java   |   4 +-
 .../queryoption/expression/ExpressionTest.java  |   6 +-
 .../core/uri/testutil/FilterValidator.java      |  51 +-
 .../core/uri/validator/UriValidatorTest.java    |  68 +--
 28 files changed, 797 insertions(+), 840 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/110c7b0e/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/SystemQueryOptionKind.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/SystemQueryOptionKind.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/SystemQueryOptionKind.java
index f0c907b..d5d60a1 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/SystemQueryOptionKind.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/SystemQueryOptionKind.java
@@ -18,10 +18,10 @@
  */
 package org.apache.olingo.server.api.uri.queryoption;
 
+
 /**
  * Defines the supported system query options.
  */
-
 public enum SystemQueryOptionKind {
 
   /**
@@ -84,12 +84,30 @@ public enum SystemQueryOptionKind {
    */
   LEVELS("$levels");
 
-  private String syntax;
+  private final String syntax;
 
   SystemQueryOptionKind(final String syntax) {
     this.syntax = syntax;
   }
 
+  /**
+   * Converts the URI syntax to an enumeration value.
+   * @param option option in the syntax used in the URI
+   * @return system query option kind representing the given option
+   *         (or <code>null</code> if the option does not represent a system query option)
+   */
+  public static SystemQueryOptionKind get(final String option) {
+    for (final SystemQueryOptionKind kind : values()) {
+      if (kind.syntax.equals(option)) {
+        return kind;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * @return URI syntax for this system query option
+   */
   @Override
   public String toString() {
     return syntax;

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/110c7b0e/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java
index 96b419c..3baaac7 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java
@@ -160,6 +160,7 @@ public class ODataHandler {
 
   public void handleException(final ODataRequest request, final ODataResponse response,
       final ODataServerError serverError, final Exception exception) {
+    final int measurementHandle = debugger.startRuntimeMeasurement("ODataHandler", "handleException");
     lastThrownException = exception;
     ErrorProcessor exceptionProcessor;
     try {
@@ -176,8 +177,9 @@ public class ODataHandler {
     } catch (final ContentNegotiatorException e) {
       requestedContentType = ContentType.JSON;
     }
-    final int measurementHandle = debugger.startRuntimeMeasurement("ErrorProcessor", "processError");
+    final int measurementError = debugger.startRuntimeMeasurement("ErrorProcessor", "processError");
     exceptionProcessor.processError(request, response, serverError, requestedContentType);
+    debugger.stopRuntimeMeasurement(measurementError);
     debugger.stopRuntimeMeasurement(measurementHandle);
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/110c7b0e/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java
index 55c1194..61581c0 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java
@@ -24,7 +24,7 @@ import java.nio.channels.Channel;
 import java.nio.channels.Channels;
 import java.nio.channels.ReadableByteChannel;
 import java.nio.channels.WritableByteChannel;
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -191,8 +191,12 @@ public class ODataHttpHandlerImpl implements ODataHttpHandler {
       odRequest.setBody(httpRequest.getInputStream());
       odRequest.setProtocol(httpRequest.getProtocol());
       odRequest.setMethod(extractMethod(httpRequest));
+      int innerHandle = debugger.startRuntimeMeasurement("ODataHttpHandlerImpl", "copyHeaders");
       copyHeaders(odRequest, httpRequest);
+      debugger.stopRuntimeMeasurement(innerHandle);
+      innerHandle = debugger.startRuntimeMeasurement("ODataHttpHandlerImpl", "fillUriInformation");
       fillUriInformation(odRequest, httpRequest, split);
+      debugger.stopRuntimeMeasurement(innerHandle);
 
       return odRequest;
     } catch (final IOException e) {
@@ -273,14 +277,11 @@ public class ODataHttpHandlerImpl implements ODataHttpHandler {
     odRequest.setRawServiceResolutionUri(rawServiceResolutionUri);
   }
 
-  static void copyHeaders(final ODataRequest odRequest, final HttpServletRequest req) {
-    for (Enumeration<?> headerNames = req.getHeaderNames(); headerNames.hasMoreElements();) {
-      String headerName = (String) headerNames.nextElement();
-      List<String> headerValues = new ArrayList<String>();
-      for (Enumeration<?> headers = req.getHeaders(headerName); headers.hasMoreElements();) {
-        String value = (String) headers.nextElement();
-        headerValues.add(value);
-      }
+  static void copyHeaders(ODataRequest odRequest, final HttpServletRequest req) {
+    for (final Enumeration<?> headerNames = req.getHeaderNames(); headerNames.hasMoreElements();) {
+      final String headerName = (String) headerNames.nextElement();
+      @SuppressWarnings("unchecked") // getHeaders() says it returns an Enumeration of String.
+      final List<String> headerValues = Collections.list(req.getHeaders(headerName));
       odRequest.addHeader(headerName, headerValues);
     }
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/110c7b0e/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriInfoImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriInfoImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriInfoImpl.java
index e6fe057..7183bde 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriInfoImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriInfoImpl.java
@@ -20,6 +20,7 @@ package org.apache.olingo.server.core.uri;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.EnumMap;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -64,7 +65,7 @@ public class UriInfoImpl implements UriInfo {
   private List<UriResource> pathParts = new ArrayList<UriResource>();
 
   private Map<SystemQueryOptionKind, SystemQueryOption> systemQueryOptions =
-      new HashMap<SystemQueryOptionKind, SystemQueryOption>();
+      new EnumMap<SystemQueryOptionKind, SystemQueryOption>(SystemQueryOptionKind.class);
   private Map<String, AliasQueryOption> aliases = new HashMap<String, AliasQueryOption>();
   private List<CustomQueryOption> customQueryOptions = new ArrayList<CustomQueryOption>();
 
@@ -155,15 +156,13 @@ public class UriInfoImpl implements UriInfo {
     return Collections.unmodifiableList(pathParts);
   }
 
-  public UriInfoImpl setQueryOptions(final List<QueryOption> list) {
-    for (final QueryOption item : list) {
-      if (item instanceof SystemQueryOption) {
-        setSystemQueryOption((SystemQueryOption) item);
-      } else if (item instanceof AliasQueryOption) {
-        addAlias((AliasQueryOption) item);
-      } else if (item instanceof CustomQueryOption) {
-        addCustomQueryOption((CustomQueryOption) item);
-      }
+  public UriInfoImpl setQueryOption(final QueryOption option) {
+    if (option instanceof SystemQueryOption) {
+      setSystemQueryOption((SystemQueryOption) option);
+    } else if (option instanceof AliasQueryOption) {
+      addAlias((AliasQueryOption) option);
+    } else if (option instanceof CustomQueryOption) {
+      addCustomQueryOption((CustomQueryOption) option);
     }
     return this;
   }
@@ -263,18 +262,22 @@ public class UriInfoImpl implements UriInfo {
   }
 
   public UriInfoImpl addAlias(final AliasQueryOption alias) {
-    aliases.put(alias.getName(), alias);
+    if (aliases.containsKey(alias.getName())) {
+      throw new ODataRuntimeException("Alias " + alias.getName() + " is already there.");
+    } else {
+      aliases.put(alias.getName(), alias);
+    }
     return this;
   }
 
   @Override
   public String getValueForAlias(final String alias) {
-    final AliasQueryOption aliasQueryOption = getAlias(alias);
+    final AliasQueryOption aliasQueryOption = aliases.get(alias);
     return aliasQueryOption == null ? null : aliasQueryOption.getText();
   }
 
-  public AliasQueryOption getAlias(final String key) {
-    return aliases.get(key);
+  public Map<String, AliasQueryOption> getAliasMap() {
+    return Collections.unmodifiableMap(aliases);
   }
 
   @Override
@@ -282,8 +285,10 @@ public class UriInfoImpl implements UriInfo {
     return Collections.unmodifiableList(new ArrayList<AliasQueryOption>(aliases.values()));
   }
 
-  public UriInfoImpl addCustomQueryOption(final CustomQueryOption item) {
-    customQueryOptions.add(item);
+  public UriInfoImpl addCustomQueryOption(final CustomQueryOption option) {
+    if (option.getName() != null && !option.getName().isEmpty()) {
+      customQueryOptions.add(option);
+    }
     return this;
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/110c7b0e/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
index d8209d8..03750a5 100644
--- 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
@@ -18,6 +18,8 @@
  */
 package org.apache.olingo.server.core.uri.parser;
 
+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.EdmNavigationProperty;
@@ -30,6 +32,7 @@ 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.AliasQueryOption;
 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;
@@ -53,10 +56,12 @@ public class ExpandParser {
 
   private final Edm edm;
   private final OData odata;
+  private final Map<String, AliasQueryOption> aliases;
 
-  public ExpandParser(final Edm edm, final OData odata) {
+  public ExpandParser(final Edm edm, final OData odata, final Map<String, AliasQueryOption> aliases) {
     this.edm = edm;
     this.odata = odata;
+    this.aliases = aliases;
   }
 
   public ExpandOption parse(UriTokenizer tokenizer, final EdmStructuredType referencedType)
@@ -92,7 +97,7 @@ public class ExpandParser {
         ParserHelper.requireNext(tokenizer, TokenKind.SLASH);
       }
 
-      UriInfoImpl resource = parseExpandPath(tokenizer, referencedType);
+      UriInfoImpl resource = parseExpandPath(tokenizer, referencedType, item);
 
       UriResourcePartTyped lastPart = (UriResourcePartTyped) resource.getLastResourcePart();
 
@@ -156,8 +161,8 @@ public class ExpandParser {
     return null;
   }
 
-  private UriInfoImpl parseExpandPath(UriTokenizer tokenizer, final EdmStructuredType referencedType)
-      throws UriParserException {
+  private UriInfoImpl parseExpandPath(UriTokenizer tokenizer, final EdmStructuredType referencedType,
+      ExpandItemImpl item) throws UriParserException {
     UriInfoImpl resource = new UriInfoImpl().setKind(UriInfoKind.resource);
 
     EdmStructuredType type = referencedType;
@@ -181,10 +186,13 @@ public class ExpandParser {
 
     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);
+      if (tokenizer.next(TokenKind.STAR)) {
+        item.setIsStar(true);
+      } else {
+        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));
     }
@@ -209,11 +217,11 @@ public class ExpandParser {
 
         } else if (!forRef && !forCount && tokenizer.next(TokenKind.EXPAND)) {
           ParserHelper.requireNext(tokenizer, TokenKind.EQ);
-          systemQueryOption = new ExpandParser(edm, odata).parse(tokenizer, referencedType);
+          systemQueryOption = new ExpandParser(edm, odata, aliases).parse(tokenizer, referencedType);
 
         } else if (tokenizer.next(TokenKind.FILTER)) {
           ParserHelper.requireNext(tokenizer, TokenKind.EQ);
-          systemQueryOption = new FilterParser(edm, odata).parse(tokenizer, referencedType, null);
+          systemQueryOption = new FilterParser(edm, odata).parse(tokenizer, referencedType, null, aliases);
 
         } else if (!forRef && !forCount && tokenizer.next(TokenKind.LEVELS)) {
           ParserHelper.requireNext(tokenizer, TokenKind.EQ);
@@ -221,7 +229,7 @@ public class ExpandParser {
 
         } else if (!forCount && tokenizer.next(TokenKind.ORDERBY)) {
           ParserHelper.requireNext(tokenizer, TokenKind.EQ);
-          systemQueryOption = new OrderByParser(edm, odata).parse(tokenizer, referencedType, null);
+          systemQueryOption = new OrderByParser(edm, odata).parse(tokenizer, referencedType, null, aliases);
 
         } else if (tokenizer.next(TokenKind.SEARCH)) {
           ParserHelper.requireNext(tokenizer, TokenKind.EQ);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/110c7b0e/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 1cf279e..a0ec676 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java
@@ -24,7 +24,7 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Deque;
-import java.util.HashMap;
+import java.util.EnumMap;
 import java.util.List;
 import java.util.Map;
 
@@ -53,6 +53,7 @@ import org.apache.olingo.server.api.uri.UriResourceFunction;
 import org.apache.olingo.server.api.uri.UriResourceLambdaVariable;
 import org.apache.olingo.server.api.uri.UriResourceNavigation;
 import org.apache.olingo.server.api.uri.UriResourcePartTyped;
+import org.apache.olingo.server.api.uri.queryoption.AliasQueryOption;
 import org.apache.olingo.server.api.uri.queryoption.expression.Alias;
 import org.apache.olingo.server.api.uri.queryoption.expression.Binary;
 import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind;
@@ -96,7 +97,7 @@ import org.apache.olingo.server.core.uri.validator.UriValidationException;
 public class ExpressionParser {
   private static final Map<TokenKind, BinaryOperatorKind> tokenToBinaryOperator;
   static {
-    Map<TokenKind, BinaryOperatorKind> temp = new HashMap<TokenKind, BinaryOperatorKind>();
+    Map<TokenKind, BinaryOperatorKind> temp = new EnumMap<TokenKind, BinaryOperatorKind>(TokenKind.class);
     temp.put(TokenKind.OrOperator, BinaryOperatorKind.OR);
     temp.put(TokenKind.AndOperator, BinaryOperatorKind.AND);
 
@@ -121,7 +122,7 @@ public class ExpressionParser {
   // 'cast' and 'isof' are handled specially.
   private static final Map<TokenKind, MethodKind> tokenToMethod;
   static {
-    Map<TokenKind, MethodKind> temp = new HashMap<TokenKind, MethodKind>();
+    Map<TokenKind, MethodKind> temp = new EnumMap<TokenKind, MethodKind>(TokenKind.class);
     temp.put(TokenKind.CeilingMethod, MethodKind.CEILING);
     temp.put(TokenKind.ConcatMethod, MethodKind.CONCAT);
     temp.put(TokenKind.ContainsMethod, MethodKind.CONTAINS);
@@ -163,6 +164,7 @@ public class ExpressionParser {
   private Deque<UriResourceLambdaVariable> lambdaVariables = new ArrayDeque<UriResourceLambdaVariable>();
   private EdmType referringType;
   private Collection<String> crossjoinEntitySetNames;
+  private Map<String, AliasQueryOption> aliases;
 
   public ExpressionParser(final Edm edm, final OData odata) {
     this.edm = edm;
@@ -170,12 +172,13 @@ public class ExpressionParser {
   }
 
   public Expression parse(UriTokenizer tokenizer, final EdmType referringType,
-      final Collection<String> crossjoinEntitySetNames)
+      final Collection<String> crossjoinEntitySetNames, final Map<String, AliasQueryOption> aliases)
       throws UriParserException, UriValidationException {
     // Initialize tokenizer.
     this.tokenizer = tokenizer;
     this.referringType = referringType;
     this.crossjoinEntitySetNames = crossjoinEntitySetNames;
+    this.aliases = aliases;
 
     return parseExpression();
   }
@@ -339,7 +342,14 @@ public class ExpressionParser {
     }
 
     if (tokenizer.next(TokenKind.ParameterAliasName)) {
-      return new AliasImpl(tokenizer.getText());
+      final String name = tokenizer.getText();
+      if (aliases.containsKey(name)) {
+        return new AliasImpl(name,
+            ParserHelper.parseAliasValue(name, null, true, true, edm, referringType, aliases));
+      } else {
+        throw new UriValidationException("Alias '" + name + "' not found.",
+            UriValidationException.MessageKeys.MISSING_ALIAS, name);
+      }
     }
 
     if (tokenizer.next(TokenKind.jsonArrayOrObject)) {
@@ -360,7 +370,8 @@ public class ExpressionParser {
       return parsePrimitive(nextPrimitive);
     }
 
-    final TokenKind nextMethod = nextMethod();
+    final TokenKind nextMethod =
+        ParserHelper.next(tokenizer, tokenToMethod.keySet().toArray(new TokenKind[tokenToMethod.size()]));
     if (nextMethod != null) {
       return parseMethod(nextMethod);
     }
@@ -630,7 +641,7 @@ public class ExpressionParser {
     } else {
       ParserHelper.requireNext(tokenizer, TokenKind.OPEN);
       final List<UriParameter> keyPredicates =
-          ParserHelper.parseKeyPredicate(tokenizer, entitySet.getEntityType(), null);
+          ParserHelper.parseKeyPredicate(tokenizer, entitySet.getEntityType(), null, edm, referringType, aliases);
       resource = new UriResourceEntitySetImpl(entitySet).setKeyPredicates(keyPredicates);
     }
     uriInfo.addResourcePart(resource);
@@ -777,7 +788,8 @@ public class ExpressionParser {
       final UriResourceNavigationPropertyImpl navigationResource =
           new UriResourceNavigationPropertyImpl((EdmNavigationProperty) property);
       navigationResource.setKeyPredicates(
-          ParserHelper.parseNavigationKeyPredicate(tokenizer, (EdmNavigationProperty) property));
+          ParserHelper.parseNavigationKeyPredicate(tokenizer, (EdmNavigationProperty) property,
+              edm, referringType, aliases));
       uriInfo.addResourcePart(navigationResource);
 
       if (navigationResource.isCollection()) {
@@ -830,13 +842,16 @@ public class ExpressionParser {
       if (lastResource instanceof UriResourceNavigation) {
         ((UriResourceNavigationPropertyImpl) lastResource).setKeyPredicates(
               ParserHelper.parseNavigationKeyPredicate(tokenizer,
-                  ((UriResourceNavigationPropertyImpl) lastResource).getProperty()));
+                  ((UriResourceNavigationPropertyImpl) lastResource).getProperty(), edm, referringType, aliases));
       } else if (lastResource instanceof UriResourceFunction
           && ((UriResourceFunction) lastResource).getType() instanceof EdmEntityType) {
         ((UriResourceFunctionImpl) lastResource).setKeyPredicates(
             ParserHelper.parseKeyPredicate(tokenizer,
                 (EdmEntityType) ((UriResourceFunction) lastResource).getType(),
-                null));
+                null,
+                edm,
+                referringType,
+                aliases));
       } else {
         throw new UriParserSemanticException("Unknown or wrong resource type.",
             UriParserSemanticException.MessageKeys.NOT_IMPLEMENTED, lastResource.toString());
@@ -911,18 +926,21 @@ public class ExpressionParser {
   private void parseFunction(final FullQualifiedName fullQualifiedName, UriInfoImpl uriInfo,
       final EdmType lastType, final boolean lastIsCollection) throws UriParserException, UriValidationException {
 
-    final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, edm, referringType, true);
+    final List<UriParameter> parameters =
+        ParserHelper.parseFunctionParameters(tokenizer, edm, referringType, true, aliases);
     final List<String> parameterNames = ParserHelper.getParameterNames(parameters);
     final EdmFunction boundFunction = edm.getBoundFunction(fullQualifiedName,
         lastType.getFullQualifiedName(), lastIsCollection, parameterNames);
 
     if (boundFunction != null) {
+      ParserHelper.validateFunctionParameters(boundFunction, parameters, edm, referringType, aliases);
       parseFunctionRest(uriInfo, boundFunction, parameters);
       return;
     }
 
     final EdmFunction unboundFunction = edm.getUnboundFunction(fullQualifiedName, parameterNames);
     if (unboundFunction != null) {
+      ParserHelper.validateFunctionParameters(unboundFunction, parameters, edm, referringType, aliases);
       parseFunctionRest(uriInfo, unboundFunction, parameters);
       return;
     }
@@ -934,7 +952,8 @@ public class ExpressionParser {
   private void parseBoundFunction(final FullQualifiedName fullQualifiedName, UriInfoImpl uriInfo,
       final UriResourcePartTyped lastResource) throws UriParserException, UriValidationException {
     final EdmType type = lastResource.getType();
-    final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, edm, referringType, true);
+    final List<UriParameter> parameters =
+        ParserHelper.parseFunctionParameters(tokenizer, edm, referringType, true, aliases);
     final List<String> parameterNames = ParserHelper.getParameterNames(parameters);
     final EdmFunction boundFunction = edm.getBoundFunction(fullQualifiedName,
         type.getFullQualifiedName(), lastResource.isCollection(), parameterNames);
@@ -942,6 +961,7 @@ public class ExpressionParser {
       throw new UriParserSemanticException("Bound function '" + fullQualifiedName + "' not found.",
           UriParserSemanticException.MessageKeys.FUNCTION_NOT_FOUND, fullQualifiedName.getFullQualifiedNameAsString());
     }
+    ParserHelper.validateFunctionParameters(boundFunction, parameters, edm, referringType, aliases);
     parseFunctionRest(uriInfo, boundFunction, parameters);
   }
 
@@ -1010,40 +1030,6 @@ public class ExpressionParser {
     }
   }
 
-  private TokenKind nextMethod() {
-    return ParserHelper.next(tokenizer,
-        TokenKind.CeilingMethod,
-        TokenKind.ConcatMethod,
-        TokenKind.ContainsMethod,
-        TokenKind.DateMethod,
-        TokenKind.DayMethod,
-        TokenKind.EndswithMethod,
-        TokenKind.FloorMethod,
-        TokenKind.FractionalsecondsMethod,
-        TokenKind.GeoDistanceMethod,
-        TokenKind.GeoIntersectsMethod,
-        TokenKind.GeoLengthMethod,
-        TokenKind.HourMethod,
-        TokenKind.IndexofMethod,
-        TokenKind.LengthMethod,
-        TokenKind.MaxdatetimeMethod,
-        TokenKind.MindatetimeMethod,
-        TokenKind.MinuteMethod,
-        TokenKind.MonthMethod,
-        TokenKind.NowMethod,
-        TokenKind.RoundMethod,
-        TokenKind.SecondMethod,
-        TokenKind.StartswithMethod,
-        TokenKind.SubstringMethod,
-        TokenKind.TimeMethod,
-        TokenKind.TolowerMethod,
-        TokenKind.TotaloffsetminutesMethod,
-        TokenKind.TotalsecondsMethod,
-        TokenKind.ToupperMethod,
-        TokenKind.TrimMethod,
-        TokenKind.YearMethod);
-  }
-
   protected static EdmType getType(final Expression expression) throws UriParserException {
     EdmType type;
     if (expression instanceof Literal) {
@@ -1060,11 +1046,12 @@ public class ExpressionParser {
       type = ((BinaryImpl) expression).getType();
     } else if (expression instanceof Method) {
       type = ((MethodImpl) expression).getType();
+    } else if (expression instanceof Alias) {
+      final AliasQueryOption alias = ((AliasImpl) expression).getAlias();
+      type = alias == null || alias.getValue() == null ? null : getType(alias.getValue());
     } else if (expression instanceof LambdaRef) {
       throw new UriParserSemanticException("Type determination not implemented.",
           UriParserSemanticException.MessageKeys.NOT_IMPLEMENTED, expression.toString());
-    } else if (expression instanceof Alias) {
-      type = null; // The alias would have to be available already parsed.
     } else {
       throw new UriParserSemanticException("Unknown expression type.",
           UriParserSemanticException.MessageKeys.NOT_IMPLEMENTED, expression.toString());
@@ -1142,7 +1129,7 @@ public class ExpressionParser {
 
   private Enumeration createEnumExpression(final String primitiveValueLiteral) throws UriParserException {
     final EdmEnumType enumType = getEnumType(primitiveValueLiteral);
-    // TODO: Can the Enumeration interface be changed to handle the value as a whole?
+    // The Enumeration interface could be extended to handle the value as a whole, in line with the primitive type.
     try {
       return new EnumerationImpl(enumType,
           Arrays.asList(enumType.fromUriLiteral(primitiveValueLiteral).split(",")));

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/110c7b0e/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 dd73009..f20b029 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
@@ -19,11 +19,13 @@
 package org.apache.olingo.server.core.uri.parser;
 
 import java.util.Collection;
+import java.util.Map;
 
 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.AliasQueryOption;
 import org.apache.olingo.server.api.uri.queryoption.FilterOption;
 import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
 import org.apache.olingo.server.core.uri.queryoption.FilterOptionImpl;
@@ -40,10 +42,10 @@ public class FilterParser {
   }
 
   public FilterOption parse(UriTokenizer tokenizer, final EdmType referencedType,
-      final Collection<String> crossjoinEntitySetNames)
+      final Collection<String> crossjoinEntitySetNames, final Map<String, AliasQueryOption> aliases)
       throws UriParserException, UriValidationException {
     final Expression filterExpression = new ExpressionParser(edm, odata)
-        .parse(tokenizer, referencedType, crossjoinEntitySetNames);
+        .parse(tokenizer, referencedType, crossjoinEntitySetNames, aliases);
     final EdmType type = ExpressionParser.getType(filterExpression);
     if (type == null || type.equals(odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean))) {
       return new FilterOptionImpl().setExpression(filterExpression);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/110c7b0e/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/OrderByParser.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/OrderByParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/OrderByParser.java
index 5ea8cb7..b008ed6 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/OrderByParser.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/OrderByParser.java
@@ -19,10 +19,12 @@
 package org.apache.olingo.server.core.uri.parser;
 
 import java.util.Collection;
+import java.util.Map;
 
 import org.apache.olingo.commons.api.edm.Edm;
 import org.apache.olingo.commons.api.edm.EdmStructuredType;
 import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.uri.queryoption.AliasQueryOption;
 import org.apache.olingo.server.api.uri.queryoption.OrderByOption;
 import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
 import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
@@ -41,12 +43,12 @@ public class OrderByParser {
   }
 
   public OrderByOption parse(UriTokenizer tokenizer, final EdmStructuredType referencedType,
-      final Collection<String> crossjoinEntitySetNames)
+      final Collection<String> crossjoinEntitySetNames, final Map<String, AliasQueryOption> aliases)
       throws UriParserException, UriValidationException {
     OrderByOptionImpl orderByOption = new OrderByOptionImpl();
     do {
       final Expression orderByExpression = new ExpressionParser(edm, odata)
-          .parse(tokenizer, referencedType, crossjoinEntitySetNames);
+          .parse(tokenizer, referencedType, crossjoinEntitySetNames, aliases);
       OrderByItemImpl item = new OrderByItemImpl();
       item.setExpression(orderByExpression);
       if (tokenizer.next(TokenKind.AscSuffix)) {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/110c7b0e/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 586cb10..e1313c1 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
@@ -20,8 +20,10 @@ package org.apache.olingo.server.core.uri.parser;
 
 import java.util.Collections;
 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.EdmStructuredType;
 import org.apache.olingo.commons.api.edm.EdmType;
 import org.apache.olingo.commons.api.ex.ODataRuntimeException;
@@ -36,21 +38,30 @@ import org.apache.olingo.server.api.uri.UriResourcePartTyped;
 import org.apache.olingo.server.api.uri.UriResourceRef;
 import org.apache.olingo.server.api.uri.UriResourceValue;
 import org.apache.olingo.server.api.uri.queryoption.AliasQueryOption;
-import org.apache.olingo.server.api.uri.queryoption.CustomQueryOption;
+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.FilterOption;
+import org.apache.olingo.server.api.uri.queryoption.OrderByItem;
+import org.apache.olingo.server.api.uri.queryoption.OrderByOption;
 import org.apache.olingo.server.api.uri.queryoption.QueryOption;
+import org.apache.olingo.server.api.uri.queryoption.SelectOption;
 import org.apache.olingo.server.api.uri.queryoption.SystemQueryOption;
 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.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.FilterOptionImpl;
 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.OrderByOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.SelectOptionImpl;
 import org.apache.olingo.server.core.uri.queryoption.SkipOptionImpl;
 import org.apache.olingo.server.core.uri.queryoption.SkipTokenOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.SystemQueryOptionImpl;
 import org.apache.olingo.server.core.uri.queryoption.TopOptionImpl;
 import org.apache.olingo.server.core.uri.validator.UriValidationException;
 
@@ -74,6 +85,32 @@ public class Parser {
       throws UriParserException, UriValidationException {
 
     UriInfoImpl contextUriInfo = new UriInfoImpl();
+
+    // Read the query options (system and custom options).
+    // This is done before parsing the resource path because the aliases have to be available there.
+    // System query options that can only be parsed with context from the resource path will be post-processed later.
+    final List<QueryOption> options =
+        query == null ? Collections.<QueryOption> emptyList() : UriDecoder.splitAndDecodeOptions(query);
+    for (final QueryOption option : options) {
+      final String optionName = option.getName();
+      // Parse the untyped option and retrieve a system-option or alias-option instance (or null for a custom option).
+      final QueryOption parsedOption = parseOption(optionName, option.getText());
+      try {
+        contextUriInfo.setQueryOption(parsedOption == null ? option : parsedOption);
+      } catch (final ODataRuntimeException e) {
+          throw new UriParserSyntaxException(
+              parsedOption instanceof SystemQueryOption ?
+                  "Double system query option!" :
+                  "Alias already specified! Name: " + optionName,
+              e,
+              parsedOption instanceof SystemQueryOption ?
+                  UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION :
+                  UriParserSyntaxException.MessageKeys.DUPLICATED_ALIAS,
+              optionName);
+      }
+    }
+
+    // Read the decoded path segments.
     EdmType contextType = null;
     boolean contextIsCollection = false;
 
@@ -85,7 +122,6 @@ public class Parser {
       numberOfSegments--;
     }
 
-    // first, read the decoded path segments
     final String firstSegment = pathSegmentsDecoded.get(0);
 
     if (firstSegment.isEmpty()) {
@@ -107,24 +143,29 @@ public class Parser {
       contextIsCollection = true;
 
     } else if (firstSegment.equals("$entity")) {
+      contextUriInfo.setKind(UriInfoKind.entityId);
       if (numberOfSegments > 1) {
         final String typeCastSegment = pathSegmentsDecoded.get(1);
         ensureLastSegment(typeCastSegment, 2, numberOfSegments);
-        contextUriInfo = new ResourcePathParser(edm).parseDollarEntityTypeCast(typeCastSegment);
-        contextType = contextUriInfo.getEntityTypeCast();
-      } else {
-        contextUriInfo.setKind(UriInfoKind.entityId);
+        contextType = new ResourcePathParser(edm, contextUriInfo.getAliasMap())
+            .parseDollarEntityTypeCast(typeCastSegment);
+        contextUriInfo.setEntityTypeCast((EdmEntityType) contextType);
       }
       contextIsCollection = false;
 
     } else if (firstSegment.startsWith("$crossjoin")) {
       ensureLastSegment(firstSegment, 1, numberOfSegments);
-      contextUriInfo = new ResourcePathParser(edm).parseCrossjoinSegment(firstSegment);
+      contextUriInfo.setKind(UriInfoKind.crossjoin);
+      final List<String> entitySetNames = new ResourcePathParser(edm, contextUriInfo.getAliasMap())
+          .parseCrossjoinSegment(firstSegment);
+      for (final String name : entitySetNames) {
+        contextUriInfo.addEntitySetName(name);
+      }
       contextIsCollection = true;
 
     } else {
       contextUriInfo.setKind(UriInfoKind.resource);
-      final ResourcePathParser resourcePathParser = new ResourcePathParser(edm);
+      final ResourcePathParser resourcePathParser = new ResourcePathParser(edm, contextUriInfo.getAliasMap());
       int count = 0;
       UriResource lastSegment = null;
       for (final String pathSegment : pathSegmentsDecoded) {
@@ -162,161 +203,168 @@ public class Parser {
       }
     }
 
-    // second, read the system query options and the custom query options
-    final List<QueryOption> options =
-        query == null ? Collections.<QueryOption> emptyList() : UriDecoder.splitAndDecodeOptions(query);
-    for (final QueryOption option : options) {
-      final String optionName = option.getName();
-      final String optionValue = option.getText();
-      if (optionName.startsWith(DOLLAR)) {
-        SystemQueryOption systemOption = null;
-        if (optionName.equals(SystemQueryOptionKind.FILTER.toString())) {
-          UriTokenizer filterTokenizer = new UriTokenizer(optionValue);
-          // The referring type could be a primitive type or a structured type.
-          systemOption = new FilterParser(edm, odata).parse(filterTokenizer,
-              contextType,
-              contextUriInfo.getEntitySetNames());
-          checkOptionEOF(filterTokenizer, optionName, optionValue);
-
-        } else if (optionName.equals(SystemQueryOptionKind.FORMAT.toString())) {
-          FormatOptionImpl formatOption = new FormatOptionImpl();
-          formatOption.setText(optionValue);
-          if (optionValue.equalsIgnoreCase(JSON)
-              || optionValue.equalsIgnoreCase(XML)
-              || optionValue.equalsIgnoreCase(ATOM)
-              || isFormatSyntaxValid(optionValue)) {
-            formatOption.setFormat(optionValue);
-          } else {
-            throw new UriParserSyntaxException("Illegal value of $format option!",
-                UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION_FORMAT, optionValue);
-          }
-          systemOption = formatOption;
-
-        } else if (optionName.equals(SystemQueryOptionKind.EXPAND.toString())) {
-          if (contextType instanceof EdmStructuredType
-              || !contextUriInfo.getEntitySetNames().isEmpty()
-              || contextUriInfo.getKind() == UriInfoKind.all) {
-            UriTokenizer expandTokenizer = new UriTokenizer(optionValue);
-            systemOption = new ExpandParser(edm, odata).parse(expandTokenizer,
-                contextType instanceof EdmStructuredType ? (EdmStructuredType) contextType : 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;
-
-        } else if (optionName.equals(SystemQueryOptionKind.LEVELS.toString())) {
-          throw new UriParserSyntaxException("System query option '$levels' is allowed only inside '$expand'!",
-              UriParserSyntaxException.MessageKeys.SYSTEM_QUERY_OPTION_LEVELS_NOT_ALLOWED_HERE);
-
-        } else if (optionName.equals(SystemQueryOptionKind.ORDERBY.toString())) {
-          UriTokenizer orderByTokenizer = new UriTokenizer(optionValue);
-          systemOption = new OrderByParser(edm, odata).parse(orderByTokenizer,
-              contextType instanceof EdmStructuredType ? (EdmStructuredType) contextType : null,
-              contextUriInfo.getEntitySetNames());
-          checkOptionEOF(orderByTokenizer, optionName, optionValue);
-
-        } else if (optionName.equals(SystemQueryOptionKind.SEARCH.toString())) {
-          systemOption = new SearchParser().parse(optionValue);
-
-        } else if (optionName.equals(SystemQueryOptionKind.SELECT.toString())) {
-          UriTokenizer selectTokenizer = new UriTokenizer(optionValue);
-          systemOption = new SelectParser(edm).parse(selectTokenizer,
-              contextType instanceof EdmStructuredType ? (EdmStructuredType) contextType : null,
-              contextIsCollection);
-          checkOptionEOF(selectTokenizer, optionName, optionValue);
-
-        } else if (optionName.equals(SystemQueryOptionKind.SKIP.toString())) {
-          SkipOptionImpl skipOption = new SkipOptionImpl();
-          skipOption.setText(optionValue);
-          skipOption.setValue(ParserHelper.parseNonNegativeInteger(optionName, optionValue, true));
-          systemOption = skipOption;
+    // Post-process system query options that need context information from the resource path.
+    parseFilterOption(contextUriInfo.getFilterOption(), contextType,
+        contextUriInfo.getEntitySetNames(), contextUriInfo.getAliasMap());
+    parseOrderByOption(contextUriInfo.getOrderByOption(), contextType,
+        contextUriInfo.getEntitySetNames(), contextUriInfo.getAliasMap());
+    parseExpandOption(contextUriInfo.getExpandOption(), contextType,
+        !contextUriInfo.getEntitySetNames().isEmpty() || contextUriInfo.getKind() == UriInfoKind.all,
+        contextUriInfo.getAliasMap());
+    parseSelectOption(contextUriInfo.getSelectOption(), contextType, contextIsCollection);
 
-        } 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);
-          topOption.setValue(ParserHelper.parseNonNegativeInteger(optionName, optionValue, true));
-          systemOption = topOption;
-
-        } else if (optionName.equals(SystemQueryOptionKind.COUNT.toString())) {
-          CountOptionImpl inlineCountOption = new CountOptionImpl();
-          inlineCountOption.setText(optionValue);
-          if (optionValue.equals("true") || optionValue.equals("false")) {
-            inlineCountOption.setValue(Boolean.parseBoolean(optionValue));
-          } else {
-            throw new UriParserSyntaxException("Illegal value of $count option!",
-                UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
-                optionName, optionValue);
-          }
-          systemOption = inlineCountOption;
+    return contextUriInfo;
+  }
 
+  private QueryOption parseOption(final String optionName, final String optionValue)
+      throws UriParserException, UriValidationException {
+    if (optionName.startsWith(DOLLAR)) {
+      final SystemQueryOptionKind kind = SystemQueryOptionKind.get(optionName);
+      if (kind == null) {
+        throw new UriParserSyntaxException("Unknown system query option!",
+            UriParserSyntaxException.MessageKeys.UNKNOWN_SYSTEM_QUERY_OPTION, optionName);
+      }
+      SystemQueryOption systemOption = null;
+      switch (kind) {
+      case SEARCH:
+        systemOption = new SearchParser().parse(optionValue);
+        break;
+      case FILTER:
+        systemOption = new FilterOptionImpl();
+        break;
+      case COUNT:
+        if (optionValue.equals("true") || optionValue.equals("false")) {
+          systemOption = new CountOptionImpl().setValue(Boolean.parseBoolean(optionValue));
         } else {
-          throw new UriParserSyntaxException("Unknown system query option!",
-              UriParserSyntaxException.MessageKeys.UNKNOWN_SYSTEM_QUERY_OPTION, optionName);
+          throw new UriParserSyntaxException("Illegal value of $count option!",
+              UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
+              optionName, optionValue);
         }
-        try {
-          contextUriInfo.setSystemQueryOption(systemOption);
-        } catch (final ODataRuntimeException e) {
-          throw new UriParserSyntaxException("Double system query option!", e,
-              UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION, optionName);
+        break;
+      case ORDERBY:
+        systemOption = new OrderByOptionImpl();
+        break;
+      case SKIP:
+        systemOption = new SkipOptionImpl()
+            .setValue(ParserHelper.parseNonNegativeInteger(optionName, optionValue, true));
+        break;
+      case SKIPTOKEN:
+        if (optionValue.isEmpty()) {
+          throw new UriParserSyntaxException("Illegal value of $skiptoken option!",
+              UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
+              optionName, optionValue);
         }
-
-      } else if (optionName.startsWith(AT)) {
-        if (contextUriInfo.getAlias(optionName) == null) {
-          // TODO: Aliases can only be parsed in the context of their usage.
-          Expression expression = null;
-          UriTokenizer aliasTokenizer = new UriTokenizer(optionValue);
-          if (aliasTokenizer.next(TokenKind.jsonArrayOrObject)) {
-            if (!aliasTokenizer.next(TokenKind.EOF)) {
-              throw new UriParserSyntaxException("Illegal value for alias '" + optionName + "'.",
-                  UriParserSyntaxException.MessageKeys.SYNTAX);
-            }
-          } else {
-            UriTokenizer aliasValueTokenizer = new UriTokenizer(optionValue);
-            expression = new ExpressionParser(edm, odata).parse(aliasValueTokenizer, null,
-                contextUriInfo.getEntitySetNames());
-            if (!aliasValueTokenizer.next(TokenKind.EOF)) {
-              throw new UriParserSyntaxException("Illegal value for alias '" + optionName + "'.",
-                  UriParserSyntaxException.MessageKeys.SYNTAX);
-            }
-          }
-          contextUriInfo.addAlias((AliasQueryOption) new AliasQueryOptionImpl()
-              .setAliasValue(expression)
-              .setName(optionName)
-              .setText(NULL.equals(optionValue) ? null : optionValue));
+        systemOption = new SkipTokenOptionImpl().setValue(optionValue);
+        break;
+      case TOP:
+        systemOption = new TopOptionImpl()
+            .setValue(ParserHelper.parseNonNegativeInteger(optionName, optionValue, true));
+        break;
+      case EXPAND:
+        systemOption = new ExpandOptionImpl();
+        break;
+      case SELECT:
+        systemOption = new SelectOptionImpl();
+        break;
+      case FORMAT:
+        if (optionValue.equalsIgnoreCase(JSON)
+            || optionValue.equalsIgnoreCase(XML)
+            || optionValue.equalsIgnoreCase(ATOM)
+            || isFormatSyntaxValid(optionValue)) {
+          systemOption = new FormatOptionImpl().setFormat(optionValue);
         } else {
-          throw new UriParserSyntaxException("Alias already specified! Name: " + optionName,
-              UriParserSyntaxException.MessageKeys.DUPLICATED_ALIAS, optionName);
+          throw new UriParserSyntaxException("Illegal value of $format option!",
+              UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION_FORMAT, optionValue);
         }
+        break;
+      case ID:
+        if (optionValue.isEmpty()) {
+          throw new UriParserSyntaxException("Illegal value of $id option!",
+              UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
+              optionName, optionValue);
+        }
+        systemOption = new IdOptionImpl().setValue(optionValue);
+        break;
+      case LEVELS:
+        throw new UriParserSyntaxException("System query option '$levels' is allowed only inside '$expand'!",
+            UriParserSyntaxException.MessageKeys.SYSTEM_QUERY_OPTION_LEVELS_NOT_ALLOWED_HERE);
+      }
+      ((SystemQueryOptionImpl) systemOption).setText(optionValue);
+      return systemOption;
 
-      } else if (!optionName.isEmpty()) {
-        contextUriInfo.addCustomQueryOption((CustomQueryOption) option);
+    } else if (optionName.startsWith(AT)) {
+      // Aliases can only be parsed in the context of their usage, so the value is not checked here.
+      return new AliasQueryOptionImpl()
+          .setName(optionName)
+          .setText(NULL.equals(optionValue) ? null : optionValue);
+
+    } else {
+      // The option is a custom query option; the caller can re-use its query option.
+      return null;
+    }
+  }
+
+  private void parseFilterOption(FilterOption filterOption, final EdmType contextType,
+      final List<String> entitySetNames, final Map<String, AliasQueryOption> aliases)
+      throws UriParserException, UriValidationException {
+    if (filterOption != null) {
+      final String optionValue = filterOption.getText();
+      UriTokenizer filterTokenizer = new UriTokenizer(optionValue);
+      // The referring type could be a primitive type or a structured type.
+      ((FilterOptionImpl) filterOption).setExpression(
+          new FilterParser(edm, odata).parse(filterTokenizer, contextType, entitySetNames, aliases)
+              .getExpression());
+      checkOptionEOF(filterTokenizer, filterOption.getName(), optionValue);
+    }
+  }
+
+  private void parseOrderByOption(OrderByOption orderByOption, final EdmType contextType,
+      final List<String> entitySetNames, final Map<String, AliasQueryOption> aliases)
+      throws UriParserException, UriValidationException {
+    if (orderByOption != null) {
+      final String optionValue = orderByOption.getText();
+      UriTokenizer orderByTokenizer = new UriTokenizer(optionValue);
+      final OrderByOption option = new OrderByParser(edm, odata).parse(orderByTokenizer,
+          contextType instanceof EdmStructuredType ? (EdmStructuredType) contextType : null,
+          entitySetNames,
+          aliases);
+      checkOptionEOF(orderByTokenizer, orderByOption.getName(), optionValue);
+      for (final OrderByItem item : option.getOrders()) {
+        ((OrderByOptionImpl) orderByOption).addOrder(item);
       }
     }
+  }
 
-    return contextUriInfo;
+  private void parseExpandOption(ExpandOption expandOption, final EdmType contextType, final boolean isCrossjoinOrAll,
+      final Map<String, AliasQueryOption> aliases) throws UriParserException, UriValidationException {
+    if (expandOption != null) {
+      if (!(contextType instanceof EdmStructuredType || isCrossjoinOrAll)) {
+        throw new UriValidationException("Expand is only allowed on structured types!",
+            UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED, expandOption.getName());
+      }
+      final String optionValue = expandOption.getText();
+      UriTokenizer expandTokenizer = new UriTokenizer(optionValue);
+      final ExpandOption option = new ExpandParser(edm, odata, aliases).parse(expandTokenizer,
+          contextType instanceof EdmStructuredType ? (EdmStructuredType) contextType : null);
+      checkOptionEOF(expandTokenizer, expandOption.getName(), optionValue);
+      for (final ExpandItem item : option.getExpandItems()) {
+        ((ExpandOptionImpl) expandOption).addExpandItem(item);
+      }
+    }
+  }
+
+  private void parseSelectOption(SelectOption selectOption, final EdmType contextType,
+      final boolean contextIsCollection) throws UriParserException, UriValidationException {
+    if (selectOption != null) {
+      final String optionValue = selectOption.getText();
+      UriTokenizer selectTokenizer = new UriTokenizer(optionValue);
+      ((SelectOptionImpl) selectOption).setSelectItems(
+          new SelectParser(edm).parse(selectTokenizer,
+              contextType instanceof EdmStructuredType ? (EdmStructuredType) contextType : null,
+              contextIsCollection)
+              .getSelectItems());
+      checkOptionEOF(selectTokenizer, selectOption.getName(), optionValue);
+    }
   }
 
   private void ensureLastSegment(final String segment, final int pos, final int size)

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/110c7b0e/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 b0c2972..9986542 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
@@ -20,6 +20,7 @@ package org.apache.olingo.server.core.uri.parser;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.EnumMap;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -27,8 +28,10 @@ import java.util.Map.Entry;
 
 import org.apache.olingo.commons.api.edm.Edm;
 import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmFunction;
 import org.apache.olingo.commons.api.edm.EdmKeyPropertyRef;
 import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
+import org.apache.olingo.commons.api.edm.EdmParameter;
 import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
 import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
 import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
@@ -39,6 +42,7 @@ 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.AliasQueryOption;
 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;
@@ -46,6 +50,8 @@ import org.apache.olingo.server.core.uri.UriParameterImpl;
 import org.apache.olingo.server.core.uri.UriResourceTypedImpl;
 import org.apache.olingo.server.core.uri.UriResourceWithKeysImpl;
 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.queryoption.expression.LiteralImpl;
 import org.apache.olingo.server.core.uri.validator.UriValidationException;
 
 public class ParserHelper {
@@ -55,7 +61,7 @@ public class ParserHelper {
   protected static final Map<TokenKind, EdmPrimitiveTypeKind> tokenToPrimitiveType;
   static {
     /* Enum and null are not present in the map. These have to be handled differently. */
-    Map<TokenKind, EdmPrimitiveTypeKind> temp = new HashMap<TokenKind, EdmPrimitiveTypeKind>();
+    Map<TokenKind, EdmPrimitiveTypeKind> temp = new EnumMap<TokenKind, EdmPrimitiveTypeKind>(TokenKind.class);
     temp.put(TokenKind.BooleanValue, EdmPrimitiveTypeKind.Boolean);
     temp.put(TokenKind.StringValue, EdmPrimitiveTypeKind.String);
     // Very large integer values are of type Edm.Decimal but this is handled elsewhere.
@@ -87,18 +93,18 @@ public class ParserHelper {
     tokenToPrimitiveType = Collections.unmodifiableMap(temp);
   }
 
-  public static void requireNext(UriTokenizer tokenizer, final TokenKind required) throws UriParserException {
+  protected static void requireNext(UriTokenizer tokenizer, final TokenKind required) throws UriParserException {
     if (!tokenizer.next(required)) {
       throw new UriParserSyntaxException("Expected token '" + required.toString() + "' not found.",
           UriParserSyntaxException.MessageKeys.SYNTAX);
     }
   }
 
-  public static void requireTokenEnd(UriTokenizer tokenizer) throws UriParserException {
+  protected static void requireTokenEnd(UriTokenizer tokenizer) throws UriParserException {
     requireNext(tokenizer, TokenKind.EOF);
   }
 
-  public static TokenKind next(UriTokenizer tokenizer, final TokenKind... kinds) {
+  protected static TokenKind next(UriTokenizer tokenizer, final TokenKind... kinds) {
     for (final TokenKind kind : kinds) {
       if (tokenizer.next(kind)) {
         return kind;
@@ -107,7 +113,7 @@ public class ParserHelper {
     return null;
   }
 
-  public static TokenKind nextPrimitiveValue(UriTokenizer tokenizer) {
+  protected static TokenKind nextPrimitiveValue(UriTokenizer tokenizer) {
     return next(tokenizer,
         TokenKind.NULL,
         TokenKind.BooleanValue,
@@ -146,7 +152,8 @@ public class ParserHelper {
   }
 
   protected static List<UriParameter> parseFunctionParameters(UriTokenizer tokenizer,
-      final Edm edm, final EdmType referringType, final boolean withComplex)
+      final Edm edm, final EdmType referringType, final boolean withComplex,
+      final Map<String, AliasQueryOption> aliases)
       throws UriParserException, UriValidationException {
     List<UriParameter> parameters = new ArrayList<UriParameter>();
     ParserHelper.requireNext(tokenizer, TokenKind.OPEN);
@@ -164,40 +171,105 @@ public class ParserHelper {
       if (tokenizer.next(TokenKind.COMMA) || tokenizer.next(TokenKind.CLOSE) || tokenizer.next(TokenKind.EOF)) {
         throw new UriParserSyntaxException("Parameter value expected.", UriParserSyntaxException.MessageKeys.SYNTAX);
       }
+      UriParameterImpl parameter = new UriParameterImpl().setName(name);
       if (tokenizer.next(TokenKind.ParameterAliasName)) {
-        parameters.add(new UriParameterImpl().setName(name).setAlias(tokenizer.getText()));
+        final String aliasName = tokenizer.getText();
+        parameter.setAlias(aliasName)
+            .setExpression(aliases.containsKey(aliasName) ? aliases.get(aliasName).getValue() : null);
       } else if (tokenizer.next(TokenKind.jsonArrayOrObject)) {
         if (withComplex) {
-          parameters.add(new UriParameterImpl().setName(name).setText(tokenizer.getText()));
+          parameter.setText(tokenizer.getText());
         } else {
           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));
+        final Expression expression = new ExpressionParser(edm, odata).parse(tokenizer, referringType, null, aliases);
+        parameter.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, "");
       } else {
         final String literalValue = tokenizer.getText();
-        parameters.add(new UriParameterImpl().setName(name)
-            .setText("null".equals(literalValue) ? null : literalValue));
+        parameter.setText("null".equals(literalValue) ? null : literalValue);
       }
+      parameters.add(parameter);
     } while (tokenizer.next(TokenKind.COMMA));
     ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
     return parameters;
   }
 
+  protected static void validateFunctionParameters(final EdmFunction function, final List<UriParameter> parameters,
+      final Edm edm, final EdmType referringType, final Map<String, AliasQueryOption> aliases)
+      throws UriParserException, UriValidationException {
+    for (final UriParameter parameter : parameters) {
+      final String parameterName = parameter.getName();
+      final EdmParameter edmParameter = function.getParameter(parameterName);
+      final boolean isNullable = edmParameter.isNullable();
+      if (parameter.getText() == null && parameter.getExpression() == null && !isNullable) {
+        if (parameter.getAlias() == null) {
+          // No alias, value is explicitly null.
+          throw new UriValidationException("Missing non-nullable parameter " + parameterName,
+              UriValidationException.MessageKeys.MISSING_PARAMETER, parameterName);
+        } else {
+          final String valueForAlias = aliases.containsKey(parameter.getAlias()) ?
+              parseAliasValue(parameter.getAlias(),
+                  edmParameter.getType(), edmParameter.isNullable(), edmParameter.isCollection(),
+                  edm, referringType, aliases).getText() :
+              null;
+          // Alias value is missing or explicitly null.
+          if (valueForAlias == null) {
+            throw new UriValidationException("Missing alias for " + parameterName,
+                UriValidationException.MessageKeys.MISSING_ALIAS, parameter.getAlias());
+          }
+        }
+      }
+    }
+  }
+
+  protected static AliasQueryOption parseAliasValue(final String name, final EdmType type, final boolean isNullable,
+      final boolean isCollection, final Edm edm, final EdmType referringType,
+      final Map<String, AliasQueryOption> aliases) throws UriParserException, UriValidationException {
+    final EdmTypeKind kind = type == null ? null : type.getKind();
+    final AliasQueryOption alias = aliases.get(name);
+    if (alias != null && alias.getText() != null) {
+      UriTokenizer aliasTokenizer = new UriTokenizer(alias.getText());
+      if (kind == null
+          || !((isCollection || kind == EdmTypeKind.COMPLEX || kind == EdmTypeKind.ENTITY ?
+          aliasTokenizer.next(TokenKind.jsonArrayOrObject) :
+          nextPrimitiveTypeValue(aliasTokenizer, (EdmPrimitiveType) type, isNullable))
+          && aliasTokenizer.next(TokenKind.EOF))) {
+        // The alias value is not an allowed literal value, so parse it again as expression.
+        aliasTokenizer = new UriTokenizer(alias.getText());
+        // Don't pass on the current alias to avoid circular references.
+        Map<String, AliasQueryOption> aliasesInner = new HashMap<String, AliasQueryOption>(aliases);
+        aliasesInner.remove(name);
+        final Expression expression = new ExpressionParser(edm, odata)
+            .parse(aliasTokenizer, referringType, null, aliasesInner);
+        final EdmType expressionType = ExpressionParser.getType(expression);
+        if (aliasTokenizer.next(TokenKind.EOF)
+            && (expressionType == null || type == null || expressionType.equals(type))) {
+          ((AliasQueryOptionImpl) alias).setAliasValue(expression);
+        } else {
+          throw new UriParserSemanticException("Illegal value for alias '" + alias.getName() + "'.",
+              UriParserSemanticException.MessageKeys.UNKNOWN_PART, alias.getText());
+        }
+      }
+    }
+    return alias;
+  }
+
   protected static List<UriParameter> parseNavigationKeyPredicate(UriTokenizer tokenizer,
-      final EdmNavigationProperty navigationProperty) throws UriParserException, UriValidationException {
+      final EdmNavigationProperty navigationProperty,
+      final Edm edm, final EdmType referringType, final Map<String, AliasQueryOption> aliases)
+      throws UriParserException, UriValidationException {
     if (tokenizer.next(TokenKind.OPEN)) {
       if (navigationProperty.isCollection()) {
-        return parseKeyPredicate(tokenizer, navigationProperty.getType(), navigationProperty.getPartner());
+        return parseKeyPredicate(tokenizer, navigationProperty.getType(), navigationProperty.getPartner(),
+            edm, referringType, aliases);
       } else {
         throw new UriParserSemanticException("A key is not allowed on non-collection navigation properties.",
             UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED);
@@ -207,7 +279,9 @@ public class ParserHelper {
   }
 
   protected static List<UriParameter> parseKeyPredicate(UriTokenizer tokenizer, final EdmEntityType edmEntityType,
-      final EdmNavigationProperty partner) throws UriParserException, UriValidationException {
+      final EdmNavigationProperty partner,
+      final Edm edm, final EdmType referringType, final Map<String, AliasQueryOption> aliases)
+      throws UriParserException, UriValidationException {
     final List<EdmKeyPropertyRef> keyPropertyRefs = edmEntityType.getKeyPropertyRefs();
     if (tokenizer.next(TokenKind.CLOSE)) {
       throw new UriParserSemanticException(
@@ -229,11 +303,11 @@ public class ParserHelper {
     }
 
     if (tokenizer.next(TokenKind.ODataIdentifier)) {
-      keys.addAll(compoundKey(tokenizer, edmEntityType));
+      keys.addAll(compoundKey(tokenizer, edmEntityType, edm, referringType, aliases));
     } else if (keyPropertyRefs.size() - referencedNames.size() == 1) {
       for (final EdmKeyPropertyRef candidate : keyPropertyRefs) {
         if (referencedNames.get(candidate.getName()) == null) {
-          keys.add(simpleKey(tokenizer, candidate));
+          keys.add(simpleKey(tokenizer, candidate, edm, referringType, aliases));
           break;
         }
       }
@@ -271,7 +345,8 @@ public class ParserHelper {
     }
   }
 
-  private static UriParameter simpleKey(UriTokenizer tokenizer, final EdmKeyPropertyRef edmKeyPropertyRef)
+  private static UriParameter simpleKey(UriTokenizer tokenizer, final EdmKeyPropertyRef edmKeyPropertyRef,
+      final Edm edm, final EdmType referringType, final Map<String, AliasQueryOption> aliases)
       throws UriParserException, UriValidationException {
     final EdmProperty edmProperty = edmKeyPropertyRef == null ? null : edmKeyPropertyRef.getProperty();
     if (nextPrimitiveTypeValue(tokenizer,
@@ -279,14 +354,15 @@ public class ParserHelper {
         edmProperty == null ? false : edmProperty.isNullable())) {
       final String literalValue = tokenizer.getText();
       ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
-      return createUriParameter(edmProperty, edmKeyPropertyRef.getName(), literalValue);
+      return createUriParameter(edmProperty, edmKeyPropertyRef.getName(), literalValue, edm, referringType, aliases);
     } else {
       throw new UriParserSemanticException("The key value is not valid.",
           UriParserSemanticException.MessageKeys.INVALID_KEY_VALUE, edmKeyPropertyRef.getName());
     }
   }
 
-  private static List<UriParameter> compoundKey(UriTokenizer tokenizer, final EdmEntityType edmEntityType)
+  private static List<UriParameter> compoundKey(UriTokenizer tokenizer, final EdmEntityType edmEntityType,
+      final Edm edm, final EdmType referringType, final Map<String, AliasQueryOption> aliases)
       throws UriParserException, UriValidationException {
 
     List<UriParameter> parameters = new ArrayList<UriParameter>();
@@ -312,7 +388,7 @@ public class ParserHelper {
         throw new UriValidationException("Unknown key property " + keyPredicateName,
             UriValidationException.MessageKeys.INVALID_KEY_PROPERTY, keyPredicateName);
       }
-      parameters.add(keyValuePair(tokenizer, keyPredicateName, edmEntityType));
+      parameters.add(keyValuePair(tokenizer, keyPredicateName, edmEntityType, edm, referringType, aliases));
       parameterNames.add(keyPredicateName);
       hasComma = tokenizer.next(TokenKind.COMMA);
       if (hasComma) {
@@ -324,8 +400,9 @@ public class ParserHelper {
     return parameters;
   }
 
-  protected static UriParameter keyValuePair(UriTokenizer tokenizer,
-      final String keyPredicateName, final EdmEntityType edmEntityType)
+  private static UriParameter keyValuePair(UriTokenizer tokenizer,
+      final String keyPredicateName, final EdmEntityType edmEntityType,
+      final Edm edm, final EdmType referringType, final Map<String, AliasQueryOption> aliases)
       throws UriParserException, UriValidationException {
     final EdmKeyPropertyRef keyPropertyRef = edmEntityType.getKeyPropertyRef(keyPredicateName);
     final EdmProperty edmProperty = keyPropertyRef == null ? null : keyPropertyRef.getProperty();
@@ -338,7 +415,7 @@ public class ParserHelper {
       throw new UriParserSyntaxException("Key value expected.", UriParserSyntaxException.MessageKeys.SYNTAX);
     }
     if (nextPrimitiveTypeValue(tokenizer, (EdmPrimitiveType) edmProperty.getType(), edmProperty.isNullable())) {
-      return createUriParameter(edmProperty, keyPredicateName, tokenizer.getText());
+      return createUriParameter(edmProperty, keyPredicateName, tokenizer.getText(), edm, referringType, aliases);
     } else {
       throw new UriParserSemanticException(keyPredicateName + " has not a valid  key value.",
           UriParserSemanticException.MessageKeys.INVALID_KEY_VALUE, keyPredicateName);
@@ -346,28 +423,43 @@ public class ParserHelper {
   }
 
   private static UriParameter createUriParameter(final EdmProperty edmProperty, final String parameterName,
-      final String literalValue) throws UriParserException, UriValidationException {
-    if (literalValue.startsWith("@")) {
-      return new UriParameterImpl()
-          .setName(parameterName)
-          .setAlias(literalValue);
-    }
-
+      final String literalValue, final Edm edm, final EdmType referringType,
+      final Map<String, AliasQueryOption> aliases) throws UriParserException, UriValidationException {
+    final AliasQueryOption alias = literalValue.startsWith("@") ?
+        getKeyAlias(literalValue, edmProperty, edm, referringType, aliases) :
+        null;
+    final String value = alias == null ? literalValue : alias.getText();
     final EdmPrimitiveType primitiveType = (EdmPrimitiveType) edmProperty.getType();
     try {
-      if (!(primitiveType.validate(primitiveType.fromUriLiteral(literalValue), edmProperty.isNullable(),
+      if (!(primitiveType.validate(primitiveType.fromUriLiteral(value), edmProperty.isNullable(),
           edmProperty.getMaxLength(), edmProperty.getPrecision(), edmProperty.getScale(), edmProperty.isUnicode()))) {
         throw new UriValidationException("Invalid key property",
             UriValidationException.MessageKeys.INVALID_KEY_PROPERTY, parameterName);
       }
     } catch (final EdmPrimitiveTypeException e) {
-      throw new UriValidationException("Invalid key property",
+      throw new UriValidationException("Invalid key property", e,
           UriValidationException.MessageKeys.INVALID_KEY_PROPERTY, parameterName);
     }
 
     return new UriParameterImpl()
         .setName(parameterName)
-        .setText("null".equals(literalValue) ? null : literalValue);
+        .setText("null".equals(literalValue) ? null : literalValue)
+        .setAlias(alias == null ? null : literalValue)
+        .setExpression(alias == null ? null :
+            alias.getValue() == null ? new LiteralImpl(value, primitiveType) : alias.getValue());
+  }
+
+  private static AliasQueryOption getKeyAlias(final String name, final EdmProperty edmProperty,
+      final Edm edm, final EdmType referringType, final Map<String, AliasQueryOption> aliases)
+      throws UriParserException, UriValidationException {
+    if (aliases.containsKey(name)) {
+      return parseAliasValue(name,
+          edmProperty.getType(), edmProperty.isNullable(), edmProperty.isCollection(),
+          edm, referringType, aliases);
+    } else {
+      throw new UriValidationException("Alias '" + name + "' for key value not found.",
+          UriValidationException.MessageKeys.MISSING_ALIAS, name);
+    }
   }
 
   private static boolean nextPrimitiveTypeValue(UriTokenizer tokenizer,

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/110c7b0e/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 87cb91a..8d6d52d 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
@@ -18,7 +18,9 @@
  */
 package org.apache.olingo.server.core.uri.parser;
 
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.olingo.commons.api.edm.Edm;
 import org.apache.olingo.commons.api.edm.EdmAction;
@@ -35,11 +37,10 @@ import org.apache.olingo.commons.api.edm.EdmStructuredType;
 import org.apache.olingo.commons.api.edm.EdmType;
 import org.apache.olingo.commons.api.edm.FullQualifiedName;
 import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
-import org.apache.olingo.server.api.uri.UriInfoKind;
 import org.apache.olingo.server.api.uri.UriParameter;
 import org.apache.olingo.server.api.uri.UriResource;
 import org.apache.olingo.server.api.uri.UriResourcePartTyped;
-import org.apache.olingo.server.core.uri.UriInfoImpl;
+import org.apache.olingo.server.api.uri.queryoption.AliasQueryOption;
 import org.apache.olingo.server.core.uri.UriResourceActionImpl;
 import org.apache.olingo.server.core.uri.UriResourceComplexPropertyImpl;
 import org.apache.olingo.server.core.uri.UriResourceCountImpl;
@@ -59,10 +60,12 @@ public class ResourcePathParser {
 
   private final Edm edm;
   private final EdmEntityContainer edmEntityContainer;
+  private final Map<String, AliasQueryOption> aliases;
   private UriTokenizer tokenizer;
 
-  public ResourcePathParser(final Edm edm) {
+  public ResourcePathParser(final Edm edm, final Map<String, AliasQueryOption> aliases) {
     this.edm = edm;
+    this.aliases = aliases;
     edmEntityContainer = edm.getEntityContainer();
   }
 
@@ -100,8 +103,7 @@ public class ResourcePathParser {
         UriParserSyntaxException.MessageKeys.SYNTAX);
   }
 
-  public UriInfoImpl parseDollarEntityTypeCast(final String pathSegment) throws UriParserException {
-    UriInfoImpl uriInfo = new UriInfoImpl().setKind(UriInfoKind.entityId);
+  public EdmEntityType parseDollarEntityTypeCast(final String pathSegment) throws UriParserException {
     tokenizer = new UriTokenizer(pathSegment);
     ParserHelper.requireNext(tokenizer, TokenKind.QualifiedName);
     final String name = tokenizer.getText();
@@ -110,18 +112,16 @@ public class ResourcePathParser {
     if (type == null) {
       throw new UriParserSemanticException("Type '" + name + "' not found.",
           UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, name);
-    } else {
-      uriInfo.setEntityTypeCast(type);
     }
-    return uriInfo;
+    return type;
   }
 
-  public UriInfoImpl parseCrossjoinSegment(final String pathSegment) throws UriParserException {
-    UriInfoImpl uriInfo = new UriInfoImpl().setKind(UriInfoKind.crossjoin);
+  public List<String> parseCrossjoinSegment(final String pathSegment) throws UriParserException {
     tokenizer = new UriTokenizer(pathSegment);
     ParserHelper.requireNext(tokenizer, TokenKind.CROSSJOIN);
     ParserHelper.requireNext(tokenizer, TokenKind.OPEN);
     // At least one entity-set name is mandatory.  Try to fetch all.
+    List<String> entitySetNames = new ArrayList<String>();
     do {
       ParserHelper.requireNext(tokenizer, TokenKind.ODataIdentifier);
       final String name = tokenizer.getText();
@@ -130,12 +130,12 @@ public class ResourcePathParser {
         throw new UriParserSemanticException("Expected Entity Set Name.",
             UriParserSemanticException.MessageKeys.UNKNOWN_PART, name);
       } else {
-        uriInfo.addEntitySetName(name);
+        entitySetNames.add(name);
       }
     } while (tokenizer.next(TokenKind.COMMA));
     ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
     ParserHelper.requireTokenEnd(tokenizer);
-    return uriInfo;
+    return entitySetNames;
   }
 
   private UriResource ref(final UriResource previous) throws UriParserException {
@@ -180,7 +180,7 @@ public class ResourcePathParser {
 
       if (tokenizer.next(TokenKind.OPEN)) {
         final List<UriParameter> keyPredicates =
-            ParserHelper.parseKeyPredicate(tokenizer, entitySetResource.getEntityType(), null);
+            ParserHelper.parseKeyPredicate(tokenizer, entitySetResource.getEntityType(), null, edm, null, aliases);
         entitySetResource.setKeyPredicates(keyPredicates);
       }
 
@@ -251,7 +251,8 @@ public class ResourcePathParser {
           UriParserSemanticException.MessageKeys.PROPERTY_NOT_IN_TYPE,
           structType.getFullQualifiedName().getFullQualifiedNameAsString(), name);
     }
-    List<UriParameter> keyPredicate = ParserHelper.parseNavigationKeyPredicate(tokenizer, navigationProperty);
+    List<UriParameter> keyPredicate =
+        ParserHelper.parseNavigationKeyPredicate(tokenizer, navigationProperty, edm, null, aliases);
     ParserHelper.requireTokenEnd(tokenizer);
     return new UriResourceNavigationPropertyImpl(navigationProperty)
         .setKeyPredicates(keyPredicate);
@@ -320,7 +321,8 @@ public class ResourcePathParser {
           ((UriResourceWithKeysImpl) previousTyped).setEntryTypeFilter(type);
         }
         if (tokenizer.next(TokenKind.OPEN)) {
-          final List<UriParameter> keys = ParserHelper.parseKeyPredicate(tokenizer, (EdmEntityType) type, null);
+          final List<UriParameter> keys =
+              ParserHelper.parseKeyPredicate(tokenizer, (EdmEntityType) type, null, edm, null, aliases);
           if (previousTyped.isCollection()) {
             ((UriResourceWithKeysImpl) previousTyped).setKeyPredicates(keys);
           } else {
@@ -359,7 +361,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, edm, null, false);
+    final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, edm, null, false, aliases);
     final List<String> names = ParserHelper.getParameterNames(parameters);
     EdmFunction function = null;
     if (edmFunctionImport != null) {
@@ -379,13 +381,15 @@ public class ResourcePathParser {
             UriParserSemanticException.MessageKeys.UNKNOWN_PART, boundFunctionName.getFullQualifiedNameAsString());
       }
     }
+    ParserHelper.validateFunctionParameters(function, parameters, edm, null, aliases);
     UriResourceFunctionImpl resource = new UriResourceFunctionImpl(edmFunctionImport, function, parameters);
     if (tokenizer.next(TokenKind.OPEN)) {
       if (function.getReturnType() != null
           && function.getReturnType().getType().getKind() == EdmTypeKind.ENTITY
           && function.getReturnType().isCollection()) {
         resource.setKeyPredicates(
-            ParserHelper.parseKeyPredicate(tokenizer, (EdmEntityType) function.getReturnType().getType(), null));
+            ParserHelper.parseKeyPredicate(tokenizer,
+                (EdmEntityType) function.getReturnType().getType(), null, edm, null, aliases));
       } else {
         throw new UriParserSemanticException("A key is not allowed.",
             UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/110c7b0e/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/SelectParser.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/SelectParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/SelectParser.java
index 00f3673..9ec29e3 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/SelectParser.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/SelectParser.java
@@ -123,11 +123,17 @@ public class SelectParser {
   }
 
   private FullQualifiedName parseAllOperationsInSchema(UriTokenizer tokenizer) throws UriParserException {
-    final String name = tokenizer.getText();
+    final String namespace = tokenizer.getText();
     if (tokenizer.next(TokenKind.DOT)) {
       if (tokenizer.next(TokenKind.STAR)) {
-        // TODO: Validate the namespace without loading the whole schema.
-        return new FullQualifiedName(name, tokenizer.getText());
+        // Validate the namespace.  Currently a namespace from a non-default schema is not supported.
+        // There is no direct access to the namespace without loading the whole schema;
+        // however, the default entity container should always be there, so its access methods can be used.
+        if (edm.getEntityContainer(new FullQualifiedName(namespace, edm.getEntityContainer().getName())) == null) {
+          throw new UriParserSemanticException("Wrong namespace '" + namespace + "'.",
+              UriParserSemanticException.MessageKeys.UNKNOWN_PART, namespace);
+        }
+        return new FullQualifiedName(namespace, tokenizer.getText());
       } else {
         throw new UriParserSemanticException("Expected star after dot.",
             UriParserSemanticException.MessageKeys.UNKNOWN_PART, "");

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/110c7b0e/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java
index 0504473..d218666 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
@@ -1311,8 +1311,18 @@ public class UriTokenizer {
         || nextJsonArrayOrObject();
   }
 
+  /**
+   * Moves past a JSON object member if found; otherwise leaves the index unchanged.
+   * @return whether a JSON object member has been found at the current index
+   */
   private boolean nextJsonMember() {
-    return nextJsonString() && nextCharacter(':') && nextJsonValue();
+    final int lastGoodIndex = index;
+    if (nextJsonString() && nextCharacter(':') && nextJsonValue()) {
+      return true;
+    } else {
+      index = lastGoodIndex;
+      return false;
+    }
   }
 
   /**


Mime
View raw message