[OLINGO-834] small URI parser improvements Signed-off-by: Christian Amend <[email protected]>
Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/8537f3a5 Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/8537f3a5 Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/8537f3a5 Branch: refs/heads/OLINGO-832_StreamSerializerPoC Commit: 8537f3a5127519555bf5e5fb8b1fb7b6e2ad3c9c Parents: 27e17ab Author: Klaus Straubinger <[email protected]> Authored: Wed Jan 20 15:22:42 2016 +0100 Committer: Christian Amend <[email protected]> Committed: Wed Jan 20 15:30:39 2016 +0100 ---------------------------------------------------------------------- .../core/uri/parser/ExpressionParser.java | 13 +- .../olingo/server/core/uri/parser/Parser.java | 32 ++--- .../server/core/uri/parser/ParserHelper.java | 14 +- .../server/core/uri/parser/UriTokenizer.java | 124 +++++++++++++++--- .../core/uri/parser/search/SearchTokenizer.java | 49 +++---- .../core/uri/parser/ExpressionParserTest.java | 129 ++++++------------- .../core/uri/parser/UriTokenizerTest.java | 15 +++ .../core/uri/parser/TestFullResourcePath.java | 5 +- .../core/uri/parser/TestUriParserImpl.java | 46 +++---- .../core/uri/validator/UriValidatorTest.java | 4 +- 10 files changed, 230 insertions(+), 201 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8537f3a5/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java index 8b28f6a..1cf279e 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java @@ -343,7 +343,7 @@ public class ExpressionParser { } if (tokenizer.next(TokenKind.jsonArrayOrObject)) { - // TODO: Can the type be determined? + // There is no obvious way how the type could be determined. return new LiteralImpl(tokenizer.getText(), null); } @@ -417,9 +417,9 @@ public class ExpressionParser { typeKind = EdmPrimitiveTypeKind.Int64; } } catch (final NumberFormatException e) { - // This should never happen because the tokenizer has figured out that the literal is an integer. - throw new UriParserSyntaxException("'" + intValueAsString + "' is not an integer.", e, - UriParserSyntaxException.MessageKeys.SYNTAX); + // The number cannot be formatted wrongly because the tokenizer already checked the format + // but it is too large for Long and therefore too large for Edm.Int64. + typeKind = EdmPrimitiveTypeKind.Decimal; } return typeKind; } @@ -686,7 +686,7 @@ public class ExpressionParser { } } else { // Must be a property. - parseMemberExpression(TokenKind.ODataIdentifier, uriInfo, null, true); // TODO: Find last resource. + parseMemberExpression(TokenKind.ODataIdentifier, uriInfo, null, true); } } @@ -996,9 +996,10 @@ public class ExpressionParser { ParserHelper.requireNext(tokenizer, TokenKind.COLON); lambdaVariables.addFirst(new UriResourceLambdaVarImpl(lambbdaVariable, lastResource == null ? referringType : lastResource.getType())); + // The ABNF suggests that the "lambaPredicateExpr" must contain at least one lambdaVariable, + // so arguably this could be checked in expression parsing or later in validation. final Expression lambdaPredicateExpr = parseExpression(); lambdaVariables.removeFirst(); - // TODO: The ABNF suggests that the "lambaPredicateExpr" must contain at least one lambdaVariable. ParserHelper.requireNext(tokenizer, TokenKind.CLOSE); if (lastTokenKind == TokenKind.ALL) { return new UriResourceLambdaAllImpl(lambbdaVariable, lambdaPredicateExpr); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8537f3a5/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java index 34944c8..586cb10 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java @@ -18,13 +18,10 @@ */ package org.apache.olingo.server.core.uri.parser; -import java.util.ArrayDeque; import java.util.Collections; -import java.util.Deque; import java.util.List; import org.apache.olingo.commons.api.edm.Edm; -import org.apache.olingo.commons.api.edm.EdmEntitySet; import org.apache.olingo.commons.api.edm.EdmStructuredType; import org.apache.olingo.commons.api.edm.EdmType; import org.apache.olingo.commons.api.ex.ODataRuntimeException; @@ -77,7 +74,7 @@ public class Parser { throws UriParserException, UriValidationException { UriInfoImpl contextUriInfo = new UriInfoImpl(); - Deque<EdmType> contextTypes = new ArrayDeque<EdmType>(); + EdmType contextType = null; boolean contextIsCollection = false; List<String> pathSegmentsDecoded = UriDecoder.splitAndDecodePath(path); @@ -107,10 +104,6 @@ public class Parser { } else if (firstSegment.equals("$all")) { ensureLastSegment(firstSegment, 1, numberOfSegments); contextUriInfo.setKind(UriInfoKind.all); - // This loads nearly the whole schema, but sooner or later '$all' needs all entity sets anyway. - for (final EdmEntitySet entitySet : edm.getEntityContainer().getEntitySets()) { - contextTypes.push(entitySet.getEntityType()); - } contextIsCollection = true; } else if (firstSegment.equals("$entity")) { @@ -118,11 +111,9 @@ public class Parser { final String typeCastSegment = pathSegmentsDecoded.get(1); ensureLastSegment(typeCastSegment, 2, numberOfSegments); contextUriInfo = new ResourcePathParser(edm).parseDollarEntityTypeCast(typeCastSegment); - contextTypes.push(contextUriInfo.getEntityTypeCast()); + contextType = contextUriInfo.getEntityTypeCast(); } else { contextUriInfo.setKind(UriInfoKind.entityId); - // The type of the entity is not known until the $id query option has been parsed. - // TODO: Set the type (needed for the evaluation of system query options). } contextIsCollection = false; @@ -166,10 +157,7 @@ public class Parser { if (lastSegment instanceof UriResourcePartTyped) { final UriResourcePartTyped typed = (UriResourcePartTyped) lastSegment; - final EdmType type = ParserHelper.getTypeInformation(typed); - if (type != null) { // could be null for, e.g., actions without return type - contextTypes.push(type); - } + contextType = ParserHelper.getTypeInformation(typed); contextIsCollection = typed.isCollection(); } } @@ -186,7 +174,7 @@ public class Parser { UriTokenizer filterTokenizer = new UriTokenizer(optionValue); // The referring type could be a primitive type or a structured type. systemOption = new FilterParser(edm, odata).parse(filterTokenizer, - contextTypes.peek(), + contextType, contextUriInfo.getEntitySetNames()); checkOptionEOF(filterTokenizer, optionName, optionValue); @@ -205,12 +193,12 @@ public class Parser { systemOption = formatOption; } else if (optionName.equals(SystemQueryOptionKind.EXPAND.toString())) { - if (contextTypes.peek() instanceof EdmStructuredType + if (contextType instanceof EdmStructuredType || !contextUriInfo.getEntitySetNames().isEmpty() - || contextUriInfo.getKind() == UriInfoKind.entityId) { // TODO: Remove once the type has been set above. + || contextUriInfo.getKind() == UriInfoKind.all) { UriTokenizer expandTokenizer = new UriTokenizer(optionValue); systemOption = new ExpandParser(edm, odata).parse(expandTokenizer, - contextTypes.peek() instanceof EdmStructuredType ? (EdmStructuredType) contextTypes.peek() : null); + contextType instanceof EdmStructuredType ? (EdmStructuredType) contextType : null); checkOptionEOF(expandTokenizer, optionName, optionValue); } else { throw new UriValidationException("Expand is only allowed on structured types!", @@ -235,7 +223,7 @@ public class Parser { } else if (optionName.equals(SystemQueryOptionKind.ORDERBY.toString())) { UriTokenizer orderByTokenizer = new UriTokenizer(optionValue); systemOption = new OrderByParser(edm, odata).parse(orderByTokenizer, - contextTypes.peek() instanceof EdmStructuredType ? (EdmStructuredType) contextTypes.peek() : null, + contextType instanceof EdmStructuredType ? (EdmStructuredType) contextType : null, contextUriInfo.getEntitySetNames()); checkOptionEOF(orderByTokenizer, optionName, optionValue); @@ -245,7 +233,7 @@ public class Parser { } else if (optionName.equals(SystemQueryOptionKind.SELECT.toString())) { UriTokenizer selectTokenizer = new UriTokenizer(optionValue); systemOption = new SelectParser(edm).parse(selectTokenizer, - contextTypes.peek() instanceof EdmStructuredType ? (EdmStructuredType) contextTypes.peek() : null, + contextType instanceof EdmStructuredType ? (EdmStructuredType) contextType : null, contextIsCollection); checkOptionEOF(selectTokenizer, optionName, optionValue); @@ -297,7 +285,7 @@ public class Parser { } else if (optionName.startsWith(AT)) { if (contextUriInfo.getAlias(optionName) == null) { - // TODO: Create a proper alias-value parser that can parse also common expressions. + // TODO: Aliases can only be parsed in the context of their usage. Expression expression = null; UriTokenizer aliasTokenizer = new UriTokenizer(optionValue); if (aliasTokenizer.next(TokenKind.jsonArrayOrObject)) { http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8537f3a5/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ParserHelper.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ParserHelper.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ParserHelper.java index 0ac888f..b0c2972 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ParserHelper.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ParserHelper.java @@ -58,7 +58,7 @@ public class ParserHelper { 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 decimal or single or double instead. + // Very large integer values are of type Edm.Decimal but this is handled elsewhere. temp.put(TokenKind.IntegerValue, EdmPrimitiveTypeKind.Int64); temp.put(TokenKind.GuidValue, EdmPrimitiveTypeKind.Guid); temp.put(TokenKind.DateValue, EdmPrimitiveTypeKind.Date); @@ -81,9 +81,8 @@ public class ParserHelper { temp.put(TokenKind.GeometryMultiLineString, EdmPrimitiveTypeKind.GeometryMultiLineString); temp.put(TokenKind.GeographyMultiPolygon, EdmPrimitiveTypeKind.GeographyMultiPolygon); temp.put(TokenKind.GeometryMultiPolygon, EdmPrimitiveTypeKind.GeometryMultiPolygon); - // TODO: Geo collections -// temp.put(TokenKind.GeographyCollection, EdmPrimitiveTypeKind.GeographyCollection); -// temp.put(TokenKind.GeometryCollection, EdmPrimitiveTypeKind.GeometryCollection); + temp.put(TokenKind.GeographyCollection, EdmPrimitiveTypeKind.GeographyCollection); + temp.put(TokenKind.GeometryCollection, EdmPrimitiveTypeKind.GeometryCollection); tokenToPrimitiveType = Collections.unmodifiableMap(temp); } @@ -141,10 +140,9 @@ public class ParserHelper { TokenKind.GeographyMultiLineString, TokenKind.GeometryMultiLineString, TokenKind.GeographyMultiPolygon, - TokenKind.GeometryMultiPolygon); - // TODO: Geo collections -// TokenKind.GeographyCollection, -// TokenKind.GeometryCollection); + TokenKind.GeometryMultiPolygon, + TokenKind.GeographyCollection, + TokenKind.GeometryCollection); } protected static List<UriParameter> parseFunctionParameters(UriTokenizer tokenizer, http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8537f3a5/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java index 5a48cc1..0504473 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java @@ -96,6 +96,8 @@ public class UriTokenizer { GeometryMultiLineString, GeographyMultiPolygon, GeometryMultiPolygon, + GeographyCollection, + GeometryCollection, jsonArrayOrObject, @@ -372,6 +374,12 @@ public class UriTokenizer { case GeometryMultiPolygon: found = nextGeoMultiPolygon(false); break; + case GeographyCollection: + found = nextGeoCollection(true); + break; + case GeometryCollection: + found = nextGeoCollection(false); + break; // Complex or Collection Value case jsonArrayOrObject: @@ -1057,10 +1065,13 @@ public class UriTokenizer { } } + private boolean nextPoint() { + return nextConstantIgnoreCase("Point") && nextPointData(); + } + private boolean nextGeoPoint(final boolean isGeography) { return nextGeoPrefix(isGeography) && nextCharacter('\'') - && nextSrid() && nextCharacter(';') - && nextConstantIgnoreCase("Point") && nextPointData() + && nextSrid() && nextCharacter(';') && nextPoint() && nextCharacter('\''); } @@ -1074,7 +1085,10 @@ public class UriTokenizer { final int lastGoodIndex = index; if (nextCharacter('(') && nextPosition()) { int count = 1; + final String firstPosition = isRing ? parseString.substring(lastGoodIndex + 1, index) : null; + int positionStart = -1; while (nextCharacter(',')) { + positionStart = index; if (nextPosition()) { count++; } else { @@ -1082,11 +1096,17 @@ public class UriTokenizer { return false; } } - // TODO: Check that the first and last ring positions are identical. if (count < (isRing ? 4 : 2)) { index = lastGoodIndex; return false; } + if (isRing) { + final String lastPosition = parseString.substring(positionStart, index); + if (!lastPosition.equals(firstPosition)) { + index = lastGoodIndex; + return false; + } + } if (!nextCharacter(')')) { index = lastGoodIndex; return false; @@ -1098,10 +1118,13 @@ public class UriTokenizer { } } + private boolean nextLineString() { + return nextConstantIgnoreCase("LineString") && nextLineStringData(false); + } + private boolean nextGeoLineString(final boolean isGeography) { return nextGeoPrefix(isGeography) && nextCharacter('\'') - && nextSrid() && nextCharacter(';') - && nextConstantIgnoreCase("LineString") && nextLineStringData(false) + && nextSrid() && nextCharacter(';') && nextLineString() && nextCharacter('\''); } @@ -1130,66 +1153,125 @@ public class UriTokenizer { } } + private boolean nextPolygon() { + return nextConstantIgnoreCase("Polygon") && nextPolygonData(); + } + private boolean nextGeoPolygon(final boolean isGeography) { return nextGeoPrefix(isGeography) && nextCharacter('\'') - && nextSrid() && nextCharacter(';') - && nextConstantIgnoreCase("Polygon") && nextPolygonData() + && nextSrid() && nextCharacter(';') && nextPolygon() && nextCharacter('\''); } private boolean nextMultiPoint() { - if (nextPointData()) { + if (nextConstantIgnoreCase("MultiPoint") && nextCharacter('(') && nextPointData()) { while (nextCharacter(',')) { if (!nextPointData()) { return false; } } } - return true; + return nextCharacter(')'); } private boolean nextGeoMultiPoint(final boolean isGeography) { return nextGeoPrefix(isGeography) && nextCharacter('\'') - && nextSrid() && nextCharacter(';') - && nextConstantIgnoreCase("MultiPoint") && nextCharacter('(') && nextMultiPoint() && nextCharacter(')') + && nextSrid() && nextCharacter(';') && nextMultiPoint() && nextCharacter('\''); } private boolean nextMultiLineString() { - if (nextLineStringData(false)) { + if (nextConstantIgnoreCase("MultiLineString") && nextCharacter('(') && nextLineStringData(false)) { while (nextCharacter(',')) { if (!nextLineStringData(false)) { return false; } } } - return true; + return nextCharacter(')'); } private boolean nextGeoMultiLineString(final boolean isGeography) { return nextGeoPrefix(isGeography) && nextCharacter('\'') - && nextSrid() && nextCharacter(';') - && nextConstantIgnoreCase("MultiLineString") - && nextCharacter('(') && nextMultiLineString() && nextCharacter(')') + && nextSrid() && nextCharacter(';') && nextMultiLineString() && nextCharacter('\''); } private boolean nextMultiPolygon() { - if (nextPolygonData()) { + if (nextConstantIgnoreCase("MultiPolygon") && nextCharacter('(') && nextPolygonData()) { while (nextCharacter(',')) { if (!nextPolygonData()) { return false; } } } - return true; + return nextCharacter(')'); } private boolean nextGeoMultiPolygon(final boolean isGeography) { return nextGeoPrefix(isGeography) && nextCharacter('\'') - && nextSrid() && nextCharacter(';') - && nextConstantIgnoreCase("MultiPolygon") - && nextCharacter('(') && nextMultiPolygon() && nextCharacter(')') + && nextSrid() && nextCharacter(';') && nextMultiPolygon() + && nextCharacter('\''); + } + + /** + * Moves past geo data if found; otherwise leaves the index unchanged. + * @return whether geo data has been found at the current index + */ + private boolean nextGeo() { + final int lastGoodIndex = index; + if (nextPoint()) { + return true; + } else { + index = lastGoodIndex; + } + if (nextLineString()) { + return true; + } else { + index = lastGoodIndex; + } + if (nextPolygon()) { + return true; + } else { + index = lastGoodIndex; + } + if (nextMultiPoint()) { + return true; + } else { + index = lastGoodIndex; + } + if (nextMultiLineString()) { + return true; + } else { + index = lastGoodIndex; + } + if (nextMultiPolygon()) { + return true; + } else { + index = lastGoodIndex; + } + if (nextCollection()) { + return true; + } else { + index = lastGoodIndex; + return false; + } + } + + private boolean nextCollection() { + if (nextConstantIgnoreCase("Collection") && nextCharacter('(') && nextGeo()) { + while (nextCharacter(',')) { + if (!nextGeo()) { + return false; + } + } + } + return nextCharacter(')'); + } + + private boolean nextGeoCollection(final boolean isGeography) { + return nextGeoPrefix(isGeography) && nextCharacter('\'') + && nextSrid() && nextCharacter(';') && nextCollection() && nextCharacter('\''); } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8537f3a5/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizer.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizer.java index 4b88869..5d7c948 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizer.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizer.java @@ -22,28 +22,20 @@ import java.util.ArrayList; import java.util.List; /** - * <code> - * searchExpr = ( OPEN BWS searchExpr BWS CLOSE - * / searchTerm - * ) [ searchOrExpr - * / searchAndExpr - * ] - * - * searchOrExpr = RWS 'OR' RWS searchExpr + * <pre> + * searchExpr = ( OPEN BWS searchExpr BWS CLOSE / searchTerm ) + * [ searchOrExpr / searchAndExpr ] + * searchOrExpr = RWS 'OR' RWS searchExpr * searchAndExpr = RWS [ 'AND' RWS ] searchExpr + * searchTerm = [ 'NOT' RWS ] ( searchPhrase / searchWord ) + * searchPhrase = quotation-mark 1*qchar-no-AMP-DQUOTE quotation-mark + * searchWord = 1*ALPHA ; Actually: any character from the Unicode categories L or Nl, + * but not the words AND, OR, and NOT + * </pre> * - * searchTerm = [ 'NOT' RWS ] ( searchPhrase / searchWord ) - * searchPhrase = quotation-mark 1*qchar-no-AMP-DQUOTE quotation-mark - * searchWord = 1*ALPHA ; Actually: any character from the Unicode categories L or Nl, - * ; but not the words AND, OR, and NOT - * </code> - * - * <b>ATTENTION:</b> For a <code>searchPhrase</code> the percent encoding is not supported by the - * <code>SearchTokenizer</code>.<br/> - * This was a decision based on that the <code>org.apache.olingo.server.core.uri.parser.Parser</code> - * already handles in his <code>parseUri</code> method each query as <code>percent decoded</code> strings (see - * line <i>177ff</i> (<code>for (RawUri.QueryOption option : uri.queryOptionListDecoded)</code>). - * + * <b>ATTENTION:</b> This class does not support a percent-encoded <code>searchPhrase</code> because the URI parser's + * {@link org.apache.olingo.server.core.uri.parser.Parser#parseUri(String, String, String) parseUri} method + * <em>percent decodes</em> each query before calling parsers of query options. */ public class SearchTokenizer { @@ -75,17 +67,18 @@ public class SearchTokenizer { protected abstract State nextChar(char c) throws SearchTokenizerException; + /** @param c allowed character */ public State allowed(final char c) { return this; } public State forbidden(final char c) throws SearchTokenizerException { - throw new SearchTokenizerException("Forbidden character in state " + getToken() + "->" + c, + throw new SearchTokenizerException("Forbidden character in state " + token + "->" + c, SearchTokenizerException.MessageKeys.FORBIDDEN_CHARACTER, "" + c); } public State invalid() throws SearchTokenizerException { - throw new SearchTokenizerException("Token " + getToken() + " is in invalid state.", + throw new SearchTokenizerException("Token " + token + " is in invalid state.", SearchTokenizerException.MessageKeys.INVALID_TOKEN_STATE); } @@ -243,7 +236,7 @@ public class SearchTokenizer { @Override public String toString() { - return getToken() + "=>{" + getLiteral() + "}"; + return token + "=>{" + getLiteral() + "}"; } } @@ -597,16 +590,14 @@ public class SearchTokenizer { } /** - * Take the search query and split into according SearchQueryToken. - * Before split into tokens the given search query is 'trimmed'. + * Takes the search query and splits it into a list of corresponding {@link SearchQueryToken}s. + * Before splitting it into tokens, leading and trailing whitespace in the given search query string is removed. * * @param searchQuery search query to be tokenized * @return list of tokens - * @throws SearchTokenizerException if something in query is not valid - * (based on OData search query ABNF) + * @throws SearchTokenizerException if something in query is not valid (based on OData search query ABNF) */ - public List<SearchQueryToken> tokenize(final String searchQuery) - throws SearchTokenizerException { + public List<SearchQueryToken> tokenize(final String searchQuery) throws SearchTokenizerException { char[] chars = searchQuery.trim().toCharArray(); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8537f3a5/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java index c764999..a5fda50 100644 --- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; +import java.util.Arrays; import java.util.Locale; import org.apache.olingo.commons.api.edm.Edm; @@ -158,14 +159,9 @@ public class ExpressionParserTest { @Test public void noParameterMethods() throws Exception { - Expression expression = parseMethod(TokenKind.NowMethod); - assertEquals("{now []}", expression.toString()); - - expression = parseMethod(TokenKind.MaxdatetimeMethod); - assertEquals("{maxdatetime []}", expression.toString()); - - expression = parseMethod(TokenKind.MindatetimeMethod); - assertEquals("{mindatetime []}", expression.toString()); + parseMethod(TokenKind.NowMethod); + parseMethod(TokenKind.MaxdatetimeMethod); + parseMethod(TokenKind.MindatetimeMethod); wrongExpression("now(1)"); } @@ -176,46 +172,21 @@ public class ExpressionParserTest { final String dateValue = "1234-12-25"; final String dateTimeOffsetValue = "1234-12-25T11:12:13.456Z"; - Expression expression = parseMethod(TokenKind.LengthMethod, stringValue); - assertEquals("{length [" + stringValue + "]}", expression.toString()); - - expression = parseMethod(TokenKind.TolowerMethod, stringValue); - assertEquals("{tolower [" + stringValue + "]}", expression.toString()); - - expression = parseMethod(TokenKind.ToupperMethod, stringValue); - assertEquals("{toupper [" + stringValue + "]}", expression.toString()); - - expression = parseMethod(TokenKind.TrimMethod, stringValue); - assertEquals("{trim [" + stringValue + "]}", expression.toString()); - - expression = parseMethod(TokenKind.YearMethod, dateValue); - assertEquals("{year [" + dateValue + "]}", expression.toString()); - - expression = parseMethod(TokenKind.MonthMethod, dateValue); - assertEquals("{month [" + dateValue + "]}", expression.toString()); - - expression = parseMethod(TokenKind.DayMethod, dateValue); - assertEquals("{day [" + dateValue + "]}", expression.toString()); - - expression = parseMethod(TokenKind.HourMethod, dateTimeOffsetValue); - assertEquals("{hour [" + dateTimeOffsetValue + "]}", expression.toString()); - - expression = parseMethod(TokenKind.MinuteMethod, dateTimeOffsetValue); - assertEquals("{minute [" + dateTimeOffsetValue + "]}", expression.toString()); - - expression = parseMethod(TokenKind.SecondMethod, dateTimeOffsetValue); - assertEquals("{second [" + dateTimeOffsetValue + "]}", expression.toString()); - - expression = parseMethod(TokenKind.DateMethod, dateTimeOffsetValue); - assertEquals("{date [" + dateTimeOffsetValue + "]}", expression.toString()); - - expression = parseMethod(TokenKind.TotalsecondsMethod, "duration'PT1H'"); - assertEquals("{totalseconds [duration'PT1H']}", expression.toString()); - - expression = parseMethod(TokenKind.RoundMethod, "3.141592653589793"); - assertEquals("{round [3.141592653589793]}", expression.toString()); - - assertEquals("{hour [null]}", parseMethod(TokenKind.HourMethod, new String[] { null }).toString()); + parseMethod(TokenKind.LengthMethod, stringValue); + parseMethod(TokenKind.TolowerMethod, stringValue); + parseMethod(TokenKind.ToupperMethod, stringValue); + parseMethod(TokenKind.TrimMethod, stringValue); + parseMethod(TokenKind.YearMethod, dateValue); + parseMethod(TokenKind.MonthMethod, dateValue); + parseMethod(TokenKind.DayMethod, dateValue); + parseMethod(TokenKind.HourMethod, dateTimeOffsetValue); + parseMethod(TokenKind.MinuteMethod, dateTimeOffsetValue); + parseMethod(TokenKind.SecondMethod, dateTimeOffsetValue); + parseMethod(TokenKind.DateMethod, dateTimeOffsetValue); + parseMethod(TokenKind.TotalsecondsMethod, "duration'PT1H'"); + parseMethod(TokenKind.RoundMethod, "3.141592653589793"); + parseMethod(TokenKind.GeoLengthMethod, "geometry'SRID=0;LineString(0 0,4 0,4 4,0 4,0 0)'"); + parseMethod(TokenKind.HourMethod, new String[] { null }); wrongExpression("trim()"); wrongExpression("trim(1)"); @@ -224,35 +195,17 @@ public class ExpressionParserTest { @Test public void twoParameterMethods() throws Exception { - Expression expression = parseMethod(TokenKind.ContainsMethod, "'a'", "'b'"); - assertEquals("{contains ['a', 'b']}", expression.toString()); - - expression = parseMethod(TokenKind.EndswithMethod, "'a'", "'b'"); - assertEquals("{endswith ['a', 'b']}", expression.toString()); - - expression = parseMethod(TokenKind.StartswithMethod, "'a'", "'b'"); - assertEquals("{startswith ['a', 'b']}", expression.toString()); - - expression = parseMethod(TokenKind.IndexofMethod, "'a'", "'b'"); - assertEquals("{indexof ['a', 'b']}", expression.toString()); - - expression = parseMethod(TokenKind.ConcatMethod, "'a'", "'b'"); - assertEquals("{concat ['a', 'b']}", expression.toString()); - - expression = parseMethod(TokenKind.GeoDistanceMethod, - "geography'SRID=0;Point(1.2 3.4)'", "geography'SRID=0;Point(5.6 7.8)'"); - assertEquals("{geo.distance [geography'SRID=0;Point(1.2 3.4)', geography'SRID=0;Point(5.6 7.8)']}", - expression.toString()); - - expression = parseMethod(TokenKind.GeoIntersectsMethod, + parseMethod(TokenKind.ContainsMethod, "'a'", "'b'"); + parseMethod(TokenKind.EndswithMethod, "'a'", "'b'"); + parseMethod(TokenKind.StartswithMethod, "'a'", "'b'"); + parseMethod(TokenKind.IndexofMethod, "'a'", "'b'"); + parseMethod(TokenKind.ConcatMethod, "'a'", "'b'"); + parseMethod(TokenKind.GeoDistanceMethod, "geography'SRID=0;Point(1.2 3.4)'", "geography'SRID=0;Point(5.6 7.8)'"); + parseMethod(TokenKind.GeoIntersectsMethod, "geometry'SRID=0;Point(1.2 3.4)'", "geometry'SRID=0;Polygon((0 0,4 0,4 4,0 4,0 0),(1 1,2 1,2 2,1 2,1 1))'"); - assertEquals("{geo.intersects [geometry'SRID=0;Point(1.2 3.4)', " - + "geometry'SRID=0;Polygon((0 0,4 0,4 4,0 4,0 0),(1 1,2 1,2 2,1 2,1 1))']}", - expression.toString()); - - assertEquals("{startswith [null, 'b']}", parseMethod(TokenKind.StartswithMethod, null, "'b'").toString()); - assertEquals("{indexof ['a', null]}", parseMethod(TokenKind.IndexofMethod, "'a'", null).toString()); + parseMethod(TokenKind.StartswithMethod, null, "'b'"); + parseMethod(TokenKind.IndexofMethod, "'a'", null); wrongExpression("concat('a')"); wrongExpression("endswith('a',1)"); @@ -260,16 +213,14 @@ public class ExpressionParserTest { @Test public void variableParameterNumberMethods() throws Exception { - Expression expression = parseMethod(TokenKind.SubstringMethod, "'abc'", "1", "2"); - assertEquals("{substring ['abc', 1, 2]}", expression.toString()); - expression = parseMethod(TokenKind.SubstringMethod, "'abc'", "1"); - assertEquals("{substring ['abc', 1]}", expression.toString()); + parseMethod(TokenKind.SubstringMethod, "'abc'", "1", "2"); + parseMethod(TokenKind.SubstringMethod, "'abc'", "1"); - assertEquals("{cast [Edm.SByte]}", parseMethod(TokenKind.CastMethod, "Edm.SByte").toString()); - assertEquals("{cast [42, Edm.SByte]}", parseMethod(TokenKind.CastMethod, "42", "Edm.SByte").toString()); + parseMethod(TokenKind.CastMethod, "Edm.SByte"); + parseMethod(TokenKind.CastMethod, "42", "Edm.SByte"); - assertEquals("{isof [Edm.SByte]}", parseMethod(TokenKind.IsofMethod, "Edm.SByte").toString()); - assertEquals("{isof [42, Edm.SByte]}", parseMethod(TokenKind.IsofMethod, "42", "Edm.SByte").toString()); + parseMethod(TokenKind.IsofMethod, "Edm.SByte"); + parseMethod(TokenKind.IsofMethod, "42", "Edm.SByte"); wrongExpression("substring('abc')"); wrongExpression("substring('abc',1,2,3)"); @@ -278,10 +229,10 @@ public class ExpressionParserTest { wrongExpression("isof(Edm.Int16,2)"); } - private Expression parseMethod(TokenKind kind, String... parameters) - throws UriParserException, UriValidationException { - String expressionString = kind.name().substring(0, kind.name().indexOf("Method")) - .toLowerCase(Locale.ROOT).replace("geo", "geo.") + '('; + private void parseMethod(TokenKind kind, String... parameters) throws UriParserException, UriValidationException { + final String methodName = kind.name().substring(0, kind.name().indexOf("Method")).toLowerCase(Locale.ROOT) + .replace("geo", "geo."); + String expressionString = methodName + '('; boolean first = true; for (final String parameter : parameters) { if (first) { @@ -293,7 +244,9 @@ public class ExpressionParserTest { } expressionString += ')'; - return parseExpression(expressionString); + final Expression expression = parseExpression(expressionString); + assertEquals('{' + methodName + ' ' + Arrays.toString(parameters) + '}', + expression.toString()); } private Expression parseExpression(final String expressionString) http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8537f3a5/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/UriTokenizerTest.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/UriTokenizerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/UriTokenizerTest.java index 78c6204..3ca49c8 100644 --- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/UriTokenizerTest.java +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/UriTokenizerTest.java @@ -624,6 +624,21 @@ public class UriTokenizerTest { 'x'); } + @Test + public void geoCollection() { + assertTrue(new UriTokenizer("geography'SRID=4326;Collection(Point(1 2))'").next(TokenKind.GeographyCollection)); + assertTrue(new UriTokenizer("geography'SRID=4326;Collection(Collection(Point(1 2),Point(3 4)))'") + .next(TokenKind.GeographyCollection)); + assertTrue(new UriTokenizer("geography'SRID=4326;Collection(LineString(1 2,3 4))'") + .next(TokenKind.GeographyCollection)); + assertTrue(new UriTokenizer("geography'SRID=4326;Collection(Polygon((0 0,1 0,0 1,0 0)))'") + .next(TokenKind.GeographyCollection)); + assertTrue(new UriTokenizer("geography'SRID=4326;Collection(MultiPoint(),MultiLineString(),MultiPolygon())'") + .next(TokenKind.GeographyCollection)); + + wrongToken(TokenKind.GeometryCollection, "geometry'SRID=0;Collection(Point(1 2),Point(3 4))'", 'x'); + } + private void wrongToken(final TokenKind kind, final String value, final char disturbCharacter) { assertFalse(new UriTokenizer(disturbCharacter + value).next(kind)); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8537f3a5/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java ---------------------------------------------------------------------- diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java index 828be80..5a03676 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java @@ -5698,12 +5698,13 @@ public class TestFullResourcePath { .root() .right().isLiteral("3.4E37").isLiteralType(oData.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Double)); - testFilter.runOnETAllPrim("15.55555555555555555555555555555555555555555555 eq 3.1") + testFilter.runOnETAllPrim("15.55555555555555555555555555555555555555555555 eq -12345678901234567890") .isBinary(BinaryOperatorKind.EQ) .left().isLiteral("15.55555555555555555555555555555555555555555555") .isLiteralType(oData.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Decimal)) .root() - .right().isLiteral("3.1").isLiteralType(oData.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Decimal)); + .right().isLiteral("-12345678901234567890") + .isLiteralType(oData.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Decimal)); testFilter.runOnETAllPrim("duration'PT1H2S' eq duration'PT3602S'") .isBinary(BinaryOperatorKind.EQ) http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8537f3a5/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestUriParserImpl.java ---------------------------------------------------------------------- diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestUriParserImpl.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestUriParserImpl.java index 138e598..1b8c398 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestUriParserImpl.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestUriParserImpl.java @@ -225,74 +225,74 @@ public class TestUriParserImpl { @Test public void entityFailOnValidation() throws Exception { // simple entity set; with qualifiedentityTypeName; with filter - testUri.runEx("$entity/olingo.odata.test1.ETTwoPrim", "$filter=PropertyInt16 eq 123&$id=ESAllKey") + testUri.runEx("$entity/olingo.odata.test1.ETTwoPrim", "$filter=PropertyInt16 eq 123&$id=ESAllPrim(1)") .isExValidation(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED); } @Test public void entity() throws Exception { // simple entity set - testUri.run("$entity", "$id=ESAllPrim").isKind(UriInfoKind.entityId) + testUri.run("$entity", "$id=ESAllPrim(1)").isKind(UriInfoKind.entityId) .isKind(UriInfoKind.entityId) - .isIdText("ESAllPrim"); + .isIdText("ESAllPrim(1)"); // simple entity set; $format before $id - testUri.run("$entity", "$format=xml&$id=ETAllPrim").isKind(UriInfoKind.entityId) + testUri.run("$entity", "$format=xml&$id=ESAllPrim(1)").isKind(UriInfoKind.entityId) .isFormatText("xml") - .isIdText("ETAllPrim"); + .isIdText("ESAllPrim(1)"); - testUri.run("$entity", "$format=xml&abc=123&$id=ESAllKey").isKind(UriInfoKind.entityId) + testUri.run("$entity", "$format=xml&abc=123&$id=ESAllPrim(1)").isKind(UriInfoKind.entityId) .isFormatText("xml") .isCustomParameter(0, "abc", "123") - .isIdText("ESAllKey"); + .isIdText("ESAllPrim(1)"); // simple entity set; $format after $id - testUri.run("$entity", "$id=ETAllPrim&$format=xml").isKind(UriInfoKind.entityId) - .isIdText("ETAllPrim") + testUri.run("$entity", "$id=ESAllPrim(1)&$format=xml").isKind(UriInfoKind.entityId) + .isIdText("ESAllPrim(1)") .isFormatText("xml"); // simple entity set; $format and custom parameter after $id - testUri.run("$entity", "$id=ETAllPrim&$format=xml&abc=123").isKind(UriInfoKind.entityId) - .isIdText("ETAllPrim") + testUri.run("$entity", "$id=ESAllPrim(1)&$format=xml&abc=123").isKind(UriInfoKind.entityId) + .isIdText("ESAllPrim(1)") .isFormatText("xml") .isCustomParameter(0, "abc", "123"); // simple entity set; $format before $id and custom parameter after $id - testUri.run("$entity", "$format=xml&$id=ETAllPrim&abc=123").isKind(UriInfoKind.entityId) + testUri.run("$entity", "$format=xml&$id=ESAllPrim(1)&abc=123").isKind(UriInfoKind.entityId) .isFormatText("xml") - .isIdText("ETAllPrim") + .isIdText("ESAllPrim(1)") .isCustomParameter(0, "abc", "123"); // simple entity set; with qualifiedentityTypeName - testUri.run("$entity/olingo.odata.test1.ETTwoPrim", "$id=ESBase") + testUri.run("$entity/olingo.odata.test1.ETTwoPrim", "$id=ESBase(111)") .isEntityType(EntityTypeProvider.nameETTwoPrim) - .isIdText("ESBase"); + .isIdText("ESBase(111)"); // simple entity set; with qualifiedentityTypeName; - testUri.run("$entity/olingo.odata.test1.ETBase", "$id=ESTwoPrim") + testUri.run("$entity/olingo.odata.test1.ETBase", "$id=ESTwoPrim(1)") .isEntityType(EntityTypeProvider.nameETBase) .isKind(UriInfoKind.entityId) - .isIdText("ESTwoPrim"); + .isIdText("ESTwoPrim(1)"); // simple entity set; with qualifiedentityTypeName; with format - testUri.run("$entity/olingo.odata.test1.ETBase", "$id=ESTwoPrim&$format=atom") + testUri.run("$entity/olingo.odata.test1.ETBase", "$id=ESTwoPrim(1)&$format=atom") .isKind(UriInfoKind.entityId) .isEntityType(EntityTypeProvider.nameETBase) - .isIdText("ESTwoPrim") + .isIdText("ESTwoPrim(1)") .isFormatText("atom"); // simple entity set; with qualifiedentityTypeName; with select - testUri.run("$entity/olingo.odata.test1.ETBase", "$id=ESTwoPrim&$select=*") + testUri.run("$entity/olingo.odata.test1.ETBase", "$id=ESTwoPrim(1)&$select=*") .isKind(UriInfoKind.entityId) .isEntityType(EntityTypeProvider.nameETBase) - .isIdText("ESTwoPrim") + .isIdText("ESTwoPrim(1)") .isSelectItemStar(0); // simple entity set; with qualifiedentityTypeName; with expand - testUri.run("$entity/olingo.odata.test1.ETBase", "$id=ESTwoPrim&$expand=*") + testUri.run("$entity/olingo.odata.test1.ETBase", "$id=ESTwoPrim(1)&$expand=*") .isKind(UriInfoKind.entityId) .isEntityType(EntityTypeProvider.nameETBase) - .isIdText("ESTwoPrim") + .isIdText("ESTwoPrim(1)") .goExpand().first().isSegmentStar(); } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8537f3a5/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/validator/UriValidatorTest.java ---------------------------------------------------------------------- diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/validator/UriValidatorTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/validator/UriValidatorTest.java index 868f9b8..673b8ff 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/validator/UriValidatorTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/validator/UriValidatorTest.java @@ -52,7 +52,7 @@ public class UriValidatorTest { private static final String URI_ALL = "$all"; private static final String URI_BATCH = "$batch"; private static final String URI_CROSSJOIN = "$crossjoin(ESAllPrim)"; - private static final String URI_ENTITY_ID = "$entity"; + private static final String URI_ENTITY_ID = "$entity/Namespace1_Alias.ETBase"; private static final String URI_METADATA = "$metadata"; private static final String URI_SERVICE = ""; private static final String URI_ENTITY_SET = "ESAllPrim"; @@ -81,7 +81,7 @@ public class UriValidatorTest { private static final String QO_FILTER = "$filter='1' eq '1'"; private static final String QO_FORMAT = "$format=bla/bla"; private static final String QO_EXPAND = "$expand=*"; - private static final String QO_ID = "$id=Products(0)"; + private static final String QO_ID = "$id=ESAllPrim(1)"; private static final String QO_COUNT = "$count=true"; private static final String QO_ORDERBY = "$orderby=true"; private static final String QO_SEARCH = "$search=bla";
