[OLINGO-834] Type checks in ExpressionParser

Signed-off-by: Christian Amend <christian.amend@sap.com>
This commit is contained in:
Klaus Straubinger 2015-12-16 16:14:12 +01:00 committed by Christian Amend
parent 2f3bc2866b
commit 104ecf43d2
9 changed files with 616 additions and 127 deletions

View File

@ -24,13 +24,26 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
import org.apache.olingo.commons.api.edm.EdmType;
import org.apache.olingo.commons.api.edm.EdmTypeDefinition;
import org.apache.olingo.commons.api.edm.FullQualifiedName;
import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
import org.apache.olingo.server.api.OData;
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;
import org.apache.olingo.server.api.uri.queryoption.expression.Enumeration;
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
import org.apache.olingo.server.api.uri.queryoption.expression.LambdaRef;
import org.apache.olingo.server.api.uri.queryoption.expression.Literal;
import org.apache.olingo.server.api.uri.queryoption.expression.Member;
import org.apache.olingo.server.api.uri.queryoption.expression.Method;
import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind;
import org.apache.olingo.server.api.uri.queryoption.expression.TypeLiteral;
import org.apache.olingo.server.api.uri.queryoption.expression.Unary;
import org.apache.olingo.server.api.uri.queryoption.expression.UnaryOperatorKind;
import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
import org.apache.olingo.server.core.uri.queryoption.expression.AliasImpl;
@ -72,10 +85,10 @@ public class ExpressionParser {
tokenToUnaryOperator = Collections.unmodifiableMap(temp);
}
// 'cast' and 'isof' are handled specially.
private static final Map<TokenKind, MethodKind> tokenToMethod;
static {
Map<TokenKind, MethodKind> temp = new HashMap<TokenKind, MethodKind>();
temp.put(TokenKind.CastMethod, MethodKind.CAST);
temp.put(TokenKind.CeilingMethod, MethodKind.CEILING);
temp.put(TokenKind.ConcatMethod, MethodKind.CONCAT);
temp.put(TokenKind.ContainsMethod, MethodKind.CONTAINS);
@ -89,7 +102,6 @@ public class ExpressionParser {
temp.put(TokenKind.GeoLengthMethod, MethodKind.GEOLENGTH);
temp.put(TokenKind.HourMethod, MethodKind.HOUR);
temp.put(TokenKind.IndexofMethod, MethodKind.INDEXOF);
temp.put(TokenKind.IsofMethod, MethodKind.ISOF);
temp.put(TokenKind.LengthMethod, MethodKind.LENGTH);
temp.put(TokenKind.MaxdatetimeMethod, MethodKind.MAXDATETIME);
temp.put(TokenKind.MindatetimeMethod, MethodKind.MINDATETIME);
@ -131,8 +143,16 @@ public class ExpressionParser {
tokenToPrimitiveType = Collections.unmodifiableMap(temp);
}
private final Edm edm;
private final OData odata;
private UriTokenizer tokenizer;
public ExpressionParser(final Edm edm, final OData odata) {
this.edm = edm;
this.odata = odata;
}
public Expression parse(UriTokenizer tokenizer) throws UriParserException {
// Initialize tokenizer.
this.tokenizer = tokenizer;
@ -144,7 +164,10 @@ public class ExpressionParser {
Expression left = parseAnd();
while (tokenizer.next(TokenKind.OrOperator)) {
final Expression right = parseAnd();
left = new BinaryImpl(left, BinaryOperatorKind.OR, right);
checkType(left, EdmPrimitiveTypeKind.Boolean);
checkType(right, EdmPrimitiveTypeKind.Boolean);
left = new BinaryImpl(left, BinaryOperatorKind.OR, right,
odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean));
}
return left;
}
@ -153,7 +176,10 @@ public class ExpressionParser {
Expression left = parseExprEquality();
while (tokenizer.next(TokenKind.AndOperator)) {
final Expression right = parseExprEquality();
left = new BinaryImpl(left, BinaryOperatorKind.AND, right);
checkType(left, EdmPrimitiveTypeKind.Boolean);
checkType(right, EdmPrimitiveTypeKind.Boolean);
left = new BinaryImpl(left, BinaryOperatorKind.AND, right,
odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean));
}
return left;
}
@ -164,12 +190,15 @@ public class ExpressionParser {
// Null for everything other than EQ or NE
while (operatorTokenKind != null) {
final Expression right = parseExprEquality();
left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right);
checkEqualityTypes(left, right);
left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right,
odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean));
operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.EqualsOperator, TokenKind.NotEqualsOperator);
}
return left;
}
// TODO: The 'isof' method has relational precedence and should appear here.
private Expression parseExprRel() throws UriParserException {
Expression left = parseExprAdd();
TokenKind operatorTokenKind = ParserHelper.next(tokenizer,
@ -178,7 +207,9 @@ public class ExpressionParser {
// Null for everything other than GT or GE or LT or LE
while (operatorTokenKind != null) {
final Expression right = parseExprAdd();
left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right);
checkRelationTypes(left, right);
left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right,
odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean));
operatorTokenKind = ParserHelper.next(tokenizer,
TokenKind.GreaterThanOperator, TokenKind.GreaterThanOrEqualsOperator,
TokenKind.LessThanOperator, TokenKind.LessThanOrEqualsOperator);
@ -192,7 +223,9 @@ public class ExpressionParser {
// Null for everything other than ADD or SUB
while (operatorTokenKind != null) {
final Expression right = parseExprMul();
left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right);
checkAddSubTypes(left, right, operatorTokenKind == TokenKind.AddOperator);
left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right,
odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Double));
operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.AddOperator, TokenKind.SubOperator);
}
return left;
@ -205,28 +238,61 @@ public class ExpressionParser {
// Null for everything other than MUL or DIV or MOD
while (operatorTokenKind != null) {
final Expression right = parseExprUnary();
left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right);
checkType(left,
EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double);
checkType(right,
EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double);
left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right,
odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Double));
operatorTokenKind = ParserHelper.next(tokenizer,
TokenKind.MulOperator, TokenKind.DivOperator, TokenKind.ModOperator);
}
return left;
}
// TODO: The 'cast' method has unary precedence and should appear here.
private Expression parseExprUnary() throws UriParserException {
Expression left = null;
TokenKind operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.MINUS, TokenKind.NotOperator);
// Null for everything other than - or NOT
while (operatorTokenKind != null) {
final Expression expression = parseExprValue();
left = new UnaryImpl(tokenToUnaryOperator.get(operatorTokenKind), expression);
final Expression expression = parseExprPrimary();
if (operatorTokenKind == TokenKind.NotOperator) {
checkType(expression, EdmPrimitiveTypeKind.Boolean);
} else {
checkType(expression,
EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double,
EdmPrimitiveTypeKind.Duration);
}
left = new UnaryImpl(tokenToUnaryOperator.get(operatorTokenKind), expression, getType(expression));
operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.MINUS, TokenKind.NotOperator);
}
if (left == null) {
left = parseExprValue();
left = parseExprPrimary();
}
return left;
}
private Expression parseExprPrimary() throws UriParserException {
final Expression left = parseExprValue();
if (isEnumType(left) && tokenizer.next(TokenKind.HasOperator)) {
ParserHelper.requireNext(tokenizer, TokenKind.EnumValue);
final String primitiveValueLiteral = tokenizer.getText();
final Expression right = new LiteralImpl(primitiveValueLiteral, getEnumType(primitiveValueLiteral));
checkEnumLiteral(right);
return new BinaryImpl(left, BinaryOperatorKind.HAS, right,
odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean));
} else {
return left;
}
}
private Expression parseExprValue() throws UriParserException {
if (tokenizer.next(TokenKind.OPEN)) {
final Expression expression = parseExpression();
@ -251,44 +317,34 @@ public class ExpressionParser {
// TODO: Consume $it expression.
}
if (tokenizer.next(TokenKind.QualifiedName)) {
// TODO: Consume typecast or bound-function expression.
}
TokenKind nextPrimitive = ParserHelper.nextPrimitive(tokenizer);
if (nextPrimitive != null) {
final String primitiveValueLiteral = tokenizer.getText();
final EdmPrimitiveTypeKind primitiveTypeKind = tokenToPrimitiveType.get(nextPrimitive);
EdmPrimitiveType type;
if (primitiveTypeKind == null) {
if (nextPrimitive == TokenKind.EnumValue) {
// TODO: Get enum type.
type = null;
type = getEnumType(primitiveValueLiteral);
} else {
// Null handling
type = null;
}
} else {
type = EdmPrimitiveTypeFactory.getInstance(primitiveTypeKind);
type = odata.createPrimitiveTypeInstance(primitiveTypeKind);
}
return new LiteralImpl(tokenizer.getText(), type);
return new LiteralImpl(primitiveValueLiteral, type);
}
// The method token text includes the opening parenthesis so that method calls can be recognized unambiguously.
// OData identifiers have to be considered after that.
TokenKind nextMethod = nextMethod();
if (nextMethod != null) {
MethodKind methodKind = tokenToMethod.get(nextMethod);
List<Expression> parameters = new ArrayList<Expression>();
// The method token text includes the opening parenthesis!
if (!tokenizer.next(TokenKind.CLOSE)) {
do {
parameters.add(parseExpression());
} while (tokenizer.next(TokenKind.COMMA));
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
}
return new MethodImpl(methodKind, parseMethodParameters(methodKind));
}
MethodImpl methodImpl = new MethodImpl(methodKind, parameters);
validateMethodParameters(methodImpl);
return methodImpl;
if (tokenizer.next(TokenKind.QualifiedName)) {
// TODO: Consume typecast or bound-function expression.
}
if (tokenizer.next(TokenKind.ODataIdentifier)) {
@ -298,85 +354,131 @@ public class ExpressionParser {
throw new UriParserSyntaxException("Unexpected token", UriParserSyntaxException.MessageKeys.SYNTAX);
}
private void validateMethodParameters(final Method method) throws UriParserException {
// We might validate parameter types in the future.
int size = method.getParameters().size();
switch (method.getMethod()) {
// Must have two Parameters.
case CONTAINS:
case ENDSWITH:
case STARTSWITH:
case INDEXOF:
case CONCAT:
case GEODISTANCE:
case GEOINTERSECTS:
if (size != 2) {
throw new UriParserSemanticException(
"The method " + method.getMethod() + " needs exactly two parameters.",
null); // TODO: message key
}
private List<Expression> parseMethodParameters(final MethodKind methodKind) throws UriParserException {
List<Expression> parameters = new ArrayList<Expression>();
switch (methodKind) {
// Must have no parameter.
case NOW:
case MAXDATETIME:
case MINDATETIME:
break;
// Must have one parameter.
case LENGTH:
case TOLOWER:
case TOUPPER:
case TRIM:
final Expression stringParameter = parseExpression();
checkType(stringParameter, EdmPrimitiveTypeKind.String);
parameters.add(stringParameter);
break;
case YEAR:
case MONTH:
case DAY:
final Expression dateParameter = parseExpression();
checkType(dateParameter, EdmPrimitiveTypeKind.Date, EdmPrimitiveTypeKind.DateTimeOffset);
parameters.add(dateParameter);
break;
case HOUR:
case MINUTE:
case SECOND:
case FRACTIONALSECONDS:
final Expression timeParameter = parseExpression();
checkType(timeParameter, EdmPrimitiveTypeKind.TimeOfDay, EdmPrimitiveTypeKind.DateTimeOffset);
parameters.add(timeParameter);
break;
case DATE:
case TIME:
case TOTALOFFSETMINUTES:
final Expression dateTimeParameter = parseExpression();
checkType(dateTimeParameter, EdmPrimitiveTypeKind.DateTimeOffset);
parameters.add(dateTimeParameter);
break;
case TOTALSECONDS:
final Expression durationParameter = parseExpression();
checkType(durationParameter, EdmPrimitiveTypeKind.Duration);
parameters.add(durationParameter);
break;
case ROUND:
case FLOOR:
case CEILING:
final Expression decimalParameter = parseExpression();
checkType(decimalParameter,
EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double);
parameters.add(decimalParameter);
break;
case GEOLENGTH:
if (size != 1) {
throw new UriParserSemanticException(
"The method '" + method.getMethod() + "' needs exactly one parameter.",
null); // TODO: message key
}
final Expression geoParameter = parseExpression();
checkType(geoParameter,
EdmPrimitiveTypeKind.GeographyLineString, EdmPrimitiveTypeKind.GeometryLineString);
parameters.add(geoParameter);
break;
// Must have no parameter.
case NOW:
case MAXDATETIME:
case MINDATETIME:
if (size > 0) {
throw new UriParserSemanticException("The method '" + method.getMethod() + "' must have no parameters.",
null); // TODO: message key
}
// Must have two parameters.
case CONTAINS:
case ENDSWITH:
case STARTSWITH:
case INDEXOF:
case CONCAT:
final Expression stringParameter1 = parseExpression();
checkType(stringParameter1, EdmPrimitiveTypeKind.String);
parameters.add(stringParameter1);
ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
final Expression stringParameter2 = parseExpression();
checkType(stringParameter2, EdmPrimitiveTypeKind.String);
parameters.add(stringParameter2);
break;
// Variable parameter number
case CAST:
case ISOF:
if (size < 1 || size > 2) {
throw new UriParserSemanticException(
"The method '" + method.getMethod() + "' must have one or two parameters.",
null); // TODO: message key
}
case GEODISTANCE:
final Expression geoParameter1 = parseExpression();
checkType(geoParameter1, EdmPrimitiveTypeKind.GeographyPoint, EdmPrimitiveTypeKind.GeometryPoint);
parameters.add(geoParameter1);
ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
final Expression geoParameter2 = parseExpression();
checkType(geoParameter2, EdmPrimitiveTypeKind.GeographyPoint, EdmPrimitiveTypeKind.GeometryPoint);
parameters.add(geoParameter2);
break;
case GEOINTERSECTS:
final Expression geoPointParameter = parseExpression();
checkType(geoPointParameter,
EdmPrimitiveTypeKind.GeographyPoint, EdmPrimitiveTypeKind.GeometryPoint);
parameters.add(geoPointParameter);
ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
final Expression geoPolygonParameter = parseExpression();
checkType(geoPolygonParameter,
EdmPrimitiveTypeKind.GeographyPolygon, EdmPrimitiveTypeKind.GeometryPolygon);
parameters.add(geoPolygonParameter);
break;
// Can have two or three parameters.
case SUBSTRING:
if (size < 2 || size > 3) {
throw new UriParserSemanticException(
"The method '" + method.getMethod() + "' must have two or three parameters.",
null); // TODO: message key
final Expression parameterFirst = parseExpression();
checkType(parameterFirst, EdmPrimitiveTypeKind.String);
parameters.add(parameterFirst);
ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
final Expression parameterSecond = parseExpression();
checkType(parameterSecond,
EdmPrimitiveTypeKind.Int64, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int16,
EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte);
parameters.add(parameterSecond);
if (tokenizer.next(TokenKind.COMMA)) {
final Expression parameterThird = parseExpression();
checkType(parameterThird,
EdmPrimitiveTypeKind.Int64, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int16,
EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte);
parameters.add(parameterThird);
}
break;
default:
throw new UriParserSemanticException(
"Unkown method '" + method.getMethod() + "'",
null); // TODO: message key
throw new UriParserSemanticException("Unkown method '" + methodKind.name() + "'",
UriParserSemanticException.MessageKeys.NOT_IMPLEMENTED, methodKind.name()); // TODO: better message
}
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
return parameters;
}
private TokenKind nextMethod() {
return ParserHelper.next(tokenizer,
TokenKind.CastMethod,
TokenKind.CeilingMethod,
TokenKind.ConcatMethod,
TokenKind.ContainsMethod,
@ -390,7 +492,6 @@ public class ExpressionParser {
TokenKind.GeoLengthMethod,
TokenKind.HourMethod,
TokenKind.IndexofMethod,
TokenKind.IsofMethod,
TokenKind.LengthMethod,
TokenKind.MaxdatetimeMethod,
TokenKind.MindatetimeMethod,
@ -409,4 +510,158 @@ public class ExpressionParser {
TokenKind.TrimMethod,
TokenKind.YearMethod);
}
private EdmType getType(final Expression expression) throws UriParserException {
EdmType type;
if (expression instanceof Literal) {
type = ((Literal) expression).getType();
} else if (expression instanceof TypeLiteral) {
type = ((TypeLiteral) expression).getType();
} else if (expression instanceof Enumeration) {
type = ((Enumeration) expression).getType();
} else if (expression instanceof Member) {
type = ((Member) expression).getType();
} else if (expression instanceof Unary) {
type = ((UnaryImpl) expression).getType();
} else if (expression instanceof Binary) {
type = ((BinaryImpl) expression).getType();
} else if (expression instanceof Method) {
type = ((MethodImpl) expression).getType();
} 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());
}
if (type != null && type.getKind() == EdmTypeKind.DEFINITION) {
type = ((EdmTypeDefinition) type).getUnderlyingType();
}
return type;
}
private boolean isType(final Expression expression, final EdmPrimitiveTypeKind... kinds) throws UriParserException {
final EdmType expressionType = getType(expression);
if (expressionType == null) {
return true;
}
for (final EdmPrimitiveTypeKind kind : kinds) {
if (expressionType.equals(odata.createPrimitiveTypeInstance(kind))) {
return true;
}
}
return false;
}
private void checkType(final Expression expression, final EdmPrimitiveTypeKind... kinds) throws UriParserException {
if (!isType(expression, kinds)) {
throw new UriParserSemanticException("Incompatible type.",
UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, // TODO: better message
getType(expression) == null ?
"" :
getType(expression).getFullQualifiedName().getFullQualifiedNameAsString());
}
}
private void checkEqualityTypes(final Expression left, final Expression right) throws UriParserException {
final EdmType leftType = getType(left);
final EdmType rightType = getType(right);
if (leftType == null || rightType == null || leftType.equals(rightType)) {
return;
}
if (leftType.getKind() != EdmTypeKind.PRIMITIVE
|| rightType.getKind() != EdmTypeKind.PRIMITIVE
|| !(((EdmPrimitiveType) leftType).isCompatible((EdmPrimitiveType) rightType)
|| ((EdmPrimitiveType) rightType).isCompatible((EdmPrimitiveType) leftType))) {
throw new UriParserSemanticException("Incompatible types.",
UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, ""); // TODO: better message
}
}
private EdmPrimitiveType getEnumType(final String primitiveValueLiteral) throws UriParserException {
final String enumTypeName = primitiveValueLiteral.substring(0, primitiveValueLiteral.indexOf('\''));
final EdmPrimitiveType type = edm.getEnumType(new FullQualifiedName(enumTypeName));
if (type == null) {
throw new UriParserSemanticException("Unknown Enum type '" + enumTypeName + "'.",
UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, enumTypeName);
}
return type;
}
private boolean isEnumType(final Expression expression) throws UriParserException {
final EdmType expressionType = getType(expression);
return expressionType == null
|| expressionType.getKind() == EdmTypeKind.ENUM
|| isType(expression,
EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte);
}
private void checkEnumLiteral(final Expression expression) throws UriParserException {
if (expression == null
|| !(expression instanceof Literal)
|| ((Literal) expression).getType() == null
|| ((Literal) expression).getType().getKind() != EdmTypeKind.ENUM) {
throw new UriParserSemanticException("Enum literal expected.",
UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, ""); // TODO: better message
}
}
private void checkRelationTypes(final Expression left, final Expression right) throws UriParserException {
final EdmType leftType = getType(left);
final EdmType rightType = getType(right);
if (leftType == null || rightType == null) {
return;
}
checkType(left,
EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double,
EdmPrimitiveTypeKind.Boolean, EdmPrimitiveTypeKind.Guid, EdmPrimitiveTypeKind.String,
EdmPrimitiveTypeKind.Date, EdmPrimitiveTypeKind.TimeOfDay,
EdmPrimitiveTypeKind.DateTimeOffset, EdmPrimitiveTypeKind.Duration);
checkType(right,
EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double,
EdmPrimitiveTypeKind.Boolean, EdmPrimitiveTypeKind.Guid, EdmPrimitiveTypeKind.String,
EdmPrimitiveTypeKind.Date, EdmPrimitiveTypeKind.TimeOfDay,
EdmPrimitiveTypeKind.DateTimeOffset, EdmPrimitiveTypeKind.Duration);
if (!(((EdmPrimitiveType) leftType).isCompatible((EdmPrimitiveType) rightType)
|| ((EdmPrimitiveType) rightType).isCompatible((EdmPrimitiveType) leftType))) {
throw new UriParserSemanticException("Incompatible types.",
UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, ""); // TODO: better message
}
}
private void checkAddSubTypes(final Expression left, final Expression right, final boolean isAdd)
throws UriParserException {
final EdmType leftType = getType(left);
final EdmType rightType = getType(right);
if (leftType == null || rightType == null
|| isType(left,
EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double)
&& isType(right,
EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double)) {
return;
}
if (isType(left, EdmPrimitiveTypeKind.DateTimeOffset)
&& (isType(right, EdmPrimitiveTypeKind.Duration)
|| isType(right, EdmPrimitiveTypeKind.DateTimeOffset) && !isAdd)) {
return;
}
if (isType(left, EdmPrimitiveTypeKind.Duration) && isType(right, EdmPrimitiveTypeKind.Duration)
|| isType(left, EdmPrimitiveTypeKind.Date)
&& (isType(right, EdmPrimitiveTypeKind.Duration) || isType(right, EdmPrimitiveTypeKind.Date) && !isAdd)) {
return;
}
throw new UriParserSemanticException("Incompatible types.",
UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, ""); // TODO: better message
}
}

View File

@ -738,7 +738,8 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
return new BinaryImpl(
(Expression) ctx.vE1.accept(this),
tokenIndex == UriLexer.ADD ? BinaryOperatorKind.ADD : BinaryOperatorKind.SUB,
(Expression) ctx.vE2.accept(this));
(Expression) ctx.vE2.accept(this),
null);
}
@Override
@ -785,7 +786,8 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
return new BinaryImpl(
(Expression) ctx.vE1.accept(this),
BinaryOperatorKind.AND,
(Expression) ctx.vE2.accept(this));
(Expression) ctx.vE2.accept(this),
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean));
}
@Override
@ -815,7 +817,8 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
return new BinaryImpl(
(Expression) ctx.vE1.accept(this),
kind,
(Expression) ctx.vE2.accept(this));
(Expression) ctx.vE2.accept(this),
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean));
}
@Override
@ -843,7 +846,8 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
return new BinaryImpl(
(Expression) ctx.vE1.accept(this),
tokenIndex == UriLexer.EQ_ALPHA ? BinaryOperatorKind.EQ : BinaryOperatorKind.NE,
(Expression) ctx.vE2.accept(this));
(Expression) ctx.vE2.accept(this),
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean));
}
@Override
@ -851,7 +855,8 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
return new BinaryImpl(
(Expression) ctx.vE1.accept(this),
BinaryOperatorKind.HAS,
(Expression) ctx.vE2.accept(this));
(Expression) ctx.vE2.accept(this),
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean));
}
@Override
@ -875,7 +880,8 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
return new BinaryImpl(
(Expression) ctx.vE1.accept(this),
kind,
(Expression) ctx.vE2.accept(this));
(Expression) ctx.vE2.accept(this),
null);
}
@Override
@ -883,7 +889,8 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
return new BinaryImpl(
(Expression) ctx.vE1.accept(this),
BinaryOperatorKind.OR,
(Expression) ctx.vE2.accept(this));
(Expression) ctx.vE2.accept(this),
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean));
}
@Override
@ -2294,7 +2301,8 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
public Expression visitAltUnary(@NotNull final UriParserParser.AltUnaryContext ctx) {
return new UnaryImpl(
ctx.unary().NOT() == null ? UnaryOperatorKind.MINUS : UnaryOperatorKind.NOT,
(Expression) ctx.commonExpr().accept(this));
(Expression) ctx.commonExpr().accept(this),
null);
}
@Override

