[OLINGO-834] ExpressionParser uses UriTokenizer

Signed-off-by: Christian Amend <christian.amend@sap.com>
This commit is contained in:
Klaus Straubinger 2015-12-11 16:48:38 +01:00 committed by Christian Amend
parent 208f26c746
commit 2f3bc2866b
9 changed files with 838 additions and 653 deletions

View File

@ -32,6 +32,7 @@ import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
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.UnaryOperatorKind;
import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
import org.apache.olingo.server.core.uri.queryoption.expression.AliasImpl;
import org.apache.olingo.server.core.uri.queryoption.expression.BinaryImpl;
import org.apache.olingo.server.core.uri.queryoption.expression.LiteralImpl;
@ -39,75 +40,73 @@ import org.apache.olingo.server.core.uri.queryoption.expression.MethodImpl;
import org.apache.olingo.server.core.uri.queryoption.expression.UnaryImpl;
public class ExpressionParser {
private Tokenizer tokenizer;
private static final Map<TokenKind, BinaryOperatorKind> tokenToBinaryOperator;
static {
Map<TokenKind, BinaryOperatorKind> temp = new HashMap<ExpressionParser.TokenKind, BinaryOperatorKind>();
temp.put(TokenKind.OR_OP, BinaryOperatorKind.OR);
temp.put(TokenKind.AND_OP, BinaryOperatorKind.AND);
Map<TokenKind, BinaryOperatorKind> temp = new HashMap<TokenKind, BinaryOperatorKind>();
temp.put(TokenKind.OrOperator, BinaryOperatorKind.OR);
temp.put(TokenKind.AndOperator, BinaryOperatorKind.AND);
temp.put(TokenKind.EQ_OP, BinaryOperatorKind.EQ);
temp.put(TokenKind.NE_OP, BinaryOperatorKind.NE);
temp.put(TokenKind.EqualsOperator, BinaryOperatorKind.EQ);
temp.put(TokenKind.NotEqualsOperator, BinaryOperatorKind.NE);
temp.put(TokenKind.GT_OP, BinaryOperatorKind.GT);
temp.put(TokenKind.GE_OP, BinaryOperatorKind.GE);
temp.put(TokenKind.LT_OP, BinaryOperatorKind.LT);
temp.put(TokenKind.LE_OP, BinaryOperatorKind.LE);
temp.put(TokenKind.GreaterThanOperator, BinaryOperatorKind.GT);
temp.put(TokenKind.GreaterThanOrEqualsOperator, BinaryOperatorKind.GE);
temp.put(TokenKind.LessThanOperator, BinaryOperatorKind.LT);
temp.put(TokenKind.LessThanOrEqualsOperator, BinaryOperatorKind.LE);
temp.put(TokenKind.ADD_OP, BinaryOperatorKind.ADD);
temp.put(TokenKind.SUB_OP, BinaryOperatorKind.SUB);
temp.put(TokenKind.AddOperator, BinaryOperatorKind.ADD);
temp.put(TokenKind.SubOperator, BinaryOperatorKind.SUB);
temp.put(TokenKind.MUL_OP, BinaryOperatorKind.MUL);
temp.put(TokenKind.DIV_OP, BinaryOperatorKind.DIV);
temp.put(TokenKind.MOD_OP, BinaryOperatorKind.MOD);
temp.put(TokenKind.MulOperator, BinaryOperatorKind.MUL);
temp.put(TokenKind.DivOperator, BinaryOperatorKind.DIV);
temp.put(TokenKind.ModOperator, BinaryOperatorKind.MOD);
tokenToBinaryOperator = Collections.unmodifiableMap(temp);
}
private static final Map<TokenKind, UnaryOperatorKind> tokenToUnaryOperator;
static {
Map<TokenKind, UnaryOperatorKind> temp = new HashMap<ExpressionParser.TokenKind, UnaryOperatorKind>();
Map<TokenKind, UnaryOperatorKind> temp = new HashMap<TokenKind, UnaryOperatorKind>();
temp.put(TokenKind.MINUS, UnaryOperatorKind.MINUS);
temp.put(TokenKind.NOT, UnaryOperatorKind.NOT);
temp.put(TokenKind.NotOperator, UnaryOperatorKind.NOT);
tokenToUnaryOperator = Collections.unmodifiableMap(temp);
}
private static final Map<TokenKind, MethodKind> tokenToMethod;
static {
Map<TokenKind, MethodKind> temp = new HashMap<ExpressionParser.TokenKind, MethodKind>();
temp.put(TokenKind.Cast, MethodKind.CAST);
temp.put(TokenKind.Ceiling, MethodKind.CEILING);
temp.put(TokenKind.Concat, MethodKind.CONCAT);
temp.put(TokenKind.Contains, MethodKind.CONTAINS);
temp.put(TokenKind.Date, MethodKind.DATE);
temp.put(TokenKind.Day, MethodKind.DAY);
temp.put(TokenKind.Endswith, MethodKind.ENDSWITH);
temp.put(TokenKind.Floor, MethodKind.FLOOR);
temp.put(TokenKind.Fractionalseconds, MethodKind.FRACTIONALSECONDS);
temp.put(TokenKind.GeoDistance, MethodKind.GEODISTANCE);
temp.put(TokenKind.GeoIntersects, MethodKind.GEOINTERSECTS);
temp.put(TokenKind.GeoLength, MethodKind.GEOLENGTH);
temp.put(TokenKind.Hour, MethodKind.HOUR);
temp.put(TokenKind.Indexof, MethodKind.INDEXOF);
temp.put(TokenKind.Isof, MethodKind.ISOF);
temp.put(TokenKind.Length, MethodKind.LENGTH);
temp.put(TokenKind.Maxdatetime, MethodKind.MAXDATETIME);
temp.put(TokenKind.Mindatetime, MethodKind.MINDATETIME);
temp.put(TokenKind.Minute, MethodKind.MINUTE);
temp.put(TokenKind.Month, MethodKind.MONTH);
temp.put(TokenKind.Now, MethodKind.NOW);
temp.put(TokenKind.Round, MethodKind.ROUND);
temp.put(TokenKind.Second, MethodKind.SECOND);
temp.put(TokenKind.Startswith, MethodKind.STARTSWITH);
temp.put(TokenKind.Substring, MethodKind.SUBSTRING);
temp.put(TokenKind.Time, MethodKind.TIME);
temp.put(TokenKind.Tolower, MethodKind.TOLOWER);
temp.put(TokenKind.Totaloffsetminutes, MethodKind.TOTALOFFSETMINUTES);
temp.put(TokenKind.Totalseconds, MethodKind.TOTALSECONDS);
temp.put(TokenKind.Toupper, MethodKind.TOUPPER);
temp.put(TokenKind.Trim, MethodKind.TRIM);
temp.put(TokenKind.Year, MethodKind.YEAR);
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);
temp.put(TokenKind.DateMethod, MethodKind.DATE);
temp.put(TokenKind.DayMethod, MethodKind.DAY);
temp.put(TokenKind.EndswithMethod, MethodKind.ENDSWITH);
temp.put(TokenKind.FloorMethod, MethodKind.FLOOR);
temp.put(TokenKind.FractionalsecondsMethod, MethodKind.FRACTIONALSECONDS);
temp.put(TokenKind.GeoDistanceMethod, MethodKind.GEODISTANCE);
temp.put(TokenKind.GeoIntersectsMethod, MethodKind.GEOINTERSECTS);
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);
temp.put(TokenKind.MinuteMethod, MethodKind.MINUTE);
temp.put(TokenKind.MonthMethod, MethodKind.MONTH);
temp.put(TokenKind.NowMethod, MethodKind.NOW);
temp.put(TokenKind.RoundMethod, MethodKind.ROUND);
temp.put(TokenKind.SecondMethod, MethodKind.SECOND);
temp.put(TokenKind.StartswithMethod, MethodKind.STARTSWITH);
temp.put(TokenKind.SubstringMethod, MethodKind.SUBSTRING);
temp.put(TokenKind.TimeMethod, MethodKind.TIME);
temp.put(TokenKind.TolowerMethod, MethodKind.TOLOWER);
temp.put(TokenKind.TotaloffsetminutesMethod, MethodKind.TOTALOFFSETMINUTES);
temp.put(TokenKind.TotalsecondsMethod, MethodKind.TOTALSECONDS);
temp.put(TokenKind.ToupperMethod, MethodKind.TOUPPER);
temp.put(TokenKind.TrimMethod, MethodKind.TRIM);
temp.put(TokenKind.YearMethod, MethodKind.YEAR);
tokenToMethod = Collections.unmodifiableMap(temp);
}
@ -115,24 +114,26 @@ public class ExpressionParser {
private static final Map<TokenKind, EdmPrimitiveTypeKind> tokenToPrimitiveType;
static {
/* Enum and null are not present in the map. These have to be handled differently */
Map<TokenKind, EdmPrimitiveTypeKind> temp = new HashMap<ExpressionParser.TokenKind, EdmPrimitiveTypeKind>();
temp.put(TokenKind.PrimitiveBooleanValue, EdmPrimitiveTypeKind.Boolean);
temp.put(TokenKind.PrimitiveStringValue, EdmPrimitiveTypeKind.String);
Map<TokenKind, EdmPrimitiveTypeKind> temp = new HashMap<TokenKind, EdmPrimitiveTypeKind>();
temp.put(TokenKind.BooleanValue, EdmPrimitiveTypeKind.Boolean);
temp.put(TokenKind.StringValue, EdmPrimitiveTypeKind.String);
// TODO:Check if int64 is correct here or if it has to be single instead
temp.put(TokenKind.PrimitiveIntegerValue, EdmPrimitiveTypeKind.Int64);
temp.put(TokenKind.PrimitiveGuidValue, EdmPrimitiveTypeKind.Guid);
temp.put(TokenKind.PrimitiveDateValue, EdmPrimitiveTypeKind.Date);
temp.put(TokenKind.PrimitiveDateTimeOffsetValue, EdmPrimitiveTypeKind.DateTimeOffset);
temp.put(TokenKind.PrimitiveTimeOfDayValue, EdmPrimitiveTypeKind.TimeOfDay);
temp.put(TokenKind.PrimitiveDecimalValue, EdmPrimitiveTypeKind.Decimal);
temp.put(TokenKind.PrimitiveDoubleValue, EdmPrimitiveTypeKind.Double);
temp.put(TokenKind.PrimitiveDurationValue, EdmPrimitiveTypeKind.Duration);
temp.put(TokenKind.PrimitiveBinaryValue, EdmPrimitiveTypeKind.Binary);
temp.put(TokenKind.IntegerValue, EdmPrimitiveTypeKind.Int64);
temp.put(TokenKind.GuidValue, EdmPrimitiveTypeKind.Guid);
temp.put(TokenKind.DateValue, EdmPrimitiveTypeKind.Date);
temp.put(TokenKind.DateTimeOffsetValue, EdmPrimitiveTypeKind.DateTimeOffset);
temp.put(TokenKind.TimeOfDayValue, EdmPrimitiveTypeKind.TimeOfDay);
temp.put(TokenKind.DecimalValue, EdmPrimitiveTypeKind.Decimal);
temp.put(TokenKind.DoubleValue, EdmPrimitiveTypeKind.Double);
temp.put(TokenKind.DurationValue, EdmPrimitiveTypeKind.Duration);
temp.put(TokenKind.BinaryValue, EdmPrimitiveTypeKind.Binary);
tokenToPrimitiveType = Collections.unmodifiableMap(temp);
}
public Expression parse(Tokenizer tokenizer) throws UriParserException {
private UriTokenizer tokenizer;
public Expression parse(UriTokenizer tokenizer) throws UriParserException {
// Initialize tokenizer.
this.tokenizer = tokenizer;
@ -141,23 +142,17 @@ public class ExpressionParser {
private Expression parseExpression() throws UriParserException {
Expression left = parseAnd();
while (is(TokenKind.OR_OP) != null) {
tokenizer.getText();
Expression right = parseAnd();
while (tokenizer.next(TokenKind.OrOperator)) {
final Expression right = parseAnd();
left = new BinaryImpl(left, BinaryOperatorKind.OR, right);
}
return left;
}
private Expression parseAnd() throws UriParserException {
Expression left = parseExprEquality();
while (is(TokenKind.AND_OP) != null) {
tokenizer.getText();
Expression right = parseExprEquality();
while (tokenizer.next(TokenKind.AndOperator)) {
final Expression right = parseExprEquality();
left = new BinaryImpl(left, BinaryOperatorKind.AND, right);
}
return left;
@ -165,110 +160,107 @@ public class ExpressionParser {
private Expression parseExprEquality() throws UriParserException {
Expression left = parseExprRel();
TokenKind nextTokenKind = is(TokenKind.EQ_OP, TokenKind.NE_OP);
TokenKind operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.EqualsOperator, TokenKind.NotEqualsOperator);
// Null for everything other than EQ or NE
while (nextTokenKind != null) {
tokenizer.getText();
Expression right = parseExprEquality();
left = new BinaryImpl(left, tokenToBinaryOperator.get(nextTokenKind), right);
nextTokenKind = is(TokenKind.EQ_OP, TokenKind.NE_OP);
while (operatorTokenKind != null) {
final Expression right = parseExprEquality();
left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right);
operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.EqualsOperator, TokenKind.NotEqualsOperator);
}
return left;
}
private Expression parseExprRel() throws UriParserException {
Expression left = parseExprAdd();
TokenKind nextTokenKind = is(TokenKind.GT_OP, TokenKind.GE_OP, TokenKind.LT_OP, TokenKind.LE_OP);
TokenKind operatorTokenKind = ParserHelper.next(tokenizer,
TokenKind.GreaterThanOperator, TokenKind.GreaterThanOrEqualsOperator,
TokenKind.LessThanOperator, TokenKind.LessThanOrEqualsOperator);
// Null for everything other than GT or GE or LT or LE
while (nextTokenKind != null) {
tokenizer.getText();
Expression right = parseExprAdd();
left = new BinaryImpl(left, tokenToBinaryOperator.get(nextTokenKind), right);
nextTokenKind = is(TokenKind.GT_OP, TokenKind.GE_OP, TokenKind.LT_OP, TokenKind.LE_OP);
while (operatorTokenKind != null) {
final Expression right = parseExprAdd();
left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right);
operatorTokenKind = ParserHelper.next(tokenizer,
TokenKind.GreaterThanOperator, TokenKind.GreaterThanOrEqualsOperator,
TokenKind.LessThanOperator, TokenKind.LessThanOrEqualsOperator);
}
return left;
}
private Expression parseExprAdd() throws UriParserException {
Expression left = parseExprMul();
TokenKind nextTokenKind = is(TokenKind.ADD_OP, TokenKind.SUB_OP);
TokenKind operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.AddOperator, TokenKind.SubOperator);
// Null for everything other than ADD or SUB
while (nextTokenKind != null) {
tokenizer.getText();
Expression right = parseExprMul();
left = new BinaryImpl(left, tokenToBinaryOperator.get(nextTokenKind), right);
nextTokenKind = is(TokenKind.ADD_OP, TokenKind.SUB_OP);
while (operatorTokenKind != null) {
final Expression right = parseExprMul();
left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right);
operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.AddOperator, TokenKind.SubOperator);
}
return left;
}
private Expression parseExprMul() throws UriParserException {
Expression left = parseExprUnary();
TokenKind nextTokenKind = is(TokenKind.MUL_OP, TokenKind.DIV_OP, TokenKind.MOD_OP);
TokenKind operatorTokenKind = ParserHelper.next(tokenizer,
TokenKind.MulOperator, TokenKind.DivOperator, TokenKind.ModOperator);
// Null for everything other than MUL or DIV or MOD
while (nextTokenKind != null) {
tokenizer.getText();
Expression right = parseExprUnary();
left = new BinaryImpl(left, tokenToBinaryOperator.get(nextTokenKind), right);
nextTokenKind = is(TokenKind.MUL_OP, TokenKind.DIV_OP, TokenKind.MOD_OP);
while (operatorTokenKind != null) {
final Expression right = parseExprUnary();
left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right);
operatorTokenKind = ParserHelper.next(tokenizer,
TokenKind.MulOperator, TokenKind.DivOperator, TokenKind.ModOperator);
}
return left;
}
private Expression parseExprUnary() throws UriParserException {
Expression left = null;
TokenKind nextTokenKind = is(TokenKind.MINUS, TokenKind.NOT);
TokenKind operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.MINUS, TokenKind.NotOperator);
// Null for everything other than - or NOT
while (nextTokenKind != null) {
tokenizer.getText();
Expression exp = parseExprValue();
left = new UnaryImpl(tokenToUnaryOperator.get(nextTokenKind), exp);
nextTokenKind = is(TokenKind.MINUS, TokenKind.NOT);
while (operatorTokenKind != null) {
final Expression expression = parseExprValue();
left = new UnaryImpl(tokenToUnaryOperator.get(operatorTokenKind), expression);
operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.MINUS, TokenKind.NotOperator);
}
if (left == null) {
left = parseExprValue();
}
return left;
}
private Expression parseExprValue() throws UriParserException {
if (is(TokenKind.OPEN) != null) {
tokenizer.getText();
Expression exp = parseExpression();
require(TokenKind.CLOSE);
return exp;
if (tokenizer.next(TokenKind.OPEN)) {
final Expression expression = parseExpression();
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
return expression;
}
if (is(TokenKind.ParameterAlias) != null) {
if (tokenizer.next(TokenKind.ParameterAliasName)) {
return new AliasImpl(tokenizer.getText());
}
if (is(TokenKind.RootExpr) != null) {
tokenizer.getText();
// TODO: Consume $root Expression.
if (tokenizer.next(TokenKind.jsonArrayOrObject)) {
// TODO: Can the type be determined?
return new LiteralImpl(tokenizer.getText(), null);
}
TokenKind nextPrimitive = isPrimitive();
if (tokenizer.next(TokenKind.ROOT)) {
// TODO: Consume $root expression.
}
if (tokenizer.next(TokenKind.IT)) {
// TODO: Consume $it expression.
}
if (tokenizer.next(TokenKind.QualifiedName)) {
// TODO: Consume typecast or bound-function expression.
}
TokenKind nextPrimitive = ParserHelper.nextPrimitive(tokenizer);
if (nextPrimitive != null) {
EdmPrimitiveTypeKind primitiveTypeKind = tokenToPrimitiveType.get(nextPrimitive);
final EdmPrimitiveTypeKind primitiveTypeKind = tokenToPrimitiveType.get(nextPrimitive);
EdmPrimitiveType type;
if (primitiveTypeKind == null) {
if (nextPrimitive == TokenKind.PrimitiveEnumValue) {
if (nextPrimitive == TokenKind.EnumValue) {
// TODO: Get enum type.
type = null;
} else {
@ -281,22 +273,16 @@ public class ExpressionParser {
return new LiteralImpl(tokenizer.getText(), type);
}
TokenKind nextMethod = isMethod();
TokenKind nextMethod = nextMethod();
if (nextMethod != null) {
MethodKind methodKind = tokenToMethod.get(nextMethod);
List<Expression> parameters = new ArrayList<Expression>();
// Consume Method name.
tokenizer.getText();
if (is(TokenKind.CLOSE) != null) {
// Consume closing parenthesis.
tokenizer.getText();
} else {
parameters.add(parseExpression());
while (is(TokenKind.COMMA) != null) {
tokenizer.getText();
// The method token text includes the opening parenthesis!
if (!tokenizer.next(TokenKind.CLOSE)) {
do {
parameters.add(parseExpression());
}
require(TokenKind.CLOSE);
} while (tokenizer.next(TokenKind.COMMA));
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
}
MethodImpl methodImpl = new MethodImpl(methodKind, parameters);
@ -305,6 +291,10 @@ public class ExpressionParser {
return methodImpl;
}
if (tokenizer.next(TokenKind.ODataIdentifier)) {
// TODO: Consume property-path or lambda-variable expression.
}
throw new UriParserSyntaxException("Unexpected token", UriParserSyntaxException.MessageKeys.SYNTAX);
}
@ -356,7 +346,7 @@ public class ExpressionParser {
case NOW:
case MAXDATETIME:
case MINDATETIME:
if (size != 0) {
if (size > 0) {
throw new UriParserSemanticException("The method '" + method.getMethod() + "' must have no parameters.",
null); // TODO: message key
}
@ -384,192 +374,39 @@ public class ExpressionParser {
}
}
private String require(TokenKind required) throws UriParserException {
if (is(required) == null) {
throw new UriParserSyntaxException("Required token: " + required,
UriParserSyntaxException.MessageKeys.SYNTAX);
}
return tokenizer.getText();
private TokenKind nextMethod() {
return ParserHelper.next(tokenizer,
TokenKind.CastMethod,
TokenKind.CeilingMethod,
TokenKind.ConcatMethod,
TokenKind.ContainsMethod,
TokenKind.DateMethod,
TokenKind.DayMethod,
TokenKind.EndswithMethod,
TokenKind.FloorMethod,
TokenKind.FractionalsecondsMethod,
TokenKind.GeoDistanceMethod,
TokenKind.GeoIntersectsMethod,
TokenKind.GeoLengthMethod,
TokenKind.HourMethod,
TokenKind.IndexofMethod,
TokenKind.IsofMethod,
TokenKind.LengthMethod,
TokenKind.MaxdatetimeMethod,
TokenKind.MindatetimeMethod,
TokenKind.MinuteMethod,
TokenKind.MonthMethod,
TokenKind.NowMethod,
TokenKind.RoundMethod,
TokenKind.SecondMethod,
TokenKind.StartswithMethod,
TokenKind.SubstringMethod,
TokenKind.TimeMethod,
TokenKind.TolowerMethod,
TokenKind.TotaloffsetminutesMethod,
TokenKind.TotalsecondsMethod,
TokenKind.ToupperMethod,
TokenKind.TrimMethod,
TokenKind.YearMethod);
}
private TokenKind is(TokenKind... kind) {
for (int i = 0; i < kind.length; i++) {
if (tokenizer.next(kind[i])) {
return kind[i];
}
}
return null;
}
private TokenKind isMethod() {
return is(TokenKind.Cast,
TokenKind.Ceiling,
TokenKind.Concat,
TokenKind.Contains,
TokenKind.Date,
TokenKind.Day,
TokenKind.Endswith,
TokenKind.Floor,
TokenKind.Fractionalseconds,
TokenKind.GeoDistance,
TokenKind.GeoIntersects,
TokenKind.GeoLength,
TokenKind.Hour,
TokenKind.Indexof,
TokenKind.Isof,
TokenKind.Length,
TokenKind.Maxdatetime,
TokenKind.Mindatetime,
TokenKind.Minute,
TokenKind.Month,
TokenKind.Now,
TokenKind.Round,
TokenKind.Second,
TokenKind.Startswith,
TokenKind.Substring,
TokenKind.Time,
TokenKind.Tolower,
TokenKind.Totaloffsetminutes,
TokenKind.Totalseconds,
TokenKind.Toupper,
TokenKind.Trim,
TokenKind.Year);
}
private TokenKind isPrimitive() {
return is(TokenKind.PrimitiveNullValue,
TokenKind.PrimitiveBooleanValue,
TokenKind.PrimitiveStringValue,
// The order of the next seven expressions is important in order to avoid
// finding partly parsed tokens (counter-intuitive as it may be, even a GUID may start with digits ...).
TokenKind.PrimitiveDoubleValue,
TokenKind.PrimitiveDecimalValue,
TokenKind.PrimitiveGuidValue,
TokenKind.PrimitiveDateTimeOffsetValue,
TokenKind.PrimitiveDateValue,
TokenKind.PrimitiveTimeOfDayValue,
TokenKind.PrimitiveIntegerValue,
TokenKind.PrimitiveDurationValue,
TokenKind.PrimitiveBinaryValue,
TokenKind.PrimitiveEnumValue);
}
public enum TokenKind {
// BINARY
OR_OP,
AND_OP,
EQ_OP,
NE_OP,
GT_OP,
GE_OP,
LT_OP,
LE_OP,
ADD_OP,
SUB_OP,
MUL_OP,
DIV_OP,
MOD_OP,
MINUS,
NOT,
// Grouping
OPEN,
CLOSE,
// PrimitiveValues
PrimitiveNullValue,
PrimitiveBooleanValue,
PrimitiveStringValue,
PrimitiveIntegerValue,
PrimitiveGuidValue,
PrimitiveDateValue,
PrimitiveDateTimeOffsetValue,
PrimitiveTimeOfDayValue,
PrimitiveDecimalValue,
PrimitiveDoubleValue,
PrimitiveDurationValue,
PrimitiveBinaryValue,
PrimitiveEnumValue,
// ExpressionValues
ParameterAlias,
ArrayOrObject,
RootExpr,
IT,
// BuiltInMethods
Cast,
Ceiling,
Concat,
Contains,
Date,
Day,
Endswith,
Floor,
Fractionalseconds,
GeoDistance,
GeoIntersects,
GeoLength,
Hour,
Indexof,
Isof,
Length,
Maxdatetime,
Mindatetime,
Minute,
Month,
Now,
Round,
Second,
Startswith,
Substring,
Time,
Tolower,
Totaloffsetminutes,
Totalseconds,
Toupper,
Trim,
Year,
COMMA
}
public static class Token {
TokenKind kind;
String text;
public Token(TokenKind kind, String text) {
this.kind = kind;
this.text = text;
}
}
public static class Tokenizer {
private List<Token> tokens;
int counter = 0;
public Tokenizer(List<Token> tokens) {
this.tokens = tokens;
}
public boolean next(TokenKind expectedKind) {
if (counter < tokens.size() && expectedKind == tokens.get(counter).kind) {
return true;
}
return false;
}
public String getText() {
String text = tokens.get(counter).text;
counter++;
return text;
}
}
}

View File

@ -208,6 +208,14 @@ public class Parser {
(UriParserException) e.getCause() :
new UriParserSyntaxException("Syntax error", e, UriParserSyntaxException.MessageKeys.SYNTAX);
}
// UriTokenizer filterTokenizer = new UriTokenizer(optionValue);
// systemOption = new FilterOptionImpl().setExpression(
// new ExpressionParser().parse(filterTokenizer));
// if (!filterTokenizer.next(TokenKind.EOF)) {
// throw new UriParserSyntaxException("Illegal value of $filter option!",
// UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
// optionName, optionValue);
// }
} else if (optionName.equals(SystemQueryOptionKind.FORMAT.toString())) {
FormatOptionImpl formatOption = new FormatOptionImpl();

View File

@ -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.server.core.uri.parser;
import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
public class ParserHelper {
public static void requireNext(UriTokenizer tokenizer, final TokenKind required) throws UriParserException {
if (!tokenizer.next(required)) {
throw new UriParserSyntaxException("Expected token '" + required.toString() + "' not found.",
UriParserSyntaxException.MessageKeys.SYNTAX);
}
}
public static void requireTokenEnd(UriTokenizer tokenizer) throws UriParserException {
requireNext(tokenizer, TokenKind.EOF);
}
public static TokenKind next(UriTokenizer tokenizer, final TokenKind... kind) {
for (int i = 0; i < kind.length; i++) {
if (tokenizer.next(kind[i])) {
return kind[i];
}
}
return null;
}
public static TokenKind nextPrimitive(UriTokenizer tokenizer) {
return next(tokenizer,
TokenKind.NULL,
TokenKind.BooleanValue,
TokenKind.StringValue,
// The order of the next seven expressions is important in order to avoid
// finding partly parsed tokens (counter-intuitive as it may be, even a GUID may start with digits ...).
TokenKind.DoubleValue,
TokenKind.DecimalValue,
TokenKind.GuidValue,
TokenKind.DateTimeOffsetValue,
TokenKind.DateValue,
TokenKind.TimeOfDayValue,
TokenKind.IntegerValue,
TokenKind.DurationValue,
TokenKind.BinaryValue,
TokenKind.EnumValue);
}
}

View File

@ -115,9 +115,9 @@ public class ResourcePathParser {
public UriInfoImpl parseDollarEntityTypeCast(final String pathSegment) throws UriParserException {
UriInfoImpl uriInfo = new UriInfoImpl().setKind(UriInfoKind.entityId);
tokenizer = new UriTokenizer(pathSegment);
requireNext(TokenKind.QualifiedName);
ParserHelper.requireNext(tokenizer, TokenKind.QualifiedName);
final String name = tokenizer.getText();
requireTokenEnd();
ParserHelper.requireTokenEnd(tokenizer);
final EdmEntityType type = edm.getEntityType(new FullQualifiedName(name));
if (type == null) {
throw new UriParserSemanticException("Type '" + name + "' not found.",
@ -131,11 +131,11 @@ public class ResourcePathParser {
public UriInfoImpl parseCrossjoinSegment(final String pathSegment) throws UriParserException {
UriInfoImpl uriInfo = new UriInfoImpl().setKind(UriInfoKind.crossjoin);
tokenizer = new UriTokenizer(pathSegment);
requireNext(TokenKind.CROSSJOIN);
requireNext(TokenKind.OPEN);
ParserHelper.requireNext(tokenizer, TokenKind.CROSSJOIN);
ParserHelper.requireNext(tokenizer, TokenKind.OPEN);
// At least one entity-set name is mandatory. Try to fetch all.
do {
requireNext(TokenKind.ODataIdentifier);
ParserHelper.requireNext(tokenizer, TokenKind.ODataIdentifier);
final String name = tokenizer.getText();
final EdmEntitySet edmEntitySet = edmEntityContainer.getEntitySet(name);
if (edmEntitySet == null) {
@ -145,13 +145,13 @@ public class ResourcePathParser {
uriInfo.addEntitySetName(name);
}
} while (tokenizer.next(TokenKind.COMMA));
requireNext(TokenKind.CLOSE);
requireTokenEnd();
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
ParserHelper.requireTokenEnd(tokenizer);
return uriInfo;
}
private UriResource ref(final UriResource previous) throws UriParserException {
requireTokenEnd();
ParserHelper.requireTokenEnd(tokenizer);
requireTyped(previous, "$ref");
if (((UriResourcePartTyped) previous).getType() instanceof EdmEntityType) {
return new UriResourceRefImpl();
@ -162,7 +162,7 @@ public class ResourcePathParser {
}
private UriResource value(final UriResource previous) throws UriParserException {
requireTokenEnd();
ParserHelper.requireTokenEnd(tokenizer);
requireTyped(previous, "$value");
if (!((UriResourcePartTyped) previous).isCollection()) {
return new UriResourceValueImpl();
@ -173,7 +173,7 @@ public class ResourcePathParser {
}
private UriResource count(final UriResource previous) throws UriParserException {
requireTokenEnd();
ParserHelper.requireTokenEnd(tokenizer);
requireTyped(previous, "$count");
if (((UriResourcePartTyped) previous).isCollection()) {
return new UriResourceCountImpl();
@ -195,19 +195,19 @@ public class ResourcePathParser {
entitySetResource.setKeyPredicates(keyPredicates);
}
requireTokenEnd();
ParserHelper.requireTokenEnd(tokenizer);
return entitySetResource;
}
final EdmSingleton edmSingleton = edmEntityContainer.getSingleton(oDataIdentifier);
if (edmSingleton != null) {
requireTokenEnd();
ParserHelper.requireTokenEnd(tokenizer);
return new UriResourceSingletonImpl().setSingleton(edmSingleton);
}
final EdmActionImport edmActionImport = edmEntityContainer.getActionImport(oDataIdentifier);
if (edmActionImport != null) {
requireTokenEnd();
ParserHelper.requireTokenEnd(tokenizer);
return new UriResourceActionImpl().setActionImport(edmActionImport);
}
@ -271,7 +271,7 @@ public class ResourcePathParser {
UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED);
}
}
requireTokenEnd();
ParserHelper.requireTokenEnd(tokenizer);
return new UriResourceNavigationPropertyImpl()
.setNavigationProperty(navigationProperty)
.setKeyPredicates(keyPredicate);
@ -288,7 +288,7 @@ public class ResourcePathParser {
previousType.getFullQualifiedName(),
previousTyped.isCollection());
if (boundAction != null) {
requireTokenEnd();
ParserHelper.requireTokenEnd(tokenizer);
return new UriResourceActionImpl().setAction(boundAction);
}
EdmStructuredType type = edm.getEntityType(name);
@ -386,7 +386,7 @@ public class ResourcePathParser {
edmProperty == null ? null : (EdmPrimitiveType) edmProperty.getType(),
edmProperty == null ? false : edmProperty.isNullable())) {
final String literalValue = tokenizer.getText();
requireNext(TokenKind.CLOSE);
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
return createUriParameter(edmProperty, edmKeyPropertyRef.getName(), literalValue);
} else {
throw new UriParserSemanticException("The key value is not valid.",
@ -424,10 +424,10 @@ public class ResourcePathParser {
parameterNames.add(keyPredicateName);
hasComma = tokenizer.next(TokenKind.COMMA);
if (hasComma) {
requireNext(TokenKind.ODataIdentifier);
ParserHelper.requireNext(tokenizer, TokenKind.ODataIdentifier);
}
} while (hasComma);
requireNext(TokenKind.CLOSE);
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
return parameters;
}
@ -440,7 +440,7 @@ public class ResourcePathParser {
throw new UriValidationException(keyPredicateName + " is not a valid key property name.",
UriValidationException.MessageKeys.INVALID_KEY_PROPERTY, keyPredicateName);
}
requireNext(TokenKind.EQ);
ParserHelper.requireNext(tokenizer, TokenKind.EQ);
if (tokenizer.next(TokenKind.COMMA) || tokenizer.next(TokenKind.CLOSE) || tokenizer.next(TokenKind.EOF)) {
throw new UriParserSyntaxException("Key value expected.", UriParserSyntaxException.MessageKeys.SYNTAX);
}
@ -512,7 +512,7 @@ public class ResourcePathParser {
}
((UriResourceTypedImpl) previousTyped).setTypeFilter(type);
}
requireTokenEnd();
ParserHelper.requireTokenEnd(tokenizer);
return null;
} else {
throw new UriParserSemanticException(
@ -572,53 +572,42 @@ public class ResourcePathParser {
UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED);
}
}
requireTokenEnd();
ParserHelper.requireTokenEnd(tokenizer);
return resource;
}
private List<UriParameter> functionParameters() throws UriParserException {
List<UriParameter> parameters = new ArrayList<UriParameter>();
requireNext(TokenKind.OPEN);
ParserHelper.requireNext(tokenizer, TokenKind.OPEN);
if (tokenizer.next(TokenKind.CLOSE)) {
return parameters;
}
do {
requireNext(TokenKind.ODataIdentifier);
ParserHelper.requireNext(tokenizer, TokenKind.ODataIdentifier);
final String name = tokenizer.getText();
if (parameters.contains(name)) {
throw new UriParserSemanticException("Duplicated function parameter " + name,
UriParserSemanticException.MessageKeys.INVALID_KEY_VALUE, name);
}
requireNext(TokenKind.EQ);
ParserHelper.requireNext(tokenizer, TokenKind.EQ);
if (tokenizer.next(TokenKind.COMMA) || tokenizer.next(TokenKind.CLOSE) || tokenizer.next(TokenKind.EOF)) {
throw new UriParserSyntaxException("Parameter value expected.", UriParserSyntaxException.MessageKeys.SYNTAX);
}
if (nextPrimitiveValue()) {
if (tokenizer.next(TokenKind.ParameterAliasName)) {
parameters.add(new UriParameterImpl().setName(name).setAlias(tokenizer.getText()));
} else if (nextPrimitiveValue()) {
final String literalValue = tokenizer.getText();
UriParameterImpl parameter = new UriParameterImpl().setName(name);
parameters.add(literalValue.startsWith("@") ?
parameter.setAlias(literalValue) :
parameter.setText("null".equals(literalValue) ? null : literalValue));
parameters.add(new UriParameterImpl().setName(name)
.setText("null".equals(literalValue) ? null : literalValue));
} else {
throw new UriParserSemanticException("Wrong parameter value.",
UriParserSemanticException.MessageKeys.INVALID_KEY_VALUE, "");
}
} while (tokenizer.next(TokenKind.COMMA));
requireNext(TokenKind.CLOSE);
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
return parameters;
}
private void requireNext(final TokenKind kind) throws UriParserException {
if (!tokenizer.next(kind)) {
throw new UriParserSyntaxException("Expected token '" + kind.toString() + "' not found.",
UriParserSyntaxException.MessageKeys.SYNTAX);
}
}
private void requireTokenEnd() throws UriParserException {
requireNext(TokenKind.EOF);
}
private boolean nextPrimitiveTypeValue(final EdmPrimitiveType primitiveType, final boolean nullable) {
final EdmPrimitiveType type = primitiveType instanceof EdmTypeDefinition ?
((EdmTypeDefinition) primitiveType).getUnderlyingType() :
@ -629,64 +618,63 @@ public class ResourcePathParser {
return true;
} else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean).equals(type)) {
return tokenizer.next(TokenKind.PrimitiveBooleanValue);
return tokenizer.next(TokenKind.BooleanValue);
} else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String).equals(type)) {
return tokenizer.next(TokenKind.PrimitiveStringValue);
return tokenizer.next(TokenKind.StringValue);
} else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.SByte).equals(type)
|| odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Byte).equals(type)
|| odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Int16).equals(type)
|| odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Int32).equals(type)
|| odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Int64).equals(type)) {
return tokenizer.next(TokenKind.PrimitiveIntegerValue);
return tokenizer.next(TokenKind.IntegerValue);
} else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Guid).equals(type)) {
return tokenizer.next(TokenKind.PrimitiveGuidValue);
return tokenizer.next(TokenKind.GuidValue);
} else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Date).equals(type)) {
return tokenizer.next(TokenKind.PrimitiveDateValue);
return tokenizer.next(TokenKind.DateValue);
} else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.DateTimeOffset).equals(type)) {
return tokenizer.next(TokenKind.PrimitiveDateTimeOffsetValue);
return tokenizer.next(TokenKind.DateTimeOffsetValue);
} else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.TimeOfDay).equals(type)) {
return tokenizer.next(TokenKind.PrimitiveTimeOfDayValue);
return tokenizer.next(TokenKind.TimeOfDayValue);
} else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Decimal).equals(type)) {
// The order is important.
// A decimal value should not be parsed as integer and let the tokenizer stop at the decimal point.
return tokenizer.next(TokenKind.PrimitiveDecimalValue)
|| tokenizer.next(TokenKind.PrimitiveIntegerValue);
return tokenizer.next(TokenKind.DecimalValue)
|| tokenizer.next(TokenKind.IntegerValue);
} else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Double).equals(type)) {
// The order is important.
// A floating-point value should not be parsed as decimal and let the tokenizer stop at 'E'.
// A decimal value should not be parsed as integer and let the tokenizer stop at the decimal point.
return tokenizer.next(TokenKind.PrimitiveDoubleValue)
|| tokenizer.next(TokenKind.PrimitiveDecimalValue)
|| tokenizer.next(TokenKind.PrimitiveIntegerValue);
return tokenizer.next(TokenKind.DoubleValue)
|| tokenizer.next(TokenKind.DecimalValue)
|| tokenizer.next(TokenKind.IntegerValue);
} else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Duration).equals(type)) {
return tokenizer.next(TokenKind.PrimitiveDurationValue);
return tokenizer.next(TokenKind.DurationValue);
} else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Binary).equals(type)) {
return tokenizer.next(TokenKind.PrimitiveBinaryValue);
return tokenizer.next(TokenKind.BinaryValue);
} else if (type.getKind() == EdmTypeKind.ENUM) {
return tokenizer.next(TokenKind.PrimitiveEnumValue);
return tokenizer.next(TokenKind.EnumValue);
} else {
return false;
}
}
private boolean nextPrimitiveValue() {
return tokenizer.next(TokenKind.ParameterAliasName)
|| tokenizer.next(TokenKind.NULL)
|| tokenizer.next(TokenKind.PrimitiveBooleanValue)
|| tokenizer.next(TokenKind.PrimitiveStringValue)
return tokenizer.next(TokenKind.NULL)
|| tokenizer.next(TokenKind.BooleanValue)
|| tokenizer.next(TokenKind.StringValue)
// The order of the next seven expressions is important in order to avoid
// finding partly parsed tokens (counter-intuitive as it may be, even a GUID may start with digits ...).
|| tokenizer.next(TokenKind.PrimitiveDoubleValue)
|| tokenizer.next(TokenKind.PrimitiveDecimalValue)
|| tokenizer.next(TokenKind.PrimitiveGuidValue)
|| tokenizer.next(TokenKind.PrimitiveDateTimeOffsetValue)
|| tokenizer.next(TokenKind.PrimitiveDateValue)
|| tokenizer.next(TokenKind.PrimitiveTimeOfDayValue)
|| tokenizer.next(TokenKind.PrimitiveIntegerValue)
|| tokenizer.next(TokenKind.DoubleValue)
|| tokenizer.next(TokenKind.DecimalValue)
|| tokenizer.next(TokenKind.GuidValue)
|| tokenizer.next(TokenKind.DateTimeOffsetValue)
|| tokenizer.next(TokenKind.DateValue)
|| tokenizer.next(TokenKind.TimeOfDayValue)
|| tokenizer.next(TokenKind.IntegerValue)
|| tokenizer.next(TokenKind.PrimitiveDurationValue)
|| tokenizer.next(TokenKind.PrimitiveBinaryValue)
|| tokenizer.next(TokenKind.PrimitiveEnumValue);
|| tokenizer.next(TokenKind.DurationValue)
|| tokenizer.next(TokenKind.BinaryValue)
|| tokenizer.next(TokenKind.EnumValue);
}
}

