[OLINGO-659] Literals returned by the URI Parser provide an EdmType

This commit is contained in:
Christian Holzer 2015-08-14 16:32:56 +02:00
parent f9c68b8ba4
commit cd11add7c1
5 changed files with 270 additions and 40 deletions

View File

@ -31,7 +31,12 @@ public interface Literal extends Expression {
public String getText();
/**
* @return Type of the literal if detected
* Numeric literals without an dot and without an e return the smallest possible Edm Integer type.
* Numeric literals without an dot, without an e and larger than 2^63 - 1 are considered as Edm.Decimal
* Numeric literals with an e, are considered to be Edm.Double
* Numeric literals with an dot and without an e, are supposed to be Edm.Decimal
*
* @return Type of the literal if detected. The type of the literal is guessed by the parser.
*/
public EdmType getType();

View File

@ -148,7 +148,7 @@ skip : SKIP_QO EQ INT;
top : TOP EQ INT;
//format : FORMAT EQ ( ATOM | JSON | XML | PCHARS SLASH PCHARS);
inlinecount : COUNT EQ booleanNonCase;
inlinecount : COUNT EQ booleanNonCaseLiteral;
search : SEARCH searchSpecialToken;
searchInline : SEARCH_INLINE searchSpecialToken;
@ -342,19 +342,19 @@ odataIdentifier : ODATAIDENTIFIER;
//;------------------------------------------------------------------------------
primitiveLiteral : nullrule
| booleanNonCase
| DECIMAL //includes double and single literals
| naninfinity
| INT //includes int16/int32 and int64 literals
| BINARY
| DATE
| DATETIMEOFFSET
| DURATION
| GUID
| string
| TIMEOFDAY
| enumLit
primitiveLiteral : nullruleLiteral
| booleanNonCaseLiteral
| decimalLiteral //includes double and single literals
| naninfinityLiteral
| intLiteral //includes int16/int32 and int64 literals
| binaryLiteral
| dateLiteral
| datetimeoffsetLiteral
| durationLiteral
| guidLiteral
| stringLiteral
| timeofdayLiteral
| enumLiteral
| geographyCollection
| geographyLineString
| geographyMultilineString
@ -371,14 +371,22 @@ primitiveLiteral : nullrule
| geometryPolygon
;
naninfinity : NANINFINITY;
nullrule : NULLVALUE;
booleanNonCase : BOOLEAN | TRUE | FALSE;
string : STRING;
nullruleLiteral : NULLVALUE;
booleanNonCaseLiteral : BOOLEAN | TRUE | FALSE;
decimalLiteral : DECIMAL;
naninfinityLiteral : NANINFINITY;
intLiteral : INT;
binaryLiteral : BINARY;
dateLiteral : DATE;
datetimeoffsetLiteral : DATETIMEOFFSET;
durationLiteral : DURATION;
guidLiteral : GUID;
stringLiteral : STRING;
timeofdayLiteral : TIMEOFDAY;
enumLit : vNS=namespace vODI=odataIdentifier vValues=STRING;
enumValues : vlODI+=odataIdentifier ( COMMA vlODI+=odataIdentifier )*;
enumLiteral : vNS=namespace vODI=odataIdentifier vValues=STRING;
enumValues : vlODI+=odataIdentifier ( COMMA vlODI+=odataIdentifier )*;
geographyCollection : GEOGRAPHY fullCollectionLiteral SQUOTE;
fullCollectionLiteral : sridLiteral collectionLiteral;

View File

@ -18,6 +18,10 @@
*/
package org.apache.olingo.server.core.uri.parser;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.ParseCancellationException;
import org.antlr.v4.runtime.tree.ParseTree;
@ -87,18 +91,23 @@ import org.apache.olingo.server.core.uri.antlr.UriParserParser.AltMultContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.AltOrContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.AnyExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.BatchEOFContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.BooleanNonCaseContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.BinaryLiteralContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.BooleanNonCaseLiteralContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.CastExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.CeilingMethodCallExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.ConcatMethodCallExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.ConstSegmentContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.ContainsMethodCallExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.CrossjoinEOFContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.DateLiteralContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.DateMethodCallExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.DatetimeoffsetLiteralContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.DayMethodCallExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.DecimalLiteralContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.DurationLiteralContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.EndsWithMethodCallExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.EntityEOFContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.EnumLitContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.EnumLiteralContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandCountOptionContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandItemContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandItemsContext;
@ -113,9 +122,11 @@ import org.apache.olingo.server.core.uri.antlr.UriParserParser.Fractionalseconds
import org.apache.olingo.server.core.uri.antlr.UriParserParser.GeoDistanceMethodCallExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.GeoIntersectsMethodCallExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.GeoLengthMethodCallExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.GuidLiteralContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.HourMethodCallExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.IndexOfMethodCallExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.InlinecountContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.IntLiteralContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.IsofExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.LengthMethodCallExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.LevelsContext;
@ -128,9 +139,9 @@ import org.apache.olingo.server.core.uri.antlr.UriParserParser.MonthMethodCallEx
import org.apache.olingo.server.core.uri.antlr.UriParserParser.NameValueOptListContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.NameValuePairContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.NamespaceContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.NaninfinityContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.NaninfinityLiteralContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.NowMethodCallExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.NullruleContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.NullruleLiteralContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.OdataIdentifierContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.OrderByContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.OrderByEOFContext;
@ -151,8 +162,10 @@ import org.apache.olingo.server.core.uri.antlr.UriParserParser.SelectSegmentCont
import org.apache.olingo.server.core.uri.antlr.UriParserParser.SkipContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.SkiptokenContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.StartsWithMethodCallExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.StringLiteralContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.SubstringMethodCallExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.TimeMethodCallExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.TimeofdayLiteralContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.ToLowerMethodCallExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.ToUpperMethodCallExprContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.TopContext;
@ -184,10 +197,6 @@ import org.apache.olingo.server.core.uri.queryoption.expression.MethodImpl;
import org.apache.olingo.server.core.uri.queryoption.expression.TypeLiteralImpl;
import org.apache.olingo.server.core.uri.queryoption.expression.UnaryImpl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* UriVisitor
*
@ -954,7 +963,7 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
}
@Override
public Object visitBooleanNonCase(final BooleanNonCaseContext ctx) {
public Object visitBooleanNonCaseLiteral(final BooleanNonCaseLiteralContext ctx) {
String text = ctx.getText().toLowerCase();
if (text.equals("false")) {
@ -1148,7 +1157,7 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
}
@Override
public Object visitEnumLit(final EnumLitContext ctx) {
public Object visitEnumLiteral(final EnumLiteralContext ctx) {
EnumerationImpl enum1 = new EnumerationImpl();
// get type
@ -1711,7 +1720,7 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
}
@Override
public Object visitNaninfinity(final NaninfinityContext ctx) {
public Object visitNaninfinityLiteral(final NaninfinityLiteralContext ctx) {
return new LiteralImpl().setType(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal)).
setText(ctx.getText());
}
@ -1723,7 +1732,7 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
}
@Override
public Object visitNullrule(final NullruleContext ctx) {
public Object visitNullruleLiteral(final NullruleLiteralContext ctx) {
return new LiteralImpl().setText("null");
}
@ -1823,15 +1832,107 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
@Override
public Object visitPrimitiveLiteral(final PrimitiveLiteralContext ctx) {
ParseTree child1 = ctx.children.get(0);
if (child1 instanceof EnumLitContext
|| child1 instanceof BooleanNonCaseContext
|| child1 instanceof NullruleContext
|| child1 instanceof NaninfinityContext) {
if (child1 instanceof EnumLiteralContext
|| child1 instanceof BooleanNonCaseLiteralContext
|| child1 instanceof NullruleLiteralContext
|| child1 instanceof NaninfinityLiteralContext
|| child1 instanceof StringLiteralContext
|| child1 instanceof IntLiteralContext
|| child1 instanceof BinaryLiteralContext
|| child1 instanceof DateLiteralContext
|| child1 instanceof DatetimeoffsetLiteralContext
|| child1 instanceof DurationLiteralContext
|| child1 instanceof GuidLiteralContext
|| child1 instanceof TimeofdayLiteralContext
|| child1 instanceof DecimalLiteralContext
|| child1 instanceof BinaryLiteralContext) {
return child1.accept(this);
}
// TODO Implement geography types and set the proper type
return new LiteralImpl().setText(ctx.getText());
}
@Override
public Object visitBinaryLiteral(BinaryLiteralContext ctx) {
return new LiteralImpl().setText(ctx.getText())
.setType(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Binary));
}
@Override
public Object visitStringLiteral(final StringLiteralContext ctx) {
return new LiteralImpl().setText(ctx.getText())
.setType(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.String));
}
@Override
public Object visitDecimalLiteral(final DecimalLiteralContext ctx) {
EdmType type = null;
if(!ctx.getText().matches(".*[eE].*")) {
type = EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal);
} else {
type = EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Double);
}
return new LiteralImpl().setText(ctx.getText()).setType(type);
}
@Override
public Object visitIntLiteral(final IntLiteralContext ctx) {
try {
final long value = Long.parseLong(ctx.getText());
EdmType type = null;
if(value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
type = EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.SByte);
} else if (value >= 0 && value <= 255) {
type = EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Byte);
} else if(value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
type = EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int16);
} else if(value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE) {
type = EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int32);
} else {
type = EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int64);
}
return new LiteralImpl().setText(ctx.getText()).setType(type);
} catch( NumberFormatException e) {
return new LiteralImpl().setText(ctx.getText())
.setType(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal));
}
}
@Override
public Object visitDateLiteral(final DateLiteralContext ctx) {
return new LiteralImpl().setText(ctx.getText())
.setType(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Date));
}
@Override
public Object visitDatetimeoffsetLiteral(final DatetimeoffsetLiteralContext ctx) {
return new LiteralImpl().setText(ctx.getText())
.setType(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.DateTimeOffset));
}
@Override
public Object visitDurationLiteral(final DurationLiteralContext ctx) {
return new LiteralImpl().setText(ctx.getText())
.setType(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Duration));
}
@Override
public Object visitGuidLiteral(final GuidLiteralContext ctx) {
return new LiteralImpl().setText(ctx.getText())
.setType(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Guid));
}
@Override
public Object visitTimeofdayLiteral(final TimeofdayLiteralContext ctx) {
return new LiteralImpl().setText(ctx.getText())
.setType(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.TimeOfDay));
}
@Override
public Object visitQueryOptions(final QueryOptionsContext ctx) {

View File

@ -25,8 +25,22 @@ import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.commons.api.http.HttpContentType;
import org.apache.olingo.commons.core.Encoder;
import org.apache.olingo.commons.core.edm.EdmProviderImpl;
import org.apache.olingo.commons.core.edm.primitivetype.EdmBinary;
import org.apache.olingo.commons.core.edm.primitivetype.EdmBoolean;
import org.apache.olingo.commons.core.edm.primitivetype.EdmByte;
import org.apache.olingo.commons.core.edm.primitivetype.EdmDate;
import org.apache.olingo.commons.core.edm.primitivetype.EdmDateTimeOffset;
import org.apache.olingo.commons.core.edm.primitivetype.EdmDecimal;
import org.apache.olingo.commons.core.edm.primitivetype.EdmDouble;
import org.apache.olingo.commons.core.edm.primitivetype.EdmDuration;
import org.apache.olingo.commons.core.edm.primitivetype.EdmGuid;
import org.apache.olingo.commons.core.edm.primitivetype.EdmInt16;
import org.apache.olingo.commons.core.edm.primitivetype.EdmInt32;
import org.apache.olingo.commons.core.edm.primitivetype.EdmInt64;
import org.apache.olingo.commons.core.edm.primitivetype.EdmSByte;
import org.apache.olingo.commons.core.edm.primitivetype.EdmString;
import org.apache.olingo.commons.core.edm.primitivetype.EdmTimeOfDay;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.processor.EntityProcessor;
import org.apache.olingo.server.api.uri.UriInfoKind;
import org.apache.olingo.server.api.uri.UriResourceKind;
import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind;
@ -5300,6 +5314,83 @@ public class TestFullResourcePath {
+ "(PropertyInt=1,PropertyString='2')");
}
@Test
public void testFilterLiteralTypes() throws Exception {
testUri.run("ESAllPrim", "$filter='1' eq 42")
.goFilter().isBinary(BinaryOperatorKind.EQ)
.left().isLiteral("'1'").isLiteralType(EdmString.getInstance())
.root()
.right().isLiteral("42").isLiteralType(EdmSByte.getInstance());
testUri.run("ESAllPrim", "$filter=127 eq 128")
.goFilter().isBinary(BinaryOperatorKind.EQ)
.left().isLiteral("127").isLiteralType(EdmSByte.getInstance())
.root()
.right().isLiteral("128").isLiteralType(EdmByte.getInstance());
testUri.run("ESAllPrim", "$filter=null eq 42.1")
.goFilter().isBinary(BinaryOperatorKind.EQ)
.left().isLiteral("null").isNullLiteralType()
.root()
.right().isLiteral("42.1").isLiteralType(EdmDecimal.getInstance());
testUri.run("ESAllPrim", "$filter=15.6E300 eq 3.4E37")
.goFilter().isBinary(BinaryOperatorKind.EQ)
.left().isLiteral("15.6E300")
.isLiteralType(EdmDouble.getInstance())
.root()
.right().isLiteral("3.4E37").isLiteralType(EdmDouble.getInstance());
testUri.run("ESAllPrim", "$filter=15.55555555555555555555555555555555555555555555 eq 3.1")
.goFilter().isBinary(BinaryOperatorKind.EQ)
.left().isLiteral("15.55555555555555555555555555555555555555555555")
.isLiteralType(EdmDecimal.getInstance())
.root()
.right().isLiteral("3.1").isLiteralType(EdmDecimal.getInstance());
testUri.run("ESAllPrim", "$filter=duration'PT1H2S' eq 2012-12-03")
.goFilter().isBinary(BinaryOperatorKind.EQ)
.left().isLiteral("duration'PT1H2S'").isLiteralType(EdmDuration.getInstance())
.root()
.right().isLiteral("2012-12-03").isLiteralType(EdmDate.getInstance());
testUri.run("ESAllPrim", "$filter=true eq 2012-12-03T07:16:23Z")
.goFilter().isBinary(BinaryOperatorKind.EQ)
.left().isLiteral("true").isLiteralType(EdmBoolean.getInstance())
.root()
.right().isLiteral("2012-12-03T07:16:23Z").isLiteralType(EdmDateTimeOffset.getInstance());
testUri.run("ESAllPrim", "$filter=07:59:59.999 eq 01234567-89ab-cdef-0123-456789abcdef")
.goFilter().isBinary(BinaryOperatorKind.EQ)
.left().isLiteral("07:59:59.999").isLiteralType(EdmTimeOfDay.getInstance())
.root()
.right().isLiteral("01234567-89ab-cdef-0123-456789abcdef").isLiteralType(EdmGuid.getInstance());
testUri.run("ESAllPrim", "$filter=binary'0FAB7B' eq true")
.goFilter().isBinary(BinaryOperatorKind.EQ)
.left().isLiteral("binary'0FAB7B'").isLiteralType(EdmBinary.getInstance())
.root()
.right().isLiteral("true").isLiteralType(EdmBoolean.getInstance());
testUri.run("ESAllPrim", "$filter=" + Short.MIN_VALUE + " eq " + Short.MAX_VALUE)
.goFilter().isBinary(BinaryOperatorKind.EQ)
.left().isLiteral("" + Short.MIN_VALUE).isLiteralType(EdmInt16.getInstance())
.root()
.right().isLiteral("" + Short.MAX_VALUE).isLiteralType(EdmInt16.getInstance());
testUri.run("ESAllPrim", "$filter=" + Integer.MIN_VALUE + " eq " + Integer.MAX_VALUE)
.goFilter().isBinary(BinaryOperatorKind.EQ)
.left().isLiteral("" + Integer.MIN_VALUE).isLiteralType(EdmInt32.getInstance())
.root()
.right().isLiteral("" + Integer.MAX_VALUE).isLiteralType(EdmInt32.getInstance());
testUri.run("ESAllPrim", "$filter=" + Long.MIN_VALUE + " eq " + Long.MAX_VALUE)
.goFilter().isBinary(BinaryOperatorKind.EQ)
.left().isLiteral("" + Long.MIN_VALUE).isLiteralType(EdmInt64.getInstance())
.root()
.right().isLiteral("" + Long.MAX_VALUE).isLiteralType(EdmInt64.getInstance());
}
public static String encode(final String decoded) throws UnsupportedEncodingException {
return Encoder.encode(decoded);
}

View File

@ -19,6 +19,8 @@
package org.apache.olingo.server.core.uri.testutil;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import java.util.List;
@ -362,7 +364,30 @@ public class FilterValidator implements TestValidator {
return this;
}
public FilterValidator isLiteralType(EdmType edmType) {
if(!(curExpression instanceof LiteralImpl)) {
fail("Current expression is nit a literal");
}
final EdmType type = ((LiteralImpl) curExpression).getType();
assertNotNull(type);
assertEquals(edmType.getClass(), type.getClass());
return this;
}
public FilterValidator isNullLiteralType() {
if(!(curExpression instanceof LiteralImpl)) {
fail("Current expression is nit a literal");
}
final EdmType type = ((LiteralImpl) curExpression).getType();
assertNull(type);
return this;
}
public FilterValidator isMethod(final MethodKind methodKind, final int parameterCount) {
if (!(curExpression instanceof MethodImpl)) {
fail("Current expression is not a methodCall");