You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by fm...@apache.org on 2013/07/26 13:22:15 UTC
[10/51] [partial] initial commit
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/FilterParserImpl.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/FilterParserImpl.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/FilterParserImpl.java
new file mode 100644
index 0000000..cfbfb9b
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/FilterParserImpl.java
@@ -0,0 +1,856 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.olingo.odata2.core.uri.expression;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.olingo.odata2.api.edm.EdmComplexType;
+import org.apache.olingo.odata2.api.edm.EdmEntityType;
+import org.apache.olingo.odata2.api.edm.EdmException;
+import org.apache.olingo.odata2.api.edm.EdmSimpleType;
+import org.apache.olingo.odata2.api.edm.EdmSimpleTypeKind;
+import org.apache.olingo.odata2.api.edm.EdmStructuralType;
+import org.apache.olingo.odata2.api.edm.EdmType;
+import org.apache.olingo.odata2.api.edm.EdmTyped;
+import org.apache.olingo.odata2.api.uri.expression.BinaryExpression;
+import org.apache.olingo.odata2.api.uri.expression.BinaryOperator;
+import org.apache.olingo.odata2.api.uri.expression.CommonExpression;
+import org.apache.olingo.odata2.api.uri.expression.ExpressionKind;
+import org.apache.olingo.odata2.api.uri.expression.ExpressionParserException;
+import org.apache.olingo.odata2.api.uri.expression.FilterExpression;
+import org.apache.olingo.odata2.api.uri.expression.LiteralExpression;
+import org.apache.olingo.odata2.api.uri.expression.MethodExpression;
+import org.apache.olingo.odata2.api.uri.expression.MethodOperator;
+import org.apache.olingo.odata2.api.uri.expression.UnaryExpression;
+import org.apache.olingo.odata2.api.uri.expression.UnaryOperator;
+import org.apache.olingo.odata2.core.edm.EdmBoolean;
+import org.apache.olingo.odata2.core.edm.EdmSimpleTypeFacadeImpl;
+
+/**
+ * @author SAP AG
+ */
+public class FilterParserImpl implements FilterParser {
+ /*do the static initialization*/
+ protected static Map<String, InfoBinaryOperator> availableBinaryOperators;
+ protected static Map<String, InfoMethod> availableMethods;
+ protected static Map<String, InfoUnaryOperator> availableUnaryOperators;
+
+ static {
+ initAvailTables();
+ }
+
+ /*instance attributes*/
+ protected EdmEntityType resourceEntityType = null;
+ protected TokenList tokenList = null;
+ protected String curExpression;
+
+ /**
+ * Creates a new FilterParser implementation
+ * @param resourceEntityType EntityType of the resource on which the filter is applied
+ */
+ public FilterParserImpl(final EdmEntityType resourceEntityType) {
+ this.resourceEntityType = resourceEntityType;
+ }
+
+ @Override
+ public FilterExpression parseFilterString(final String filterExpression) throws ExpressionParserException, ExpressionParserInternalError {
+ return parseFilterString(filterExpression, false);
+ }
+
+ public FilterExpression parseFilterString(final String filterExpression, final boolean allowOnlyBinary) throws ExpressionParserException, ExpressionParserInternalError {
+ CommonExpression node = null;
+ curExpression = filterExpression;
+ try {
+ // Throws TokenizerException and FilterParserException. FilterParserException is caught somewhere above
+ tokenList = new Tokenizer(filterExpression).tokenize();
+ if (!tokenList.hasTokens()) {
+ return new FilterExpressionImpl(filterExpression);
+ }
+ } catch (TokenizerException tokenizerException) {
+ // Tested with TestParserExceptions.TestPMparseFilterString
+ throw FilterParserExceptionImpl.createERROR_IN_TOKENIZER(tokenizerException, curExpression);
+ }
+
+ try {
+ CommonExpression nodeLeft = readElement(null);
+ node = readElements(nodeLeft, 0);
+ } catch (ExpressionParserException filterParserException) {
+ // Add empty filterTree to Exception
+ // Tested for original throw point
+ filterParserException.setFilterTree(new FilterExpressionImpl(filterExpression));
+ throw filterParserException;
+ }
+
+ // Post check
+ if (tokenList.tokenCount() > tokenList.currentToken) //this indicates that not all tokens have been read
+ {
+ // Tested with TestParserExceptions.TestPMparseFilterString
+ throw FilterParserExceptionImpl.createINVALID_TRAILING_TOKEN_DETECTED_AFTER_PARSING(tokenList.elementAt(tokenList.currentToken), filterExpression);
+ }
+
+ // Create and return filterExpression node
+ if ((allowOnlyBinary == true) && (node.getEdmType() != null) && (node.getEdmType() != EdmSimpleTypeKind.Boolean.getEdmSimpleTypeInstance())) {
+ // Tested with TestParserExceptions.testAdditionalStuff CASE 9
+ throw FilterParserExceptionImpl.createTYPE_EXPECTED_AT(EdmBoolean.getInstance(), node.getEdmType(), 1, curExpression);
+ }
+
+ return new FilterExpressionImpl(filterExpression, node);
+ }
+
+ protected CommonExpression readElements(final CommonExpression leftExpression, final int priority) throws ExpressionParserException, ExpressionParserInternalError {
+ CommonExpression leftNode = leftExpression;
+ CommonExpression rightNode;
+ BinaryExpression binaryNode;
+
+ ActualBinaryOperator operator = readBinaryOperator();
+ ActualBinaryOperator nextOperator;
+
+ while ((operator != null) && (operator.getOP().getPriority() >= priority)) {
+ tokenList.next(); //eat the operator
+ rightNode = readElement(leftNode, operator); //throws FilterParserException, FilterParserInternalError
+ if (rightNode == null) {
+ // Tested with TestParserExceptions.testAdditionalStuff CASE 10
+ throw FilterParserExceptionImpl.createEXPRESSION_EXPECTED_AFTER_POS(operator.getToken().getPosition() + operator.getToken().getUriLiteral().length(), curExpression);
+ }
+ nextOperator = readBinaryOperator();
+
+ // It must be "while" because for example in "Filter=a or c eq d and e eq f"
+ // after reading the "eq" operator the "and" operator must be consumed too. This is due to the fact that "and" has a higher priority than "or"
+ while ((nextOperator != null) && (nextOperator.getOP().getPriority() > operator.getOP().getPriority())) {
+ //recurse until the a binary operator with a lower priority is detected
+ rightNode = readElements(rightNode, nextOperator.getOP().getPriority());
+ nextOperator = readBinaryOperator();
+ }
+
+ // Although the member operator is also a binary operator, there is some special handling in the filterTree
+ if (operator.getOP().getOperator() == BinaryOperator.PROPERTY_ACCESS) {
+ binaryNode = new MemberExpressionImpl(leftNode, rightNode);
+ } else {
+ binaryNode = new BinaryExpressionImpl(operator.getOP(), leftNode, rightNode, operator.getToken());
+ }
+
+ try {
+ validateBinaryOperatorTypes(binaryNode);
+ } catch (ExpressionParserException expressionException) {
+ // Extend the error information
+ // Tested for original throw point
+ expressionException.setFilterTree(binaryNode);
+ throw expressionException;
+ }
+
+ leftNode = binaryNode;
+ operator = readBinaryOperator();
+ }
+
+ //Add special handling for expressions like $filter=notsupportedfunction('a')
+ //If this special handling is not in place the error text would be
+ //-->Invalid token "(" detected after parsing at position 21 in "notsupportedfunction('a')".
+ //with this special handling we ensure that the error text would be
+
+ Token token = tokenList.lookToken();
+ if (token != null) {
+ if ((leftNode.getKind() == ExpressionKind.PROPERTY) && (tokenList.lookToken().getKind() == TokenKind.OPENPAREN)) {
+ // Tested with TestParserExceptions.testAdditionalStuff CASE 2
+ throw FilterParserExceptionImpl.createINVALID_METHOD_CALL(leftNode, tokenList.lookPrevToken(), curExpression);
+ }
+ }
+
+ return leftNode;
+ }
+
+ /**
+ * Reads the content between parenthesis. Its is expected that the current token is of kind {@link TokenKind#OPENPAREN}
+ * because it MUST be check in the calling method ( when read the method name and the '(' is read).
+ * @return An expression which reflects the content within the parenthesis
+ * @throws ExpressionParserException
+ * While reading the elements in the parenthesis an error occurred
+ * @throws TokenizerMessage
+ * The next token did not match the expected token
+ */
+ protected CommonExpression readParenthesis() throws ExpressionParserException, ExpressionParserInternalError {
+ // The existing of a '(' is verified BEFORE this method is called --> so it's a internal error
+ Token openParenthesis = tokenList.expectToken(TokenKind.OPENPAREN, true);
+
+ CommonExpression firstExpression = readElement(null);
+ CommonExpression parenthesisExpression = readElements(firstExpression, 0);
+
+ // check for ')'
+ try {
+ tokenList.expectToken(TokenKind.CLOSEPAREN); //TokenizerMessage
+ } catch (TokenizerExpectError e) {
+ // Internal parsing error, even if there are no more token (then there should be a different exception).
+ // Tested with TestParserExceptions.TestPMreadParenthesis
+ throw FilterParserExceptionImpl.createMISSING_CLOSING_PHARENTHESIS(openParenthesis.getPosition(), curExpression, e);
+ }
+ return parenthesisExpression;
+ }
+
+ /**
+ * Read the parameters of a method expression
+ * @param methodInfo
+ * Signature information about the method whose parameters should be read
+ * @param methodExpression
+ * Method expression to which the read parameters are added
+ * @return
+ * The method expression input parameter
+ * @throws ExpressionParserException
+ * @throws ExpressionParserInternalError
+ * @throws TokenizerExpectError
+ * The next token did not match the expected token
+ */
+ protected MethodExpression readParameters(final InfoMethod methodInfo, final MethodExpressionImpl methodExpression, final Token methodToken) throws ExpressionParserException, ExpressionParserInternalError {
+ CommonExpression expression;
+ boolean expectAnotherExpression = false;
+ boolean readComma = true;
+
+ // The existing of a '(' is verified BEFORE this method is called --> so it's a internal error
+ Token openParenthesis = tokenList.expectToken(TokenKind.OPENPAREN, true); //throws FilterParserInternalError
+
+ Token token = tokenList.lookToken();
+ if (token == null) {
+ //Tested with TestParserExceptions.TestPMreadParameters CASE 1 e.g. "$filter=concat("
+ throw FilterParserExceptionImpl.createEXPRESSION_EXPECTED_AFTER_POS(openParenthesis, curExpression);
+ }
+
+ while (token.getKind() != TokenKind.CLOSEPAREN) {
+ if (readComma == false) {
+ //Tested with TestParserExceptions.TestPMreadParameters CASE 12 e.g. "$filter=concat('a' 'b')"
+ throw FilterParserExceptionImpl.createCOMMA_OR_CLOSING_PHARENTHESIS_EXPECTED_AFTER_POS(tokenList.lookPrevToken(), curExpression);
+ }
+ expression = readElement(null);
+ if (expression != null) {
+ expression = readElements(expression, 0);
+ }
+
+ if ((expression == null) && (expectAnotherExpression == true)) {
+ //Tested with TestParserExceptions.TestPMreadParameters CASE 4 e.g. "$filter=concat(,"
+ throw FilterParserExceptionImpl.createEXPRESSION_EXPECTED_AFTER_POS(token, curExpression);
+ } else if (expression != null) //parameter list may be empty
+ {
+ methodExpression.appendParameter(expression);
+ }
+
+ token = tokenList.lookToken();
+ if (token == null) {
+ //Tested with TestParserExceptions.TestPMreadParameters CASE 2 e.g. "$filter=concat(123"
+ throw FilterParserExceptionImpl.createCOMMA_OR_CLOSING_PHARENTHESIS_EXPECTED_AFTER_POS(tokenList.lookPrevToken(), curExpression);
+ }
+
+ if (token.getKind() == TokenKind.COMMA) {
+ expectAnotherExpression = true;
+ if (expression == null) {
+ //Tested with TestParserExceptions.TestPMreadParameters CASE 3 e.g. "$filter=concat(,"
+ throw FilterParserExceptionImpl.createEXPRESSION_EXPECTED_AT_POS(token, curExpression);
+ }
+
+ tokenList.expectToken(",", true);
+ readComma = true;
+ } else {
+ readComma = false;
+ }
+ }
+
+ // because the while loop above only exits if a ')' has been found it is an
+ // internal error if there is not ')'
+ tokenList.expectToken(TokenKind.CLOSEPAREN, true);
+
+ //---check parameter count
+ int count = methodExpression.getParameters().size();
+ if ((methodInfo.getMinParameter() > -1) && (count < methodInfo.getMinParameter())) {
+ //Tested with TestParserExceptions.TestPMreadParameters CASE 12
+ throw FilterParserExceptionImpl.createMETHOD_WRONG_ARG_COUNT(methodExpression, methodToken, curExpression);
+ }
+
+ if ((methodInfo.getMaxParameter() > -1) && (count > methodInfo.getMaxParameter())) {
+ //Tested with TestParserExceptions.TestPMreadParameters CASE 15
+ throw FilterParserExceptionImpl.createMETHOD_WRONG_ARG_COUNT(methodExpression, methodToken, curExpression);
+ }
+
+ return methodExpression;
+ }
+
+ protected CommonExpression readElement(final CommonExpression leftExpression) throws ExpressionParserException, ExpressionParserInternalError {
+ return readElement(leftExpression, null);
+ }
+
+ /**
+ * Reads: Unary operators, Methods, Properties, ...
+ * but not binary operators which are handelt in {@link #readElements(CommonExpression, int)}
+ * @param leftExpression
+ * Used while parsing properties. In this case ( e.g. parsing "a/b") the property "a" ( as leftExpression of "/") is relevant
+ * to verify whether the property "b" exists inside the edm
+ * @return a CommonExpression
+ * @throws ExpressionParserException
+ * @throws ExpressionParserInternalError
+ * @throws TokenizerMessage
+ */
+ protected CommonExpression readElement(final CommonExpression leftExpression, final ActualBinaryOperator leftOperator) throws ExpressionParserException, ExpressionParserInternalError {
+ CommonExpression node = null;
+ Token token;
+ Token lookToken;
+ lookToken = tokenList.lookToken();
+ if (lookToken == null) {
+ return null;
+ }
+
+ switch (lookToken.getKind()) {
+ case OPENPAREN:
+ node = readParenthesis();
+ return node;
+ case CLOSEPAREN: // ')' finishes a parenthesis (it is no extra token)" +
+ case COMMA: //. " ',' is a separator for function parameters (it is no extra token)" +
+ return null;
+ default:
+ // continue
+ }
+
+ //-->Check if the token is a unary operator
+ InfoUnaryOperator unaryOperator = isUnaryOperator(lookToken);
+ if (unaryOperator != null) {
+ return readUnaryoperator(lookToken, unaryOperator);
+ }
+
+ //---expect the look ahead token
+ token = tokenList.expectToken(lookToken.getUriLiteral(), true);
+ lookToken = tokenList.lookToken();
+
+ //-->Check if the token is a method
+ //To avoid name clashes between method names and property names we accept here only method names if a "(" follows.
+ //Hence the parser accepts a property named "concat"
+ InfoMethod methodOperator = isMethod(token, lookToken);
+ if (methodOperator != null) {
+ return readMethod(token, methodOperator);
+ }
+
+ //-->Check if token is a terminal
+ //is a terminal e.g. a Value like an EDM.String 'hugo' or 125L or 1.25D"
+ if (token.getKind() == TokenKind.SIMPLE_TYPE) {
+ LiteralExpression literal = new LiteralExpressionImpl(token.getUriLiteral(), token.getJavaLiteral());
+ return literal;
+ }
+
+ //-->Check if token is a property, e.g. "name" or "address"
+ if (token.getKind() == TokenKind.LITERAL) {
+ PropertyExpressionImpl property = new PropertyExpressionImpl(token.getUriLiteral(), token.getJavaLiteral());
+ validateEdmProperty(leftExpression, property, token, leftOperator);
+ return property;
+ }
+
+ // not Tested, should not occur
+ throw ExpressionParserInternalError.createCOMMON();
+ }
+
+ protected CommonExpression readUnaryoperator(final Token lookToken, final InfoUnaryOperator unaryOperator) throws ExpressionParserException, ExpressionParserInternalError {
+ tokenList.expectToken(lookToken.getUriLiteral(), true);
+
+ CommonExpression operand = readElement(null);
+ UnaryExpression unaryExpression = new UnaryExpressionImpl(unaryOperator, operand);
+ validateUnaryOperatorTypes(unaryExpression); //throws ExpressionInvalidOperatorTypeException
+
+ return unaryExpression;
+ }
+
+ protected CommonExpression readMethod(final Token token, final InfoMethod methodOperator) throws ExpressionParserException, ExpressionParserInternalError {
+ MethodExpressionImpl method = new MethodExpressionImpl(methodOperator);
+
+ readParameters(methodOperator, method, token);
+ validateMethodTypes(method, token); //throws ExpressionInvalidOperatorTypeException
+
+ return method;
+ }
+
+ protected ActualBinaryOperator readBinaryOperator() {
+ InfoBinaryOperator operator = null;
+ Token token = tokenList.lookToken();
+ if (token == null) {
+ return null;
+ }
+ if ((token.getKind() == TokenKind.SYMBOL) && (token.getUriLiteral().equals("/"))) {
+ operator = availableBinaryOperators.get(token.getUriLiteral());
+ } else if (token.getKind() == TokenKind.LITERAL) {
+ operator = availableBinaryOperators.get(token.getUriLiteral());
+ }
+
+ if (operator == null) {
+ return null;
+ }
+
+ return new ActualBinaryOperator(operator, token);
+ }
+
+ /**
+ * Check if a token is a UnaryOperator ( e.g. "not" or "-" )
+ *
+ * @param token Token to be checked
+ *
+ * @return
+ * <li>An instance of {@link InfoUnaryOperator} containing information about the specific unary operator</li>
+ * <li><code>null</code> if the token is not an unary operator</li>
+ */
+ protected InfoUnaryOperator isUnaryOperator(final Token token) {
+ if ((token.getKind() == TokenKind.LITERAL) || (token.getKind() == TokenKind.SYMBOL)) {
+ InfoUnaryOperator operator = availableUnaryOperators.get(token.getUriLiteral());
+ return operator;
+ }
+ return null;
+ }
+
+ protected InfoMethod isMethod(final Token token, final Token lookToken) {
+ if ((lookToken != null) && (lookToken.getKind() == TokenKind.OPENPAREN)) {
+ return availableMethods.get(token.getUriLiteral());
+ }
+ return null;
+ }
+
+ protected void validateEdmProperty(final CommonExpression leftExpression, final PropertyExpressionImpl property, final Token propertyToken, final ActualBinaryOperator actBinOp) throws ExpressionParserException, ExpressionParserInternalError {
+
+ // Exist if no edm provided
+ if (resourceEntityType == null) {
+ return;
+ }
+
+ if (leftExpression == null) {
+ //e.g. "$filter=city eq 'Hong Kong'" --> "city" is checked against the resource entity type of the last URL segment
+ validateEdmPropertyOfStructuredType(resourceEntityType, property, propertyToken);
+ return;
+ }
+ //e.g. "$filter='Hong Kong' eq address/city" --> city is "checked" against the type of the property "address".
+ // "address" itself must be a (navigation)property of the resource entity type of the last URL segment AND
+ // "address" must have a structural edm type
+ EdmType parentType = leftExpression.getEdmType(); //parentType point now to the type of property "address"
+
+ if ((actBinOp != null) && (actBinOp.operator.getOperator() != BinaryOperator.PROPERTY_ACCESS)) {
+ validateEdmPropertyOfStructuredType(resourceEntityType, property, propertyToken);
+ return;
+ } else {
+ if ((leftExpression.getKind() != ExpressionKind.PROPERTY) && (leftExpression.getKind() != ExpressionKind.MEMBER)) {
+ if (actBinOp != null) {
+ //Tested with TestParserExceptions.TestPMvalidateEdmProperty CASE 6
+ throw FilterParserExceptionImpl.createLEFT_SIDE_NOT_A_PROPERTY(actBinOp.token, curExpression);
+ } else {
+ // not Tested, should not occur
+ throw ExpressionParserInternalError.createCOMMON();
+ }
+
+ }
+ }
+
+ if (parentType instanceof EdmEntityType) {
+ //e.g. "$filter='Hong Kong' eq navigationProp/city" --> "navigationProp" is a navigation property with a entity type
+ validateEdmPropertyOfStructuredType((EdmStructuralType) parentType, property, propertyToken);
+ } else if (parentType instanceof EdmComplexType) {
+ //e.g. "$filter='Hong Kong' eq address/city" --> "address" is a property with a complex type
+ validateEdmPropertyOfStructuredType((EdmStructuralType) parentType, property, propertyToken);
+ } else {
+ //e.g. "$filter='Hong Kong' eq name/city" --> "name is of type String"
+ //Tested with TestParserExceptions.TestPMvalidateEdmProperty CASE 5
+ throw FilterParserExceptionImpl.createLEFT_SIDE_NOT_STRUCTURAL_TYPE(parentType, property, propertyToken, curExpression);
+ }
+
+ return;
+ }
+
+ protected void validateEdmPropertyOfStructuredType(final EdmStructuralType parentType, final PropertyExpressionImpl property, final Token propertyToken) throws ExpressionParserException, ExpressionParserInternalError {
+ try {
+ String propertyName = property.getUriLiteral();
+ EdmTyped edmProperty = parentType.getProperty(propertyName);
+
+ if (edmProperty != null) {
+ property.setEdmProperty(edmProperty);
+ property.setEdmType(edmProperty.getType());
+ } else {
+ //Tested with TestParserExceptions.TestPMvalidateEdmProperty CASE 3
+ throw FilterParserExceptionImpl.createPROPERTY_NAME_NOT_FOUND_IN_TYPE(parentType, property, propertyToken, curExpression);
+ }
+
+ } catch (EdmException e) {
+ // not Tested, should not occur
+ throw ExpressionParserInternalError.createERROR_ACCESSING_EDM(e);
+ }
+ }
+
+ /*
+ protected void validateEdmPropertyOfComplexType1(EdmComplexType parentType, PropertyExpressionImpl property, Token propertyToken) throws FilterParserException, FilterParserInternalError
+ {
+ try {
+ String propertyName = property.getUriLiteral();
+ EdmTyped edmProperty = parentType.getProperty(propertyName);
+
+ if (edmProperty != null)
+ {
+ property.setEdmProperty(edmProperty);
+ property.setEdmType(edmProperty.getType());
+ }
+ else
+ {
+ //Tested with TestParserExceptions.TestPMvalidateEdmProperty CASE 3
+ throw FilterParserExceptionImpl.createPROPERTY_NAME_NOT_FOUND_IN_TYPE(parentType, property, propertyToken, curExpression);
+ }
+
+ } catch (EdmException e) {
+ // not Tested, should not occur
+ throw FilterParserInternalError.createERROR_ACCESSING_EDM(e);
+ }
+ }
+
+ protected void validateEdmPropertyOfEntityType1(EdmEntityType parentType, PropertyExpressionImpl property, Token propertyToken) throws FilterParserException, FilterParserInternalError
+ {
+ try {
+ String propertyName = property.getUriLiteral();
+ EdmTyped edmProperty = parentType.getProperty(propertyName);
+
+ if (edmProperty != null)
+ {
+ property.setEdmProperty(edmProperty);
+ property.setEdmType(edmProperty.getType());
+ }
+ else
+ {
+ //Tested with TestParserExceptions.TestPMvalidateEdmProperty CASE 1
+ throw FilterParserExceptionImpl.createPROPERTY_NAME_NOT_FOUND_IN_TYPE(parentType, property, propertyToken, curExpression);
+ }
+
+ } catch (EdmException e) {
+ // not Tested, should not occur
+ throw FilterParserInternalError.createERROR_ACCESSING_EDM(e);
+ }
+ }*/
+
+ protected void validateUnaryOperatorTypes(final UnaryExpression unaryExpression) throws ExpressionParserInternalError {
+ InfoUnaryOperator unOpt = availableUnaryOperators.get(unaryExpression.getOperator().toUriLiteral());
+ EdmType operandType = unaryExpression.getOperand().getEdmType();
+
+ if ((operandType == null) && (resourceEntityType == null)) {
+ return;
+ }
+
+ List<EdmType> actualParameterTypes = new ArrayList<EdmType>();
+ actualParameterTypes.add(operandType);
+
+ ParameterSet parameterSet = unOpt.validateParameterSet(actualParameterTypes);
+ if (parameterSet != null) {
+ unaryExpression.setEdmType(parameterSet.getReturnType());
+ }
+ }
+
+ protected void validateBinaryOperatorTypes(final BinaryExpression binaryExpression) throws ExpressionParserException, ExpressionParserInternalError {
+ InfoBinaryOperator binOpt = availableBinaryOperators.get(binaryExpression.getOperator().toUriLiteral());
+
+ List<EdmType> actualParameterTypes = new ArrayList<EdmType>();
+ EdmType operand = binaryExpression.getLeftOperand().getEdmType();
+
+ if ((operand == null) && (resourceEntityType == null)) {
+ return;
+ }
+ actualParameterTypes.add(operand);
+
+ operand = binaryExpression.getRightOperand().getEdmType();
+
+ if ((operand == null) && (resourceEntityType == null)) {
+ return;
+ }
+ actualParameterTypes.add(operand);
+
+ ParameterSet parameterSet = binOpt.validateParameterSet(actualParameterTypes);
+ if (parameterSet == null) {
+ BinaryExpressionImpl binaryExpressionImpl = (BinaryExpressionImpl) binaryExpression;
+
+ // Tested with TestParserExceptions.TestPMvalidateBinaryOperator
+ throw FilterParserExceptionImpl.createINVALID_TYPES_FOR_BINARY_OPERATOR(binaryExpression.getOperator(), binaryExpression.getLeftOperand().getEdmType(), binaryExpression.getRightOperand().getEdmType(), binaryExpressionImpl.getToken(), curExpression);
+ }
+ binaryExpression.setEdmType(parameterSet.getReturnType());
+ }
+
+ protected void validateMethodTypes(final MethodExpression methodExpression, final Token methodToken) throws ExpressionParserException, ExpressionParserInternalError {
+ InfoMethod methOpt = availableMethods.get(methodExpression.getUriLiteral());
+
+ List<EdmType> actualParameterTypes = new ArrayList<EdmType>();
+
+ //If there are no parameter then don't perform a type check
+ if (methodExpression.getParameters().size() == 0) {
+ return;
+ }
+
+ for (CommonExpression parameter : methodExpression.getParameters()) {
+ //If there is not at parsing time its not possible to determine the type of eg myPropertyName.
+ //Since this should not cause validation errors null type node arguments are leading to bypass
+ //the validation
+ if ((parameter.getEdmType() == null) && (resourceEntityType == null)) {
+ return;
+ }
+ actualParameterTypes.add(parameter.getEdmType());
+ }
+
+ ParameterSet parameterSet = methOpt.validateParameterSet(actualParameterTypes);
+ //If there is not returntype then the input parameter
+ if (parameterSet == null) {
+ // Tested with TestParserExceptions.testPMvalidateMethodTypes CASE 1
+ throw FilterParserExceptionImpl.createMETHOD_WRONG_INPUT_TYPE((MethodExpressionImpl) methodExpression, methodToken, curExpression);
+ }
+ methodExpression.setEdmType(parameterSet.getReturnType());
+ }
+
+ static void initAvailTables() {
+ Map<String, InfoBinaryOperator> lAvailableBinaryOperators = new HashMap<String, InfoBinaryOperator>();
+ Map<String, InfoMethod> lAvailableMethods = new HashMap<String, InfoMethod>();
+ Map<String, InfoUnaryOperator> lAvailableUnaryOperators = new HashMap<String, InfoUnaryOperator>();
+
+ //create type validators
+ //InputTypeValidator typeValidatorPromotion = new InputTypeValidator.TypePromotionValidator();
+ ParameterSetCombination combination = null;
+ //create type helpers
+ EdmSimpleType boolean_ = EdmSimpleTypeFacadeImpl.getEdmSimpleType(EdmSimpleTypeKind.Boolean);
+ EdmSimpleType sbyte = EdmSimpleTypeFacadeImpl.getEdmSimpleType(EdmSimpleTypeKind.SByte);
+ EdmSimpleType byte_ = EdmSimpleTypeFacadeImpl.getEdmSimpleType(EdmSimpleTypeKind.Byte);
+ EdmSimpleType int16 = EdmSimpleTypeFacadeImpl.getEdmSimpleType(EdmSimpleTypeKind.Int16);
+ EdmSimpleType int32 = EdmSimpleTypeFacadeImpl.getEdmSimpleType(EdmSimpleTypeKind.Int32);
+ EdmSimpleType int64 = EdmSimpleTypeFacadeImpl.getEdmSimpleType(EdmSimpleTypeKind.Int64);
+ EdmSimpleType single = EdmSimpleTypeFacadeImpl.getEdmSimpleType(EdmSimpleTypeKind.Single);
+ EdmSimpleType double_ = EdmSimpleTypeFacadeImpl.getEdmSimpleType(EdmSimpleTypeKind.Double);
+ EdmSimpleType decimal = EdmSimpleTypeFacadeImpl.getEdmSimpleType(EdmSimpleTypeKind.Decimal);
+ EdmSimpleType string = EdmSimpleTypeFacadeImpl.getEdmSimpleType(EdmSimpleTypeKind.String);
+ EdmSimpleType time = EdmSimpleTypeFacadeImpl.getEdmSimpleType(EdmSimpleTypeKind.Time);
+ EdmSimpleType datetime = EdmSimpleTypeFacadeImpl.getEdmSimpleType(EdmSimpleTypeKind.DateTime);
+ EdmSimpleType datetimeoffset = EdmSimpleTypeFacadeImpl.getEdmSimpleType(EdmSimpleTypeKind.DateTimeOffset);
+ EdmSimpleType guid = EdmSimpleTypeFacadeImpl.getEdmSimpleType(EdmSimpleTypeKind.Guid);
+ EdmSimpleType binary = EdmSimpleTypeFacadeImpl.getEdmSimpleType(EdmSimpleTypeKind.Binary);
+
+ //---Memeber member access---
+ lAvailableBinaryOperators.put("/", new InfoBinaryOperator(BinaryOperator.PROPERTY_ACCESS, "Primary", 100, new ParameterSetCombination.PSCReturnTypeEqLastParameter()));//todo fix this
+
+ //---Multiplicative---
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(sbyte, sbyte, sbyte));
+ combination.add(new ParameterSet(byte_, byte_, byte_));
+ combination.add(new ParameterSet(int16, int16, int16));
+ combination.add(new ParameterSet(int32, int32, int32));
+ combination.add(new ParameterSet(int64, int64, int64));
+ combination.add(new ParameterSet(single, single, single));
+ combination.add(new ParameterSet(double_, double_, double_));
+
+ combination.add(new ParameterSet(decimal, decimal, decimal));
+
+ lAvailableBinaryOperators.put(BinaryOperator.MUL.toUriLiteral(), new InfoBinaryOperator(BinaryOperator.MUL, "Multiplicative", 60, combination));
+ lAvailableBinaryOperators.put(BinaryOperator.DIV.toUriLiteral(), new InfoBinaryOperator(BinaryOperator.DIV, "Multiplicative", 60, combination));
+ lAvailableBinaryOperators.put(BinaryOperator.MODULO.toUriLiteral(), new InfoBinaryOperator(BinaryOperator.MODULO, "Multiplicative", 60, combination));
+
+ //---Additive---
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(sbyte, sbyte, sbyte));
+ combination.add(new ParameterSet(byte_, byte_, byte_));
+ combination.add(new ParameterSet(int16, int16, int16));
+ combination.add(new ParameterSet(int32, int32, int32));
+ combination.add(new ParameterSet(int64, int64, int64));
+ combination.add(new ParameterSet(single, single, single));
+ combination.add(new ParameterSet(double_, double_, double_));
+ combination.add(new ParameterSet(decimal, decimal, decimal));
+
+ lAvailableBinaryOperators.put(BinaryOperator.ADD.toUriLiteral(), new InfoBinaryOperator(BinaryOperator.ADD, "Additive", 50, combination));
+ lAvailableBinaryOperators.put(BinaryOperator.SUB.toUriLiteral(), new InfoBinaryOperator(BinaryOperator.SUB, "Additive", 50, combination));
+
+ //---Relational---
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(boolean_, string, string));
+ combination.add(new ParameterSet(boolean_, time, time));
+ combination.add(new ParameterSet(boolean_, datetime, datetime));
+ combination.add(new ParameterSet(boolean_, datetimeoffset, datetimeoffset));
+ combination.add(new ParameterSet(boolean_, guid, guid));
+ combination.add(new ParameterSet(boolean_, sbyte, sbyte));
+ combination.add(new ParameterSet(boolean_, byte_, byte_));
+ combination.add(new ParameterSet(boolean_, int16, int16));
+ combination.add(new ParameterSet(boolean_, int32, int32));
+ combination.add(new ParameterSet(boolean_, int64, int64));
+ combination.add(new ParameterSet(boolean_, single, single));
+ combination.add(new ParameterSet(boolean_, double_, double_));
+ combination.add(new ParameterSet(boolean_, decimal, decimal));
+ combination.add(new ParameterSet(boolean_, binary, binary));
+
+ lAvailableBinaryOperators.put(BinaryOperator.LT.toUriLiteral(), new InfoBinaryOperator(BinaryOperator.LT, "Relational", 40, combination));
+ lAvailableBinaryOperators.put(BinaryOperator.GT.toUriLiteral(), new InfoBinaryOperator(BinaryOperator.GT, "Relational", 40, combination));
+ lAvailableBinaryOperators.put(BinaryOperator.GE.toUriLiteral(), new InfoBinaryOperator(BinaryOperator.GE, "Relational", 40, combination));
+ lAvailableBinaryOperators.put(BinaryOperator.LE.toUriLiteral(), new InfoBinaryOperator(BinaryOperator.LE, "Relational", 40, combination));
+
+ //---Equality---
+ //combination = new ParameterSetCombination.PSCflex();
+ combination.addFirst(new ParameterSet(boolean_, boolean_, boolean_));
+ /*combination.add(new ParameterSet(boolean_, string, string));
+ combination.add(new ParameterSet(boolean_, time, time));
+ combination.add(new ParameterSet(boolean_, datetime, datetime));
+ combination.add(new ParameterSet(boolean_, datetimeoffset, datetimeoffset));
+ combination.add(new ParameterSet(boolean_, guid, guid));
+ combination.add(new ParameterSet(boolean_, sbyte, sbyte));
+ combination.add(new ParameterSet(boolean_, byte_, byte_));
+ combination.add(new ParameterSet(boolean_, int16, int16));
+ combination.add(new ParameterSet(boolean_, int32, int32));
+ combination.add(new ParameterSet(boolean_, int64, int64));
+ combination.add(new ParameterSet(boolean_, single, single));
+ combination.add(new ParameterSet(boolean_, double_, double_));
+ combination.add(new ParameterSet(boolean_, decimal, decimal));
+ combination.add(new ParameterSet(boolean_, binary, binary));*/
+
+ lAvailableBinaryOperators.put(BinaryOperator.EQ.toUriLiteral(), new InfoBinaryOperator(BinaryOperator.EQ, "Equality", 30, combination));
+ lAvailableBinaryOperators.put(BinaryOperator.NE.toUriLiteral(), new InfoBinaryOperator(BinaryOperator.NE, "Equality", 30, combination));
+
+ //"---Conditinal AND---
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(boolean_, boolean_, boolean_));
+
+ lAvailableBinaryOperators.put(BinaryOperator.AND.toUriLiteral(), new InfoBinaryOperator(BinaryOperator.AND, "Conditinal", 20, combination));
+
+ //---Conditinal OR---
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(boolean_, boolean_, boolean_));
+
+ lAvailableBinaryOperators.put(BinaryOperator.OR.toUriLiteral(), new InfoBinaryOperator(BinaryOperator.OR, "Conditinal", 10, combination));
+
+ //endswith
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(boolean_, string, string));
+ lAvailableMethods.put(MethodOperator.ENDSWITH.toUriLiteral(), new InfoMethod(MethodOperator.ENDSWITH, 2, 2, combination));
+
+ //indexof
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(int32, string, string));
+ lAvailableMethods.put(MethodOperator.INDEXOF.toUriLiteral(), new InfoMethod(MethodOperator.INDEXOF, 2, 2, combination));
+
+ //startswith
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(boolean_, string, string));
+ lAvailableMethods.put(MethodOperator.STARTSWITH.toUriLiteral(), new InfoMethod(MethodOperator.STARTSWITH, 2, 2, combination));
+
+ //tolower
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(string, string));
+ lAvailableMethods.put(MethodOperator.TOLOWER.toUriLiteral(), new InfoMethod(MethodOperator.TOLOWER, combination));
+
+ //toupper
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(string, string));
+ lAvailableMethods.put(MethodOperator.TOUPPER.toUriLiteral(), new InfoMethod(MethodOperator.TOUPPER, combination));
+
+ //trim
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(string, string));
+ lAvailableMethods.put(MethodOperator.TRIM.toUriLiteral(), new InfoMethod(MethodOperator.TRIM, combination));
+
+ //substring
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(string, string, int32));
+ combination.add(new ParameterSet(string, string, int32, int32));
+ lAvailableMethods.put(MethodOperator.SUBSTRING.toUriLiteral(), new InfoMethod(MethodOperator.SUBSTRING, 1, -1, combination));
+
+ //substringof
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(boolean_, string, string));
+ lAvailableMethods.put(MethodOperator.SUBSTRINGOF.toUriLiteral(), new InfoMethod(MethodOperator.SUBSTRINGOF, 1, -1, combination));
+
+ //concat
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(string, string, string).setFurtherType(string));
+ lAvailableMethods.put(MethodOperator.CONCAT.toUriLiteral(), new InfoMethod(MethodOperator.CONCAT, 2, -1, combination));
+
+ //length
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(int32, string));
+ lAvailableMethods.put(MethodOperator.LENGTH.toUriLiteral(), new InfoMethod(MethodOperator.LENGTH, combination));
+
+ //year
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(int32, datetime));
+ lAvailableMethods.put(MethodOperator.YEAR.toUriLiteral(), new InfoMethod(MethodOperator.YEAR, combination));
+
+ //month
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(int32, datetime));
+ lAvailableMethods.put(MethodOperator.MONTH.toUriLiteral(), new InfoMethod(MethodOperator.MONTH, combination));
+
+ //day
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(int32, datetime));
+ lAvailableMethods.put(MethodOperator.DAY.toUriLiteral(), new InfoMethod(MethodOperator.DAY, combination));
+
+ //hour
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(int32, datetime));
+ combination.add(new ParameterSet(int32, time));
+ combination.add(new ParameterSet(int32, datetimeoffset));
+ lAvailableMethods.put(MethodOperator.HOUR.toUriLiteral(), new InfoMethod(MethodOperator.HOUR, combination));
+
+ //minute
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(int32, datetime));
+ combination.add(new ParameterSet(int32, time));
+ combination.add(new ParameterSet(int32, datetimeoffset));
+ lAvailableMethods.put(MethodOperator.MINUTE.toUriLiteral(), new InfoMethod(MethodOperator.MINUTE, combination));
+
+ //second
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(int32, datetime));
+ combination.add(new ParameterSet(int32, time));
+ combination.add(new ParameterSet(int32, datetimeoffset));
+ lAvailableMethods.put(MethodOperator.SECOND.toUriLiteral(), new InfoMethod(MethodOperator.SECOND, combination));
+
+ //round
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(decimal, decimal));
+ combination.add(new ParameterSet(double_, double_));
+ lAvailableMethods.put(MethodOperator.ROUND.toUriLiteral(), new InfoMethod(MethodOperator.ROUND, combination));
+
+ //ceiling
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(decimal, decimal));
+ combination.add(new ParameterSet(double_, double_));
+ lAvailableMethods.put(MethodOperator.CEILING.toUriLiteral(), new InfoMethod(MethodOperator.CEILING, combination));
+
+ //floor
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(decimal, decimal));
+ combination.add(new ParameterSet(double_, double_));
+ lAvailableMethods.put(MethodOperator.FLOOR.toUriLiteral(), new InfoMethod(MethodOperator.FLOOR, combination));
+
+ //---unary---
+
+ //minus
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(sbyte, sbyte));
+ combination.add(new ParameterSet(byte_, byte_));
+ combination.add(new ParameterSet(int16, int16));
+ combination.add(new ParameterSet(int32, int32));
+ combination.add(new ParameterSet(int64, int64));
+ combination.add(new ParameterSet(single, single));
+ combination.add(new ParameterSet(double_, double_));
+ combination.add(new ParameterSet(decimal, decimal));
+
+ //minus
+ lAvailableUnaryOperators.put(UnaryOperator.MINUS.toUriLiteral(), new InfoUnaryOperator(UnaryOperator.MINUS, "minus", combination));
+
+ //not
+ combination = new ParameterSetCombination.PSCflex();
+ combination.add(new ParameterSet(boolean_, boolean_));
+ lAvailableUnaryOperators.put(UnaryOperator.NOT.toUriLiteral(), new InfoUnaryOperator(UnaryOperator.NOT, "not", combination));
+
+ availableBinaryOperators = Collections.unmodifiableMap(lAvailableBinaryOperators);
+ availableMethods = Collections.unmodifiableMap(lAvailableMethods);
+ availableUnaryOperators = Collections.unmodifiableMap(lAvailableUnaryOperators);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/InfoBinaryOperator.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/InfoBinaryOperator.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/InfoBinaryOperator.java
new file mode 100644
index 0000000..fdcf225
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/InfoBinaryOperator.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.olingo.odata2.core.uri.expression;
+
+import java.util.List;
+
+import org.apache.olingo.odata2.api.edm.EdmType;
+import org.apache.olingo.odata2.api.uri.expression.BinaryOperator;
+
+/**
+ * Describes a binary operator which is allowed in OData expressions
+ * @author SAP AG
+ */
+class InfoBinaryOperator {
+ private BinaryOperator operator;
+ private String category;
+ private String syntax;
+ private int priority;
+ ParameterSetCombination combination;
+
+ public InfoBinaryOperator(final BinaryOperator operator, final String category, final int priority, final ParameterSetCombination combination) {
+ this.operator = operator;
+ this.category = category;
+ syntax = operator.toUriLiteral();
+ this.priority = priority;
+ this.combination = combination;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public String getSyntax() {
+ return syntax;
+ }
+
+ public BinaryOperator getOperator() {
+ return operator;
+ }
+
+ public int getPriority() {
+ return priority;
+ }
+
+ public ParameterSet validateParameterSet(final List<EdmType> actualParameterTypes) throws ExpressionParserInternalError {
+ return combination.validate(actualParameterTypes);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/InfoMethod.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/InfoMethod.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/InfoMethod.java
new file mode 100644
index 0000000..38778f3
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/InfoMethod.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.olingo.odata2.core.uri.expression;
+
+import java.util.List;
+
+import org.apache.olingo.odata2.api.edm.EdmType;
+import org.apache.olingo.odata2.api.uri.expression.MethodOperator;
+
+/**
+ * Describes a method expression which is allowed in OData expressions
+ * @author SAP AG
+ */
+class InfoMethod {
+
+ public MethodOperator method;
+ public String syntax;
+ public int minParameter;
+ public int maxParameter;
+ ParameterSetCombination combination;
+
+ public InfoMethod(final MethodOperator method, final ParameterSetCombination combination) {
+ this.method = method;
+ syntax = method.toUriLiteral();
+ minParameter = 1;
+ maxParameter = 1;
+ this.combination = combination;
+ }
+
+ public InfoMethod(final MethodOperator method, final int minParameters, final int maxParameters, final ParameterSetCombination combination) {
+ this.method = method;
+ syntax = method.toUriLiteral();
+ minParameter = minParameters;
+ maxParameter = maxParameters;
+ this.combination = combination;
+ }
+
+ public InfoMethod(final MethodOperator method, final String string, final int minParameters, final int maxParameters, final ParameterSetCombination combination) {
+ this.method = method;
+ syntax = string;
+ minParameter = minParameters;
+ maxParameter = maxParameters;
+ this.combination = combination;
+ }
+
+ public MethodOperator getMethod() {
+ return method;
+ }
+
+ public String getSyntax() {
+ return syntax;
+ }
+
+ public int getMinParameter() {
+ return minParameter;
+ }
+
+ public int getMaxParameter() {
+ return maxParameter;
+ }
+
+ public ParameterSet validateParameterSet(final List<EdmType> actualParameterTypes) throws ExpressionParserInternalError {
+ return combination.validate(actualParameterTypes);
+ }
+
+ /**
+ * Returns the EdmType of the returned value of a Method
+ * If a method may have different return types (depending on the input type) null will be returned.
+ */
+ public EdmType getReturnType() {
+ return combination.getReturnType();
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/InfoUnaryOperator.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/InfoUnaryOperator.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/InfoUnaryOperator.java
new file mode 100644
index 0000000..16463d9
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/InfoUnaryOperator.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.olingo.odata2.core.uri.expression;
+
+import java.util.List;
+
+import org.apache.olingo.odata2.api.edm.EdmType;
+import org.apache.olingo.odata2.api.uri.expression.UnaryOperator;
+
+/**
+ * Describes a unary operator which is allowed in OData expressions
+ * @author SAP AG
+ */
+class InfoUnaryOperator {
+ UnaryOperator operator;
+ private String category;
+ private String syntax;
+ ParameterSetCombination combination;
+
+ public InfoUnaryOperator(final UnaryOperator operator, final String category, final ParameterSetCombination combination) {
+ this.operator = operator;
+ this.category = category;
+ syntax = operator.toUriLiteral();
+ this.combination = combination;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public String getSyntax() {
+ return syntax;
+ }
+
+ public UnaryOperator getOperator() {
+ return operator;
+ }
+
+ public ParameterSet validateParameterSet(final List<EdmType> actualParameterTypes) throws ExpressionParserInternalError {
+ return combination.validate(actualParameterTypes);
+ }
+
+ /**
+ * Returns the EdmType of the returned value of a Method
+ * If a method may have different return types (depending on the input type) null will be returned.
+ */
+ /*
+ public EdmType getReturnType()
+ {
+ int parameterCount = allowedParameterTypes.size();
+ if (parameterCount == 0)
+ return null;
+
+ if (parameterCount == 1)
+ return allowedParameterTypes.get(0).getReturnType();
+
+ //There are more than 1 possible return type, check if they are equal, if not return null.
+ EdmType returnType = allowedParameterTypes.get(0).getReturnType();
+ for (int i = 1; i < parameterCount; i++)
+ if (returnType != allowedParameterTypes.get(i))
+ return null;
+
+ return returnType;
+ }*/
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/InputTypeValidator.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/InputTypeValidator.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/InputTypeValidator.java
new file mode 100644
index 0000000..246b769
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/InputTypeValidator.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.olingo.odata2.core.uri.expression;
+
+import java.util.List;
+
+import org.apache.olingo.odata2.api.edm.EdmType;
+
+public interface InputTypeValidator {
+
+ public EdmType validateParameterSet(List<ParameterSet> allowedParameterTypes, List<EdmType> actualParameterTypes) throws ExpressionParserInternalError;
+
+ public static class TypePromotionValidator implements InputTypeValidator {
+
+ @Override
+ public EdmType validateParameterSet(final List<ParameterSet> allowedParameterTypes, final List<EdmType> actualParameterTypes) throws ExpressionParserInternalError {
+ //first check for exact parameter combination
+ for (ParameterSet parameterSet : allowedParameterTypes) {
+ boolean s = parameterSet.equals(actualParameterTypes, false);
+ if (s) {
+ return parameterSet.getReturnType();
+ }
+ }
+
+ //first check for parameter combination with promotion
+ for (ParameterSet parameterSet : allowedParameterTypes) {
+ boolean s = parameterSet.equals(actualParameterTypes, true);
+ if (s) {
+ return parameterSet.getReturnType();
+ }
+ }
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/JsonVisitor.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/JsonVisitor.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/JsonVisitor.java
new file mode 100644
index 0000000..48bb8fe
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/JsonVisitor.java
@@ -0,0 +1,227 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.olingo.odata2.core.uri.expression;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.List;
+
+import org.apache.olingo.odata2.api.edm.Edm;
+import org.apache.olingo.odata2.api.edm.EdmException;
+import org.apache.olingo.odata2.api.edm.EdmLiteral;
+import org.apache.olingo.odata2.api.edm.EdmType;
+import org.apache.olingo.odata2.api.edm.EdmTyped;
+import org.apache.olingo.odata2.api.uri.expression.BinaryExpression;
+import org.apache.olingo.odata2.api.uri.expression.BinaryOperator;
+import org.apache.olingo.odata2.api.uri.expression.CommonExpression;
+import org.apache.olingo.odata2.api.uri.expression.ExpressionVisitor;
+import org.apache.olingo.odata2.api.uri.expression.FilterExpression;
+import org.apache.olingo.odata2.api.uri.expression.LiteralExpression;
+import org.apache.olingo.odata2.api.uri.expression.MemberExpression;
+import org.apache.olingo.odata2.api.uri.expression.MethodExpression;
+import org.apache.olingo.odata2.api.uri.expression.MethodOperator;
+import org.apache.olingo.odata2.api.uri.expression.OrderByExpression;
+import org.apache.olingo.odata2.api.uri.expression.OrderExpression;
+import org.apache.olingo.odata2.api.uri.expression.PropertyExpression;
+import org.apache.olingo.odata2.api.uri.expression.SortOrder;
+import org.apache.olingo.odata2.api.uri.expression.UnaryExpression;
+import org.apache.olingo.odata2.api.uri.expression.UnaryOperator;
+import org.apache.olingo.odata2.core.ep.util.JsonStreamWriter;
+
+/**
+ * @author SAP AG
+ */
+public class JsonVisitor implements ExpressionVisitor {
+
+ @Override
+ public Object visitFilterExpression(final FilterExpression filterExpression, final String expressionString, final Object expression) {
+ return expression;
+ }
+
+ @Override
+ public Object visitBinary(final BinaryExpression binaryExpression, final BinaryOperator operator, final Object leftSide, final Object rightSide) {
+ try {
+ StringWriter writer = new StringWriter();
+ JsonStreamWriter jsonStreamWriter = new JsonStreamWriter(writer);
+ jsonStreamWriter.beginObject()
+ .namedStringValueRaw("nodeType", binaryExpression.getKind().toString()).separator()
+ .namedStringValue("operator", operator.toUriLiteral()).separator()
+ .namedStringValueRaw("type", getType(binaryExpression)).separator()
+ .name("left").unquotedValue(leftSide.toString()).separator()
+ .name("right").unquotedValue(rightSide.toString())
+ .endObject();
+ writer.flush();
+ return writer.toString();
+ } catch (final IOException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public Object visitOrderByExpression(final OrderByExpression orderByExpression, final String expressionString, final List<Object> orders) {
+ try {
+ StringWriter writer = new StringWriter();
+ JsonStreamWriter jsonStreamWriter = new JsonStreamWriter(writer);
+ jsonStreamWriter.beginObject()
+ .namedStringValueRaw("nodeType", "order collection").separator()
+ .name("orders")
+ .beginArray();
+ boolean first = true;
+ for (final Object order : orders) {
+ if (first) {
+ first = false;
+ } else {
+ jsonStreamWriter.separator();
+ }
+ jsonStreamWriter.unquotedValue(order.toString());
+ }
+ jsonStreamWriter.endArray()
+ .endObject();
+ writer.flush();
+ return writer.toString();
+ } catch (final IOException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public Object visitOrder(final OrderExpression orderExpression, final Object filterResult, final SortOrder sortOrder) {
+ try {
+ StringWriter writer = new StringWriter();
+ JsonStreamWriter jsonStreamWriter = new JsonStreamWriter(writer);
+ jsonStreamWriter.beginObject()
+ .namedStringValueRaw("nodeType", orderExpression.getKind().toString()).separator()
+ .namedStringValueRaw("sortorder", sortOrder.toString()).separator()
+ .name("expression").unquotedValue(filterResult.toString())
+ .endObject();
+ writer.flush();
+ return writer.toString();
+ } catch (final IOException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public Object visitLiteral(final LiteralExpression literal, final EdmLiteral edmLiteral) {
+ try {
+ StringWriter writer = new StringWriter();
+ JsonStreamWriter jsonStreamWriter = new JsonStreamWriter(writer);
+ jsonStreamWriter.beginObject()
+ .namedStringValueRaw("nodeType", literal.getKind().toString()).separator()
+ .namedStringValueRaw("type", getType(literal)).separator()
+ .namedStringValue("value", edmLiteral.getLiteral())
+ .endObject();
+ writer.flush();
+ return writer.toString();
+ } catch (final IOException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public Object visitMethod(final MethodExpression methodExpression, final MethodOperator method, final List<Object> parameters) {
+ try {
+ StringWriter writer = new StringWriter();
+ JsonStreamWriter jsonStreamWriter = new JsonStreamWriter(writer);
+ jsonStreamWriter.beginObject()
+ .namedStringValueRaw("nodeType", methodExpression.getKind().toString()).separator()
+ .namedStringValueRaw("operator", method.toUriLiteral()).separator()
+ .namedStringValueRaw("type", getType(methodExpression)).separator()
+ .name("parameters")
+ .beginArray();
+ boolean first = true;
+ for (Object parameter : parameters) {
+ if (first) {
+ first = false;
+ } else {
+ jsonStreamWriter.separator();
+ }
+ jsonStreamWriter.unquotedValue(parameter.toString());
+ }
+ jsonStreamWriter.endArray()
+ .endObject();
+ writer.flush();
+ return writer.toString();
+ } catch (final IOException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public Object visitMember(final MemberExpression memberExpression, final Object path, final Object property) {
+ try {
+ StringWriter writer = new StringWriter();
+ JsonStreamWriter jsonStreamWriter = new JsonStreamWriter(writer);
+ jsonStreamWriter.beginObject()
+ .namedStringValueRaw("nodeType", memberExpression.getKind().toString()).separator()
+ .namedStringValueRaw("type", getType(memberExpression)).separator()
+ .name("source").unquotedValue(path.toString()).separator()
+ .name("path").unquotedValue(property.toString())
+ .endObject();
+ writer.flush();
+ return writer.toString();
+ } catch (final IOException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public Object visitProperty(final PropertyExpression propertyExpression, final String uriLiteral, final EdmTyped edmProperty) {
+ try {
+ StringWriter writer = new StringWriter();
+ JsonStreamWriter jsonStreamWriter = new JsonStreamWriter(writer);
+ jsonStreamWriter.beginObject()
+ .namedStringValueRaw("nodeType", propertyExpression.getKind().toString()).separator()
+ .namedStringValue("name", uriLiteral).separator()
+ .namedStringValueRaw("type", getType(propertyExpression))
+ .endObject();
+ writer.flush();
+ return writer.toString();
+ } catch (final IOException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public Object visitUnary(final UnaryExpression unaryExpression, final UnaryOperator operator, final Object operand) {
+ try {
+ StringWriter writer = new StringWriter();
+ JsonStreamWriter jsonStreamWriter = new JsonStreamWriter(writer);
+ jsonStreamWriter.beginObject()
+ .namedStringValueRaw("nodeType", unaryExpression.getKind().toString()).separator()
+ .namedStringValueRaw("operator", operator.toUriLiteral()).separator()
+ .namedStringValueRaw("type", getType(unaryExpression)).separator()
+ .name("operand").unquotedValue(operand.toString())
+ .endObject();
+ writer.flush();
+ return writer.toString();
+ } catch (final IOException e) {
+ return null;
+ }
+ }
+
+ private static String getType(final CommonExpression expression) {
+ try {
+ final EdmType type = expression.getEdmType();
+ return type == null ? null : type.getNamespace() + Edm.DELIMITER + type.getName();
+ } catch (final EdmException e) {
+ return "EdmException occurred: " + e.getMessage();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/LiteralExpressionImpl.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/LiteralExpressionImpl.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/LiteralExpressionImpl.java
new file mode 100644
index 0000000..bc41e28
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/LiteralExpressionImpl.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.olingo.odata2.core.uri.expression;
+
+import org.apache.olingo.odata2.api.edm.EdmLiteral;
+import org.apache.olingo.odata2.api.edm.EdmType;
+import org.apache.olingo.odata2.api.uri.expression.CommonExpression;
+import org.apache.olingo.odata2.api.uri.expression.ExpressionKind;
+import org.apache.olingo.odata2.api.uri.expression.ExpressionVisitor;
+import org.apache.olingo.odata2.api.uri.expression.LiteralExpression;
+
+public class LiteralExpressionImpl implements LiteralExpression {
+
+ private EdmType edmType;
+ private EdmLiteral edmLiteral;
+ private String uriLiteral;
+
+ public LiteralExpressionImpl(final String uriLiteral, final EdmLiteral javaLiteral) {
+ this.uriLiteral = uriLiteral;
+ edmLiteral = javaLiteral;
+ edmType = edmLiteral.getType();
+ }
+
+ @Override
+ public EdmType getEdmType() {
+ return edmType;
+ }
+
+ @Override
+ public CommonExpression setEdmType(final EdmType edmType) {
+ this.edmType = edmType;
+ return this;
+ }
+
+ @Override
+ public ExpressionKind getKind() {
+ return ExpressionKind.LITERAL;
+ }
+
+ @Override
+ public String getUriLiteral() {
+ return uriLiteral;
+ }
+
+ @Override
+ public Object accept(final ExpressionVisitor visitor) {
+ Object ret = visitor.visitLiteral(this, edmLiteral);
+ return ret;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/MemberExpressionImpl.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/MemberExpressionImpl.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/MemberExpressionImpl.java
new file mode 100644
index 0000000..b4d5ed8
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/MemberExpressionImpl.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.olingo.odata2.core.uri.expression;
+
+import org.apache.olingo.odata2.api.edm.EdmType;
+import org.apache.olingo.odata2.api.exception.ODataApplicationException;
+import org.apache.olingo.odata2.api.uri.expression.BinaryExpression;
+import org.apache.olingo.odata2.api.uri.expression.BinaryOperator;
+import org.apache.olingo.odata2.api.uri.expression.CommonExpression;
+import org.apache.olingo.odata2.api.uri.expression.ExceptionVisitExpression;
+import org.apache.olingo.odata2.api.uri.expression.ExpressionKind;
+import org.apache.olingo.odata2.api.uri.expression.ExpressionVisitor;
+import org.apache.olingo.odata2.api.uri.expression.MemberExpression;
+
+/**
+ * @author SAP AG
+ */
+public class MemberExpressionImpl implements BinaryExpression, MemberExpression {
+ CommonExpression path;
+ CommonExpression property;
+ EdmType edmType;
+
+ public MemberExpressionImpl(final CommonExpression path, final CommonExpression property) {
+ this.path = path;
+ this.property = property;
+ edmType = property.getEdmType();
+ }
+
+ @Override
+ public CommonExpression getPath() {
+ return path;
+ }
+
+ @Override
+ public CommonExpression getProperty() {
+ return property;
+ }
+
+ @Override
+ public EdmType getEdmType() {
+ return edmType;
+ }
+
+ @Override
+ public CommonExpression setEdmType(final EdmType edmType) {
+ this.edmType = edmType;
+ return this;
+ }
+
+ @Override
+ public BinaryOperator getOperator() {
+ return BinaryOperator.PROPERTY_ACCESS;
+ }
+
+ @Override
+ public ExpressionKind getKind() {
+ return ExpressionKind.MEMBER;
+ }
+
+ @Override
+ public String getUriLiteral() {
+ return BinaryOperator.PROPERTY_ACCESS.toUriLiteral();
+ }
+
+ @Override
+ public Object accept(final ExpressionVisitor visitor) throws ExceptionVisitExpression, ODataApplicationException {
+ Object retSource = path.accept(visitor);
+ Object retPath = property.accept(visitor);
+
+ Object ret = visitor.visitMember(this, retSource, retPath);
+ return ret;
+ }
+
+ @Override
+ public CommonExpression getLeftOperand() {
+ return path;
+ }
+
+ @Override
+ public CommonExpression getRightOperand() {
+ return property;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/MethodExpressionImpl.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/MethodExpressionImpl.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/MethodExpressionImpl.java
new file mode 100644
index 0000000..26a8ba4
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/MethodExpressionImpl.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.olingo.odata2.core.uri.expression;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.olingo.odata2.api.edm.EdmType;
+import org.apache.olingo.odata2.api.exception.ODataApplicationException;
+import org.apache.olingo.odata2.api.uri.expression.CommonExpression;
+import org.apache.olingo.odata2.api.uri.expression.ExceptionVisitExpression;
+import org.apache.olingo.odata2.api.uri.expression.ExpressionKind;
+import org.apache.olingo.odata2.api.uri.expression.ExpressionVisitor;
+import org.apache.olingo.odata2.api.uri.expression.MethodExpression;
+import org.apache.olingo.odata2.api.uri.expression.MethodOperator;
+
+/**
+ * @author SAP AG
+ */
+public class MethodExpressionImpl implements MethodExpression {
+
+ private InfoMethod infoMethod;
+ private EdmType returnType;
+ private List<CommonExpression> actualParameters;
+
+ public MethodExpressionImpl(final InfoMethod infoMethod) {
+ this.infoMethod = infoMethod;
+ returnType = infoMethod.getReturnType();
+ actualParameters = new ArrayList<CommonExpression>();
+ }
+
+ @Override
+ public EdmType getEdmType() {
+ return returnType;
+ }
+
+ @Override
+ public CommonExpression setEdmType(final EdmType edmType) {
+ returnType = edmType;
+ return this;
+ }
+
+ @Override
+ public MethodOperator getMethod() {
+ return infoMethod.getMethod();
+ }
+
+ public InfoMethod getMethodInfo() {
+ return infoMethod;
+ }
+
+ @Override
+ public List<CommonExpression> getParameters() {
+ return actualParameters;
+ }
+
+ @Override
+ public int getParameterCount() {
+ return actualParameters.size();
+ }
+
+ /**
+ * @param expression
+ * @return A self reference for method chaining"
+ */
+ public MethodExpressionImpl appendParameter(final CommonExpression expression) {
+ actualParameters.add(expression);
+ return this;
+ }
+
+ @Override
+ public ExpressionKind getKind() {
+ return ExpressionKind.METHOD;
+ }
+
+ @Override
+ public String getUriLiteral() {
+ return infoMethod.getSyntax();
+ }
+
+ @Override
+ public Object accept(final ExpressionVisitor visitor) throws ExceptionVisitExpression, ODataApplicationException {
+ ArrayList<Object> retParameters = new ArrayList<Object>();
+ for (CommonExpression parameter : actualParameters) {
+ Object retParameter = parameter.accept(visitor);
+ retParameters.add(retParameter);
+ }
+
+ Object ret = visitor.visitMethod(this, getMethod(), retParameters);
+ return ret;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/OrderByExpressionImpl.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/OrderByExpressionImpl.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/OrderByExpressionImpl.java
new file mode 100644
index 0000000..7e28dc2
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/OrderByExpressionImpl.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.olingo.odata2.core.uri.expression;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.olingo.odata2.api.edm.EdmType;
+import org.apache.olingo.odata2.api.exception.ODataApplicationException;
+import org.apache.olingo.odata2.api.uri.expression.CommonExpression;
+import org.apache.olingo.odata2.api.uri.expression.ExceptionVisitExpression;
+import org.apache.olingo.odata2.api.uri.expression.ExpressionKind;
+import org.apache.olingo.odata2.api.uri.expression.ExpressionVisitor;
+import org.apache.olingo.odata2.api.uri.expression.OrderByExpression;
+import org.apache.olingo.odata2.api.uri.expression.OrderExpression;
+
+/**
+ * @author SAP AG
+ */
+public class OrderByExpressionImpl implements OrderByExpression {
+ private String orderbyString;
+
+ List<OrderExpression> orders;
+
+ public OrderByExpressionImpl(final String orderbyString) {
+ this.orderbyString = orderbyString;
+ orders = new ArrayList<OrderExpression>();
+ }
+
+ @Override
+ public String getExpressionString() {
+ return orderbyString;
+ }
+
+ @Override
+ public List<OrderExpression> getOrders() {
+ return orders;
+ }
+
+ @Override
+ public int getOrdersCount() {
+ return orders.size();
+ }
+
+ public void addOrder(final OrderExpression orderNode) {
+ orders.add(orderNode);
+ }
+
+ @Override
+ public ExpressionKind getKind() {
+ return ExpressionKind.ORDERBY;
+ }
+
+ @Override
+ public EdmType getEdmType() {
+ return null;
+ }
+
+ @Override
+ public CommonExpression setEdmType(final EdmType edmType) {
+ return this;
+ }
+
+ @Override
+ public String getUriLiteral() {
+ return orderbyString;
+ }
+
+ @Override
+ public Object accept(final ExpressionVisitor visitor) throws ExceptionVisitExpression, ODataApplicationException {
+ ArrayList<Object> retParameters = new ArrayList<Object>();
+ for (OrderExpression order : orders) {
+ Object retParameter = order.accept(visitor);
+ retParameters.add(retParameter);
+ }
+
+ Object ret = visitor.visitOrderByExpression(this, orderbyString, retParameters);
+ return ret;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/OrderByParser.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/OrderByParser.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/OrderByParser.java
new file mode 100644
index 0000000..c1ab197
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/expression/OrderByParser.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.olingo.odata2.core.uri.expression;
+
+import org.apache.olingo.odata2.api.exception.ODataMessageException;
+import org.apache.olingo.odata2.api.uri.expression.ExpressionParserException;
+import org.apache.olingo.odata2.api.uri.expression.OrderByExpression;
+
+/**
+ * Interface which defines a method for parsing a $orderby expression to allow different parser implementations
+ * <p>
+ * The current expression parser supports expressions as defined in the OData specification 2.0 with the following restrictions
+ * - the methods "cast", "isof" and "replace" are not supported
+ *
+ * The expression parser can be used with providing an Entity Data Model (EDM) an without providing it.
+ * <p>When a EDM is provided the expression parser will be as strict as possible. That means:
+ * <li>All properties used in the expression must be defined inside the EDM</li>
+ * <li>The types of EDM properties will be checked against the lists of allowed type per method, binary- and unary operator</li>
+ * </p>
+ * <p>If no EDM is provided the expression parser performs a lax validation
+ * <li>The properties used in the expression are not looked up inside the EDM and the type of the expression node representing the
+ * property will be "null"</li>
+ * <li>Expression node with EDM-types which are "null" are not considered during the parameter type validation, to the return type of the parent expression node will
+ * also become "null"</li>
+ *
+ * @author SAP AG
+ */
+public interface OrderByParser {
+ /**
+ * Parses a $orderby expression string and creates an $orderby expression tree
+ * @param orderByExpression
+ * The $orderby expression string ( for example "name asc" ) to be parsed
+ * @return
+ * The $orderby expression tree
+ * @throws ExpressionParserException
+ * Exception thrown due to errors while parsing the $orderby expression string
+ * @throws ODataMessageException
+ * Used for extensibility
+ */
+ abstract OrderByExpression parseOrderByString(String orderByExpression) throws ExpressionParserException, ODataMessageException;
+}