View File

@ -80,6 +80,7 @@ public class UriTokenizer {
GreaterThanOrEqualsOperator,
LessThanOperator,
LessThanOrEqualsOperator,
HasOperator,
AddOperator,
SubOperator,
MulOperator,
@ -140,6 +141,7 @@ public class UriTokenizer {
* The order in which this method is called with different token kinds is important,
* not only for performance reasons but also if tokens can start with the same characters
* (e.g., a qualified name starts with an OData identifier).
* The index is advanced to the end of this token if the token is found.
* @param allowedTokenKind the kind of token to expect
* @return <code>true</code> if the token is found; <code>false</code> otherwise
* @see #getText()
@ -288,6 +290,9 @@ public class UriTokenizer {
case LessThanOrEqualsOperator:
found = nextBinaryOperator("le");
break;
case HasOperator:
found = nextBinaryOperator("has");
break;
case AddOperator:
found = nextBinaryOperator("add");
break;

View File

@ -18,6 +18,7 @@
*/
package org.apache.olingo.server.core.uri.queryoption.expression;
import org.apache.olingo.commons.api.edm.EdmType;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.uri.queryoption.expression.Binary;
import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind;
@ -30,11 +31,14 @@ public class BinaryImpl implements Binary {
private final Expression left;
private final BinaryOperatorKind operator;
private final Expression right;
private final EdmType type;
public BinaryImpl(final Expression left, final BinaryOperatorKind operator, final Expression right) {
public BinaryImpl(final Expression left, final BinaryOperatorKind operator, final Expression right,
final EdmType type) {
this.left = left;
this.operator = operator;
this.right = right;
this.type = type;
}
@Override
@ -52,6 +56,10 @@ public class BinaryImpl implements Binary {
return right;
}
public EdmType getType() {
return type;
}
@Override
public <T> T accept(final ExpressionVisitor<T> visitor) throws ExpressionVisitException, ODataApplicationException {
T left = this.left.accept(visitor);

View File

@ -22,12 +22,16 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.commons.api.edm.EdmType;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitException;
import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitor;
import org.apache.olingo.server.api.uri.queryoption.expression.Method;
import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind;
import org.apache.olingo.server.api.uri.queryoption.expression.TypeLiteral;
import org.apache.olingo.server.core.ODataImpl;
public class MethodImpl implements Method {
@ -44,6 +48,72 @@ public class MethodImpl implements Method {
return method;
}
public EdmType getType() {
EdmPrimitiveTypeKind kind = null;
switch (method) {
case CONTAINS:
case STARTSWITH:
case ENDSWITH:
kind = EdmPrimitiveTypeKind.Boolean;
break;
case LENGTH:
case INDEXOF:
kind = EdmPrimitiveTypeKind.Int32;
break;
case SUBSTRING:
case TOLOWER:
case TOUPPER:
case TRIM:
case CONCAT:
kind = EdmPrimitiveTypeKind.String;
break;
case YEAR:
case MONTH:
case DAY:
case HOUR:
case MINUTE:
case SECOND:
kind = EdmPrimitiveTypeKind.Int32;
break;
case FRACTIONALSECONDS:
case TOTALSECONDS:
kind = EdmPrimitiveTypeKind.Decimal;
break;
case DATE:
kind = EdmPrimitiveTypeKind.Date;
break;
case TIME:
kind = EdmPrimitiveTypeKind.TimeOfDay;
break;
case TOTALOFFSETMINUTES:
kind = EdmPrimitiveTypeKind.Int32;
break;
case MINDATETIME:
case MAXDATETIME:
case NOW:
kind = EdmPrimitiveTypeKind.DateTimeOffset;
break;
case ROUND:
case FLOOR:
case CEILING:
kind = EdmPrimitiveTypeKind.Double; // Needs to be refined if Decimal must be distinguished from Double.
break;
case GEODISTANCE:
case GEOLENGTH:
kind = EdmPrimitiveTypeKind.Double;
break;
case GEOINTERSECTS:
kind = EdmPrimitiveTypeKind.Boolean;
break;
case CAST:
return ((TypeLiteral) parameters.get(parameters.size() - 1)).getType();
case ISOF:
kind = EdmPrimitiveTypeKind.Boolean;
break;
}
return new ODataImpl().createPrimitiveTypeInstance(kind);
}
@Override
public List<Expression> getParameters() {
return parameters == null ?

View File

@ -18,6 +18,7 @@
*/
package org.apache.olingo.server.core.uri.queryoption.expression;
import org.apache.olingo.commons.api.edm.EdmType;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitException;
@ -29,10 +30,12 @@ public class UnaryImpl implements Unary {
private final UnaryOperatorKind operator;
private final Expression expression;
private final EdmType type;
public UnaryImpl(final UnaryOperatorKind operator, final Expression expression) {
public UnaryImpl(final UnaryOperatorKind operator, final Expression expression, final EdmType type) {
this.operator = operator;
this.expression = expression;
this.type = type;
}
@Override
@ -45,6 +48,10 @@ public class UnaryImpl implements Unary {
return expression;
}
public EdmType getType() {
return type;
}
@Override
public <T> T accept(final ExpressionVisitor<T> visitor) throws ExpressionVisitException, ODataApplicationException {
T operand = expression.accept(visitor);

View File

@ -21,15 +21,19 @@ package org.apache.olingo.server.core.uri.parser;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Locale;
import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
import org.junit.Test;
public class ExpressionParserTest {
private final OData odata = OData.newInstance();
@Test
public void equality() throws Exception {
Expression expression = parseExpression("5 eq 5");
@ -37,6 +41,12 @@ public class ExpressionParserTest {
expression = parseExpression("5 ne 5");
assertEquals("{5 NE 5}", expression.toString());
assertEquals("{1 EQ null}", parseExpression("1 eq null").toString());
assertEquals("{null NE 2}", parseExpression("null ne 2").toString());
assertEquals("{null EQ null}", parseExpression("null eq null").toString());
wrongExpression("5 eq '5'");
}
@Test
@ -52,6 +62,14 @@ public class ExpressionParserTest {
expression = parseExpression("5 le 5");
assertEquals("{5 LE 5}", expression.toString());
assertEquals("{5 LE 5.1}", parseExpression("5 le 5.1").toString());
assertEquals("{1 GT null}", parseExpression("1 gt null").toString());
assertEquals("{null GE 2}", parseExpression("null ge 2").toString());
assertEquals("{null LE null}", parseExpression("null le null").toString());
wrongExpression("5 gt duration'PT5H'");
}
@Test
@ -59,8 +77,37 @@ public class ExpressionParserTest {
Expression expression = parseExpression("5 add 5");
assertEquals("{5 ADD 5}", expression.toString());
expression = parseExpression("5 sub 5");
assertEquals("{5 SUB 5}", expression.toString());
expression = parseExpression("5 sub 5.1");
assertEquals("{5 SUB 5.1}", expression.toString());
expression = parseExpression("2000-02-29 sub 2016-02-29");
assertEquals("{2000-02-29 SUB 2016-02-29}", expression.toString());
expression = parseExpression("2000-02-29T00:00:00Z sub 2016-02-29T01:02:03Z");
assertEquals("{2000-02-29T00:00:00Z SUB 2016-02-29T01:02:03Z}", expression.toString());
expression = parseExpression("duration'PT1H' add duration'PT1M'");
assertEquals("{duration'PT1H' ADD duration'PT1M'}", expression.toString());
expression = parseExpression("2016-01-01 add duration'P60D'");
assertEquals("{2016-01-01 ADD duration'P60D'}", expression.toString());
expression = parseExpression("2000-02-29T00:00:00Z add duration'PT12H'");
assertEquals("{2000-02-29T00:00:00Z ADD duration'PT12H'}", expression.toString());
assertEquals("{1 ADD null}", parseExpression("1 add null").toString());
assertEquals("{null ADD 2}", parseExpression("null add 2").toString());
assertEquals("{null SUB null}", parseExpression("null sub null").toString());
wrongExpression("1 add '2'");
wrongExpression("'1' add 2");
wrongExpression("1 add 2000-02-29");
wrongExpression("11:12:13 sub 2000-02-29T11:12:13Z");
wrongExpression("2000-02-29 add 2016-02-29");
wrongExpression("2000-02-29T00:00:00Z add 2016-02-29T01:02:03Z");
wrongExpression("2000-02-29T00:00:00Z add 1");
wrongExpression("2000-02-29 sub 1");
wrongExpression("duration'P7D' add 2000-02-29");
}
@Test
@ -73,6 +120,8 @@ public class ExpressionParserTest {
expression = parseExpression("5 mod 5");
assertEquals("{5 MOD 5}", expression.toString());
wrongExpression("1 mod '2'");
}
@Test
@ -81,9 +130,12 @@ public class ExpressionParserTest {
assertEquals("{MINUS 5}", expression.toString());
assertEquals("{MINUS -1}", parseExpression("--1").toString());
assertEquals("{MINUS duration'PT1M'}", parseExpression("-duration'PT1M'").toString());
expression = parseExpression("not 5");
assertEquals("{NOT 5}", expression.toString());
expression = parseExpression("not false");
assertEquals("{NOT false}", expression.toString());
wrongExpression("-11:12:13");
}
@Test
@ -111,6 +163,8 @@ public class ExpressionParserTest {
expression = parseMethod(TokenKind.MindatetimeMethod);
assertEquals("{mindatetime []}", expression.toString());
wrongExpression("now(1)");
}
@Test
@ -148,16 +202,79 @@ public class ExpressionParserTest {
expression = parseMethod(TokenKind.SecondMethod, dateTimeOffsetValue);
assertEquals("{second [" + dateTimeOffsetValue + "]}", expression.toString());
expression = parseMethod(TokenKind.DateMethod, dateTimeOffsetValue);
assertEquals("{date [" + dateTimeOffsetValue + "]}", expression.toString());
expression = parseMethod(TokenKind.TotalsecondsMethod, "duration'PT1H'");
assertEquals("{totalseconds [duration'PT1H']}", expression.toString());
expression = parseMethod(TokenKind.RoundMethod, "3.141592653589793");
assertEquals("{round [3.141592653589793]}", expression.toString());
assertEquals("{hour [null]}", parseMethod(TokenKind.HourMethod, new String[] { null }).toString());
wrongExpression("trim()");
wrongExpression("trim(1)");
wrongExpression("ceiling('1.2')");
}
@Test
public void twoParameterMethods() throws Exception {
Expression expression = parseMethod(TokenKind.ContainsMethod, "'a'", "'b'");
assertEquals("{contains ['a', 'b']}", expression.toString());
expression = parseMethod(TokenKind.EndswithMethod, "'a'", "'b'");
assertEquals("{endswith ['a', 'b']}", expression.toString());
expression = parseMethod(TokenKind.StartswithMethod, "'a'", "'b'");
assertEquals("{startswith ['a', 'b']}", expression.toString());
expression = parseMethod(TokenKind.IndexofMethod, "'a'", "'b'");
assertEquals("{indexof ['a', 'b']}", expression.toString());
expression = parseMethod(TokenKind.ConcatMethod, "'a'", "'b'");
assertEquals("{concat ['a', 'b']}", expression.toString());
// TODO: Geo methods.
// expression = parseMethod(TokenKind.GeoDistanceMethod,
// "geography'SRID=0;Point(1.2 3.4)'", "geography'SRID=0;Point(5.6 7.8)'");
// assertEquals("{geo.distance [geography'SRID=0;Point(1.2 3.4)', geography'SRID=0;Point(5.6 7.8)']}",
// expression.toString());
//
// expression = parseMethod(TokenKind.GeoIntersectsMethod);
// assertEquals("{geo.intersects []}", expression.toString());
assertEquals("{startswith [null, 'b']}", parseMethod(TokenKind.StartswithMethod, null, "'b'").toString());
assertEquals("{indexof ['a', null]}", parseMethod(TokenKind.IndexofMethod, "'a'", null).toString());
wrongExpression("concat('a')");
wrongExpression("endswith('a',1)");
}
@Test
public void variableParameterNumberMethods() throws Exception {
Expression expression = parseMethod(TokenKind.SubstringMethod, "'abc'", "1", "2");
assertEquals("{substring ['abc', 1, 2]}", expression.toString());
expression = parseMethod(TokenKind.SubstringMethod, "'abc'", "1");
assertEquals("{substring ['abc', 1]}", expression.toString());
wrongExpression("substring('abc')");
wrongExpression("substring('abc',1,2,3)");
wrongExpression("substring(1,2)");
}
private Expression parseMethod(TokenKind kind, String... parameters) throws UriParserException {
String expressionString = kind.name().substring(0, kind.name().indexOf("Method"))
.toLowerCase(Locale.ROOT).replace("geo", "geo.") + '(';
for (int i = 0; i < parameters.length; i++) {
if (i > 0) {
boolean first = true;
for (final String parameter : parameters) {
if (first) {
first = false;
} else {
expressionString += ',';
}
expressionString += parameters[i];
expressionString += parameter;
}
expressionString += ')';
@ -168,9 +285,18 @@ public class ExpressionParserTest {
private Expression parseExpression(final String expressionString) throws UriParserException {
UriTokenizer tokenizer = new UriTokenizer(expressionString);
Expression expression = new ExpressionParser().parse(tokenizer);
Expression expression = new ExpressionParser(null, odata).parse(tokenizer);
assertNotNull(expression);
assertTrue(tokenizer.next(TokenKind.EOF));
return expression;
}
private void wrongExpression(final String expressionString) {
try {
new ExpressionParser(null, odata).parse(new UriTokenizer(expressionString));
fail("Expected exception not thrown.");
} catch (final UriParserException e) {
assertNotNull(e);
}
}
}

View File

@ -412,7 +412,7 @@ public class UriTokenizerTest {
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.EOF));
tokenizer = new UriTokenizer("1 gt 2 or 3 ge 4 or 5 lt 6");
tokenizer = new UriTokenizer("1 gt 2 or 3 ge 4 or 5 lt 6 or 7 has namespace.name'flag1,flag2'");
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.GreaterThanOperator));
assertTrue(tokenizer.next(TokenKind.IntegerValue));
@ -424,6 +424,10 @@ public class UriTokenizerTest {
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.LessThanOperator));
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.OrOperator));
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.HasOperator));
assertTrue(tokenizer.next(TokenKind.EnumValue));
assertTrue(tokenizer.next(TokenKind.EOF));
}

View File

@ -31,14 +31,13 @@ import org.apache.olingo.commons.api.edm.EdmAction;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmEnumType;
import org.apache.olingo.commons.api.edm.EdmFunction;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.edmx.EdmxReference;
import org.apache.olingo.server.api.uri.UriInfoKind;
import org.apache.olingo.server.api.uri.UriInfoResource;
import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind;
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitException;
import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind;
import org.apache.olingo.server.api.uri.queryoption.expression.UnaryOperatorKind;
import org.apache.olingo.server.core.uri.UriInfoImpl;
@ -53,7 +52,8 @@ import org.apache.olingo.server.tecsvc.provider.FunctionProvider;
import org.junit.Test;
public class ExpressionTest {
private static final Edm edm = OData.newInstance().createServiceMetadata(
private static final OData odata = OData.newInstance();
private static final Edm edm = odata.createServiceMetadata(
new EdmTechProvider(), Collections.<EdmxReference> emptyList()).getEdm();
@Test
@ -69,7 +69,7 @@ public class ExpressionTest {
}
@Test
public void aliasExpression() throws ExpressionVisitException, ODataApplicationException {
public void aliasExpression() throws Exception {
AliasImpl expression = new AliasImpl("Test");
assertEquals("Test", expression.getParameterName());
@ -79,47 +79,50 @@ public class ExpressionTest {
}
@Test
public void binaryExpression() throws ExpressionVisitException, ODataApplicationException {
Expression expressionLeft = new LiteralImpl("A", null);
Expression expressionRight = new LiteralImpl("B", null);
public void binaryExpression() throws Exception {
Expression expressionLeft = new LiteralImpl("2", odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Byte));
Expression expressionRight = new LiteralImpl("-1", odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.SByte));
BinaryImpl expression = new BinaryImpl(expressionLeft, BinaryOperatorKind.SUB, expressionRight);
BinaryImpl expression = new BinaryImpl(expressionLeft, BinaryOperatorKind.SUB, expressionRight,
odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Byte));
assertEquals(expressionLeft, expression.getLeftOperand());
assertEquals(expressionRight, expression.getRightOperand());
assertEquals(BinaryOperatorKind.SUB, expression.getOperator());
assertEquals(odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Byte), expression.getType());
String output = expression.accept(new FilterTreeToText());
assertEquals("<<A> sub <B>>", output);
assertEquals("<<2> sub <-1>>", output);
}
@Test
public void enumerationExpression() throws ExpressionVisitException, ODataApplicationException {
public void enumerationExpression() throws Exception {
EdmEnumType type = edm.getEnumType(EnumTypeProvider.nameENString);
assertNotNull(type);
EnumerationImpl expression = new EnumerationImpl(type, Arrays.asList("A", "B"));
EnumerationImpl expression = new EnumerationImpl(type, Arrays.asList("String1", "String2"));
assertEquals(type, expression.getType());
assertEquals("A", expression.getValues().get(0));
assertEquals("B", expression.getValues().get(1));
assertEquals("<olingo.odata.test1.ENString<A,B>>", expression.accept(new FilterTreeToText()));
assertEquals("String1", expression.getValues().get(0));
assertEquals("String2", expression.getValues().get(1));
assertEquals("<olingo.odata.test1.ENString<String1,String2>>", expression.accept(new FilterTreeToText()));
}
@Test
public void lambdaRefExpression() throws ExpressionVisitException, ODataApplicationException {
public void lambdaRefExpression() throws Exception {
LambdaRefImpl expression = new LambdaRefImpl("A");
assertEquals("A", expression.getVariableName());
assertEquals("<A>", expression.accept(new FilterTreeToText()));
}
@Test
public void literalExpression() throws ExpressionVisitException, ODataApplicationException {
LiteralImpl expression = new LiteralImpl("A", null);
assertEquals("A", expression.getText());
assertEquals("<A>", expression.accept(new FilterTreeToText()));
public void literalExpression() throws Exception {
LiteralImpl expression = new LiteralImpl("'A'", odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String));
assertEquals("'A'", expression.getText());
assertEquals("<'A'>", expression.accept(new FilterTreeToText()));
assertEquals(odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String), expression.getType());
}
@Test
public void memberExpression() throws ExpressionVisitException, ODataApplicationException {
public void memberExpression() throws Exception {
EdmEntityType entityType = edm.getEntityType(EntityTypeProvider.nameETKeyNav);
// UriResourceImpl
@ -189,20 +192,21 @@ public class ExpressionTest {
}
@Test
public void methodCallExpression() throws ExpressionVisitException, ODataApplicationException {
Expression p0 = new LiteralImpl("A", null);
Expression p1 = new LiteralImpl("B", null);
public void methodCallExpression() throws Exception {
Expression p0 = new LiteralImpl("'A'", odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String));
Expression p1 = new LiteralImpl("'B'", odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String));
MethodImpl expression = new MethodImpl(MethodKind.CONCAT, Arrays.asList(p0, p1));
assertEquals(MethodKind.CONCAT, expression.getMethod());
assertEquals("<concat(<A>,<B>)>", expression.accept(new FilterTreeToText()));
assertEquals("<concat(<'A'>,<'B'>)>", expression.accept(new FilterTreeToText()));
assertEquals(odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String), expression.getType());
assertEquals(p0, expression.getParameters().get(0));
assertEquals(p1, expression.getParameters().get(1));
}
@Test
public void typeLiteralExpression() throws ExpressionVisitException, ODataApplicationException {
public void typeLiteralExpression() throws Exception {
EdmEntityType entityBaseType = edm.getEntityType(EntityTypeProvider.nameETBaseTwoKeyNav);
TypeLiteralImpl expression = new TypeLiteralImpl(entityBaseType);
@ -211,13 +215,15 @@ public class ExpressionTest {
}
@Test
public void unaryExpression() throws ExpressionVisitException, ODataApplicationException {
Expression operand = new LiteralImpl("A", null);
UnaryImpl expression = new UnaryImpl(UnaryOperatorKind.MINUS, operand);
public void unaryExpression() throws Exception {
Expression operand = new LiteralImpl("1.2", odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Decimal));
UnaryImpl expression = new UnaryImpl(UnaryOperatorKind.MINUS, operand,
odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Decimal));
assertEquals(UnaryOperatorKind.MINUS, expression.getOperator());
assertEquals(operand, expression.getOperand());
assertEquals(odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Decimal), expression.getType());
assertEquals("<- <A>>", expression.accept(new FilterTreeToText()));
assertEquals("<- <1.2>>", expression.accept(new FilterTreeToText()));
}
}