[OLINGO-834] URI resource-path parser in Java Signed-off-by: Michael Bolz <[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/927ecb93 Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/927ecb93 Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/927ecb93 Branch: refs/heads/OLINGO-834_Filter_Parser Commit: 927ecb93e34a8a1d4b5ff1dfefb96882b50a028b Parents: d88913f Author: Klaus Straubinger <[email protected]> Authored: Fri Dec 4 15:47:51 2015 +0100 Committer: Michael Bolz <[email protected]> Committed: Mon Dec 7 13:17:50 2015 +0100 ---------------------------------------------------------------------- .../olingo/fit/tecsvc/client/BasicITCase.java | 2 +- .../core/edm/primitivetype/EdmBinary.java | 12 +- .../edm/provider/EdmActionImportImplTest.java | 2 +- .../core/edm/provider/EdmProviderImplTest.java | 4 +- .../core/edm/provider/EdmSchemaImplTest.java | 1 + .../apache/olingo/server/core/ErrorHandler.java | 8 +- .../olingo/server/core/ServiceDispatcher.java | 4 +- .../olingo/server/core/ServiceRequest.java | 6 +- .../apache/olingo/server/core/ODataHandler.java | 4 +- .../olingo/server/core/uri/UriHelperImpl.java | 9 +- .../uri/UriResourceNavigationPropertyImpl.java | 5 +- .../core/uri/UriResourceWithKeysImpl.java | 3 +- .../olingo/server/core/uri/parser/Parser.java | 202 +++--- .../core/uri/parser/ResourcePathParser.java | 692 +++++++++++++++++++ .../core/uri/parser/UriParseTreeVisitor.java | 2 +- .../server/core/uri/parser/UriTokenizer.java | 592 ++++++++++++++++ .../server/core/uri/validator/UriValidator.java | 74 +- .../core/uri/parser/UriTokenizerTest.java | 369 ++++++++++ .../uri/parser/search/SearchTokenizerTest.java | 1 - .../server/tecsvc/data/DataProviderTest.java | 4 +- .../server/core/PreconditionsValidatorTest.java | 25 +- .../serializer/utils/ContextURLHelperTest.java | 4 +- .../olingo/server/core/uri/UriHelperTest.java | 4 +- .../server/core/uri/UriResourceImplTest.java | 14 +- .../core/uri/antlr/TestFullResourcePath.java | 154 +++-- .../core/uri/antlr/TestUriParserImpl.java | 15 +- .../server/core/uri/parser/ParserTest.java | 4 +- .../core/uri/testutil/FilterValidator.java | 62 +- .../core/uri/testutil/ParserWithLogging.java | 5 +- .../core/uri/testutil/ResourceValidator.java | 13 +- .../core/uri/testutil/TestUriValidator.java | 14 +- .../core/uri/validator/UriValidatorTest.java | 17 +- 32 files changed, 1973 insertions(+), 354 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/927ecb93/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java ---------------------------------------------------------------------- diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java index a09cafa..c8e4848 100644 --- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java @@ -245,7 +245,7 @@ public class BasicITCase extends AbstractParamTecSvcITCase { } catch (final ODataClientErrorException e) { assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), e.getStatusLine().getStatusCode()); final ODataError error = e.getODataError(); - assertThat(error.getMessage(), containsString("key property")); + assertThat(error.getMessage(), containsString("key")); } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/927ecb93/lib/commons-core/src/main/java/org/apache/olingo/commons/core/edm/primitivetype/EdmBinary.java ---------------------------------------------------------------------- diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/edm/primitivetype/EdmBinary.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/edm/primitivetype/EdmBinary.java index 735250a..bdbc3fa 100644 --- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/edm/primitivetype/EdmBinary.java +++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/edm/primitivetype/EdmBinary.java @@ -133,19 +133,19 @@ public class EdmBinary extends SingletonPrimitiveType { final Boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale, final Boolean isUnicode) { - return value == null - ? isNullable == null || isNullable - : isBase64(value.getBytes(UTF_8)) && validateMaxLength(value, maxLength); + return value == null ? + isNullable == null || isNullable : + isBase64(value.getBytes(UTF_8)) && validateMaxLength(value, maxLength); } private static boolean validateMaxLength(final String value, final Integer maxLength) { - return maxLength == null ? true - : // Every three bytes are represented as four base-64 characters. + return maxLength == null ? true : + // Every three bytes are represented as four base-64 characters. // Additionally, there could be up to two padding "=" characters // if the number of bytes is not a multiple of three, // and there could be line feeds, possibly with carriage returns. maxLength >= (value.length() - lineEndingsLength(value)) * 3 / 4 - - (value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0); + - (value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0); } private static int lineEndingsLength(final String value) { http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/927ecb93/lib/commons-core/src/test/java/org/apache/olingo/server/core/edm/provider/EdmActionImportImplTest.java ---------------------------------------------------------------------- diff --git a/lib/commons-core/src/test/java/org/apache/olingo/server/core/edm/provider/EdmActionImportImplTest.java b/lib/commons-core/src/test/java/org/apache/olingo/server/core/edm/provider/EdmActionImportImplTest.java index 02824f4..e4cd593 100644 --- a/lib/commons-core/src/test/java/org/apache/olingo/server/core/edm/provider/EdmActionImportImplTest.java +++ b/lib/commons-core/src/test/java/org/apache/olingo/server/core/edm/provider/EdmActionImportImplTest.java @@ -96,7 +96,7 @@ public class EdmActionImportImplTest { String target = "nonExisting"; CsdlActionImport providerActionImport = new CsdlActionImport().setName("actionImportName").setEntitySet(target); EdmProviderImpl edm = mock(EdmProviderImpl.class); - when(edm.getEntityContainer(null)).thenReturn(container); + when(edm.getEntityContainer()).thenReturn(container); EdmActionImport actionImport = new EdmActionImportImpl(edm, container, providerActionImport); actionImport.getReturnedEntitySet(); } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/927ecb93/lib/commons-core/src/test/java/org/apache/olingo/server/core/edm/provider/EdmProviderImplTest.java ---------------------------------------------------------------------- diff --git a/lib/commons-core/src/test/java/org/apache/olingo/server/core/edm/provider/EdmProviderImplTest.java b/lib/commons-core/src/test/java/org/apache/olingo/server/core/edm/provider/EdmProviderImplTest.java index 6ce6ab8..7ba529a 100644 --- a/lib/commons-core/src/test/java/org/apache/olingo/server/core/edm/provider/EdmProviderImplTest.java +++ b/lib/commons-core/src/test/java/org/apache/olingo/server/core/edm/provider/EdmProviderImplTest.java @@ -183,7 +183,7 @@ public class EdmProviderImplTest { when(localProvider.getAliasInfos()).thenThrow(new ODataException("msg")); Edm localEdm = new EdmProviderImpl(localProvider); - localEdm.getEntityContainer(null); + localEdm.getEntityContainer(); } @Test @@ -193,7 +193,7 @@ public class EdmProviderImplTest { assertEquals(FQN.getNamespace(), entityContainer.getNamespace()); assertEquals(FQN.getName(), entityContainer.getName()); - entityContainer = edm.getEntityContainer(null); + entityContainer = edm.getEntityContainer(); assertNotNull(entityContainer); assertEquals(FQN.getNamespace(), entityContainer.getNamespace()); assertEquals(FQN.getName(), entityContainer.getName()); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/927ecb93/lib/commons-core/src/test/java/org/apache/olingo/server/core/edm/provider/EdmSchemaImplTest.java ---------------------------------------------------------------------- diff --git a/lib/commons-core/src/test/java/org/apache/olingo/server/core/edm/provider/EdmSchemaImplTest.java b/lib/commons-core/src/test/java/org/apache/olingo/server/core/edm/provider/EdmSchemaImplTest.java index e6502af..2c95ce1 100644 --- a/lib/commons-core/src/test/java/org/apache/olingo/server/core/edm/provider/EdmSchemaImplTest.java +++ b/lib/commons-core/src/test/java/org/apache/olingo/server/core/edm/provider/EdmSchemaImplTest.java @@ -229,6 +229,7 @@ public class EdmSchemaImplTest { assertTrue(container == edm.getEntityContainer(new FullQualifiedName(schema.getNamespace(), container.getName()))); assertTrue(container == edm.getEntityContainer(null)); + assertTrue(container == edm.getEntityContainer()); } private class LocalProvider implements CsdlEdmProvider { http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/927ecb93/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ErrorHandler.java ---------------------------------------------------------------------- diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ErrorHandler.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ErrorHandler.java index 5f425da..33f65cd 100644 --- a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ErrorHandler.java +++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ErrorHandler.java @@ -90,13 +90,15 @@ public class ErrorHandler { final ODataServerError serverError) { ContentType requestedContentType; try { - UriInfo uriInfo = new Parser().parseUri(request.getRawODataPath(), request.getRawQueryPath(), - null, this.metadata.getEdm()); + final UriInfo uriInfo = new Parser(metadata.getEdm(), odata) + .parseUri(request.getRawODataPath(), request.getRawQueryPath(), null); requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, this.customContent, RepresentationType.ERROR); } catch (final ContentNegotiatorException e) { requestedContentType = ContentType.JSON; - } catch (UriParserException e) { + } catch (final UriParserException e) { + requestedContentType = ContentType.JSON; + } catch (final UriValidationException e) { requestedContentType = ContentType.JSON; } processError(response, serverError, requestedContentType); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/927ecb93/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceDispatcher.java ---------------------------------------------------------------------- diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceDispatcher.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceDispatcher.java index 17ec358..45de757 100644 --- a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceDispatcher.java +++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceDispatcher.java @@ -76,8 +76,8 @@ public class ServiceDispatcher extends RequestURLHierarchyVisitor { public void execute(ODataRequest odRequest, ODataResponse odResponse) throws ODataLibraryException, ODataApplicationException { - UriInfo uriInfo = new Parser().parseUri(odRequest.getRawODataPath(), odRequest.getRawQueryPath(), null, - this.metadata.getEdm()); + UriInfo uriInfo = new Parser(this.metadata.getEdm(), odata) + .parseUri(odRequest.getRawODataPath(), odRequest.getRawQueryPath(), null); new UriValidator().validate(uriInfo, odRequest.getMethod()); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/927ecb93/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceRequest.java ---------------------------------------------------------------------- diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceRequest.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceRequest.java index 8195b85..0796144 100644 --- a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceRequest.java +++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceRequest.java @@ -47,6 +47,7 @@ import org.apache.olingo.server.api.uri.UriInfo; import org.apache.olingo.server.core.requests.DataRequest; import org.apache.olingo.server.core.uri.parser.Parser; import org.apache.olingo.server.core.uri.parser.UriParserException; +import org.apache.olingo.server.core.uri.validator.UriValidationException; public abstract class ServiceRequest { protected OData odata; @@ -240,7 +241,7 @@ public abstract class ServiceRequest { return null; } - public DataRequest parseLink(URI uri) throws UriParserException, URISyntaxException { + public DataRequest parseLink(URI uri) throws UriParserException, UriValidationException, URISyntaxException { String path = "/"; URI servicePath = new URI(getODataRequest().getRawBaseUri()); path = servicePath.getPath(); @@ -253,8 +254,7 @@ public abstract class ServiceRequest { rawPath = rawPath.substring(e+path.length()); } - UriInfo uriInfo = new Parser().parseUri(rawPath, uri.getQuery(), null, - this.serviceMetadata.getEdm()); + UriInfo uriInfo = new Parser(serviceMetadata.getEdm(), odata).parseUri(rawPath, uri.getQuery(), null); ServiceDispatcher dispatcher = new ServiceDispatcher(odata, serviceMetadata, null, customContentType); dispatcher.visit(uriInfo); return (DataRequest)dispatcher.request; http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/927ecb93/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java index 12f2dfd..8c2e286 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java @@ -129,8 +129,8 @@ public class ODataHandler { final int measurementUriParser = debugger.startRuntimeMeasurement("UriParser", "parseUri"); try { - uriInfo = new Parser().parseUri(request.getRawODataPath(), request.getRawQueryPath(), null, - serviceMetadata.getEdm()); + uriInfo = new Parser(serviceMetadata.getEdm(), odata) + .parseUri(request.getRawODataPath(), request.getRawQueryPath(), null); } catch (final ODataLibraryException e) { debugger.stopRuntimeMeasurement(measurementUriParser); debugger.stopRuntimeMeasurement(measurementHandle); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/927ecb93/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriHelperImpl.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriHelperImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriHelperImpl.java index c463765..3bc5ad0 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriHelperImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriHelperImpl.java @@ -29,6 +29,7 @@ import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; import org.apache.olingo.commons.api.edm.EdmProperty; import org.apache.olingo.commons.api.edm.EdmStructuredType; import org.apache.olingo.commons.core.Encoder; +import org.apache.olingo.server.api.ODataLibraryException; import org.apache.olingo.server.api.deserializer.DeserializerException; import org.apache.olingo.server.api.deserializer.DeserializerException.MessageKeys; import org.apache.olingo.server.api.serializer.SerializerException; @@ -39,9 +40,9 @@ import org.apache.olingo.server.api.uri.UriResourceEntitySet; import org.apache.olingo.server.api.uri.UriResourceKind; import org.apache.olingo.server.api.uri.queryoption.ExpandOption; import org.apache.olingo.server.api.uri.queryoption.SelectOption; +import org.apache.olingo.server.core.ODataImpl; import org.apache.olingo.server.core.serializer.utils.ContextURLHelper; import org.apache.olingo.server.core.uri.parser.Parser; -import org.apache.olingo.server.core.uri.parser.UriParserException; public class UriHelperImpl implements UriHelper { @@ -107,8 +108,8 @@ public class UriHelperImpl implements UriHelper { oDataPath = oDataPath.startsWith("/") ? oDataPath : "/" + oDataPath; try { - final List<UriResource> uriResourceParts = new Parser().parseUri(oDataPath, null, null, edm) - .getUriResourceParts(); + final List<UriResource> uriResourceParts = + new Parser(edm, new ODataImpl()).parseUri(oDataPath, null, null).getUriResourceParts(); if (uriResourceParts.size() == 1 && uriResourceParts.get(0).getKind() == UriResourceKind.entitySet) { final UriResourceEntitySet entityUriResource = (UriResourceEntitySet) uriResourceParts.get(0); @@ -117,7 +118,7 @@ public class UriHelperImpl implements UriHelper { throw new DeserializerException("Invalid entity binding link", MessageKeys.INVALID_ENTITY_BINDING_LINK, entityId); - } catch (UriParserException e) { + } catch (final ODataLibraryException e) { throw new DeserializerException("Invalid entity binding link", e, MessageKeys.INVALID_ENTITY_BINDING_LINK, entityId); } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/927ecb93/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriResourceNavigationPropertyImpl.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriResourceNavigationPropertyImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriResourceNavigationPropertyImpl.java index 1a4fa44..f4390b5 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriResourceNavigationPropertyImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriResourceNavigationPropertyImpl.java @@ -49,10 +49,7 @@ public class UriResourceNavigationPropertyImpl extends UriResourceWithKeysImpl i @Override public boolean isCollection() { - if (keyPredicates != null) { - return false; - } - return navigationProperty.isCollection(); + return navigationProperty.isCollection() && keyPredicates == null; } @Override http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/927ecb93/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriResourceWithKeysImpl.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriResourceWithKeysImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriResourceWithKeysImpl.java index 54d62a1..c300d78 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriResourceWithKeysImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriResourceWithKeysImpl.java @@ -18,7 +18,6 @@ */ package org.apache.olingo.server.core.uri; -import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -48,7 +47,7 @@ public abstract class UriResourceWithKeysImpl extends UriResourceImpl implements public List<UriParameter> getKeyPredicates() { return keyPredicates == null ? Collections.<UriParameter> emptyList() : - new ArrayList<UriParameter>(keyPredicates); + Collections.unmodifiableList(keyPredicates); } public UriResourceWithKeysImpl setKeyPredicates(final List<UriParameter> list) { http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/927ecb93/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 d12b853..5caaaeb 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,7 +18,6 @@ */ package org.apache.olingo.server.core.uri.parser; -import java.util.ArrayList; import java.util.List; import org.antlr.v4.runtime.ANTLRInputStream; @@ -31,11 +30,15 @@ import org.antlr.v4.runtime.Token; 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.ex.ODataRuntimeException; +import org.apache.olingo.server.api.OData; import org.apache.olingo.server.api.uri.UriInfo; import org.apache.olingo.server.api.uri.UriInfoKind; import org.apache.olingo.server.api.uri.UriResource; +import org.apache.olingo.server.api.uri.UriResourceAction; import org.apache.olingo.server.api.uri.UriResourceCount; +import org.apache.olingo.server.api.uri.UriResourceFunction; import org.apache.olingo.server.api.uri.UriResourcePartTyped; import org.apache.olingo.server.api.uri.UriResourceRef; import org.apache.olingo.server.api.uri.UriResourceValue; @@ -46,18 +49,14 @@ import org.apache.olingo.server.api.uri.queryoption.SystemQueryOption; 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.AllEOFContext; -import org.apache.olingo.server.core.uri.antlr.UriParserParser.BatchEOFContext; -import org.apache.olingo.server.core.uri.antlr.UriParserParser.CrossjoinEOFContext; -import org.apache.olingo.server.core.uri.antlr.UriParserParser.EntityEOFContext; import org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandItemsEOFContext; import org.apache.olingo.server.core.uri.antlr.UriParserParser.FilterExpressionEOFContext; -import org.apache.olingo.server.core.uri.antlr.UriParserParser.MetadataEOFContext; import org.apache.olingo.server.core.uri.antlr.UriParserParser.OrderByEOFContext; -import org.apache.olingo.server.core.uri.antlr.UriParserParser.PathSegmentEOFContext; import org.apache.olingo.server.core.uri.antlr.UriParserParser.SelectEOFContext; +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; @@ -71,6 +70,7 @@ import org.apache.olingo.server.core.uri.queryoption.SelectOptionImpl; import org.apache.olingo.server.core.uri.queryoption.SkipOptionImpl; import org.apache.olingo.server.core.uri.queryoption.SkipTokenOptionImpl; import org.apache.olingo.server.core.uri.queryoption.TopOptionImpl; +import org.apache.olingo.server.core.uri.validator.UriValidationException; public class Parser { private static final String ATOM = "atom"; @@ -78,19 +78,22 @@ public class Parser { private static final String XML = "xml"; private static final String AT = "@"; private static final String NULL = "null"; + int logLevel = 0; + private final Edm edm; + private final OData odata; private enum ParserEntryRules { - All, Batch, CrossJoin, Entity, ExpandItems, FilterExpression, Metadata, PathSegment, Orderby, Select, Search + ExpandItems, FilterExpression, Orderby, Select } - public Parser setLogLevel(final int logLevel) { - this.logLevel = logLevel; - return this; + public Parser(final Edm edm, final OData odata) { + this.edm = edm; + this.odata = odata; } - public UriInfo parseUri(final String path, final String query, final String fragment, final Edm edm) - throws UriParserException { + 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); @@ -104,65 +107,74 @@ public class Parser { if (firstSegment.isEmpty()) { ensureLastSegment(firstSegment, 0, uri.pathSegmentListDecoded.size()); context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.service); - } else if (firstSegment.startsWith("$batch")) { - ensureLastSegment(firstSegment, 1, uri.pathSegmentListDecoded.size()); - BatchEOFContext ctxBatchEOF = - (BatchEOFContext) parseRule(uri.pathSegmentListDecoded.get(0), ParserEntryRules.Batch); - uriParseTreeVisitor.visitBatchEOF(ctxBatchEOF); - } else if (firstSegment.startsWith("$metadata")) { + } else if (firstSegment.equals("$batch")) { ensureLastSegment(firstSegment, 1, uri.pathSegmentListDecoded.size()); - MetadataEOFContext ctxMetadataEOF = - (MetadataEOFContext) parseRule(uri.pathSegmentListDecoded.get(0), ParserEntryRules.Metadata); - - uriParseTreeVisitor.visitMetadataEOF(ctxMetadataEOF); + context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.batch); + } else if (firstSegment.equals("$metadata")) { + ensureLastSegment(firstSegment, 1, uri.pathSegmentListDecoded.size()); + context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.metadata); context.contextUriInfo.setFragment(uri.fragment); - } else if (firstSegment.startsWith("$entity")) { - context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.entityId); + } else if (firstSegment.equals("$all")) { + ensureLastSegment(firstSegment, 1, uri.pathSegmentListDecoded.size()); + context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.all); + + } else if (firstSegment.equals("$entity")) { if (uri.pathSegmentListDecoded.size() > 1) { final String typeCastSegment = uri.pathSegmentListDecoded.get(1); ensureLastSegment(typeCastSegment, 2, uri.pathSegmentListDecoded.size()); - EntityEOFContext ctxEntityEOF = - (EntityEOFContext) parseRule(typeCastSegment, ParserEntryRules.Entity); - uriParseTreeVisitor.visitEntityEOF(ctxEntityEOF); + context.contextUriInfo = new ResourcePathParser(edm, odata).parseDollarEntityTypeCast(typeCastSegment); + context.contextTypes.push( + uriParseTreeVisitor.new TypeInformation(context.contextUriInfo.getEntityTypeCast(), false)); + } else { + context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.entityId); } - } else if (firstSegment.startsWith("$all")) { - ensureLastSegment(firstSegment, 1, uri.pathSegmentListDecoded.size()); - AllEOFContext ctxResourcePathEOF = - (AllEOFContext) parseRule(uri.pathSegmentListDecoded.get(0), ParserEntryRules.All); - - uriParseTreeVisitor.visitAllEOF(ctxResourcePathEOF); } else if (firstSegment.startsWith("$crossjoin")) { ensureLastSegment(firstSegment, 1, uri.pathSegmentListDecoded.size()); - CrossjoinEOFContext ctxResourcePathEOF = - (CrossjoinEOFContext) parseRule(uri.pathSegmentListDecoded.get(0), ParserEntryRules.CrossJoin); - - uriParseTreeVisitor.visitCrossjoinEOF(ctxResourcePathEOF); - } else { - List<PathSegmentEOFContext> ctxPathSegments = new ArrayList<PathSegmentEOFContext>(); - for (String pathSegment : uri.pathSegmentListDecoded) { - PathSegmentEOFContext ctxPathSegment = - (PathSegmentEOFContext) parseRule(pathSegment, ParserEntryRules.PathSegment); - ctxPathSegments.add(ctxPathSegment); + context.contextUriInfo = new ResourcePathParser(edm, odata) + .parseCrossjoinSegment(uri.pathSegmentListDecoded.get(0)); + final EdmEntityContainer container = edm.getEntityContainer(); + for (final String name : context.contextUriInfo.getEntitySetNames()) { + context.contextTypes.push( + uriParseTreeVisitor.new TypeInformation(container.getEntitySet(name).getEntityType(), true)); } + } else { context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.resource); - - for (PathSegmentEOFContext ctxPathSegment : ctxPathSegments) { - // add checks for batch, entity, metadata, all, crossjoin - uriParseTreeVisitor.visitPathSegmentEOF(ctxPathSegment); + final ResourcePathParser resourcePathParser = new ResourcePathParser(edm, odata); + int count = 0; + UriResource lastSegment = null; + for (final String pathSegment : uri.pathSegmentListDecoded) { + count++; + final UriResource segment = resourcePathParser.parsePathSegment(pathSegment, lastSegment); + if (segment != null) { + if (segment instanceof UriResourceCount + || segment instanceof UriResourceRef + || segment instanceof UriResourceValue) { + ensureLastSegment(pathSegment, count, uri.pathSegmentListDecoded.size()); + } else if (segment instanceof UriResourceAction + || segment instanceof UriResourceFunction + && !((UriResourceFunction) segment).getFunction().isComposable()) { + if (count < uri.pathSegmentListDecoded.size()) { + throw new UriValidationException( + "The segment of an action or of a non-composable function must be the last resource-path segment.", + UriValidationException.MessageKeys.UNALLOWED_RESOURCE_PATH, + uri.pathSegmentListDecoded.get(count)); + } + lastSegment = segment; + } else if (segment instanceof UriResourceStartingTypeFilterImpl) { + throw new UriParserSemanticException("First resource-path segment must not be namespace-qualified.", + UriParserSemanticException.MessageKeys.NAMESPACE_NOT_ALLOWED_AT_FIRST_ELEMENT); + } else { + lastSegment = segment; + } + context.contextUriInfo.addResourcePart(segment); + } } - UriResource lastSegment = context.contextUriInfo.getLastResourcePart(); - if (lastSegment instanceof UriResourceCount - || lastSegment instanceof UriResourceRef - || lastSegment instanceof UriResourceValue) { - final List<UriResource> parts = context.contextUriInfo.getUriResourceParts(); - lastSegment = parts.get(parts.size() - 2); - } if (lastSegment instanceof UriResourcePartTyped) { UriResourcePartTyped typed = (UriResourcePartTyped) lastSegment; @@ -174,13 +186,12 @@ public class Parser { } // second, read the system query options and the custom query options - for (RawUri.QueryOption option : uri.queryOptionListDecoded) { + for (final RawUri.QueryOption option : uri.queryOptionListDecoded) { if (option.name.startsWith("$")) { SystemQueryOption systemOption = null; if (option.name.equals(SystemQueryOptionKind.FILTER.toString())) { FilterExpressionEOFContext ctxFilterExpression = (FilterExpressionEOFContext) parseRule(option.value, ParserEntryRules.FilterExpression); - systemOption = (FilterOptionImpl) uriParseTreeVisitor.visitFilterExpressionEOF(ctxFilterExpression); } else if (option.name.equals(SystemQueryOptionKind.FORMAT.toString())) { @@ -201,7 +212,6 @@ public class Parser { } else if (option.name.equals(SystemQueryOptionKind.EXPAND.toString())) { ExpandItemsEOFContext ctxExpandItems = (ExpandItemsEOFContext) parseRule(option.value, ParserEntryRules.ExpandItems); - systemOption = (ExpandOptionImpl) uriParseTreeVisitor.visitExpandItemsEOF(ctxExpandItems); } else if (option.name.equals(SystemQueryOptionKind.ID.toString())) { @@ -210,22 +220,24 @@ public class Parser { idOption.setText(option.value); idOption.setValue(option.value); systemOption = idOption; + } else if (option.name.equals(SystemQueryOptionKind.LEVELS.toString())) { throw new UriParserSyntaxException("System query option '$levels' is allowed only inside '$expand'!", UriParserSyntaxException.MessageKeys.SYSTEM_QUERY_OPTION_LEVELS_NOT_ALLOWED_HERE); + } else if (option.name.equals(SystemQueryOptionKind.ORDERBY.toString())) { OrderByEOFContext ctxOrderByExpression = (OrderByEOFContext) parseRule(option.value, ParserEntryRules.Orderby); - systemOption = (OrderByOptionImpl) uriParseTreeVisitor.visitOrderByEOF(ctxOrderByExpression); + } else if (option.name.equals(SystemQueryOptionKind.SEARCH.toString())) { - SearchParser searchParser = new SearchParser(); - systemOption = searchParser.parse(option.value); + systemOption = new SearchParser().parse(option.value); + } else if (option.name.equals(SystemQueryOptionKind.SELECT.toString())) { SelectEOFContext ctxSelectEOF = (SelectEOFContext) parseRule(option.value, ParserEntryRules.Select); - systemOption = (SelectOptionImpl) uriParseTreeVisitor.visitSelectEOF(ctxSelectEOF); + } else if (option.name.equals(SystemQueryOptionKind.SKIP.toString())) { SkipOptionImpl skipOption = new SkipOptionImpl(); skipOption.setName(option.name); @@ -238,12 +250,14 @@ public class Parser { option.name, option.value); } systemOption = skipOption; + } else if (option.name.equals(SystemQueryOptionKind.SKIPTOKEN.toString())) { SkipTokenOptionImpl skipTokenOption = new SkipTokenOptionImpl(); skipTokenOption.setName(option.name); skipTokenOption.setText(option.value); skipTokenOption.setValue(option.value); systemOption = skipTokenOption; + } else if (option.name.equals(SystemQueryOptionKind.TOP.toString())) { TopOptionImpl topOption = new TopOptionImpl(); topOption.setName(option.name); @@ -256,6 +270,7 @@ public class Parser { option.name, option.value); } systemOption = topOption; + } else if (option.name.equals(SystemQueryOptionKind.COUNT.toString())) { CountOptionImpl inlineCountOption = new CountOptionImpl(); inlineCountOption.setName(option.name); @@ -268,6 +283,7 @@ public class Parser { option.name, option.value); } systemOption = inlineCountOption; + } else { throw new UriParserSyntaxException("Unknown system query option!", UriParserSyntaxException.MessageKeys.UNKNOWN_SYSTEM_QUERY_OPTION, option.name); @@ -278,12 +294,23 @@ public class Parser { throw new UriParserSyntaxException("Double system query option!", e, UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION, option.name); } + } else if (option.name.startsWith(AT)) { if (context.contextUriInfo.getAlias(option.name) == null) { - final FilterExpressionEOFContext filterExpCtx = - (FilterExpressionEOFContext) parseRule(option.value, ParserEntryRules.FilterExpression); - final Expression expression = ((FilterOption) uriParseTreeVisitor.visitFilterExpressionEOF(filterExpCtx)) - .getExpression(); + // TODO: Create a proper alias-value parser that can parse also common expressions. + Expression expression = null; + if (!option.value.isEmpty() && (option.value.charAt(0) == '[' || option.value.charAt(0) == '{')) { + UriTokenizer tokenizer = new UriTokenizer(option.value); + if (!(tokenizer.next(TokenKind.jsonArrayOrObject) && tokenizer.next(TokenKind.EOF))) { + throw new UriParserSyntaxException("Illegal value for alias '" + option.name + "'.", + UriParserSyntaxException.MessageKeys.SYNTAX); + } + } else { + final FilterExpressionEOFContext filterExpCtx = + (FilterExpressionEOFContext) parseRule(option.value, ParserEntryRules.FilterExpression); + expression = ((FilterOption) uriParseTreeVisitor.visitFilterExpressionEOF(filterExpCtx)) + .getExpression(); + } context.contextUriInfo.addAlias((AliasQueryOption) new AliasQueryOptionImpl() .setAliasValue(expression) .setName(option.name) @@ -292,6 +319,7 @@ public class Parser { throw new UriParserSyntaxException("Alias already specified! Name: " + option.name, UriParserSyntaxException.MessageKeys.DUPLICATED_ALIAS, option.name); } + } else { context.contextUriInfo.addCustomQueryOption((CustomQueryOption) new CustomQueryOptionImpl() @@ -354,21 +382,6 @@ public class Parser { // parse switch (entryPoint) { - case All: - ret = parser.allEOF(); - break; - case Batch: - ret = parser.batchEOF(); - break; - case CrossJoin: - ret = parser.crossjoinEOF(); - break; - case Metadata: - ret = parser.metadataEOF(); - break; - case PathSegment: - ret = parser.pathSegmentEOF(); - break; case FilterExpression: lexer.mode(Lexer.DEFAULT_MODE); ret = parser.filterExpressionEOF(); @@ -381,15 +394,9 @@ public class Parser { lexer.mode(Lexer.DEFAULT_MODE); ret = parser.expandItemsEOF(); break; - case Entity: - ret = parser.entityEOF(); - break; case Select: ret = parser.selectEOF(); break; - case Search: - ret = parser.searchInline(); - break; default: break; @@ -414,21 +421,6 @@ public class Parser { // parse switch (entryPoint) { - case All: - ret = parser.allEOF(); - break; - case Batch: - ret = parser.batchEOF(); - break; - case CrossJoin: - ret = parser.crossjoinEOF(); - break; - case Metadata: - ret = parser.metadataEOF(); - break; - case PathSegment: - ret = parser.pathSegmentEOF(); - break; case FilterExpression: lexer.mode(Lexer.DEFAULT_MODE); ret = parser.filterExpressionEOF(); @@ -441,15 +433,9 @@ public class Parser { lexer.mode(Lexer.DEFAULT_MODE); ret = parser.expandItemsEOF(); break; - case Entity: - ret = parser.entityEOF(); - break; case Select: ret = parser.selectEOF(); break; - case Search: - ret = parser.searchInline(); - break; default: break; } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/927ecb93/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 new file mode 100644 index 0000000..9852011 --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.java @@ -0,0 +1,692 @@ +/* + * 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 java.util.ArrayList; +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.EdmAction; +import org.apache.olingo.commons.api.edm.EdmActionImport; +import org.apache.olingo.commons.api.edm.EdmEntityContainer; +import org.apache.olingo.commons.api.edm.EdmEntitySet; +import org.apache.olingo.commons.api.edm.EdmEntityType; +import org.apache.olingo.commons.api.edm.EdmFunction; +import org.apache.olingo.commons.api.edm.EdmFunctionImport; +import org.apache.olingo.commons.api.edm.EdmKeyPropertyRef; +import org.apache.olingo.commons.api.edm.EdmNavigationProperty; +import org.apache.olingo.commons.api.edm.EdmPrimitiveType; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; +import org.apache.olingo.commons.api.edm.EdmProperty; +import org.apache.olingo.commons.api.edm.EdmSingleton; +import org.apache.olingo.commons.api.edm.EdmStructuredType; +import org.apache.olingo.commons.api.edm.EdmType; +import org.apache.olingo.commons.api.edm.EdmTypeDefinition; +import org.apache.olingo.commons.api.edm.FullQualifiedName; +import org.apache.olingo.commons.api.edm.constants.EdmTypeKind; +import org.apache.olingo.server.api.OData; +import org.apache.olingo.server.api.uri.UriInfoKind; +import org.apache.olingo.server.api.uri.UriParameter; +import org.apache.olingo.server.api.uri.UriResource; +import org.apache.olingo.server.api.uri.UriResourcePartTyped; +import org.apache.olingo.server.core.uri.UriInfoImpl; +import org.apache.olingo.server.core.uri.UriParameterImpl; +import org.apache.olingo.server.core.uri.UriResourceActionImpl; +import org.apache.olingo.server.core.uri.UriResourceComplexPropertyImpl; +import org.apache.olingo.server.core.uri.UriResourceCountImpl; +import org.apache.olingo.server.core.uri.UriResourceEntitySetImpl; +import org.apache.olingo.server.core.uri.UriResourceFunctionImpl; +import org.apache.olingo.server.core.uri.UriResourceNavigationPropertyImpl; +import org.apache.olingo.server.core.uri.UriResourcePrimitivePropertyImpl; +import org.apache.olingo.server.core.uri.UriResourceRefImpl; +import org.apache.olingo.server.core.uri.UriResourceSingletonImpl; +import org.apache.olingo.server.core.uri.UriResourceTypedImpl; +import org.apache.olingo.server.core.uri.UriResourceValueImpl; +import org.apache.olingo.server.core.uri.UriResourceWithKeysImpl; +import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind; +import org.apache.olingo.server.core.uri.validator.UriValidationException; + +public class ResourcePathParser { + + private final Edm edm; + private final EdmEntityContainer edmEntityContainer; + private final OData odata; + private UriTokenizer tokenizer; + + public ResourcePathParser(final Edm edm, final OData odata) { + this.edm = edm; + edmEntityContainer = edm.getEntityContainer(); + this.odata = odata; + } + + public UriResource parsePathSegment(final String pathSegment, UriResource previous) + throws UriParserException, UriValidationException { + tokenizer = new UriTokenizer(pathSegment); + + // The order is important. + // A qualified name should not be parsed as identifier and let the tokenizer halt at '.'. + + if (previous == null) { + if (tokenizer.next(TokenKind.QualifiedName)) { + throw new UriParserSemanticException("The initial segment must not be namespace-qualified.", + UriParserSemanticException.MessageKeys.NAMESPACE_NOT_ALLOWED_AT_FIRST_ELEMENT, + new FullQualifiedName(tokenizer.getText()).getNamespace()); + } else if (tokenizer.next(TokenKind.ODataIdentifier)) { + return leadingResourcePathSegment(); + } + + } else { + if (tokenizer.next(TokenKind.REF)) { + return ref(previous); + } else if (tokenizer.next(TokenKind.VALUE)) { + return value(previous); + } else if (tokenizer.next(TokenKind.COUNT)) { + return count(previous); + } else if (tokenizer.next(TokenKind.QualifiedName)) { + return boundOperationOrTypeCast(previous); + } else if (tokenizer.next(TokenKind.ODataIdentifier)) { + return navigationOrProperty(previous); + } + } + + throw new UriParserSyntaxException("Unexpected start of resource-path segment.", + UriParserSyntaxException.MessageKeys.SYNTAX); + } + + public UriInfoImpl parseDollarEntityTypeCast(final String pathSegment) throws UriParserException { + UriInfoImpl uriInfo = new UriInfoImpl().setKind(UriInfoKind.entityId); + tokenizer = new UriTokenizer(pathSegment); + requireNext(TokenKind.QualifiedName); + final String name = tokenizer.getText(); + requireTokenEnd(); + final EdmEntityType type = edm.getEntityType(new FullQualifiedName(name)); + if (type == null) { + throw new UriParserSemanticException("Type '" + name + "' not found.", + UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, name); + } else { + uriInfo.setEntityTypeCast(type); + } + return uriInfo; + } + + public UriInfoImpl parseCrossjoinSegment(final String pathSegment) throws UriParserException { + UriInfoImpl uriInfo = new UriInfoImpl().setKind(UriInfoKind.crossjoin); + tokenizer = new UriTokenizer(pathSegment); + requireNext(TokenKind.CROSSJOIN); + requireNext(TokenKind.OPEN); + // At least one entity-set name is mandatory. Try to fetch all. + do { + requireNext(TokenKind.ODataIdentifier); + final String name = tokenizer.getText(); + final EdmEntitySet edmEntitySet = edmEntityContainer.getEntitySet(name); + if (edmEntitySet == null) { + throw new UriParserSemanticException("Expected Entity Set Name.", + UriParserSemanticException.MessageKeys.UNKNOWN_PART, name); + } else { + uriInfo.addEntitySetName(name); + } + } while (tokenizer.next(TokenKind.COMMA)); + requireNext(TokenKind.CLOSE); + requireTokenEnd(); + return uriInfo; + } + + private UriResource ref(final UriResource previous) throws UriParserException { + requireTokenEnd(); + requireTyped(previous, "$ref"); + if (((UriResourcePartTyped) previous).getType() instanceof EdmEntityType) { + return new UriResourceRefImpl(); + } else { + throw new UriParserSemanticException("$ref is only allowed on entity types.", + UriParserSemanticException.MessageKeys.ONLY_FOR_ENTITY_TYPES, "$ref"); + } + } + + private UriResource value(final UriResource previous) throws UriParserException { + requireTokenEnd(); + requireTyped(previous, "$value"); + if (!((UriResourcePartTyped) previous).isCollection()) { + return new UriResourceValueImpl(); + } else { + throw new UriParserSemanticException("$value is only allowed on typed path segments.", + UriParserSemanticException.MessageKeys.ONLY_FOR_TYPED_PARTS, "$value"); + } + } + + private UriResource count(final UriResource previous) throws UriParserException { + requireTokenEnd(); + requireTyped(previous, "$count"); + if (((UriResourcePartTyped) previous).isCollection()) { + return new UriResourceCountImpl(); + } else { + throw new UriParserSemanticException("$count is only allowed on collections.", + UriParserSemanticException.MessageKeys.ONLY_FOR_COLLECTIONS, "$count"); + } + } + + private UriResource leadingResourcePathSegment() throws UriParserException, UriValidationException { + final String oDataIdentifier = tokenizer.getText(); + + final EdmEntitySet edmEntitySet = edmEntityContainer.getEntitySet(oDataIdentifier); + if (edmEntitySet != null) { + final UriResourceEntitySetImpl entitySetResource = new UriResourceEntitySetImpl().setEntitSet(edmEntitySet); + + if (tokenizer.next(TokenKind.OPEN)) { + final List<UriParameter> keyPredicates = keyPredicate(entitySetResource.getEntityType(), null); + entitySetResource.setKeyPredicates(keyPredicates); + } + + requireTokenEnd(); + return entitySetResource; + } + + final EdmSingleton edmSingleton = edmEntityContainer.getSingleton(oDataIdentifier); + if (edmSingleton != null) { + requireTokenEnd(); + return new UriResourceSingletonImpl().setSingleton(edmSingleton); + } + + final EdmActionImport edmActionImport = edmEntityContainer.getActionImport(oDataIdentifier); + if (edmActionImport != null) { + requireTokenEnd(); + return new UriResourceActionImpl().setActionImport(edmActionImport); + } + + final EdmFunctionImport edmFunctionImport = edmEntityContainer.getFunctionImport(oDataIdentifier); + if (edmFunctionImport != null) { + return functionCall(edmFunctionImport, null, null, false); + } + + if (tokenizer.next(TokenKind.OPEN) || tokenizer.next(TokenKind.EOF)) { + throw new UriParserSemanticException("Unexpected start of resource-path segment.", + UriParserSemanticException.MessageKeys.RESOURCE_NOT_FOUND, oDataIdentifier); + } else { + throw new UriParserSyntaxException("Unexpected start of resource-path segment.", + UriParserSyntaxException.MessageKeys.SYNTAX); + } + } + + private UriResource navigationOrProperty(final UriResource previous) + throws UriParserException, UriValidationException { + final String name = tokenizer.getText(); + + UriResourcePartTyped previousTyped = null; + EdmStructuredType structType = null; + requireTyped(previous, name); + if (((UriResourcePartTyped) previous).getType() instanceof EdmStructuredType) { + previousTyped = (UriResourcePartTyped) previous; + final EdmType previousTypeFilter = getPreviousTypeFilter(previousTyped); + structType = (EdmStructuredType) (previousTypeFilter == null ? previousTyped.getType() : previousTypeFilter); + } else { + throw new UriParserSemanticException( + "Cannot parse '" + name + "'; previous path segment is not a structural type.", + UriParserSemanticException.MessageKeys.RESOURCE_PART_MUST_BE_PRECEDED_BY_STRUCTURAL_TYPE, name); + } + + if (previousTyped.isCollection()) { + throw new UriParserSemanticException("Property '" + name + "' is not allowed after collection.", + UriParserSemanticException.MessageKeys.PROPERTY_AFTER_COLLECTION, name); + } + + final EdmProperty property = structType.getStructuralProperty(name); + if (property != null) { + return property.isPrimitive() + || property.getType().getKind() == EdmTypeKind.ENUM + || property.getType().getKind() == EdmTypeKind.DEFINITION ? + new UriResourcePrimitivePropertyImpl().setProperty(property) : + new UriResourceComplexPropertyImpl().setProperty(property); + } + final EdmNavigationProperty navigationProperty = structType.getNavigationProperty(name); + if (navigationProperty == null) { + throw new UriParserSemanticException("Property '" + name + "' not found in type '" + + structType.getFullQualifiedName().getFullQualifiedNameAsString() + "'", + UriParserSemanticException.MessageKeys.PROPERTY_NOT_IN_TYPE, + structType.getFullQualifiedName().getFullQualifiedNameAsString(), name); + } + List<UriParameter> keyPredicate = null; + if (tokenizer.next(TokenKind.OPEN)) { + if (navigationProperty.isCollection()) { + keyPredicate = keyPredicate(navigationProperty.getType(), navigationProperty.getPartner()); + } else { + throw new UriParserSemanticException("A key is not allowed on non-collection navigation properties.", + UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED); + } + } + requireTokenEnd(); + return new UriResourceNavigationPropertyImpl() + .setNavigationProperty(navigationProperty) + .setKeyPredicates(keyPredicate); + } + + private UriResource boundOperationOrTypeCast(UriResource previous) + 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) { + requireTokenEnd(); + return new UriResourceActionImpl().setAction(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()); + } + + private void requireTyped(final UriResource previous, final String forWhat) throws UriParserException { + if (previous == null || !(previous instanceof UriResourcePartTyped)) { + throw new UriParserSemanticException("Path segment before '" + forWhat + "' is not typed.", + UriParserSemanticException.MessageKeys.PREVIOUS_PART_NOT_TYPED, forWhat); + } + } + + private List<UriParameter> keyPredicate(final EdmEntityType edmEntityType, final EdmNavigationProperty partner) + throws UriParserException, UriValidationException { + final List<EdmKeyPropertyRef> keyPropertyRefs = edmEntityType.getKeyPropertyRefs(); + if (tokenizer.next(TokenKind.CLOSE)) { + throw new UriParserSemanticException( + "Expected " + keyPropertyRefs.size() + " key predicates but none.", + UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES, + Integer.toString(keyPropertyRefs.size()), "0"); + } + List<UriParameter> keys = new ArrayList<UriParameter>(); + Map<String, String> referencedNames = new HashMap<String, String>(); + + if (partner != null) { + // Prepare list of potentially missing keys to be filled from referential constraints. + for (final String name : edmEntityType.getKeyPredicateNames()) { + final String referencedName = partner.getReferencingPropertyName(name); + if (referencedName != null) { + referencedNames.put(name, referencedName); + } + } + } + + if (tokenizer.next(TokenKind.ODataIdentifier)) { + keys.addAll(compoundKey(edmEntityType)); + } else if (keyPropertyRefs.size() - referencedNames.size() == 1) { + for (final EdmKeyPropertyRef candidate : keyPropertyRefs) { + if (referencedNames.get(candidate.getName()) == null) { + keys.add(simpleKey(candidate)); + break; + } + } + } else { + throw new UriParserSemanticException( + "Expected " + (keyPropertyRefs.size() -referencedNames.size()) + " key predicates but found one.", + UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES, + Integer.toString(keyPropertyRefs.size() - referencedNames.size()), "1"); + } + + if (keys.size() < keyPropertyRefs.size() && partner != null) { + // Fill missing keys from referential constraints. + for (final String name : edmEntityType.getKeyPredicateNames()) { + boolean found = false; + for (final UriParameter key : keys) { + if (name.equals(key.getName())) { + found = true; + break; + } + } + if (!found && referencedNames.get(name) != null) { + keys.add(0, new UriParameterImpl().setName(name).setReferencedProperty(referencedNames.get(name))); + } + } + } + + // Check that all key predicates are filled from the URI. + if (keys.size() < keyPropertyRefs.size()) { + throw new UriParserSemanticException( + "Expected " + keyPropertyRefs.size() + " key predicates but found " + keys.size() + ".", + UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES, + Integer.toString(keyPropertyRefs.size()), Integer.toString(keys.size())); + } else { + return keys; + } + } + + private UriParameter simpleKey(final EdmKeyPropertyRef edmKeyPropertyRef) + throws UriParserException, UriValidationException { + final EdmProperty edmProperty = edmKeyPropertyRef == null ? null : edmKeyPropertyRef.getProperty(); + if (nextPrimitiveTypeValue( + edmProperty == null ? null : (EdmPrimitiveType) edmProperty.getType(), + edmProperty == null ? false : edmProperty.isNullable())) { + final String literalValue = tokenizer.getText(); + requireNext(TokenKind.CLOSE); + return createUriParameter(edmProperty, edmKeyPropertyRef.getName(), literalValue); + } else { + throw new UriParserSemanticException("The key value is not valid.", + UriParserSemanticException.MessageKeys.INVALID_KEY_VALUE, edmKeyPropertyRef.getName()); + } + } + + private List<UriParameter> compoundKey(final EdmEntityType edmEntityType) + throws UriParserException, UriValidationException { + + List<UriParameter> parameters = new ArrayList<UriParameter>(); + List<String> parameterNames = new ArrayList<String>(); + + // To validate that each key predicate is exactly specified once, we use a list to pick from. + List<String> remainingKeyNames = new ArrayList<String>(edmEntityType.getKeyPredicateNames()); + + // At least one key predicate is mandatory. Try to fetch all. + boolean hasComma = false; + do { + final String keyPredicateName = tokenizer.getText(); + if (parameterNames.contains(keyPredicateName)) { + throw new UriValidationException("Duplicated key property " + keyPredicateName, + UriValidationException.MessageKeys.DOUBLE_KEY_PROPERTY, keyPredicateName); + } + if (remainingKeyNames.isEmpty()) { + throw new UriParserSemanticException("Too many key properties.", + UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES, + Integer.toString(parameters.size()), Integer.toString(parameters.size() + 1)); + } + if (!remainingKeyNames.remove(keyPredicateName)) { + throw new UriValidationException("Unknown key property " + keyPredicateName, + UriValidationException.MessageKeys.INVALID_KEY_PROPERTY, keyPredicateName); + } + parameters.add(keyValuePair(keyPredicateName, edmEntityType)); + parameterNames.add(keyPredicateName); + hasComma = tokenizer.next(TokenKind.COMMA); + if (hasComma) { + requireNext(TokenKind.ODataIdentifier); + } + } while (hasComma); + requireNext(TokenKind.CLOSE); + + return parameters; + } + + private UriParameter keyValuePair(final String keyPredicateName, final EdmEntityType edmEntityType) + throws UriParserException, UriValidationException { + final EdmKeyPropertyRef keyPropertyRef = edmEntityType.getKeyPropertyRef(keyPredicateName); + final EdmProperty edmProperty = keyPropertyRef == null ? null : keyPropertyRef.getProperty(); + if (edmProperty == null) { + throw new UriValidationException(keyPredicateName + " is not a valid key property name.", + UriValidationException.MessageKeys.INVALID_KEY_PROPERTY, keyPredicateName); + } + requireNext(TokenKind.EQ); + if (tokenizer.next(TokenKind.COMMA) || tokenizer.next(TokenKind.CLOSE) || tokenizer.next(TokenKind.EOF)) { + throw new UriParserSyntaxException("Key value expected.", UriParserSyntaxException.MessageKeys.SYNTAX); + } + if (nextPrimitiveTypeValue((EdmPrimitiveType) edmProperty.getType(), edmProperty.isNullable())) { + return createUriParameter(edmProperty, keyPredicateName, tokenizer.getText()); + } else { + throw new UriParserSemanticException(keyPredicateName + " has not a valid key value.", + UriParserSemanticException.MessageKeys.INVALID_KEY_VALUE, keyPredicateName); + } + } + + private UriParameter createUriParameter(final EdmProperty edmProperty, final String parameterName, + final String literalValue) throws UriParserException, UriValidationException { + if (literalValue.startsWith("@")) { + return new UriParameterImpl() + .setName(parameterName) + .setAlias(literalValue); + } + + final EdmPrimitiveType primitiveType = (EdmPrimitiveType) edmProperty.getType(); + try { + if (!(primitiveType.validate(primitiveType.fromUriLiteral(literalValue), edmProperty.isNullable(), + edmProperty.getMaxLength(), edmProperty.getPrecision(), edmProperty.getScale(), edmProperty.isUnicode()))) { + throw new UriValidationException("Invalid key property", + UriValidationException.MessageKeys.INVALID_KEY_PROPERTY, parameterName); + } + } catch (final EdmPrimitiveTypeException e) { + throw new UriValidationException("Invalid key property", + UriValidationException.MessageKeys.INVALID_KEY_PROPERTY, parameterName); + } + + return new UriParameterImpl() + .setName(parameterName) + .setText("null".equals(literalValue) ? null : literalValue); + } + + private UriResource typeCast(final FullQualifiedName name, final EdmStructuredType type, + final UriResourcePartTyped previousTyped) throws UriParserException, UriValidationException { + if (type.compatibleTo(previousTyped.getType())) { + EdmType previousTypeFilter = null; + if (previousTyped instanceof UriResourceWithKeysImpl) { + if (previousTyped.isCollection()) { + previousTypeFilter = ((UriResourceWithKeysImpl) previousTyped).getTypeFilterOnCollection(); + if (previousTypeFilter != null) { + throw new UriParserSemanticException("Type filters are not chainable.", + UriParserSemanticException.MessageKeys.TYPE_FILTER_NOT_CHAINABLE, + previousTypeFilter.getName(), type.getName()); + } + ((UriResourceWithKeysImpl) previousTyped).setCollectionTypeFilter(type); + } else { + previousTypeFilter = ((UriResourceWithKeysImpl) previousTyped).getTypeFilterOnEntry(); + if (previousTypeFilter != null) { + throw new UriParserSemanticException("Type filters are not chainable.", + UriParserSemanticException.MessageKeys.TYPE_FILTER_NOT_CHAINABLE, + previousTypeFilter.getName(), type.getName()); + } + ((UriResourceWithKeysImpl) previousTyped).setEntryTypeFilter(type); + } + if (tokenizer.next(TokenKind.OPEN)) { + ((UriResourceWithKeysImpl) previousTyped).setKeyPredicates( + keyPredicate((EdmEntityType) type, null)); + } + } else { + previousTypeFilter = ((UriResourceTypedImpl) previousTyped).getTypeFilter(); + if (previousTypeFilter != null) { + throw new UriParserSemanticException("Type filters are not chainable.", + UriParserSemanticException.MessageKeys.TYPE_FILTER_NOT_CHAINABLE, + previousTypeFilter.getName(), type.getName()); + } + ((UriResourceTypedImpl) previousTyped).setTypeFilter(type); + } + requireTokenEnd(); + return null; + } else { + throw new UriParserSemanticException( + "Type filter not compatible to previous path segment: " + name.getFullQualifiedNameAsString(), + UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER, name.getFullQualifiedNameAsString()); + } + } + + private EdmType getPreviousTypeFilter(final UriResourcePartTyped previousTyped) { + if (previousTyped instanceof UriResourceWithKeysImpl) { + return ((UriResourceWithKeysImpl) previousTyped).getTypeFilterOnEntry() == null ? + ((UriResourceWithKeysImpl) previousTyped).getTypeFilterOnCollection() : + ((UriResourceWithKeysImpl) previousTyped).getTypeFilterOnEntry(); + } else { + return ((UriResourceTypedImpl) previousTyped).getTypeFilter(); + } + } + + private UriResource functionCall(final EdmFunctionImport edmFunctionImport, + final FullQualifiedName boundFunctionName, final FullQualifiedName bindingParameterTypeName, + final boolean isBindingParameterCollection) throws UriParserException, UriValidationException { + final List<UriParameter> parameters = functionParameters(); + List<String> names = new ArrayList<String>(); + for (final UriParameter parameter : parameters) { + names.add(parameter.getName()); + } + EdmFunction function = null; + if (edmFunctionImport != null) { + function = edmFunctionImport.getUnboundFunction(names); + if (function == null) { + throw new UriParserSemanticException( + "Function of function import '" + edmFunctionImport.getName() + "' " + + "with parameters " + names.toString() + " not found.", + UriParserSemanticException.MessageKeys.FUNCTION_NOT_FOUND, edmFunctionImport.getName(), names.toString()); + } + } else { + function = edm.getBoundFunction(boundFunctionName, + bindingParameterTypeName, isBindingParameterCollection, names); + if (function == null) { + throw new UriParserSemanticException( + "Function " + boundFunctionName + " not found.", + UriParserSemanticException.MessageKeys.UNKNOWN_PART, boundFunctionName.getFullQualifiedNameAsString()); + } + } + UriResourceFunctionImpl resource = new UriResourceFunctionImpl() + .setFunctionImport(edmFunctionImport, null) + .setFunction(function) + .setParameters(parameters); + if (tokenizer.next(TokenKind.OPEN)) { + if (function.getReturnType() != null + && function.getReturnType().getType().getKind() == EdmTypeKind.ENTITY + && function.getReturnType().isCollection()) { + resource.setKeyPredicates( + keyPredicate((EdmEntityType) function.getReturnType().getType(), null)); + } else { + throw new UriParserSemanticException("A key is not allowed.", + UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED); + } + } + requireTokenEnd(); + return resource; + } + + private List<UriParameter> functionParameters() throws UriParserException { + List<UriParameter> parameters = new ArrayList<UriParameter>(); + requireNext(TokenKind.OPEN); + if (tokenizer.next(TokenKind.CLOSE)) { + return parameters; + } + do { + requireNext(TokenKind.ODataIdentifier); + final String name = tokenizer.getText(); + if (parameters.contains(name)) { + throw new UriParserSemanticException("Duplicated function parameter " + name, + UriParserSemanticException.MessageKeys.INVALID_KEY_VALUE, name); + } + requireNext(TokenKind.EQ); + if (tokenizer.next(TokenKind.COMMA) || tokenizer.next(TokenKind.CLOSE) || tokenizer.next(TokenKind.EOF)) { + throw new UriParserSyntaxException("Parameter value expected.", UriParserSyntaxException.MessageKeys.SYNTAX); + } + if (nextPrimitiveValue()) { + final String literalValue = tokenizer.getText(); + UriParameterImpl parameter = new UriParameterImpl().setName(name); + parameters.add(literalValue.startsWith("@") ? + parameter.setAlias(literalValue) : + parameter.setText("null".equals(literalValue) ? null : literalValue)); + } else { + throw new UriParserSemanticException("Wrong parameter value.", + UriParserSemanticException.MessageKeys.INVALID_KEY_VALUE, ""); + } + } while (tokenizer.next(TokenKind.COMMA)); + requireNext(TokenKind.CLOSE); + return parameters; + } + + private void requireNext(final TokenKind kind) throws UriParserException { + if (!tokenizer.next(kind)) { + throw new UriParserSyntaxException("Expected token '" + kind.toString() + "' not found.", + UriParserSyntaxException.MessageKeys.SYNTAX); + } + } + + private void requireTokenEnd() throws UriParserException { + requireNext(TokenKind.EOF); + } + + private boolean nextPrimitiveTypeValue(final EdmPrimitiveType primitiveType, final boolean nullable) { + final EdmPrimitiveType type = primitiveType instanceof EdmTypeDefinition ? + ((EdmTypeDefinition) primitiveType).getUnderlyingType() : + primitiveType; + if (tokenizer.next(TokenKind.ParameterAliasName)) { + return true; + } else if (nullable && tokenizer.next(TokenKind.NULL)) { + return true; + + } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean).equals(type)) { + return tokenizer.next(TokenKind.PrimitiveBooleanValue); + } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.String).equals(type)) { + return tokenizer.next(TokenKind.PrimitiveStringValue); + } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.SByte).equals(type) + || odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Byte).equals(type) + || odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Int16).equals(type) + || odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Int32).equals(type) + || odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Int64).equals(type)) { + return tokenizer.next(TokenKind.PrimitiveIntegerValue); + } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Guid).equals(type)) { + return tokenizer.next(TokenKind.PrimitiveGuidValue); + } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Date).equals(type)) { + return tokenizer.next(TokenKind.PrimitiveDateValue); + } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.DateTimeOffset).equals(type)) { + return tokenizer.next(TokenKind.PrimitiveDateTimeOffsetValue); + } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.TimeOfDay).equals(type)) { + return tokenizer.next(TokenKind.PrimitiveTimeOfDayValue); + } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Decimal).equals(type)) { + // The order is important. + // A decimal value should not be parsed as integer and let the tokenizer stop at the decimal point. + return tokenizer.next(TokenKind.PrimitiveDecimalValue) + || tokenizer.next(TokenKind.PrimitiveIntegerValue); + } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Double).equals(type)) { + // The order is important. + // A floating-point value should not be parsed as decimal and let the tokenizer stop at 'E'. + // A decimal value should not be parsed as integer and let the tokenizer stop at the decimal point. + return tokenizer.next(TokenKind.PrimitiveDoubleValue) + || tokenizer.next(TokenKind.PrimitiveDecimalValue) + || tokenizer.next(TokenKind.PrimitiveIntegerValue); + } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Duration).equals(type)) { + return tokenizer.next(TokenKind.PrimitiveDurationValue); + } else if (odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Binary).equals(type)) { + return tokenizer.next(TokenKind.PrimitiveBinaryValue); + } else if (type.getKind() == EdmTypeKind.ENUM) { + return tokenizer.next(TokenKind.PrimitiveEnumValue); + } else { + return false; + } + } + + private boolean nextPrimitiveValue() { + return tokenizer.next(TokenKind.ParameterAliasName) + || tokenizer.next(TokenKind.NULL) + || tokenizer.next(TokenKind.PrimitiveBooleanValue) + || tokenizer.next(TokenKind.PrimitiveStringValue) + + // The order of the next seven expressions is important in order to avoid + // finding partly parsed tokens (counter-intuitive as it may be, even a GUID may start with digits ...). + || tokenizer.next(TokenKind.PrimitiveDoubleValue) + || tokenizer.next(TokenKind.PrimitiveDecimalValue) + || tokenizer.next(TokenKind.PrimitiveGuidValue) + || tokenizer.next(TokenKind.PrimitiveDateTimeOffsetValue) + || tokenizer.next(TokenKind.PrimitiveDateValue) + || tokenizer.next(TokenKind.PrimitiveTimeOfDayValue) + || tokenizer.next(TokenKind.PrimitiveIntegerValue) + + || tokenizer.next(TokenKind.PrimitiveDurationValue) + || tokenizer.next(TokenKind.PrimitiveBinaryValue) + || tokenizer.next(TokenKind.PrimitiveEnumValue); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/927ecb93/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java index 1e33a19..8740d66 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java @@ -250,7 +250,7 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> { public UriParseTreeVisitor(final Edm edm, final UriContext context) { this.edm = edm; this.context = context; - edmEntityContainer = edm.getEntityContainer(null); + edmEntityContainer = edm.getEntityContainer(); } @Override