View File

@ -92,7 +92,7 @@ public class SelectParser {
if (type.compatibleTo(referencedType)) {
item.setTypeFilter(type);
if (tokenizer.next(TokenKind.SLASH)) {
requireNext(tokenizer, TokenKind.ODataIdentifier);
ParserHelper.requireNext(tokenizer, TokenKind.ODataIdentifier);
UriInfoImpl resource = new UriInfoImpl().setKind(UriInfoKind.resource);
addSelectPath(tokenizer, type, resource);
item.setResourcePath(resource);
@ -105,7 +105,7 @@ public class SelectParser {
}
} else {
requireNext(tokenizer, TokenKind.ODataIdentifier);
ParserHelper.requireNext(tokenizer, TokenKind.ODataIdentifier);
// The namespace or its alias could be a single OData identifier.
final FullQualifiedName allOperationsInSchema = parseAllOperationsInSchema(tokenizer);
if (allOperationsInSchema != null) {
@ -167,10 +167,10 @@ public class SelectParser {
List<String> names = new ArrayList<String>();
if (tokenizer.next(TokenKind.OPEN)) {
do {
requireNext(tokenizer, TokenKind.ODataIdentifier);
ParserHelper.requireNext(tokenizer, TokenKind.ODataIdentifier);
names.add(tokenizer.getText());
} while (tokenizer.next(TokenKind.COMMA));
requireNext(tokenizer, TokenKind.CLOSE);
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
}
return names;
}
@ -231,11 +231,4 @@ public class SelectParser {
}
}
}
private void requireNext(UriTokenizer tokenizer, final TokenKind kind) throws UriParserSyntaxException {
if (!tokenizer.next(kind)) {
throw new UriParserSyntaxException("Illegal $select expression.",
UriParserSyntaxException.MessageKeys.SYNTAX);
}
}
}

View File

@ -24,6 +24,8 @@ package org.apache.olingo.server.core.uri.parser;
* Since only the index is "moved", backing out while parsing a token is easy and used throughout.
* There is intentionally no method to push back tokens (although it would be easy to add such a method)
* because this tokenizer should behave like a classical token-consuming tokenizer.</p>
* <p>Whitespace is not an extra token but consumed with the tokens that require whitespace.
* Optional whitespace is not supported.</p>
*/
public class UriTokenizer {
@ -35,6 +37,9 @@ public class UriTokenizer {
VALUE,
COUNT,
CROSSJOIN,
ROOT,
IT,
OPEN,
CLOSE,
COMMA,
@ -44,6 +49,7 @@ public class UriTokenizer {
EQ,
STAR,
PLUS,
MINUS,
NULL,
// variable-value tokens (convention: mixed case)
@ -51,20 +57,68 @@ public class UriTokenizer {
QualifiedName,
ParameterAliasName,
PrimitiveBooleanValue,
PrimitiveStringValue,
PrimitiveIntegerValue,
PrimitiveGuidValue,
PrimitiveDateValue,
PrimitiveDateTimeOffsetValue,
PrimitiveTimeOfDayValue,
PrimitiveDecimalValue,
PrimitiveDoubleValue,
PrimitiveDurationValue,
PrimitiveBinaryValue,
PrimitiveEnumValue,
BooleanValue,
StringValue,
IntegerValue,
GuidValue,
DateValue,
DateTimeOffsetValue,
TimeOfDayValue,
DecimalValue,
DoubleValue,
DurationValue,
BinaryValue,
EnumValue,
jsonArrayOrObject
jsonArrayOrObject,
OrOperator,
AndOperator,
EqualsOperator,
NotEqualsOperator,
GreaterThanOperator,
GreaterThanOrEqualsOperator,
LessThanOperator,
LessThanOrEqualsOperator,
AddOperator,
SubOperator,
MulOperator,
DivOperator,
ModOperator,
NotOperator,
CastMethod,
CeilingMethod,
ConcatMethod,
ContainsMethod,
DateMethod,
DayMethod,
EndswithMethod,
FloorMethod,
FractionalsecondsMethod,
GeoDistanceMethod,
GeoIntersectsMethod,
GeoLengthMethod,
HourMethod,
IndexofMethod,
IsofMethod,
LengthMethod,
MaxdatetimeMethod,
MindatetimeMethod,
MinuteMethod,
MonthMethod,
NowMethod,
RoundMethod,
SecondMethod,
StartswithMethod,
SubstringMethod,
TimeMethod,
TolowerMethod,
TotaloffsetminutesMethod,
TotalsecondsMethod,
ToupperMethod,
TrimMethod,
YearMethod
}
private final String parseString;
@ -111,6 +165,13 @@ public class UriTokenizer {
case CROSSJOIN:
found = nextConstant("$crossjoin");
break;
case ROOT:
found = nextConstant("$root");
break;
case IT:
found = nextConstant("$it");
break;
case OPEN:
found = nextCharacter('(');
break;
@ -138,6 +199,9 @@ public class UriTokenizer {
case PLUS:
found = nextCharacter('+');
break;
case MINUS:
found = nextCharacter('-');
break;
case NULL:
found = nextConstant("null");
break;
@ -157,47 +221,189 @@ public class UriTokenizer {
break;
// Primitive Values
case PrimitiveBooleanValue:
case BooleanValue:
found = nextBooleanValue();
break;
case PrimitiveStringValue:
case StringValue:
found = nextStringValue();
break;
case PrimitiveIntegerValue:
case IntegerValue:
found = nextIntegerValue(true);
break;
case PrimitiveGuidValue:
case GuidValue:
found = nextGuidValue();
break;
case PrimitiveDateValue:
case DateValue:
found = nextDateValue();
break;
case PrimitiveDateTimeOffsetValue:
case DateTimeOffsetValue:
found = nextDateTimeOffsetValue();
break;
case PrimitiveTimeOfDayValue:
case TimeOfDayValue:
found = nextTimeOfDayValue();
break;
case PrimitiveDecimalValue:
case DecimalValue:
found = nextDecimalValue();
break;
case PrimitiveDoubleValue:
case DoubleValue:
found = nextDoubleValue();
break;
case PrimitiveDurationValue:
case DurationValue:
found = nextDurationValue();
break;
case PrimitiveBinaryValue:
case BinaryValue:
found = nextBinaryValue();
break;
case PrimitiveEnumValue:
case EnumValue:
found = nextEnumValue();
break;
// Primitive Values
// Complex or Collection Value
case jsonArrayOrObject:
found = nextJsonArrayOrObject();
break;
// Operators
case OrOperator:
found = nextBinaryOperator("or");
break;
case AndOperator:
found = nextBinaryOperator("and");
break;
case EqualsOperator:
found = nextBinaryOperator("eq");
break;
case NotEqualsOperator:
found = nextBinaryOperator("ne");
break;
case GreaterThanOperator:
found = nextBinaryOperator("gt");
break;
case GreaterThanOrEqualsOperator:
found = nextBinaryOperator("ge");
break;
case LessThanOperator:
found = nextBinaryOperator("lt");
break;
case LessThanOrEqualsOperator:
found = nextBinaryOperator("le");
break;
case AddOperator:
found = nextBinaryOperator("add");
break;
case SubOperator:
found = nextBinaryOperator("sub");
break;
case MulOperator:
found = nextBinaryOperator("mul");
break;
case DivOperator:
found = nextBinaryOperator("div");
break;
case ModOperator:
found = nextBinaryOperator("mod");
break;
case NotOperator:
found = nextConstant("not") && nextWhitespace();
break;
// Methods
case CastMethod:
found = nextMethod("cast");
break;
case CeilingMethod:
found = nextMethod("ceiling");
break;
case ConcatMethod:
found = nextMethod("concat");
break;
case ContainsMethod:
found = nextMethod("contains");
break;
case DateMethod:
found = nextMethod("date");
break;
case DayMethod:
found = nextMethod("day");
break;
case EndswithMethod:
found = nextMethod("endswith");
break;
case FloorMethod:
found = nextMethod("floor");
break;
case FractionalsecondsMethod:
found = nextMethod("fractionalseconds");
break;
case GeoDistanceMethod:
found = nextMethod("geo.distance");
break;
case GeoIntersectsMethod:
found = nextMethod("geo.intersects");
break;
case GeoLengthMethod:
found = nextMethod("geo.length");
break;
case HourMethod:
found = nextMethod("hour");
break;
case IndexofMethod:
found = nextMethod("indexof");
break;
case IsofMethod:
found = nextMethod("isof");
break;
case LengthMethod:
found = nextMethod("length");
break;
case MaxdatetimeMethod:
found = nextMethod("maxdatetime");
break;
case MindatetimeMethod:
found = nextMethod("mindatetime");
break;
case MinuteMethod:
found = nextMethod("minute");
break;
case MonthMethod:
found = nextMethod("month");
break;
case NowMethod:
found = nextMethod("now");
break;
case RoundMethod:
found = nextMethod("round");
break;
case SecondMethod:
found = nextMethod("second");
break;
case StartswithMethod:
found = nextMethod("startswith");
break;
case SubstringMethod:
found = nextMethod("substring");
break;
case TimeMethod:
found = nextMethod("time");
break;
case TolowerMethod:
found = nextMethod("tolower");
break;
case TotaloffsetminutesMethod:
found = nextMethod("totaloffsetminutes");
break;
case TotalsecondsMethod:
found = nextMethod("totalseconds");
break;
case ToupperMethod:
found = nextMethod("toupper");
break;
case TrimMethod:
found = nextMethod("trim");
break;
case YearMethod:
found = nextMethod("year");
break;
}
if (found) {
@ -300,6 +506,37 @@ public class UriTokenizer {
return nextCharacter('+') || nextCharacter('-');
}
/**
* Moves past whitespace (space or horizontal tabulator) characters if found;
* otherwise leaves the index unchanged.
* @return whether whitespace characters have been found at the current index
*/
private boolean nextWhitespace() {
int count = 0;
while (nextCharacter(' ') || nextCharacter('\t')) {
count++;
}
return count > 0;
}
/**
* Moves past the given whitespace-surrounded operator constant if found;
* otherwise leaves the index unchanged.
* @return whether the operator has been found at the current index
*/
private boolean nextBinaryOperator(final String operator) {
return nextWhitespace() && nextConstant(operator) && nextWhitespace();
}
/**
* Moves past the given method name and its immediately following opening parenthesis if found;
* otherwise leaves the index unchanged.
* @return whether the method has been found at the current index
*/
private boolean nextMethod(final String methodName) {
return nextConstant(methodName) && nextCharacter('(');
}
/**
* Moves past an OData identifier if found; otherwise leaves the index unchanged.
* @return whether an OData identifier has been found at the current index

View File

@ -53,6 +53,6 @@ public class UnaryImpl implements Unary {
@Override
public String toString() {
return "{" + operator + " " + expression + '}';
return "{" + operator.name() + " " + expression + '}';
}
}

View File

@ -20,191 +20,157 @@ 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 java.util.ArrayList;
import java.util.Locale;
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
import org.apache.olingo.server.core.uri.parser.ExpressionParser.Token;
import org.apache.olingo.server.core.uri.parser.ExpressionParser.TokenKind;
import org.apache.olingo.server.core.uri.parser.ExpressionParser.Tokenizer;
import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
import org.junit.Test;
public class ExpressionParserTest {
@Test
public void equality() throws Exception {
Expression expression = parseExpression(TokenKind.EQ_OP);
Expression expression = parseExpression("5 eq 5");
assertEquals("{5 EQ 5}", expression.toString());
expression = parseExpression(TokenKind.NE_OP);
expression = parseExpression("5 ne 5");
assertEquals("{5 NE 5}", expression.toString());
}
@Test
public void relational() throws Exception {
Expression expression = parseExpression(TokenKind.GT_OP);
Expression expression = parseExpression("5 gt 5");
assertEquals("{5 GT 5}", expression.toString());
expression = parseExpression(TokenKind.GE_OP);
expression = parseExpression("5 ge 5");
assertEquals("{5 GE 5}", expression.toString());
expression = parseExpression(TokenKind.LT_OP);
expression = parseExpression("5 lt 5");
assertEquals("{5 LT 5}", expression.toString());
expression = parseExpression(TokenKind.LE_OP);
expression = parseExpression("5 le 5");
assertEquals("{5 LE 5}", expression.toString());
}
@Test
public void additive() throws Exception {
Expression expression = parseExpression(TokenKind.ADD_OP);
Expression expression = parseExpression("5 add 5");
assertEquals("{5 ADD 5}", expression.toString());
expression = parseExpression(TokenKind.SUB_OP);
expression = parseExpression("5 sub 5");
assertEquals("{5 SUB 5}", expression.toString());
}
@Test
public void multiplicative() throws Exception {
Expression expression = parseExpression(TokenKind.MUL_OP);
Expression expression = parseExpression("5 mul 5");
assertEquals("{5 MUL 5}", expression.toString());
expression = parseExpression(TokenKind.DIV_OP);
expression = parseExpression("5 div 5");
assertEquals("{5 DIV 5}", expression.toString());
expression = parseExpression(TokenKind.MOD_OP);
expression = parseExpression("5 mod 5");
assertEquals("{5 MOD 5}", expression.toString());
}
@Test
public void unary() throws Exception {
ArrayList<Token> tokens = new ArrayList<Token>();
tokens.add(new Token(TokenKind.MINUS, ""));
tokens.add(new Token(TokenKind.PrimitiveIntegerValue, "5"));
Tokenizer tokenizer = new Tokenizer(tokens);
Expression expression = new ExpressionParser().parse(tokenizer);
assertEquals("{- 5}", expression.toString());
Expression expression = parseExpression("-5");
assertEquals("{MINUS 5}", expression.toString());
tokens = new ArrayList<Token>();
tokens.add(new Token(TokenKind.NOT, ""));
tokens.add(new Token(TokenKind.PrimitiveIntegerValue, "5"));
tokenizer = new Tokenizer(tokens);
expression = new ExpressionParser().parse(tokenizer);
assertEquals("{not 5}", expression.toString());
assertEquals("{MINUS -1}", parseExpression("--1").toString());
expression = parseExpression("not 5");
assertEquals("{NOT 5}", expression.toString());
}
@Test
public void grouping() throws Exception {
ArrayList<Token> tokens = new ArrayList<Token>();
tokens.add(new Token(TokenKind.MINUS, ""));
tokens.add(new Token(TokenKind.PrimitiveIntegerValue, "5"));
tokens.add(new Token(TokenKind.ADD_OP, ""));
tokens.add(new Token(TokenKind.PrimitiveIntegerValue, "5"));
Tokenizer tokenizer = new Tokenizer(tokens);
Expression expression = new ExpressionParser().parse(tokenizer);
assertEquals("{{- 5} ADD 5}", expression.toString());
Expression expression = parseExpression("-5 add 5");
assertEquals("{{MINUS 5} ADD 5}", expression.toString());
tokens = new ArrayList<Token>();
tokens.add(new Token(TokenKind.MINUS, ""));
tokens.add(new Token(TokenKind.OPEN, ""));
tokens.add(new Token(TokenKind.PrimitiveIntegerValue, "5"));
tokens.add(new Token(TokenKind.ADD_OP, ""));
tokens.add(new Token(TokenKind.PrimitiveIntegerValue, "5"));
tokens.add(new Token(TokenKind.CLOSE, ""));
tokenizer = new Tokenizer(tokens);
expression = new ExpressionParser().parse(tokenizer);
assertEquals("{- {5 ADD 5}}", expression.toString());
expression = parseExpression("-(5 add 5)");
assertEquals("{MINUS {5 ADD 5}}", expression.toString());
}
@Test
public void precedence() throws Exception {
assertEquals("{{MINUS 1} ADD {2 DIV 3}}", parseExpression("-1 add 2 div 3").toString());
assertEquals("{true OR {{NOT false} AND true}}", parseExpression("true or not false and true").toString());
}
@Test
public void noParameterMethods() throws Exception {
Expression expression = parseMethod(TokenKind.Now);
Expression expression = parseMethod(TokenKind.NowMethod);
assertEquals("{now []}", expression.toString());
expression = parseMethod(TokenKind.Maxdatetime);
expression = parseMethod(TokenKind.MaxdatetimeMethod);
assertEquals("{maxdatetime []}", expression.toString());
expression = parseMethod(TokenKind.Mindatetime);
expression = parseMethod(TokenKind.MindatetimeMethod);
assertEquals("{mindatetime []}", expression.toString());
}
@Test
public void oneParameterMethods() throws Exception {
Expression expression = parseMethod(TokenKind.Length, TokenKind.PrimitiveStringValue);
assertEquals("{length [String1]}", expression.toString());
final String stringValue = "'abc'";
final String dateValue = "1234-12-25";
final String dateTimeOffsetValue = "1234-12-25T11:12:13.456Z";
expression = parseMethod(TokenKind.Tolower, TokenKind.PrimitiveStringValue);
assertEquals("{tolower [String1]}", expression.toString());
Expression expression = parseMethod(TokenKind.LengthMethod, stringValue);
assertEquals("{length [" + stringValue + "]}", expression.toString());
expression = parseMethod(TokenKind.Toupper, TokenKind.PrimitiveStringValue);
assertEquals("{toupper [String1]}", expression.toString());
expression = parseMethod(TokenKind.TolowerMethod, stringValue);
assertEquals("{tolower [" + stringValue + "]}", expression.toString());
expression = parseMethod(TokenKind.Trim, TokenKind.PrimitiveStringValue);
assertEquals("{trim [String1]}", expression.toString());
expression = parseMethod(TokenKind.ToupperMethod, stringValue);
assertEquals("{toupper [" + stringValue + "]}", expression.toString());
expression = parseMethod(TokenKind.Year, TokenKind.PrimitiveDateValue);
assertEquals("{year [Date1]}", expression.toString());
expression = parseMethod(TokenKind.TrimMethod, stringValue);
assertEquals("{trim [" + stringValue + "]}", expression.toString());
expression = parseMethod(TokenKind.Month, TokenKind.PrimitiveDateValue);
assertEquals("{month [Date1]}", expression.toString());
expression = parseMethod(TokenKind.YearMethod, dateValue);
assertEquals("{year [" + dateValue + "]}", expression.toString());
expression = parseMethod(TokenKind.Day, TokenKind.PrimitiveDateValue);
assertEquals("{day [Date1]}", expression.toString());
expression = parseMethod(TokenKind.MonthMethod, dateValue);
assertEquals("{month [" + dateValue + "]}", expression.toString());
expression = parseMethod(TokenKind.Hour, TokenKind.PrimitiveDateTimeOffsetValue);
assertEquals("{hour [DateTimeOffset1]}", expression.toString());
expression = parseMethod(TokenKind.DayMethod, dateValue);
assertEquals("{day [" + dateValue + "]}", expression.toString());
expression = parseMethod(TokenKind.Minute, TokenKind.PrimitiveDateTimeOffsetValue);
assertEquals("{minute [DateTimeOffset1]}", expression.toString());
expression = parseMethod(TokenKind.HourMethod, dateTimeOffsetValue);
assertEquals("{hour [" + dateTimeOffsetValue + "]}", expression.toString());
expression = parseMethod(TokenKind.Second, TokenKind.PrimitiveDateTimeOffsetValue);
assertEquals("{second [DateTimeOffset1]}", expression.toString());
expression = parseMethod(TokenKind.MinuteMethod, dateTimeOffsetValue);
assertEquals("{minute [" + dateTimeOffsetValue + "]}", expression.toString());
expression = parseMethod(TokenKind.SecondMethod, dateTimeOffsetValue);
assertEquals("{second [" + dateTimeOffsetValue + "]}", expression.toString());
}
@Test
public void twoParameterMethods() {
}
private Expression parseMethod(TokenKind... kind) throws UriParserException {
ArrayList<Token> tokens = new ArrayList<Token>();
tokens.add(new Token(kind[0], ""));
for (int i = 1; i < kind.length; i++) {
String text = null;
switch (kind[i]) {
case PrimitiveStringValue:
text = "String" + i;
break;
case PrimitiveDateValue:
text = "Date" + i;
break;
case PrimitiveDateTimeOffsetValue:
text = "DateTimeOffset" + i;
break;
default:
text = "" + i;
break;
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) {
expressionString += ',';
}
tokens.add(new Token(kind[i], text));
expressionString += parameters[i];
}
expressionString += ')';
tokens.add(new Token(TokenKind.CLOSE, ""));
Tokenizer tokenizer = new Tokenizer(tokens);
Expression expression = new ExpressionParser().parse(tokenizer);
Expression expression = parseExpression(expressionString);
assertNotNull(expression);
return expression;
}
private Expression parseExpression(TokenKind operator) throws UriParserException {
ArrayList<Token> tokens = new ArrayList<Token>();
tokens.add(new Token(TokenKind.PrimitiveIntegerValue, "5"));
tokens.add(new Token(operator, ""));
tokens.add(new Token(TokenKind.PrimitiveIntegerValue, "5"));
Tokenizer tokenizer = new Tokenizer(tokens);
private Expression parseExpression(final String expressionString) throws UriParserException {
UriTokenizer tokenizer = new UriTokenizer(expressionString);
Expression expression = new ExpressionParser().parse(tokenizer);
assertNotNull(expression);
assertTrue(tokenizer.next(TokenKind.EOF));
return expression;
}
}

View File

@ -22,6 +22,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.Locale;
import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
import org.junit.Test;
@ -44,6 +46,8 @@ public class UriTokenizerTest {
assertTrue(new UriTokenizer("$value").next(TokenKind.VALUE));
assertTrue(new UriTokenizer("$count").next(TokenKind.COUNT));
assertTrue(new UriTokenizer("$crossjoin").next(TokenKind.CROSSJOIN));
assertTrue(new UriTokenizer("$root").next(TokenKind.ROOT));
assertTrue(new UriTokenizer("$it").next(TokenKind.IT));
assertTrue(new UriTokenizer("null").next(TokenKind.NULL));
wrongToken(TokenKind.REF, "$ref", 'x');
@ -51,19 +55,19 @@ public class UriTokenizerTest {
@Test
public void sequence() {
final UriTokenizer tokenizer = new UriTokenizer("(A=1,B=2);.*/+");
final UriTokenizer tokenizer = new UriTokenizer("(A=1,B=2);.*/+-");
assertTrue(tokenizer.next(TokenKind.OPEN));
assertFalse(tokenizer.next(TokenKind.OPEN));
assertTrue(tokenizer.next(TokenKind.ODataIdentifier));
assertEquals("A", tokenizer.getText());
assertTrue(tokenizer.next(TokenKind.EQ));
assertTrue(tokenizer.next(TokenKind.PrimitiveIntegerValue));
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertEquals("1", tokenizer.getText());
assertTrue(tokenizer.next(TokenKind.COMMA));
assertTrue(tokenizer.next(TokenKind.ODataIdentifier));
assertEquals("B", tokenizer.getText());
assertTrue(tokenizer.next(TokenKind.EQ));
assertTrue(tokenizer.next(TokenKind.PrimitiveIntegerValue));
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertEquals("2", tokenizer.getText());
assertFalse(tokenizer.next(TokenKind.EOF));
assertTrue(tokenizer.next(TokenKind.CLOSE));
@ -72,6 +76,7 @@ public class UriTokenizerTest {
assertTrue(tokenizer.next(TokenKind.STAR));
assertTrue(tokenizer.next(TokenKind.SLASH));
assertTrue(tokenizer.next(TokenKind.PLUS));
assertTrue(tokenizer.next(TokenKind.MINUS));
assertTrue(tokenizer.next(TokenKind.EOF));
}
@ -107,7 +112,7 @@ public class UriTokenizerTest {
final UriTokenizer tokenizer = new UriTokenizer("multi.part.namespace.name.1");
assertTrue(tokenizer.next(TokenKind.QualifiedName));
assertTrue(tokenizer.next(TokenKind.DOT));
assertTrue(tokenizer.next(TokenKind.PrimitiveIntegerValue));
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.EOF));
assertFalse(new UriTokenizer("name").next(TokenKind.QualifiedName));
@ -127,174 +132,174 @@ public class UriTokenizerTest {
@Test
public void booleanValue() {
assertTrue(new UriTokenizer("true").next(TokenKind.PrimitiveBooleanValue));
assertTrue(new UriTokenizer("tRuE").next(TokenKind.PrimitiveBooleanValue));
assertTrue(new UriTokenizer("false").next(TokenKind.PrimitiveBooleanValue));
assertTrue(new UriTokenizer("False").next(TokenKind.PrimitiveBooleanValue));
assertTrue(new UriTokenizer("true").next(TokenKind.BooleanValue));
assertTrue(new UriTokenizer("tRuE").next(TokenKind.BooleanValue));
assertTrue(new UriTokenizer("false").next(TokenKind.BooleanValue));
assertTrue(new UriTokenizer("False").next(TokenKind.BooleanValue));
wrongToken(TokenKind.PrimitiveBooleanValue, "true", 'x');
wrongToken(TokenKind.BooleanValue, "true", 'x');
}
@Test
public void string() {
assertTrue(new UriTokenizer("'ABC'").next(TokenKind.PrimitiveStringValue));
assertTrue(new UriTokenizer("'€\uFDFC'").next(TokenKind.PrimitiveStringValue));
assertTrue(new UriTokenizer("'ABC'").next(TokenKind.StringValue));
assertTrue(new UriTokenizer("'€\uFDFC'").next(TokenKind.StringValue));
assertTrue(new UriTokenizer('\'' + String.valueOf(Character.toChars(0x1F603)) + '\'')
.next(TokenKind.PrimitiveStringValue));
.next(TokenKind.StringValue));
final UriTokenizer tokenizer = new UriTokenizer("'AB''''C'''D");
assertTrue(tokenizer.next(TokenKind.PrimitiveStringValue));
assertTrue(tokenizer.next(TokenKind.StringValue));
assertEquals("'AB''''C'''", tokenizer.getText());
assertTrue(tokenizer.next(TokenKind.ODataIdentifier));
assertEquals("D", tokenizer.getText());
assertFalse(new UriTokenizer("A").next(TokenKind.PrimitiveStringValue));
assertFalse(new UriTokenizer("'A").next(TokenKind.PrimitiveStringValue));
assertFalse(new UriTokenizer("A").next(TokenKind.StringValue));
assertFalse(new UriTokenizer("'A").next(TokenKind.StringValue));
}
@Test
public void integer() {
assertTrue(new UriTokenizer("1").next(TokenKind.PrimitiveIntegerValue));
assertTrue(new UriTokenizer("1.").next(TokenKind.PrimitiveIntegerValue));
assertFalse(new UriTokenizer(".1").next(TokenKind.PrimitiveIntegerValue));
assertTrue(new UriTokenizer("-1").next(TokenKind.PrimitiveIntegerValue));
assertTrue(new UriTokenizer("1234567890").next(TokenKind.PrimitiveIntegerValue));
assertTrue(new UriTokenizer("1").next(TokenKind.IntegerValue));
assertTrue(new UriTokenizer("1.").next(TokenKind.IntegerValue));
assertFalse(new UriTokenizer(".1").next(TokenKind.IntegerValue));
assertTrue(new UriTokenizer("-1").next(TokenKind.IntegerValue));
assertTrue(new UriTokenizer("1234567890").next(TokenKind.IntegerValue));
}
@Test
public void guid() {
assertTrue(new UriTokenizer("12345678-abcd-ef12-1234-567890ABCDEF").next(TokenKind.PrimitiveGuidValue));
wrongToken(TokenKind.PrimitiveGuidValue, "12345678-1234-1234-1234-123456789ABC", 'G');
assertTrue(new UriTokenizer("12345678-abcd-ef12-1234-567890ABCDEF").next(TokenKind.GuidValue));
wrongToken(TokenKind.GuidValue, "12345678-1234-1234-1234-123456789ABC", 'G');
}
@Test
public void date() {
assertTrue(new UriTokenizer("12345-12-25").next(TokenKind.PrimitiveDateValue));
assertTrue(new UriTokenizer("-0001-12-24").next(TokenKind.PrimitiveDateValue));
assertFalse(new UriTokenizer("1234-13-01").next(TokenKind.PrimitiveDateValue));
assertFalse(new UriTokenizer("1234-12-32").next(TokenKind.PrimitiveDateValue));
assertFalse(new UriTokenizer("123-01-01").next(TokenKind.PrimitiveDateValue));
assertFalse(new UriTokenizer("1234-00-01").next(TokenKind.PrimitiveDateValue));
assertFalse(new UriTokenizer("1234-01-00").next(TokenKind.PrimitiveDateValue));
wrongToken(TokenKind.PrimitiveDateValue, "2000-12-29", 'A');
wrongToken(TokenKind.PrimitiveDateValue, "0001-01-01", 'A');
wrongToken(TokenKind.PrimitiveDateValue, "-12345-01-31", 'A');
assertTrue(new UriTokenizer("12345-12-25").next(TokenKind.DateValue));
assertTrue(new UriTokenizer("-0001-12-24").next(TokenKind.DateValue));
assertFalse(new UriTokenizer("1234-13-01").next(TokenKind.DateValue));
assertFalse(new UriTokenizer("1234-12-32").next(TokenKind.DateValue));
assertFalse(new UriTokenizer("123-01-01").next(TokenKind.DateValue));
assertFalse(new UriTokenizer("1234-00-01").next(TokenKind.DateValue));
assertFalse(new UriTokenizer("1234-01-00").next(TokenKind.DateValue));
wrongToken(TokenKind.DateValue, "2000-12-29", 'A');
wrongToken(TokenKind.DateValue, "0001-01-01", 'A');
wrongToken(TokenKind.DateValue, "-12345-01-31", 'A');
}
@Test
public void dateTimeOffset() {
assertTrue(new UriTokenizer("1234-12-25T11:12:13.456Z").next(TokenKind.PrimitiveDateTimeOffsetValue));
assertTrue(new UriTokenizer("-1234-12-25t01:12z").next(TokenKind.PrimitiveDateTimeOffsetValue));
assertTrue(new UriTokenizer("-1234-12-25T21:22:23+01:00").next(TokenKind.PrimitiveDateTimeOffsetValue));
assertTrue(new UriTokenizer("1234-12-25T11:12:13-00:30").next(TokenKind.PrimitiveDateTimeOffsetValue));
assertFalse(new UriTokenizer("1234-10-01").next(TokenKind.PrimitiveDateTimeOffsetValue));
wrongToken(TokenKind.PrimitiveDateTimeOffsetValue, "-1234-12-25T11:12:13.456+01:00", 'P');
assertTrue(new UriTokenizer("1234-12-25T11:12:13.456Z").next(TokenKind.DateTimeOffsetValue));
assertTrue(new UriTokenizer("-1234-12-25t01:12z").next(TokenKind.DateTimeOffsetValue));
assertTrue(new UriTokenizer("-1234-12-25T21:22:23+01:00").next(TokenKind.DateTimeOffsetValue));
assertTrue(new UriTokenizer("1234-12-25T11:12:13-00:30").next(TokenKind.DateTimeOffsetValue));
assertFalse(new UriTokenizer("1234-10-01").next(TokenKind.DateTimeOffsetValue));
wrongToken(TokenKind.DateTimeOffsetValue, "-1234-12-25T11:12:13.456+01:00", 'P');
}
@Test
public void timeOfDay() {
assertTrue(new UriTokenizer("11:12:13").next(TokenKind.PrimitiveTimeOfDayValue));
assertTrue(new UriTokenizer("11:12:13.456").next(TokenKind.PrimitiveTimeOfDayValue));
assertFalse(new UriTokenizer("24:00:00").next(TokenKind.PrimitiveTimeOfDayValue));
assertFalse(new UriTokenizer("01:60:00").next(TokenKind.PrimitiveTimeOfDayValue));
assertFalse(new UriTokenizer("01:00:60").next(TokenKind.PrimitiveTimeOfDayValue));
assertFalse(new UriTokenizer("01:00:00.").next(TokenKind.PrimitiveTimeOfDayValue));
assertFalse(new UriTokenizer("0:02:03").next(TokenKind.PrimitiveTimeOfDayValue));
assertFalse(new UriTokenizer("01:0:03").next(TokenKind.PrimitiveTimeOfDayValue));
assertFalse(new UriTokenizer("01:02:0").next(TokenKind.PrimitiveTimeOfDayValue));
wrongToken(TokenKind.PrimitiveTimeOfDayValue, "11:12", '-');
assertTrue(new UriTokenizer("11:12:13").next(TokenKind.TimeOfDayValue));
assertTrue(new UriTokenizer("11:12:13.456").next(TokenKind.TimeOfDayValue));
assertFalse(new UriTokenizer("24:00:00").next(TokenKind.TimeOfDayValue));
assertFalse(new UriTokenizer("01:60:00").next(TokenKind.TimeOfDayValue));
assertFalse(new UriTokenizer("01:00:60").next(TokenKind.TimeOfDayValue));
assertFalse(new UriTokenizer("01:00:00.").next(TokenKind.TimeOfDayValue));
assertFalse(new UriTokenizer("0:02:03").next(TokenKind.TimeOfDayValue));
assertFalse(new UriTokenizer("01:0:03").next(TokenKind.TimeOfDayValue));
assertFalse(new UriTokenizer("01:02:0").next(TokenKind.TimeOfDayValue));
wrongToken(TokenKind.TimeOfDayValue, "11:12", '-');
}
@Test
public void decimal() {
assertTrue(new UriTokenizer("1.2").next(TokenKind.PrimitiveDecimalValue));
assertFalse(new UriTokenizer(".1").next(TokenKind.PrimitiveDecimalValue));
assertTrue(new UriTokenizer("-12.34").next(TokenKind.PrimitiveDecimalValue));
assertTrue(new UriTokenizer("1234567890.0123456789").next(TokenKind.PrimitiveDecimalValue));
assertFalse(new UriTokenizer("0,1").next(TokenKind.PrimitiveDecimalValue));
assertFalse(new UriTokenizer("0..1").next(TokenKind.PrimitiveDecimalValue));
assertTrue(new UriTokenizer("1.2").next(TokenKind.DecimalValue));
assertFalse(new UriTokenizer(".1").next(TokenKind.DecimalValue));
assertTrue(new UriTokenizer("-12.34").next(TokenKind.DecimalValue));
assertTrue(new UriTokenizer("1234567890.0123456789").next(TokenKind.DecimalValue));
assertFalse(new UriTokenizer("0,1").next(TokenKind.DecimalValue));
assertFalse(new UriTokenizer("0..1").next(TokenKind.DecimalValue));
}
@Test
public void doubleValue() {
assertTrue(new UriTokenizer("NaN").next(TokenKind.PrimitiveDoubleValue));
assertTrue(new UriTokenizer("-INF").next(TokenKind.PrimitiveDoubleValue));
assertTrue(new UriTokenizer("INF").next(TokenKind.PrimitiveDoubleValue));
assertFalse(new UriTokenizer("inf").next(TokenKind.PrimitiveDoubleValue));
assertTrue(new UriTokenizer("1.2E3").next(TokenKind.PrimitiveDoubleValue));
assertTrue(new UriTokenizer("-12.34e-05").next(TokenKind.PrimitiveDoubleValue));
assertTrue(new UriTokenizer("1E2").next(TokenKind.PrimitiveDoubleValue));
assertFalse(new UriTokenizer("1.E2").next(TokenKind.PrimitiveDoubleValue));
wrongToken(TokenKind.PrimitiveDoubleValue, "-12.34E+5", 'i');
assertTrue(new UriTokenizer("NaN").next(TokenKind.DoubleValue));
assertTrue(new UriTokenizer("-INF").next(TokenKind.DoubleValue));
assertTrue(new UriTokenizer("INF").next(TokenKind.DoubleValue));
assertFalse(new UriTokenizer("inf").next(TokenKind.DoubleValue));
assertTrue(new UriTokenizer("1.2E3").next(TokenKind.DoubleValue));
assertTrue(new UriTokenizer("-12.34e-05").next(TokenKind.DoubleValue));
assertTrue(new UriTokenizer("1E2").next(TokenKind.DoubleValue));
assertFalse(new UriTokenizer("1.E2").next(TokenKind.DoubleValue));
wrongToken(TokenKind.DoubleValue, "-12.34E+5", 'i');
}
@Test
public void duration() {
assertTrue(new UriTokenizer("duration'P'").next(TokenKind.PrimitiveDurationValue));
assertTrue(new UriTokenizer("DURATION'P1D'").next(TokenKind.PrimitiveDurationValue));
assertTrue(new UriTokenizer("duration'PT'").next(TokenKind.PrimitiveDurationValue));
assertTrue(new UriTokenizer("duration'PT1H'").next(TokenKind.PrimitiveDurationValue));
assertTrue(new UriTokenizer("duration'pt1M'").next(TokenKind.PrimitiveDurationValue));
assertTrue(new UriTokenizer("duration'PT1S'").next(TokenKind.PrimitiveDurationValue));
assertTrue(new UriTokenizer("duration'PT1.2s'").next(TokenKind.PrimitiveDurationValue));
assertTrue(new UriTokenizer("duration'-p1dt2h3m4.5s'").next(TokenKind.PrimitiveDurationValue));
assertFalse(new UriTokenizer("-p1dt2h3m4.5s").next(TokenKind.PrimitiveDurationValue));
assertFalse(new UriTokenizer("duration'-p1dt2h3m4.5s").next(TokenKind.PrimitiveDurationValue));
assertFalse(new UriTokenizer("duration'2h3m4s'").next(TokenKind.PrimitiveDurationValue));
wrongToken(TokenKind.PrimitiveDurationValue, "duration'P1DT2H3M4.5S'", ':');
assertTrue(new UriTokenizer("duration'P'").next(TokenKind.DurationValue));
assertTrue(new UriTokenizer("DURATION'P1D'").next(TokenKind.DurationValue));
assertTrue(new UriTokenizer("duration'PT'").next(TokenKind.DurationValue));
assertTrue(new UriTokenizer("duration'PT1H'").next(TokenKind.DurationValue));
assertTrue(new UriTokenizer("duration'pt1M'").next(TokenKind.DurationValue));
assertTrue(new UriTokenizer("duration'PT1S'").next(TokenKind.DurationValue));
assertTrue(new UriTokenizer("duration'PT1.2s'").next(TokenKind.DurationValue));
assertTrue(new UriTokenizer("duration'-p1dt2h3m4.5s'").next(TokenKind.DurationValue));
assertFalse(new UriTokenizer("-p1dt2h3m4.5s").next(TokenKind.DurationValue));
assertFalse(new UriTokenizer("duration'-p1dt2h3m4.5s").next(TokenKind.DurationValue));
assertFalse(new UriTokenizer("duration'2h3m4s'").next(TokenKind.DurationValue));
wrongToken(TokenKind.DurationValue, "duration'P1DT2H3M4.5S'", ':');
}
@Test
public void binary() {
assertTrue(new UriTokenizer("binary''").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("Binary'bm93'").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary''").next(TokenKind.BinaryValue));
assertTrue(new UriTokenizer("Binary'bm93'").next(TokenKind.BinaryValue));
// all cases with three base64 characters (and one fill character) at the end
assertTrue(new UriTokenizer("binary'QUA='").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'QUE='").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'QUI='").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'QUM='").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'QUQ='").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'QUU='").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'QUY='").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'QUc='").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'QUg='").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'QUk='").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'QUo='").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'QUs='").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'QUw='").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'QU0='").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'QU4='").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'QU8='").next(TokenKind.PrimitiveBinaryValue));
assertFalse(new UriTokenizer("binary'QUB='").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'QUA='").next(TokenKind.BinaryValue));
assertTrue(new UriTokenizer("binary'QUE='").next(TokenKind.BinaryValue));
assertTrue(new UriTokenizer("binary'QUI='").next(TokenKind.BinaryValue));
assertTrue(new UriTokenizer("binary'QUM='").next(TokenKind.BinaryValue));
assertTrue(new UriTokenizer("binary'QUQ='").next(TokenKind.BinaryValue));
assertTrue(new UriTokenizer("binary'QUU='").next(TokenKind.BinaryValue));
assertTrue(new UriTokenizer("binary'QUY='").next(TokenKind.BinaryValue));
assertTrue(new UriTokenizer("binary'QUc='").next(TokenKind.BinaryValue));
assertTrue(new UriTokenizer("binary'QUg='").next(TokenKind.BinaryValue));
assertTrue(new UriTokenizer("binary'QUk='").next(TokenKind.BinaryValue));
assertTrue(new UriTokenizer("binary'QUo='").next(TokenKind.BinaryValue));
assertTrue(new UriTokenizer("binary'QUs='").next(TokenKind.BinaryValue));
assertTrue(new UriTokenizer("binary'QUw='").next(TokenKind.BinaryValue));
assertTrue(new UriTokenizer("binary'QU0='").next(TokenKind.BinaryValue));
assertTrue(new UriTokenizer("binary'QU4='").next(TokenKind.BinaryValue));
assertTrue(new UriTokenizer("binary'QU8='").next(TokenKind.BinaryValue));
assertFalse(new UriTokenizer("binary'QUB='").next(TokenKind.BinaryValue));
// all cases with two base64 characters (and two fill characters) at the end
assertTrue(new UriTokenizer("BINARY'VGVzdA=='").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'U-RnZQ=='").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'Yg=='").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'Yw=='").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("BINARY'VGVzdA=='").next(TokenKind.BinaryValue));
assertTrue(new UriTokenizer("binary'U-RnZQ=='").next(TokenKind.BinaryValue));
assertTrue(new UriTokenizer("binary'Yg=='").next(TokenKind.BinaryValue));
assertTrue(new UriTokenizer("binary'Yw=='").next(TokenKind.BinaryValue));
// without optional fill character
assertTrue(new UriTokenizer("binary'T0RhdGE'").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'T0RhdGE'").next(TokenKind.BinaryValue));
// special character '_' (the other, '-', already has been used above)
assertTrue(new UriTokenizer("binary'V_ZydGVy'").next(TokenKind.PrimitiveBinaryValue));
assertTrue(new UriTokenizer("binary'V_ZydGVy'").next(TokenKind.BinaryValue));
wrongToken(TokenKind.PrimitiveBinaryValue, "binary'VGVzdA=='", '+');
wrongToken(TokenKind.BinaryValue, "binary'VGVzdA=='", '+');
}
@Test
public void enumValue() {
assertTrue(new UriTokenizer("namespace.name'value'").next(TokenKind.PrimitiveEnumValue));
assertTrue(new UriTokenizer("namespace.name'flag1,flag2,-3'").next(TokenKind.PrimitiveEnumValue));
assertFalse(new UriTokenizer("namespace.name'1flag'").next(TokenKind.PrimitiveEnumValue));
assertFalse(new UriTokenizer("namespace.name'flag1,,flag2'").next(TokenKind.PrimitiveEnumValue));
assertFalse(new UriTokenizer("namespace.name',value'").next(TokenKind.PrimitiveEnumValue));
assertFalse(new UriTokenizer("namespace.name'value,'").next(TokenKind.PrimitiveEnumValue));
assertFalse(new UriTokenizer("namespace.name''").next(TokenKind.PrimitiveEnumValue));
assertFalse(new UriTokenizer("'1'").next(TokenKind.PrimitiveEnumValue));
assertFalse(new UriTokenizer("1").next(TokenKind.PrimitiveEnumValue));
wrongToken(TokenKind.PrimitiveEnumValue, "namespace.name'_1,_2,3'", ';');
assertTrue(new UriTokenizer("namespace.name'value'").next(TokenKind.EnumValue));
assertTrue(new UriTokenizer("namespace.name'flag1,flag2,-3'").next(TokenKind.EnumValue));
assertFalse(new UriTokenizer("namespace.name'1flag'").next(TokenKind.EnumValue));
assertFalse(new UriTokenizer("namespace.name'flag1,,flag2'").next(TokenKind.EnumValue));
assertFalse(new UriTokenizer("namespace.name',value'").next(TokenKind.EnumValue));
assertFalse(new UriTokenizer("namespace.name'value,'").next(TokenKind.EnumValue));
assertFalse(new UriTokenizer("namespace.name''").next(TokenKind.EnumValue));
assertFalse(new UriTokenizer("'1'").next(TokenKind.EnumValue));
assertFalse(new UriTokenizer("1").next(TokenKind.EnumValue));
wrongToken(TokenKind.EnumValue, "namespace.name'_1,_2,3'", ';');
}
@Test
@ -360,6 +365,92 @@ public class UriTokenizerTest {
wrongToken(TokenKind.jsonArrayOrObject, "[{\"name\":+123.456},null]", '\\');
}
@Test
public void operators() {
UriTokenizer tokenizer = new UriTokenizer("1 ne 2");
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertFalse(tokenizer.next(TokenKind.EqualsOperator));
assertTrue(tokenizer.next(TokenKind.NotEqualsOperator));
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.EOF));
tokenizer = new UriTokenizer("1ne 2");
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertFalse(tokenizer.next(TokenKind.NotEqualsOperator));
tokenizer = new UriTokenizer("1 ne2");
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertFalse(tokenizer.next(TokenKind.NotEqualsOperator));
tokenizer = new UriTokenizer("1 \tle\t\t\t2");
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.LessThanOrEqualsOperator));
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.EOF));
assertFalse(new UriTokenizer("nottrue").next(TokenKind.NotOperator));
assertFalse(new UriTokenizer("no true").next(TokenKind.NotOperator));
tokenizer = new UriTokenizer("true or not false and 1 eq 2 add 3 sub 4 mul 5 div 6 mod 7");
assertTrue(tokenizer.next(TokenKind.BooleanValue));
assertTrue(tokenizer.next(TokenKind.OrOperator));
assertTrue(tokenizer.next(TokenKind.NotOperator));
assertTrue(tokenizer.next(TokenKind.BooleanValue));
assertTrue(tokenizer.next(TokenKind.AndOperator));
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.EqualsOperator));
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.AddOperator));
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.SubOperator));
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.MulOperator));
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.DivOperator));
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.ModOperator));
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.EOF));
tokenizer = new UriTokenizer("1 gt 2 or 3 ge 4 or 5 lt 6");
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.GreaterThanOperator));
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.OrOperator));
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.GreaterThanOrEqualsOperator));
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.OrOperator));
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.LessThanOperator));
assertTrue(tokenizer.next(TokenKind.IntegerValue));
assertTrue(tokenizer.next(TokenKind.EOF));
}
@Test
public void methods() {
UriTokenizer tokenizer = new UriTokenizer("now()");
assertTrue(tokenizer.next(TokenKind.NowMethod));
assertTrue(tokenizer.next(TokenKind.CLOSE));
assertTrue(tokenizer.next(TokenKind.EOF));
assertFalse(new UriTokenizer("no w()").next(TokenKind.NowMethod));
assertFalse(new UriTokenizer("now ()").next(TokenKind.NowMethod));
assertTrue(new UriTokenizer("maxdatetime()").next(TokenKind.MaxdatetimeMethod));
assertTrue(new UriTokenizer("mindatetime()").next(TokenKind.MindatetimeMethod));
for (final TokenKind tokenKind : TokenKind.values()) {
if (tokenKind.name().endsWith("Method")) {
assertTrue(tokenKind.name(),
new UriTokenizer(
tokenKind.name().substring(0, tokenKind.name().indexOf("Method"))
.toLowerCase(Locale.ROOT).replace("geo", "geo.") + '(')
.next(tokenKind));
}
}
}
private void wrongToken(final TokenKind kind, final String value, final char disturbCharacter) {
assertFalse(new UriTokenizer(disturbCharacter + value).next(kind));