You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ch...@apache.org on 2016/01/12 14:08:26 UTC
[25/30] olingo-odata4 git commit: [OLINGO-834] $expand parser in Java
+ clean-up
[OLINGO-834] $expand parser in Java + clean-up
Signed-off-by: Christian Amend <ch...@sap.com>
Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/8925274c
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/8925274c
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/8925274c
Branch: refs/heads/master
Commit: 8925274c0b5bd6936c3f6c1d3ab55608ced2cf13
Parents: 8919d3e
Author: Klaus Straubinger <kl...@sap.com>
Authored: Thu Jan 7 13:55:34 2016 +0100
Committer: Christian Amend <ch...@sap.com>
Committed: Thu Jan 7 14:02:09 2016 +0100
----------------------------------------------------------------------
.../tecsvc/client/FilterSystemQueryITCase.java | 7 +-
.../tecsvc/client/OrderBySystemQueryITCase.java | 7 +-
.../server/api/uri/queryoption/ExpandItem.java | 1 -
.../server/core/uri/parser/ExpandParser.java | 282 +++
.../core/uri/parser/ExpressionParser.java | 332 +--
.../server/core/uri/parser/FilterParser.java | 11 +-
.../olingo/server/core/uri/parser/Parser.java | 234 +-
.../server/core/uri/parser/ParserHelper.java | 34 +-
.../core/uri/parser/ResourcePathParser.java | 62 +-
.../server/core/uri/parser/SearchParser.java | 108 +
.../server/core/uri/parser/UriContext.java | 115 -
.../core/uri/parser/UriParseTreeVisitor.java | 2313 ------------------
.../server/core/uri/parser/UriTokenizer.java | 211 +-
.../uri/queryoption/expression/AliasImpl.java | 4 +
.../queryoption/expression/EnumerationImpl.java | 6 +
.../queryoption/expression/LambdaRefImpl.java | 5 +
.../queryoption/expression/TypeLiteralImpl.java | 5 +
.../server-core-exceptions-i18n.properties | 8 +-
.../core/uri/parser/ExpressionParserTest.java | 14 +-
.../core/uri/parser/UriTokenizerTest.java | 82 +-
.../search/SearchParserAndTokenizerTest.java | 44 +-
.../core/uri/antlr/TestFullResourcePath.java | 1311 +++++-----
.../olingo/server/core/uri/antlr/TestLexer.java | 450 ++--
.../core/uri/antlr/TestUriParserImpl.java | 220 +-
.../server/core/uri/parser/ParserTest.java | 56 +-
.../core/uri/testutil/ExpandValidator.java | 19 +-
.../core/uri/testutil/FilterValidator.java | 56 +-
.../core/uri/testutil/TestUriValidator.java | 38 +-
.../core/uri/testutil/TokenValidator.java | 127 -
29 files changed, 1941 insertions(+), 4221 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java
index ca6eb21..6865a65 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java
@@ -35,11 +35,8 @@ import org.apache.olingo.commons.api.edm.FullQualifiedName;
import org.apache.olingo.commons.api.http.HttpHeader;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import org.junit.Assert;
-import org.junit.Ignore;
import org.junit.Test;
-// TODO
-@Ignore
public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase {
private static final String ES_COMP_ALL_PRIM = "ESCompAllPrim";
@@ -212,7 +209,7 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase {
sendRequest(ES_TWO_KEY_NAV, "PropertyComp/PropertyComp/PropertyBoolean eq not null");
assertEquals(0, result.getBody().getEntities().size());
- result = sendRequest(ES_TWO_KEY_NAV, "PropertyComp/PropertyComp/PropertyBoolean eq 0 add -(5 add null)");
+ result = sendRequest(ES_TWO_KEY_NAV, "PropertyComp/PropertyComp/PropertyInt16 eq 0 add -(5 add null)");
assertEquals(0, result.getBody().getEntities().size());
}
@@ -357,7 +354,7 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase {
@Test
public void notOperator() {
- ODataRetrieveResponse<ClientEntitySet> result = sendRequest(ES_TWO_KEY_NAV, "not(PropertyInt16 eq 1)");
+ ODataRetrieveResponse<ClientEntitySet> result = sendRequest(ES_TWO_KEY_NAV, "not (PropertyInt16 eq 1)");
assertEquals(2, result.getBody().getEntities().size());
ClientEntity clientEntity = result.getBody().getEntities().get(0);
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/OrderBySystemQueryITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/OrderBySystemQueryITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/OrderBySystemQueryITCase.java
index 0e31b33..8e16a70 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/OrderBySystemQueryITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/OrderBySystemQueryITCase.java
@@ -30,11 +30,8 @@ import org.apache.olingo.client.api.domain.ClientEntitySet;
import org.apache.olingo.client.api.domain.ClientValuable;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import org.junit.Assert;
-import org.junit.Ignore;
import org.junit.Test;
-// TODO
-@Ignore
public class OrderBySystemQueryITCase extends AbstractParamTecSvcITCase {
private static final String ES_TWO_PRIM = "ESTwoPrim";
@@ -74,7 +71,7 @@ public class OrderBySystemQueryITCase extends AbstractParamTecSvcITCase {
@Test
public void multipleOrderBy() {
- final ODataRetrieveResponse<ClientEntitySet> response = sendRequest(ES_ALL_PRIM, "PropertyByte, PropertyInt16");
+ final ODataRetrieveResponse<ClientEntitySet> response = sendRequest(ES_ALL_PRIM, "PropertyByte,PropertyInt16");
assertEquals(3, response.getBody().getEntities().size());
ClientEntity clientEntity = response.getBody().getEntities().get(0);
@@ -90,7 +87,7 @@ public class OrderBySystemQueryITCase extends AbstractParamTecSvcITCase {
@Test
public void multipleOrderByDescending() {
final ODataRetrieveResponse<ClientEntitySet> response =
- sendRequest(ES_ALL_PRIM, "PropertyByte, PropertyInt16 desc");
+ sendRequest(ES_ALL_PRIM, "PropertyByte,PropertyInt16 desc");
assertEquals(3, response.getBody().getEntities().size());
ClientEntity clientEntity = response.getBody().getEntities().get(0);
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/ExpandItem.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/ExpandItem.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/ExpandItem.java
index a16c137..fccf844 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/ExpandItem.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/ExpandItem.java
@@ -38,7 +38,6 @@ public interface ExpandItem {
FilterOption getFilterOption();
/**
- * <b>CURRENTLY NOT SUPPORTED. WILL ALWAYS RETURN NULL</b>
* @return Information of the option $search when used within $expand
*/
SearchOption getSearchOption();
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpandParser.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpandParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpandParser.java
new file mode 100644
index 0000000..d8209d8
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpandParser.java
@@ -0,0 +1,282 @@
+/*
+ * 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.commons.api.edm.Edm;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
+import org.apache.olingo.commons.api.edm.EdmProperty;
+import org.apache.olingo.commons.api.edm.EdmStructuredType;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
+import org.apache.olingo.commons.api.ex.ODataRuntimeException;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.uri.UriInfoKind;
+import org.apache.olingo.server.api.uri.UriResourceNavigation;
+import org.apache.olingo.server.api.uri.UriResourcePartTyped;
+import org.apache.olingo.server.api.uri.queryoption.ExpandItem;
+import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
+import org.apache.olingo.server.api.uri.queryoption.LevelsExpandOption;
+import org.apache.olingo.server.api.uri.queryoption.SystemQueryOption;
+import org.apache.olingo.server.api.uri.queryoption.SystemQueryOptionKind;
+import org.apache.olingo.server.core.uri.UriInfoImpl;
+import org.apache.olingo.server.core.uri.UriResourceComplexPropertyImpl;
+import org.apache.olingo.server.core.uri.UriResourceCountImpl;
+import org.apache.olingo.server.core.uri.UriResourceNavigationPropertyImpl;
+import org.apache.olingo.server.core.uri.UriResourceRefImpl;
+import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
+import org.apache.olingo.server.core.uri.queryoption.CountOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.ExpandItemImpl;
+import org.apache.olingo.server.core.uri.queryoption.ExpandOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.LevelsOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.SkipOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.TopOptionImpl;
+import org.apache.olingo.server.core.uri.validator.UriValidationException;
+
+public class ExpandParser {
+
+ private final Edm edm;
+ private final OData odata;
+
+ public ExpandParser(final Edm edm, final OData odata) {
+ this.edm = edm;
+ this.odata = odata;
+ }
+
+ public ExpandOption parse(UriTokenizer tokenizer, final EdmStructuredType referencedType)
+ throws UriParserException, UriValidationException {
+ ExpandOptionImpl expandOption = new ExpandOptionImpl();
+ do {
+ final ExpandItem item = parseItem(tokenizer, referencedType);
+ expandOption.addExpandItem(item);
+ } while (tokenizer.next(TokenKind.COMMA));
+
+ return expandOption;
+ }
+
+ private ExpandItem parseItem(UriTokenizer tokenizer, final EdmStructuredType referencedType)
+ throws UriParserException, UriValidationException {
+ ExpandItemImpl item = new ExpandItemImpl();
+ if (tokenizer.next(TokenKind.STAR)) {
+ item.setIsStar(true);
+ if (tokenizer.next(TokenKind.SLASH)) {
+ ParserHelper.requireNext(tokenizer, TokenKind.REF);
+ item.setIsRef(true);
+ } else if (tokenizer.next(TokenKind.OPEN)) {
+ ParserHelper.requireNext(tokenizer, TokenKind.LEVELS);
+ ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+ item.setSystemQueryOption((SystemQueryOption) parseLevels(tokenizer));
+ ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
+ }
+
+ } else {
+ final EdmStructuredType typeCast = parseTypeCast(tokenizer, referencedType);
+ if (typeCast != null) {
+ item.setTypeFilter(typeCast);
+ ParserHelper.requireNext(tokenizer, TokenKind.SLASH);
+ }
+
+ UriInfoImpl resource = parseExpandPath(tokenizer, referencedType);
+
+ UriResourcePartTyped lastPart = (UriResourcePartTyped) resource.getLastResourcePart();
+
+ boolean hasSlash = false;
+ if (tokenizer.next(TokenKind.SLASH)) {
+ hasSlash = true;
+ if (lastPart instanceof UriResourceNavigation) {
+ UriResourceNavigationPropertyImpl navigationResource = (UriResourceNavigationPropertyImpl) lastPart;
+ final EdmNavigationProperty navigationProperty = navigationResource.getProperty();
+ final EdmStructuredType typeCastSuffix = parseTypeCast(tokenizer, navigationProperty.getType());
+ if (typeCastSuffix != null) {
+ if (navigationProperty.isCollection()) {
+ navigationResource.setCollectionTypeFilter(typeCastSuffix);
+ } else {
+ navigationResource.setEntryTypeFilter(typeCastSuffix);
+ }
+ hasSlash = false;
+ }
+ }
+ }
+
+ final EdmStructuredType newReferencedType = (EdmStructuredType) lastPart.getType();
+ final boolean newReferencedIsCollection = lastPart.isCollection();
+ if (hasSlash || tokenizer.next(TokenKind.SLASH)) {
+ if (tokenizer.next(TokenKind.REF)) {
+ resource.addResourcePart(new UriResourceRefImpl());
+ parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, true, false);
+ } else {
+ ParserHelper.requireNext(tokenizer, TokenKind.COUNT);
+ resource.addResourcePart(new UriResourceCountImpl());
+ parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, false, true);
+ }
+ } else {
+ parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, false, false);
+ }
+
+ item.setResourcePath(resource);
+ }
+
+ return item;
+ }
+
+ private EdmStructuredType parseTypeCast(UriTokenizer tokenizer, final EdmStructuredType referencedType)
+ throws UriParserException {
+ if (tokenizer.next(TokenKind.QualifiedName)) {
+ final FullQualifiedName qualifiedName = new FullQualifiedName(tokenizer.getText());
+ final EdmStructuredType type = referencedType instanceof EdmEntityType ?
+ edm.getEntityType(qualifiedName) :
+ edm.getComplexType(qualifiedName);
+ if (type == null) {
+ throw new UriParserSemanticException("Type '" + qualifiedName + "' not found.",
+ UriParserSemanticException.MessageKeys.UNKNOWN_PART, qualifiedName.getFullQualifiedNameAsString());
+ } else {
+ if (!type.compatibleTo(referencedType)) {
+ throw new UriParserSemanticException("The type cast '" + qualifiedName + "' is not compatible.",
+ UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER, type.getName());
+ }
+ }
+ return type;
+ }
+ return null;
+ }
+
+ private UriInfoImpl parseExpandPath(UriTokenizer tokenizer, final EdmStructuredType referencedType)
+ throws UriParserException {
+ UriInfoImpl resource = new UriInfoImpl().setKind(UriInfoKind.resource);
+
+ EdmStructuredType type = referencedType;
+ String name = null;
+ while (tokenizer.next(TokenKind.ODataIdentifier)) {
+ name = tokenizer.getText();
+ final EdmProperty property = referencedType.getStructuralProperty(name);
+ if (property != null && property.getType().getKind() == EdmTypeKind.COMPLEX) {
+ type = (EdmStructuredType) property.getType();
+ UriResourceComplexPropertyImpl complexResource = new UriResourceComplexPropertyImpl(property);
+ ParserHelper.requireNext(tokenizer, TokenKind.SLASH);
+ final EdmStructuredType typeCast = parseTypeCast(tokenizer, type);
+ if (typeCast != null) {
+ complexResource.setTypeFilter(typeCast);
+ ParserHelper.requireNext(tokenizer, TokenKind.SLASH);
+ type = typeCast;
+ }
+ resource.addResourcePart(complexResource);
+ }
+ }
+
+ final EdmNavigationProperty navigationProperty = type.getNavigationProperty(name);
+ if (navigationProperty == null) {
+ // TODO: could also have been star after complex property (and maybe type cast)
+ throw new UriParserSemanticException(
+ "Navigation Property '" + name + "' not found in type '" + type.getFullQualifiedName() + "'.",
+ UriParserSemanticException.MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE, type.getName(), name);
+ } else {
+ resource.addResourcePart(new UriResourceNavigationPropertyImpl(navigationProperty));
+ }
+
+ return resource;
+ }
+
+ private void parseOptions(UriTokenizer tokenizer,
+ final EdmStructuredType referencedType, final boolean referencedIsCollection,
+ ExpandItemImpl item,
+ final boolean forRef, final boolean forCount) throws UriParserException, UriValidationException {
+ if (tokenizer.next(TokenKind.OPEN)) {
+ do {
+ SystemQueryOption systemQueryOption;
+ if (!forCount && tokenizer.next(TokenKind.COUNT)) {
+ ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+ ParserHelper.requireNext(tokenizer, TokenKind.BooleanValue);
+ CountOptionImpl countOption = new CountOptionImpl();
+ countOption.setText(tokenizer.getText());
+ countOption.setValue(Boolean.parseBoolean(tokenizer.getText()));
+ systemQueryOption = countOption;
+
+ } else if (!forRef && !forCount && tokenizer.next(TokenKind.EXPAND)) {
+ ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+ systemQueryOption = new ExpandParser(edm, odata).parse(tokenizer, referencedType);
+
+ } else if (tokenizer.next(TokenKind.FILTER)) {
+ ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+ systemQueryOption = new FilterParser(edm, odata).parse(tokenizer, referencedType, null);
+
+ } else if (!forRef && !forCount && tokenizer.next(TokenKind.LEVELS)) {
+ ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+ systemQueryOption = (SystemQueryOption) parseLevels(tokenizer);
+
+ } else if (!forCount && tokenizer.next(TokenKind.ORDERBY)) {
+ ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+ systemQueryOption = new OrderByParser(edm, odata).parse(tokenizer, referencedType, null);
+
+ } else if (tokenizer.next(TokenKind.SEARCH)) {
+ ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+ systemQueryOption = new SearchParser().parse(tokenizer);
+
+ } else if (!forRef && !forCount && tokenizer.next(TokenKind.SELECT)) {
+ ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+ systemQueryOption = new SelectParser(edm).parse(tokenizer, referencedType, referencedIsCollection);
+
+ } else if (!forCount && tokenizer.next(TokenKind.SKIP)) {
+ ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+ ParserHelper.requireNext(tokenizer, TokenKind.IntegerValue);
+ final int value = ParserHelper.parseNonNegativeInteger(SystemQueryOptionKind.SKIP.toString(),
+ tokenizer.getText(), true);
+ SkipOptionImpl skipOption = new SkipOptionImpl();
+ skipOption.setText(tokenizer.getText());
+ skipOption.setValue(value);
+ systemQueryOption = skipOption;
+
+ } else if (!forCount && tokenizer.next(TokenKind.TOP)) {
+ ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+ ParserHelper.requireNext(tokenizer, TokenKind.IntegerValue);
+ final int value = ParserHelper.parseNonNegativeInteger(SystemQueryOptionKind.TOP.toString(),
+ tokenizer.getText(), true);
+ TopOptionImpl topOption = new TopOptionImpl();
+ topOption.setText(tokenizer.getText());
+ topOption.setValue(value);
+ systemQueryOption = topOption;
+
+ } else {
+ throw new UriParserSyntaxException("Allowed query option expected.",
+ UriParserSyntaxException.MessageKeys.SYNTAX);
+ }
+ try {
+ item.setSystemQueryOption(systemQueryOption);
+ } catch (final ODataRuntimeException e) {
+ throw new UriParserSyntaxException("Double system query option '" + systemQueryOption.getName() + "'.", e,
+ UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION, systemQueryOption.getName());
+ }
+ } while (tokenizer.next(TokenKind.SEMI));
+ ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
+ }
+ }
+
+ private LevelsExpandOption parseLevels(UriTokenizer tokenizer) throws UriParserException {
+ final LevelsOptionImpl option = new LevelsOptionImpl();
+ if (tokenizer.next(TokenKind.MAX)) {
+ option.setText(tokenizer.getText());
+ option.setMax();
+ } else {
+ ParserHelper.requireNext(tokenizer, TokenKind.IntegerValue);
+ option.setText(tokenizer.getText());
+ option.setValue(
+ ParserHelper.parseNonNegativeInteger(SystemQueryOptionKind.LEVELS.toString(), tokenizer.getText(), false));
+ }
+ return option;
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/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 049880f..6fa415f 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
@@ -47,9 +47,6 @@ import org.apache.olingo.commons.api.edm.EdmType;
import org.apache.olingo.commons.api.edm.EdmTypeDefinition;
import org.apache.olingo.commons.api.edm.FullQualifiedName;
import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
-import org.apache.olingo.commons.core.edm.primitivetype.EdmByte;
-import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
-import org.apache.olingo.commons.core.edm.primitivetype.EdmSByte;
import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.uri.UriParameter;
import org.apache.olingo.server.api.uri.UriResourceFunction;
@@ -161,7 +158,7 @@ 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 */
+ /* Enum and null are not present in the map. These have to be handled differently. */
Map<TokenKind, EdmPrimitiveTypeKind> temp = new HashMap<TokenKind, EdmPrimitiveTypeKind>();
temp.put(TokenKind.BooleanValue, EdmPrimitiveTypeKind.Boolean);
temp.put(TokenKind.StringValue, EdmPrimitiveTypeKind.String);
@@ -242,9 +239,9 @@ public class ExpressionParser {
}
private Expression parseExprRel() throws UriParserException, UriValidationException {
- if(tokenizer.next(TokenKind.IsofMethod)) {
- // The isof method is a terminal. So no further operators are allowed
- return parseIsOfMethod(TokenKind.IsofMethod);
+ if (tokenizer.next(TokenKind.IsofMethod)) {
+ // The isof method is a terminal. So no further operators are allowed.
+ return parseIsOfOrCastMethod(MethodKind.ISOF);
} else {
Expression left = parseExprAdd();
TokenKind operatorTokenKind = ParserHelper.next(tokenizer,
@@ -264,30 +261,25 @@ public class ExpressionParser {
}
}
- private Expression parseIsOfMethod(final TokenKind lastToken) throws UriParserException, UriValidationException {
- if(lastToken == TokenKind.IsofMethod) {
- // The TokenKind 'IsOfMethod' consumes also the opening parenthesis
-
- // The first parameter could be an expression or a type literal
- final List<Expression> parameters = new ArrayList<Expression>();
+ private Expression parseIsOfOrCastMethod(final MethodKind kind) throws UriParserException, UriValidationException {
+ // The TokenKind 'IsOfMethod' consumes also the opening parenthesis.
+ // The first parameter could be an expression or a type literal.
+ List<Expression> parameters = new ArrayList<Expression>();
+ parameters.add(parseExpression());
+ if (!(parameters.get(0) instanceof TypeLiteral)) {
+ // The first parameter is not a type literal, so there must be a second parameter.
+ ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
parameters.add(parseExpression());
- if(!(parameters.get(0) instanceof TypeLiteral)) {
- // The first parameter is not a type literal, so there must be a second parameter
- ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
- parameters.add(parseExpression());
-
- // The second parameter must be a type literal
- if(!(parameters.get(1) instanceof TypeLiteral)) {
- throw new UriParserSemanticException("Type literal extected",
- UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER);
- }
+
+ // The second parameter must be a type literal.
+ if (!(parameters.get(1) instanceof TypeLiteral)) {
+ throw new UriParserSemanticException("Type literal expected.",
+ UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER);
}
-
- ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
- return new MethodImpl(MethodKind.ISOF, parameters);
- } else {
- throw new UriParserSyntaxException("Unexpected token.", UriParserSyntaxException.MessageKeys.SYNTAX);
}
+
+ ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
+ return new MethodImpl(kind, parameters);
}
private Expression parseExprAdd() throws UriParserException, UriValidationException {
@@ -296,9 +288,9 @@ public class ExpressionParser {
// Null for everything other than ADD or SUB
while (operatorTokenKind != null) {
final Expression right = parseExprMul();
- checkAddSubTypes(left, right, operatorTokenKind == TokenKind.AddOperator);
- left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right,
- odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Double));
+ final EdmType resultType = getAddSubTypeAndCheckLeftAndRight(left, right,
+ operatorTokenKind == TokenKind.SubOperator);
+ left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right, resultType);
operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.AddOperator, TokenKind.SubOperator);
}
return left;
@@ -328,10 +320,7 @@ public class ExpressionParser {
}
private Expression parseExprUnary() throws UriParserException, UriValidationException {
- final TokenKind operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.MINUS, TokenKind.NotOperator,
- TokenKind.CastMethod);
-
- if(operatorTokenKind == TokenKind.MINUS) {
+ if (tokenizer.next(TokenKind.MinusOperator)) {
final Expression expression = parseExprPrimary();
checkType(expression,
EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
@@ -339,37 +328,14 @@ public class ExpressionParser {
EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double,
EdmPrimitiveTypeKind.Duration);
return new UnaryImpl(UnaryOperatorKind.MINUS, expression, getType(expression));
- } else if(operatorTokenKind == TokenKind.NotOperator) {
+ } else if (tokenizer.next(TokenKind.NotOperator)) {
final Expression expression = parseExprPrimary();
checkType(expression, EdmPrimitiveTypeKind.Boolean);
return new UnaryImpl(UnaryOperatorKind.NOT, expression, getType(expression));
- } else if(operatorTokenKind == TokenKind.CastMethod) {
- return parseCastMethod(operatorTokenKind);
+ } else if (tokenizer.next(TokenKind.CastMethod)) {
+ return parseIsOfOrCastMethod(MethodKind.CAST);
} else {
- final Expression expression = parseExprPrimary();
- return expression;
- }
- }
-
- private Expression parseCastMethod(final TokenKind lastToken) throws UriParserException, UriValidationException {
- // The TokenKind 'CastMethod' consumes also the opening parenthesis
- if(lastToken == TokenKind.CastMethod) {
- final List<Expression> parameters = new ArrayList<Expression>();
- parameters.add(parseExpression());
-
- if(!(parameters.get(0) instanceof TypeLiteral)) {
- ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
- parameters.add(parseExpression());
- if(!(parameters.get(1) instanceof TypeLiteral)) {
- throw new UriParserSemanticException("Type literal extected",
- UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER);
- }
- }
-
- ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
- return new MethodImpl(MethodKind.CAST, parameters);
- } else {
- throw new UriParserSyntaxException("Unexpected token.", UriParserSyntaxException.MessageKeys.SYNTAX);
+ return parseExprPrimary();
}
}
@@ -433,22 +399,20 @@ public class ExpressionParser {
private Expression parseMethod(final TokenKind nextMethod) throws UriParserException, UriValidationException {
// The method token text includes the opening parenthesis so that method calls can be recognized unambiguously.
// OData identifiers have to be considered after that.
-
final MethodKind methodKind = tokenToMethod.get(nextMethod);
return new MethodImpl(methodKind, parseMethodParameters(methodKind));
}
- private Expression parsePrimitive(TokenKind nextPrimitive) throws UriParserException {
+ private Expression parsePrimitive(final TokenKind primitiveTokenKind) throws UriParserException {
final String primitiveValueLiteral = tokenizer.getText();
- if (nextPrimitive == TokenKind.EnumValue) {
+ if (primitiveTokenKind == TokenKind.EnumValue) {
return createEnumExpression(primitiveValueLiteral);
} else {
- EdmPrimitiveTypeKind primitiveTypeKind = tokenToPrimitiveType.get(nextPrimitive);
-
- if(primitiveTypeKind == EdmPrimitiveTypeKind.Int64) {
+ EdmPrimitiveTypeKind primitiveTypeKind = tokenToPrimitiveType.get(primitiveTokenKind);
+ if (primitiveTypeKind == EdmPrimitiveTypeKind.Int64) {
primitiveTypeKind = determineIntegerType(primitiveValueLiteral);
}
-
+
final EdmPrimitiveType type = primitiveTypeKind == null ?
// Null handling
null :
@@ -472,12 +436,11 @@ public class ExpressionParser {
} else {
typeKind = EdmPrimitiveTypeKind.Int64;
}
- } catch (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",
+ } 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);
}
-
return typeKind;
}
@@ -622,19 +585,19 @@ public class ExpressionParser {
if (filterType == null) {
filterType = edm.getComplexType(fullQualifiedName);
}
-
- if(filterType == null) {
- filterType = getEdmType(fullQualifiedName);
+
+ if (filterType == null) {
+ filterType = getPrimitiveType(fullQualifiedName);
}
-
- if(filterType == null) {
+
+ if (filterType == null) {
filterType = edm.getEnumType(fullQualifiedName);
}
-
- if(filterType == null) {
+
+ if (filterType == null) {
filterType = edm.getTypeDefinition(fullQualifiedName);
}
-
+
if (filterType != null) {
if (tokenizer.next(TokenKind.SLASH)) {
// Leading type cast
@@ -642,30 +605,29 @@ public class ExpressionParser {
startTypeFilter = filterType;
final TokenKind tokenKind = ParserHelper.next(tokenizer, TokenKind.QualifiedName, TokenKind.ODataIdentifier);
- parseMemberExpression(tokenKind, uriInfo, new UriResourceStartingTypeFilterImpl(filterType, false),
- false);
+ parseMemberExpression(tokenKind, uriInfo, new UriResourceStartingTypeFilterImpl(filterType, false), false);
} else {
// Type literal
return new TypeLiteralImpl(filterType);
}
} else {
- // Must be bound or unbound function.
+ // Must be bound or unbound function.
parseFunction(fullQualifiedName, uriInfo, referringType, true);
}
} else if (lastTokenKind == TokenKind.ODataIdentifier) {
parseFirstMemberODataIdentifier(uriInfo);
}
-
+
return new MemberImpl(uriInfo, startTypeFilter);
}
- private EdmType getEdmType(final FullQualifiedName fullQualifiedName) {
- if(!fullQualifiedName.getNamespace().equals(EdmPrimitiveType.EDM_NAMESPACE)) {
+ private EdmType getPrimitiveType(final FullQualifiedName fullQualifiedName) {
+ if (EdmPrimitiveType.EDM_NAMESPACE.equals(fullQualifiedName.getNamespace())) {
+ final EdmPrimitiveTypeKind primitiveTypeKind = EdmPrimitiveTypeKind.valueOf(fullQualifiedName.getName());
+ return primitiveTypeKind == null ? null : odata.createPrimitiveTypeInstance(primitiveTypeKind);
+ } else {
return null;
}
-
- final EdmPrimitiveTypeKind primitiveTypeKind = EdmPrimitiveTypeKind.valueOfFQN(fullQualifiedName);
- return primitiveTypeKind == null ? null : EdmPrimitiveTypeFactory.getInstance(primitiveTypeKind);
}
private void parseDollarRoot(UriInfoImpl uriInfo) throws UriParserException, UriValidationException {
@@ -694,7 +656,7 @@ public class ExpressionParser {
parseSingleNavigationExpr(uriInfo, resource);
}
- private void parseDollarIt(UriInfoImpl uriInfo, EdmType referringType)
+ private void parseDollarIt(UriInfoImpl uriInfo, final EdmType referringType)
throws UriParserException, UriValidationException {
UriResourceItImpl itResource = new UriResourceItImpl(referringType, false);
uriInfo.addResourcePart(itResource);
@@ -759,16 +721,14 @@ public class ExpressionParser {
if (edmEntityType != null) {
if (allowTypeFilter) {
setTypeFilter(lastResource, edmEntityType);
-
- if(tokenizer.next(TokenKind.SLASH)) {
- final TokenKind nextTokenKind = ParserHelper.next(tokenizer, TokenKind.QualifiedName,
- TokenKind.ODataIdentifier);
- if(nextTokenKind == TokenKind.ODataIdentifier) {
- parsePropertyPathExpr(uriInfo, lastResource);
- } else if(nextTokenKind == TokenKind.QualifiedName) {
+
+ if (tokenizer.next(TokenKind.SLASH)) {
+ if (tokenizer.next(TokenKind.QualifiedName)) {
parseBoundFunction(fullQualifiedName, uriInfo, lastResource);
+ } else if (tokenizer.next(TokenKind.ODataIdentifier)) {
+ parsePropertyPathExpr(uriInfo, lastResource);
} else {
- throw new UriParserSyntaxException("Extected OData Identifier or Full Qualified Name",
+ throw new UriParserSyntaxException("Expected OData Identifier or Full Qualified Name.",
UriParserSyntaxException.MessageKeys.SYNTAX);
}
}
@@ -814,7 +774,9 @@ public class ExpressionParser {
if (property == null) {
throw new UriParserSemanticException("Unknown property.",
- UriParserSemanticException.MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE, oDataIdentifier);
+ UriParserSemanticException.MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE,
+ lastType.getFullQualifiedName().getFullQualifiedNameAsString(),
+ oDataIdentifier);
}
if (property.getType() instanceof EdmComplexType) {
@@ -823,7 +785,9 @@ public class ExpressionParser {
uriInfo.addResourcePart(complexResource);
if (property.isCollection()) {
- parseCollectionPathExpr(uriInfo, complexResource);
+ if (tokenizer.next(TokenKind.SLASH)) {
+ parseCollectionPathExpr(uriInfo, complexResource);
+ }
} else {
parseComplexPathExpr(uriInfo, complexResource);
}
@@ -847,7 +811,9 @@ public class ExpressionParser {
uriInfo.addResourcePart(primitiveResource);
if (property.isCollection()) {
- parseCollectionPathExpr(uriInfo, primitiveResource);
+ if (tokenizer.next(TokenKind.SLASH)) {
+ parseCollectionPathExpr(uriInfo, primitiveResource);
+ }
} else {
parseSinglePathExpr(uriInfo, primitiveResource);
}
@@ -856,7 +822,6 @@ public class ExpressionParser {
private void parseSingleNavigationExpr(UriInfoImpl uriInfo, final UriResourcePartTyped lastResource)
throws UriParserException, UriValidationException {
- // TODO: Is that correct?
if (tokenizer.next(TokenKind.SLASH)) {
final TokenKind tokenKind = ParserHelper.next(tokenizer, TokenKind.QualifiedName, TokenKind.ODataIdentifier);
parseMemberExpression(tokenKind, uriInfo, lastResource, true);
@@ -865,8 +830,22 @@ public class ExpressionParser {
private void parseCollectionNavigationExpr(UriInfoImpl uriInfo, UriResourcePartTyped lastResource)
throws UriParserException, UriValidationException {
- // TODO: Is type cast missing?
- if (tokenizer.next(TokenKind.OPEN)) {
+ boolean hasSlash = false;
+ if (tokenizer.next(TokenKind.SLASH)) {
+ hasSlash = true;
+ if (tokenizer.next(TokenKind.QualifiedName)) {
+ final FullQualifiedName qualifiedName = new FullQualifiedName(tokenizer.getText());
+ final EdmEntityType edmEntityType = edm.getEntityType(qualifiedName);
+ if (edmEntityType == null) {
+ parseBoundFunction(qualifiedName, uriInfo, lastResource);
+ } else {
+ setTypeFilter(lastResource, edmEntityType);
+ }
+ hasSlash = false;
+ }
+ }
+
+ if (!hasSlash && tokenizer.next(TokenKind.OPEN)) {
if (lastResource instanceof UriResourceNavigation) {
((UriResourceNavigationPropertyImpl) lastResource).setKeyPredicates(
ParserHelper.parseNavigationKeyPredicate(tokenizer,
@@ -883,7 +862,10 @@ public class ExpressionParser {
}
parseSingleNavigationExpr(uriInfo, lastResource);
}
- parseCollectionPathExpr(uriInfo, lastResource);
+
+ if (hasSlash || tokenizer.next(TokenKind.SLASH)) {
+ parseCollectionPathExpr(uriInfo, lastResource);
+ }
}
private void parseSinglePathExpr(UriInfoImpl uriInfo, final UriResourcePartTyped lastResource)
@@ -932,25 +914,23 @@ public class ExpressionParser {
private void parseCollectionPathExpr(UriInfoImpl uriInfo, final UriResourcePartTyped lastResource)
throws UriParserException, UriValidationException {
-
- if (tokenizer.next(TokenKind.SLASH)) {
- if (tokenizer.next(TokenKind.COUNT)) {
- uriInfo.addResourcePart(new UriResourceCountImpl());
- } else if (tokenizer.next(TokenKind.ANY)) {
- uriInfo.addResourcePart(parseLambdaRest(TokenKind.ANY, lastResource));
- } else if (tokenizer.next(TokenKind.ALL)) {
- uriInfo.addResourcePart(parseLambdaRest(TokenKind.ALL, lastResource));
- } else if (tokenizer.next(TokenKind.QualifiedName)) {
- final FullQualifiedName fullQualifiedName = new FullQualifiedName(tokenizer.getText());
- parseBoundFunction(fullQualifiedName, uriInfo, lastResource);
- }
+ // The initial slash (see grammar) must have been checked and consumed by the caller.
+ if (tokenizer.next(TokenKind.COUNT)) {
+ uriInfo.addResourcePart(new UriResourceCountImpl());
+ } else if (tokenizer.next(TokenKind.ANY)) {
+ uriInfo.addResourcePart(parseLambdaRest(TokenKind.ANY, lastResource));
+ } else if (tokenizer.next(TokenKind.ALL)) {
+ uriInfo.addResourcePart(parseLambdaRest(TokenKind.ALL, lastResource));
+ } else if (tokenizer.next(TokenKind.QualifiedName)) {
+ final FullQualifiedName fullQualifiedName = new FullQualifiedName(tokenizer.getText());
+ parseBoundFunction(fullQualifiedName, uriInfo, lastResource);
}
}
private void parseFunction(final FullQualifiedName fullQualifiedName, UriInfoImpl uriInfo,
final EdmType lastType, final boolean lastIsCollection) throws UriParserException, UriValidationException {
- final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, true);
+ final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, edm, referringType, true);
final List<String> parameterNames = ParserHelper.getParameterNames(parameters);
final EdmFunction boundFunction = edm.getBoundFunction(fullQualifiedName,
lastType.getFullQualifiedName(), lastIsCollection, parameterNames);
@@ -966,18 +946,19 @@ public class ExpressionParser {
return;
}
- throw new UriParserSemanticException("No function found.",
+ throw new UriParserSemanticException("No function '" + fullQualifiedName + "' found.",
UriParserSemanticException.MessageKeys.FUNCTION_NOT_FOUND, fullQualifiedName.getFullQualifiedNameAsString());
}
private void parseBoundFunction(final FullQualifiedName fullQualifiedName, UriInfoImpl uriInfo,
final UriResourcePartTyped lastResource) throws UriParserException, UriValidationException {
- final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, true);
+ final EdmType type = lastResource.getType();
+ final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, edm, referringType, true);
final List<String> parameterNames = ParserHelper.getParameterNames(parameters);
final EdmFunction boundFunction = edm.getBoundFunction(fullQualifiedName,
- lastResource.getType().getFullQualifiedName(), lastResource.isCollection(), parameterNames);
+ type.getFullQualifiedName(), lastResource.isCollection(), parameterNames);
if (boundFunction == null) {
- throw new UriParserSemanticException("Bound function not found.",
+ throw new UriParserSemanticException("Bound function '" + fullQualifiedName + "' not found.",
UriParserSemanticException.MessageKeys.FUNCTION_NOT_FOUND, fullQualifiedName.getFullQualifiedNameAsString());
}
parseFunctionRest(uriInfo, boundFunction, parameters);
@@ -995,19 +976,23 @@ public class ExpressionParser {
if (function.isComposable()) {
if (edmType instanceof EdmEntityType ) {
if (isCollection) {
- parseCollectionNavigationExpr(uriInfo, functionResource);
+ parseCollectionNavigationExpr(uriInfo, functionResource);
} else {
parseSingleNavigationExpr(uriInfo, functionResource);
}
} else if (edmType instanceof EdmComplexType) {
if (isCollection) {
- parseCollectionPathExpr(uriInfo, functionResource);
+ if (tokenizer.next(TokenKind.SLASH)) {
+ parseCollectionPathExpr(uriInfo, functionResource);
+ }
} else {
parseComplexPathExpr(uriInfo, functionResource);
}
} else if (edmType instanceof EdmPrimitiveType) {
if (isCollection) {
- parseCollectionPathExpr(uriInfo, functionResource);
+ if (tokenizer.next(TokenKind.SLASH)) {
+ parseCollectionPathExpr(uriInfo, functionResource);
+ }
} else {
parseSinglePathExpr(uriInfo, functionResource);
}
@@ -1077,7 +1062,7 @@ public class ExpressionParser {
TokenKind.YearMethod);
}
- private EdmType getType(final Expression expression) throws UriParserException {
+ protected static EdmType getType(final Expression expression) throws UriParserException {
EdmType type;
if (expression instanceof Literal) {
type = ((Literal) expression).getType();
@@ -1108,13 +1093,12 @@ public class ExpressionParser {
return type;
}
- private boolean isType(final Expression expression, final EdmPrimitiveTypeKind... kinds) throws UriParserException {
- final EdmType expressionType = getType(expression);
- if (expressionType == null) {
+ private boolean isType(final EdmType type, final EdmPrimitiveTypeKind... kinds) throws UriParserException {
+ if (type == null) {
return true;
}
for (final EdmPrimitiveTypeKind kind : kinds) {
- if (expressionType.equals(odata.createPrimitiveTypeInstance(kind))) {
+ if (type.equals(odata.createPrimitiveTypeInstance(kind))) {
return true;
}
}
@@ -1122,12 +1106,13 @@ public class ExpressionParser {
}
private void checkType(final Expression expression, final EdmPrimitiveTypeKind... kinds) throws UriParserException {
- if (!isType(expression, kinds)) {
+ final EdmType type = getType(expression);
+ if (!isType(type, kinds)) {
throw new UriParserSemanticException("Incompatible type.",
UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, // TODO: better message
- getType(expression) == null ?
+ type == null ?
"" :
- getType(expression).getFullQualifiedName().getFullQualifiedNameAsString());
+ type.getFullQualifiedName().getFullQualifiedNameAsString());
}
}
@@ -1137,21 +1122,21 @@ public class ExpressionParser {
if (leftType == null || rightType == null || leftType.equals(rightType)) {
return;
}
-
+
// Numeric promotion for Edm.Byte and Edm.SByte
- if((leftType instanceof EdmByte || leftType instanceof EdmSByte)
- && (rightType instanceof EdmByte || rightType instanceof EdmSByte)) {
+ if (isType(leftType, EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte)
+ && isType(rightType, EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte)) {
return;
}
-
+
if (leftType.getKind() != EdmTypeKind.PRIMITIVE
|| rightType.getKind() != EdmTypeKind.PRIMITIVE
|| !(((EdmPrimitiveType) leftType).isCompatible((EdmPrimitiveType) rightType)
- || ((EdmPrimitiveType) rightType).isCompatible((EdmPrimitiveType) leftType)))
- {
+ || ((EdmPrimitiveType) rightType).isCompatible((EdmPrimitiveType) leftType))) {
throw new UriParserSemanticException("Incompatible types.",
- UriParserSemanticException.MessageKeys.TYPES_NOT_COMPATIBLE, leftType.getFullQualifiedName().toString(),
- rightType.getFullQualifiedName().toString());
+ UriParserSemanticException.MessageKeys.TYPES_NOT_COMPATIBLE,
+ leftType.getFullQualifiedName().getFullQualifiedNameAsString(),
+ rightType.getFullQualifiedName().getFullQualifiedNameAsString());
}
}
@@ -1164,12 +1149,12 @@ public class ExpressionParser {
}
return type;
}
-
+
private boolean isEnumType(final Expression expression) throws UriParserException {
final EdmType expressionType = getType(expression);
return expressionType == null
|| expressionType.getKind() == EdmTypeKind.ENUM
- || isType(expression,
+ || isType(expressionType,
EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte);
}
@@ -1208,44 +1193,69 @@ public class ExpressionParser {
EdmPrimitiveTypeKind.Date, EdmPrimitiveTypeKind.TimeOfDay,
EdmPrimitiveTypeKind.DateTimeOffset, EdmPrimitiveTypeKind.Duration);
if (!(((EdmPrimitiveType) leftType).isCompatible((EdmPrimitiveType) rightType)
- || ((EdmPrimitiveType) rightType).isCompatible((EdmPrimitiveType) leftType))) {
+ || ((EdmPrimitiveType) rightType).isCompatible((EdmPrimitiveType) leftType))) {
throw new UriParserSemanticException("Incompatible types.",
- UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, ""); // TODO: better message
+ UriParserSemanticException.MessageKeys.TYPES_NOT_COMPATIBLE,
+ leftType.getFullQualifiedName().getFullQualifiedNameAsString(),
+ rightType.getFullQualifiedName().getFullQualifiedNameAsString());
}
}
- private void checkAddSubTypes(final Expression left, final Expression right, final boolean isAdd)
+ private EdmType getAddSubTypeAndCheckLeftAndRight(final Expression left, final Expression right, final boolean isSub)
throws UriParserException {
final EdmType leftType = getType(left);
final EdmType rightType = getType(right);
- if (leftType == null || rightType == null
- || isType(left,
+ if (leftType == null || rightType == null) {
+ return null;
+ }
+ if (isType(leftType,
EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double)
- && isType(right,
+ && isType(rightType,
EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double)) {
- return;
+ // The result type must be able to handle the overflow,
+ // so we return always a wider type than the types of the operands.
+ if (isType(leftType, EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double)
+ || isType(rightType,
+ EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double)) {
+ return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Double);
+ } else if (isType(leftType, EdmPrimitiveTypeKind.Int64) || isType(rightType, EdmPrimitiveTypeKind.Int64)) {
+ return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Decimal);
+ } else if (isType(leftType, EdmPrimitiveTypeKind.Int32) || isType(rightType, EdmPrimitiveTypeKind.Int32)) {
+ return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Int64);
+ } else if (isType(leftType, EdmPrimitiveTypeKind.Int16) || isType(rightType, EdmPrimitiveTypeKind.Int16)) {
+ return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Int32);
+ } else {
+ return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Int16);
+ }
}
- if (isType(left, EdmPrimitiveTypeKind.DateTimeOffset)
- && (isType(right, EdmPrimitiveTypeKind.Duration)
- || isType(right, EdmPrimitiveTypeKind.DateTimeOffset) && !isAdd)) {
- return;
+ if ((isType(leftType, EdmPrimitiveTypeKind.DateTimeOffset)
+ || isType(leftType, EdmPrimitiveTypeKind.Duration))
+ && isType(rightType, EdmPrimitiveTypeKind.Duration)) {
+ return leftType;
}
- if (isType(left, EdmPrimitiveTypeKind.Duration) && isType(right, EdmPrimitiveTypeKind.Duration)
- || isType(left, EdmPrimitiveTypeKind.Date)
- && (isType(right, EdmPrimitiveTypeKind.Duration) || isType(right, EdmPrimitiveTypeKind.Date) && !isAdd)) {
- return;
+ if (isType(leftType, EdmPrimitiveTypeKind.Date) && isType(rightType, EdmPrimitiveTypeKind.Duration)) {
+ return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.DateTimeOffset);
+ }
+ if (isSub
+ && (isType(leftType, EdmPrimitiveTypeKind.DateTimeOffset)
+ && isType(rightType, EdmPrimitiveTypeKind.DateTimeOffset)
+ || isType(leftType, EdmPrimitiveTypeKind.Date)
+ && isType(rightType, EdmPrimitiveTypeKind.Date))) {
+ return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Duration);
}
throw new UriParserSemanticException("Incompatible types.",
- UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, ""); // TODO: better message
+ UriParserSemanticException.MessageKeys.TYPES_NOT_COMPATIBLE,
+ leftType.getFullQualifiedName().getFullQualifiedNameAsString(),
+ rightType.getFullQualifiedName().getFullQualifiedNameAsString());
}
private void checkStructuredTypeFilter(final EdmType type, final EdmType filterType)
throws UriParserException {
- if (!(filterType instanceof EdmStructuredType && ((EdmStructuredType)filterType).compatibleTo(type))) {
+ if (!(filterType instanceof EdmStructuredType && ((EdmStructuredType) filterType).compatibleTo(type))) {
throw new UriParserSemanticException("Incompatible type filter.",
UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER,
filterType.getFullQualifiedName().getFullQualifiedNameAsString());
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/FilterParser.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/FilterParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/FilterParser.java
index e2767a1..dd73009 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/FilterParser.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/FilterParser.java
@@ -21,6 +21,7 @@ package org.apache.olingo.server.core.uri.parser;
import java.util.Collection;
import org.apache.olingo.commons.api.edm.Edm;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.commons.api.edm.EdmType;
import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.uri.queryoption.FilterOption;
@@ -43,7 +44,13 @@ public class FilterParser {
throws UriParserException, UriValidationException {
final Expression filterExpression = new ExpressionParser(edm, odata)
.parse(tokenizer, referencedType, crossjoinEntitySetNames);
- // TODO: Check that the expression is boolean.
- return new FilterOptionImpl().setExpression(filterExpression);
+ final EdmType type = ExpressionParser.getType(filterExpression);
+ if (type == null || type.equals(odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean))) {
+ return new FilterOptionImpl().setExpression(filterExpression);
+ } else {
+ throw new UriParserSemanticException("Filter expressions must be boolean.",
+ UriParserSemanticException.MessageKeys.TYPES_NOT_COMPATIBLE,
+ "Edm.Boolean", type.getFullQualifiedName().getFullQualifiedNameAsString());
+ }
}
}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/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 47efda5..3125fa6 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,18 +18,11 @@
*/
package org.apache.olingo.server.core.uri.parser;
+import java.util.ArrayDeque;
+import java.util.Deque;
import java.util.List;
-import org.antlr.v4.runtime.ANTLRInputStream;
-import org.antlr.v4.runtime.BailErrorStrategy;
-import org.antlr.v4.runtime.CommonTokenStream;
-import org.antlr.v4.runtime.Lexer;
-import org.antlr.v4.runtime.ParserRuleContext;
-import org.antlr.v4.runtime.RecognitionException;
-import org.antlr.v4.runtime.atn.PredictionMode;
-import org.antlr.v4.runtime.misc.ParseCancellationException;
import org.apache.olingo.commons.api.edm.Edm;
-import org.apache.olingo.commons.api.edm.EdmEntityContainer;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
import org.apache.olingo.commons.api.edm.EdmStructuredType;
import org.apache.olingo.commons.api.edm.EdmType;
@@ -52,14 +45,10 @@ import org.apache.olingo.server.api.uri.queryoption.SystemQueryOptionKind;
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
import org.apache.olingo.server.core.uri.UriInfoImpl;
import org.apache.olingo.server.core.uri.UriResourceStartingTypeFilterImpl;
-import org.apache.olingo.server.core.uri.antlr.UriLexer;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser;
-import org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandItemsEOFContext;
import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
import org.apache.olingo.server.core.uri.parser.search.SearchParser;
import org.apache.olingo.server.core.uri.queryoption.AliasQueryOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.CountOptionImpl;
-import org.apache.olingo.server.core.uri.queryoption.ExpandOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.FormatOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.IdOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.SkipOptionImpl;
@@ -77,8 +66,6 @@ public class Parser {
private final Edm edm;
private final OData odata;
- private enum ParserEntryRules { ExpandItems }
-
public Parser(final Edm edm, final OData odata) {
this.edm = edm;
this.odata = odata;
@@ -87,8 +74,9 @@ public class Parser {
public UriInfo parseUri(final String path, final String query, final String fragment)
throws UriParserException, UriValidationException {
- UriContext context = new UriContext();
- UriParseTreeVisitor uriParseTreeVisitor = new UriParseTreeVisitor(edm, context);
+ UriInfoImpl contextUriInfo = new UriInfoImpl();
+ Deque<EdmType> contextTypes = new ArrayDeque<EdmType>();
+ boolean contextIsCollection = false;
final List<String> pathSegmentsDecoded = UriDecoder.splitAndDecodePath(path);
final int numberOfSegments = pathSegmentsDecoded.size();
@@ -98,49 +86,46 @@ public class Parser {
if (firstSegment.isEmpty()) {
ensureLastSegment(firstSegment, 0, numberOfSegments);
- context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.service);
+ contextUriInfo.setKind(UriInfoKind.service);
} else if (firstSegment.equals("$batch")) {
ensureLastSegment(firstSegment, 1, numberOfSegments);
- context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.batch);
+ contextUriInfo.setKind(UriInfoKind.batch);
} else if (firstSegment.equals("$metadata")) {
ensureLastSegment(firstSegment, 1, numberOfSegments);
- context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.metadata);
- context.contextUriInfo.setFragment(fragment);
+ contextUriInfo.setKind(UriInfoKind.metadata);
+ contextUriInfo.setFragment(fragment);
} else if (firstSegment.equals("$all")) {
ensureLastSegment(firstSegment, 1, numberOfSegments);
- context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.all);
+ 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()) {
- context.contextTypes.push(entitySet.getEntityType());
+ contextTypes.push(entitySet.getEntityType());
}
- context.isCollection = true;
+ contextIsCollection = true;
} else if (firstSegment.equals("$entity")) {
if (numberOfSegments > 1) {
final String typeCastSegment = pathSegmentsDecoded.get(1);
ensureLastSegment(typeCastSegment, 2, numberOfSegments);
- context.contextUriInfo = new ResourcePathParser(edm).parseDollarEntityTypeCast(typeCastSegment);
- context.contextTypes.push(context.contextUriInfo.getEntityTypeCast());
+ contextUriInfo = new ResourcePathParser(edm).parseDollarEntityTypeCast(typeCastSegment);
+ contextTypes.push(contextUriInfo.getEntityTypeCast());
} else {
- context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.entityId);
+ 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).
}
- context.isCollection = false;
+ contextIsCollection = false;
} else if (firstSegment.startsWith("$crossjoin")) {
ensureLastSegment(firstSegment, 1, numberOfSegments);
- context.contextUriInfo = new ResourcePathParser(edm).parseCrossjoinSegment(firstSegment);
- final EdmEntityContainer container = edm.getEntityContainer();
- for (final String name : context.contextUriInfo.getEntitySetNames()) {
- context.contextTypes.push(container.getEntitySet(name).getEntityType());
- }
- context.isCollection = true;
+ contextUriInfo = new ResourcePathParser(edm).parseCrossjoinSegment(firstSegment);
+ contextIsCollection = true;
} else {
- context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.resource);
+ contextUriInfo.setKind(UriInfoKind.resource);
final ResourcePathParser resourcePathParser = new ResourcePathParser(edm);
int count = 0;
UriResource lastSegment = null;
@@ -168,7 +153,7 @@ public class Parser {
} else {
lastSegment = segment;
}
- context.contextUriInfo.addResourcePart(segment);
+ contextUriInfo.addResourcePart(segment);
}
}
@@ -176,9 +161,9 @@ public class Parser {
final UriResourcePartTyped typed = (UriResourcePartTyped) lastSegment;
final EdmType type = ParserHelper.getTypeInformation(typed);
if (type != null) { // could be null for, e.g., actions without return type
- context.contextTypes.push(type);
+ contextTypes.push(type);
}
- context.isCollection = typed.isCollection();
+ contextIsCollection = typed.isCollection();
}
}
@@ -191,9 +176,10 @@ public class Parser {
SystemQueryOption systemOption = null;
if (optionName.equals(SystemQueryOptionKind.FILTER.toString())) {
UriTokenizer filterTokenizer = new UriTokenizer(optionValue);
- // The Referring type could also be a primitive type not only a structured type
- systemOption = new FilterParser(edm, odata).parse(filterTokenizer, context.contextTypes.peek(),
- context.contextUriInfo.getEntitySetNames());
+ // The referring type could be a primitive type or a structured type.
+ systemOption = new FilterParser(edm, odata).parse(filterTokenizer,
+ contextTypes.peek(),
+ contextUriInfo.getEntitySetNames());
checkOptionEOF(filterTokenizer, optionName, optionValue);
} else if (optionName.equals(SystemQueryOptionKind.FORMAT.toString())) {
@@ -211,19 +197,26 @@ public class Parser {
systemOption = formatOption;
} else if (optionName.equals(SystemQueryOptionKind.EXPAND.toString())) {
- try {
- ExpandItemsEOFContext ctxExpandItems =
- (ExpandItemsEOFContext) parseRule(optionValue, ParserEntryRules.ExpandItems);
- systemOption = (ExpandOptionImpl) uriParseTreeVisitor.visitExpandItemsEOF(ctxExpandItems);
- } catch (final ParseCancellationException e) {
- throw e.getCause() instanceof UriParserException ?
- (UriParserException) e.getCause() :
- new UriParserSyntaxException("Syntax error", e, UriParserSyntaxException.MessageKeys.SYNTAX);
+ if (contextTypes.peek() instanceof EdmStructuredType
+ || !contextUriInfo.getEntitySetNames().isEmpty()
+ || contextUriInfo.getKind() == UriInfoKind.entityId) { // TODO: Remove once the type has been set above.
+ UriTokenizer expandTokenizer = new UriTokenizer(optionValue);
+ systemOption = new ExpandParser(edm, odata).parse(expandTokenizer,
+ contextTypes.peek() instanceof EdmStructuredType ? (EdmStructuredType) contextTypes.peek() : null);
+ checkOptionEOF(expandTokenizer, optionName, optionValue);
+ } else {
+ throw new UriValidationException("Expand is only allowed on structured types!",
+ UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED, optionName);
}
} else if (optionName.equals(SystemQueryOptionKind.ID.toString())) {
IdOptionImpl idOption = new IdOptionImpl();
idOption.setText(optionValue);
+ if (optionValue == null || optionValue.isEmpty()) {
+ throw new UriParserSyntaxException("Illegal value of $id option!",
+ UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
+ optionName, optionValue);
+ }
idOption.setValue(optionValue);
systemOption = idOption;
@@ -234,10 +227,8 @@ public class Parser {
} else if (optionName.equals(SystemQueryOptionKind.ORDERBY.toString())) {
UriTokenizer orderByTokenizer = new UriTokenizer(optionValue);
systemOption = new OrderByParser(edm, odata).parse(orderByTokenizer,
- context.contextTypes.peek() instanceof EdmStructuredType ?
- (EdmStructuredType) context.contextTypes.peek() :
- null,
- context.contextUriInfo.getEntitySetNames());
+ contextTypes.peek() instanceof EdmStructuredType ? (EdmStructuredType) contextTypes.peek() : null,
+ contextUriInfo.getEntitySetNames());
checkOptionEOF(orderByTokenizer, optionName, optionValue);
} else if (optionName.equals(SystemQueryOptionKind.SEARCH.toString())) {
@@ -246,40 +237,31 @@ public class Parser {
} else if (optionName.equals(SystemQueryOptionKind.SELECT.toString())) {
UriTokenizer selectTokenizer = new UriTokenizer(optionValue);
systemOption = new SelectParser(edm).parse(selectTokenizer,
- context.contextTypes.peek() instanceof EdmStructuredType ?
- (EdmStructuredType) context.contextTypes.peek() :
- null,
- context.isCollection);
+ contextTypes.peek() instanceof EdmStructuredType ? (EdmStructuredType) contextTypes.peek() : null,
+ contextIsCollection);
checkOptionEOF(selectTokenizer, optionName, optionValue);
} else if (optionName.equals(SystemQueryOptionKind.SKIP.toString())) {
SkipOptionImpl skipOption = new SkipOptionImpl();
skipOption.setText(optionValue);
- try {
- skipOption.setValue(Integer.parseInt(optionValue));
- } catch (final NumberFormatException e) {
- throw new UriParserSyntaxException("Illegal value of $skip option!", e,
- UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
- optionName, optionValue);
- }
+ skipOption.setValue(ParserHelper.parseNonNegativeInteger(optionName, optionValue, true));
systemOption = skipOption;
} else if (optionName.equals(SystemQueryOptionKind.SKIPTOKEN.toString())) {
SkipTokenOptionImpl skipTokenOption = new SkipTokenOptionImpl();
skipTokenOption.setText(optionValue);
+ if (optionValue == null || optionValue.isEmpty()) {
+ throw new UriParserSyntaxException("Illegal value of $skiptoken option!",
+ UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
+ optionName, optionValue);
+ }
skipTokenOption.setValue(optionValue);
systemOption = skipTokenOption;
} else if (optionName.equals(SystemQueryOptionKind.TOP.toString())) {
TopOptionImpl topOption = new TopOptionImpl();
topOption.setText(optionValue);
- try {
- topOption.setValue(Integer.parseInt(optionValue));
- } catch (final NumberFormatException e) {
- throw new UriParserSyntaxException("Illegal value of $top option!", e,
- UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
- optionName, optionValue);
- }
+ topOption.setValue(ParserHelper.parseNonNegativeInteger(optionName, optionValue, true));
systemOption = topOption;
} else if (optionName.equals(SystemQueryOptionKind.COUNT.toString())) {
@@ -299,14 +281,14 @@ public class Parser {
UriParserSyntaxException.MessageKeys.UNKNOWN_SYSTEM_QUERY_OPTION, optionName);
}
try {
- context.contextUriInfo.setSystemQueryOption(systemOption);
+ contextUriInfo.setSystemQueryOption(systemOption);
} catch (final ODataRuntimeException e) {
throw new UriParserSyntaxException("Double system query option!", e,
UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION, optionName);
}
} else if (optionName.startsWith(AT)) {
- if (context.contextUriInfo.getAlias(optionName) == null) {
+ if (contextUriInfo.getAlias(optionName) == null) {
// TODO: Create a proper alias-value parser that can parse also common expressions.
Expression expression = null;
UriTokenizer aliasTokenizer = new UriTokenizer(optionValue);
@@ -318,13 +300,13 @@ public class Parser {
} else {
UriTokenizer aliasValueTokenizer = new UriTokenizer(optionValue);
expression = new ExpressionParser(edm, odata).parse(aliasValueTokenizer, null,
- context.contextUriInfo.getEntitySetNames());
+ contextUriInfo.getEntitySetNames());
if (!aliasValueTokenizer.next(TokenKind.EOF)) {
throw new UriParserSyntaxException("Illegal value for alias '" + optionName + "'.",
UriParserSyntaxException.MessageKeys.SYNTAX);
}
}
- context.contextUriInfo.addAlias((AliasQueryOption) new AliasQueryOptionImpl()
+ contextUriInfo.addAlias((AliasQueryOption) new AliasQueryOptionImpl()
.setAliasValue(expression)
.setName(optionName)
.setText(NULL.equals(optionValue) ? null : optionValue));
@@ -334,11 +316,11 @@ public class Parser {
}
} else {
- context.contextUriInfo.addCustomQueryOption((CustomQueryOption) option);
+ contextUriInfo.addCustomQueryOption((CustomQueryOption) option);
}
}
- return context.contextUriInfo;
+ return contextUriInfo;
}
private void ensureLastSegment(final String segment, final int pos, final int size)
@@ -362,102 +344,4 @@ public class Parser {
optionName, optionValue);
}
}
-
- private ParserRuleContext parseRule(final String input, final ParserEntryRules entryPoint)
- throws UriParserSyntaxException {
- UriParserParser parser = null;
- UriLexer lexer = null;
- ParserRuleContext ret = null;
-
- // Use 2 stage approach to improve performance
- // see https://github.com/antlr/antlr4/issues/192
-
- // stage = 1
- try {
-
- // create parser
- lexer = new UriLexer(new ANTLRInputStream(input));
- parser = new UriParserParser(new CommonTokenStream(lexer));
-
- // Set error strategy
- addStage1ErrorStrategy(parser);
-
- // Set error collector
- addStage1ErrorListener(parser);
-
- // user the faster LL parsing
- parser.getInterpreter().setPredictionMode(PredictionMode.SLL);
-
- // parse
- switch (entryPoint) {
- case ExpandItems:
- lexer.mode(Lexer.DEFAULT_MODE);
- ret = parser.expandItemsEOF();
- break;
- default:
- break;
-
- }
-
- } catch (ParseCancellationException hardException) {
- // stage = 2
- try {
-
- // create parser
- lexer = new UriLexer(new ANTLRInputStream(input));
- parser = new UriParserParser(new CommonTokenStream(lexer));
-
- // Set error strategy
- addStage2ErrorStrategy(parser);
-
- // Set error collector
- addStage2ErrorListener(parser);
-
- // Use the slower SLL parsing
- parser.getInterpreter().setPredictionMode(PredictionMode.LL);
-
- // parse
- switch (entryPoint) {
- case ExpandItems:
- lexer.mode(Lexer.DEFAULT_MODE);
- ret = parser.expandItemsEOF();
- break;
- default:
- break;
- }
-
- } catch (final RecognitionException weakException) {
- throw new UriParserSyntaxException("Error in syntax", weakException,
- UriParserSyntaxException.MessageKeys.SYNTAX);
-
- // exceptionOnStage = 2;
- }
- } catch (final RecognitionException hardException) {
- throw new UriParserSyntaxException("Error in syntax", hardException,
- UriParserSyntaxException.MessageKeys.SYNTAX);
- }
-
- return ret;
- }
-
- protected void addStage1ErrorStrategy(final UriParserParser parser) {
- // Throw exception at first syntax error
- parser.setErrorHandler(new BailErrorStrategy());
-
- }
-
- protected void addStage2ErrorStrategy(final UriParserParser parser) {
- // Throw exception at first syntax error
- parser.setErrorHandler(new BailErrorStrategy());
- }
-
- protected void addStage1ErrorListener(final UriParserParser parser) {
- // No error logging to System.out or System.err, only exceptions used (depending on ErrorStrategy)
- parser.removeErrorListeners();
- }
-
- protected void addStage2ErrorListener(final UriParserParser parser) {
- // No error logging to System.out or System.err, only exceptions used (depending on ErrorStrategy)
- parser.removeErrorListeners();
- }
}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/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 65ee461..7f4abf7 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
@@ -23,6 +23,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmKeyPropertyRef;
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
@@ -36,6 +37,8 @@ import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.uri.UriParameter;
import org.apache.olingo.server.api.uri.UriResourcePartTyped;
+import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
+import org.apache.olingo.server.api.uri.queryoption.expression.Literal;
import org.apache.olingo.server.core.ODataImpl;
import org.apache.olingo.server.core.uri.UriParameterImpl;
import org.apache.olingo.server.core.uri.UriResourceTypedImpl;
@@ -88,8 +91,9 @@ public class ParserHelper {
TokenKind.EnumValue);
}
- protected static List<UriParameter> parseFunctionParameters(UriTokenizer tokenizer, final boolean withComplex)
- throws UriParserException {
+ protected static List<UriParameter> parseFunctionParameters(UriTokenizer tokenizer,
+ final Edm edm, final EdmType referringType, final boolean withComplex)
+ throws UriParserException, UriValidationException {
List<UriParameter> parameters = new ArrayList<UriParameter>();
ParserHelper.requireNext(tokenizer, TokenKind.OPEN);
if (tokenizer.next(TokenKind.CLOSE)) {
@@ -115,6 +119,13 @@ public class ParserHelper {
throw new UriParserSemanticException("A JSON array or object is not allowed as parameter value.",
UriParserSemanticException.MessageKeys.COMPLEX_PARAMETER_IN_RESOURCE_PATH, tokenizer.getText());
}
+ } else if (withComplex) {
+ final Expression expression = new ExpressionParser(edm, odata).parse(tokenizer, referringType, null);
+ parameters.add(new UriParameterImpl().setName(name)
+ .setText(expression instanceof Literal ?
+ "null".equals(((Literal) expression).getText()) ? null : ((Literal) expression).getText() :
+ null)
+ .setExpression(expression instanceof Literal ? null : expression));
} else if (nextPrimitiveValue(tokenizer) == null) {
throw new UriParserSemanticException("Wrong parameter value.",
UriParserSemanticException.MessageKeys.INVALID_KEY_VALUE, "");
@@ -387,4 +398,23 @@ public class ParserHelper {
return type;
}
+
+ protected static int parseNonNegativeInteger(final String optionName, final String optionValue,
+ final boolean zeroAllowed) throws UriParserException {
+ int value;
+ try {
+ value = Integer.parseInt(optionValue);
+ } catch (final NumberFormatException e) {
+ throw new UriParserSyntaxException("Illegal value of '" + optionName + "' option!", e,
+ UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
+ optionName, optionValue);
+ }
+ if (value > 0 || value == 0 && zeroAllowed) {
+ return value;
+ } else {
+ throw new UriParserSyntaxException("Illegal value of '" + optionName + "' option!",
+ UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
+ optionName, optionValue);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.java
index 1cd4d7a..87cb91a 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.java
@@ -261,30 +261,33 @@ public class ResourcePathParser {
throws UriParserException, UriValidationException {
final FullQualifiedName name = new FullQualifiedName(tokenizer.getText());
requireTyped(previous, name.getFullQualifiedNameAsString());
- final UriResourcePartTyped previousTyped = (UriResourcePartTyped) previous;
- final EdmType previousTypeFilter = getPreviousTypeFilter(previousTyped);
- final EdmType previousType = previousTypeFilter == null ? previousTyped.getType() : previousTypeFilter;
- final EdmAction boundAction = edm.getBoundAction(name,
- previousType.getFullQualifiedName(),
- previousTyped.isCollection());
- if (boundAction != null) {
- ParserHelper.requireTokenEnd(tokenizer);
- return new UriResourceActionImpl(boundAction);
- }
- EdmStructuredType type = edm.getEntityType(name);
- if (type == null) {
- type = edm.getComplexType(name);
- }
- if (type != null) {
- return typeCast(name, type, previousTyped);
- }
- if (tokenizer.next(TokenKind.EOF)) {
- throw new UriParserSemanticException("Type '" + name.getFullQualifiedNameAsString() + "' not found.",
- UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, name.getFullQualifiedNameAsString());
- }
- return functionCall(null, name,
- previousType.getFullQualifiedName(),
- previousTyped.isCollection());
+ final UriResourcePartTyped previousTyped = (UriResourcePartTyped) previous;
+ final EdmType previousTypeFilter = getPreviousTypeFilter(previousTyped);
+ final EdmType previousType = previousTypeFilter == null ? previousTyped.getType() : previousTypeFilter;
+
+ // We check for bound actions first because they cannot be followed by anything.
+ final EdmAction boundAction =
+ edm.getBoundAction(name, previousType.getFullQualifiedName(), previousTyped.isCollection());
+ if (boundAction != null) {
+ ParserHelper.requireTokenEnd(tokenizer);
+ return new UriResourceActionImpl(boundAction);
+ }
+
+ // Type casts can be syntactically indistinguishable from bound function calls in the case of additional keys.
+ // But normally they are shorter, so they come next.
+ final EdmStructuredType type = previousTyped.getType() instanceof EdmEntityType ?
+ edm.getEntityType(name) :
+ edm.getComplexType(name);
+ if (type != null) {
+ return typeCast(name, type, previousTyped);
+ }
+ if (tokenizer.next(TokenKind.EOF)) {
+ throw new UriParserSemanticException("Type '" + name.getFullQualifiedNameAsString() + "' not found.",
+ UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, name.getFullQualifiedNameAsString());
+ }
+
+ // Now a bound function call is the only remaining option.
+ return functionCall(null, name, previousType.getFullQualifiedName(), previousTyped.isCollection());
}
private void requireTyped(final UriResource previous, final String forWhat) throws UriParserException {
@@ -317,8 +320,13 @@ public class ResourcePathParser {
((UriResourceWithKeysImpl) previousTyped).setEntryTypeFilter(type);
}
if (tokenizer.next(TokenKind.OPEN)) {
- ((UriResourceWithKeysImpl) previousTyped).setKeyPredicates(
- ParserHelper.parseKeyPredicate(tokenizer, (EdmEntityType) type, null));
+ final List<UriParameter> keys = ParserHelper.parseKeyPredicate(tokenizer, (EdmEntityType) type, null);
+ if (previousTyped.isCollection()) {
+ ((UriResourceWithKeysImpl) previousTyped).setKeyPredicates(keys);
+ } else {
+ throw new UriParserSemanticException("Key not allowed here.",
+ UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED);
+ }
}
} else {
previousTypeFilter = ((UriResourceTypedImpl) previousTyped).getTypeFilter();
@@ -351,7 +359,7 @@ public class ResourcePathParser {
private UriResource functionCall(final EdmFunctionImport edmFunctionImport,
final FullQualifiedName boundFunctionName, final FullQualifiedName bindingParameterTypeName,
final boolean isBindingParameterCollection) throws UriParserException, UriValidationException {
- final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, false);
+ final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, edm, null, false);
final List<String> names = ParserHelper.getParameterNames(parameters);
EdmFunction function = null;
if (edmFunctionImport != null) {
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/SearchParser.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/SearchParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/SearchParser.java
new file mode 100644
index 0000000..a072327
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/SearchParser.java
@@ -0,0 +1,108 @@
+/*
+ * 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.api.uri.queryoption.SearchOption;
+import org.apache.olingo.server.api.uri.queryoption.search.SearchBinaryOperatorKind;
+import org.apache.olingo.server.api.uri.queryoption.search.SearchExpression;
+import org.apache.olingo.server.api.uri.queryoption.search.SearchTerm;
+import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
+import org.apache.olingo.server.core.uri.parser.search.SearchBinaryImpl;
+import org.apache.olingo.server.core.uri.parser.search.SearchParserException;
+import org.apache.olingo.server.core.uri.parser.search.SearchTermImpl;
+import org.apache.olingo.server.core.uri.parser.search.SearchUnaryImpl;
+import org.apache.olingo.server.core.uri.queryoption.SearchOptionImpl;
+
+/**
+ * Parses search expressions according to the following (rewritten) grammar:
+ * <pre>
+ * SearchExpr ::= ExprOR
+ * ExprOR ::= ExprAnd ('OR' ExprAnd)*
+ * ExprAnd ::= Term ('AND'? Term)*
+ * Term ::= ('NOT'? (Word | Phrase)) | ('(' SearchExpr ')')
+ * </pre>
+ */
+public class SearchParser {
+
+ public SearchOption parse(UriTokenizer tokenizer) throws SearchParserException {
+ SearchOptionImpl searchOption = new SearchOptionImpl();
+ searchOption.setSearchExpression(processExprOr(tokenizer));
+ return searchOption;
+ }
+
+ private SearchExpression processExprOr(UriTokenizer tokenizer) throws SearchParserException {
+ SearchExpression left = processExprAnd(tokenizer);
+
+ while (tokenizer.next(TokenKind.OrOperatorSearch)) {
+ final SearchExpression right = processExprAnd(tokenizer);
+ left = new SearchBinaryImpl(left, SearchBinaryOperatorKind.OR, right);
+ }
+
+ return left;
+ }
+
+ private SearchExpression processExprAnd(UriTokenizer tokenizer) throws SearchParserException {
+ SearchExpression left = processTerm(tokenizer);
+
+ while (tokenizer.next(TokenKind.AndOperatorSearch)) { // Could be whitespace or whitespace-surrounded 'AND'.
+ final SearchExpression right = processTerm(tokenizer);
+ left = new SearchBinaryImpl(left, SearchBinaryOperatorKind.AND, right);
+ }
+
+ return left;
+ }
+
+ private SearchExpression processTerm(UriTokenizer tokenizer) throws SearchParserException {
+ if (tokenizer.next(TokenKind.OPEN)) {
+ final SearchExpression expr = processExprOr(tokenizer);
+ if (!tokenizer.next(TokenKind.CLOSE)) {
+ throw new SearchParserException("Missing close parenthesis after open parenthesis.",
+ SearchParserException.MessageKeys.MISSING_CLOSE);
+ }
+ return expr;
+ } else if (tokenizer.next(TokenKind.NotOperatorSearch)) {
+ return processNot(tokenizer);
+ } else if (tokenizer.next(TokenKind.Word)) {
+ return new SearchTermImpl(tokenizer.getText());
+ } else if (tokenizer.next(TokenKind.Phrase)) {
+ return processPhrase(tokenizer);
+ } else {
+ throw new SearchParserException("Expected PHRASE or WORD not found.",
+ SearchParserException.MessageKeys.EXPECTED_DIFFERENT_TOKEN, "PHRASE, WORD", "");
+ }
+ }
+
+ private SearchExpression processNot(UriTokenizer tokenizer) throws SearchParserException {
+ if (tokenizer.next(TokenKind.Word)) {
+ return new SearchUnaryImpl(new SearchTermImpl(tokenizer.getText()));
+ } else if (tokenizer.next(TokenKind.Phrase)) {
+ return new SearchUnaryImpl(processPhrase(tokenizer));
+ } else {
+ throw new SearchParserException("NOT must be followed by a term.",
+ SearchParserException.MessageKeys.INVALID_NOT_OPERAND, "");
+ }
+ }
+
+ private SearchTerm processPhrase(UriTokenizer tokenizer) {
+ final String literal = tokenizer.getText();
+ return new SearchTermImpl(literal.substring(1, literal.length() - 1)
+ .replace("\\\"", "\"")
+ .replace("\\\\", "\\"));
+ }
+}