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 index 03750a596..2fc0fafcf 100644 --- 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 @@ -6,9 +6,9 @@ * 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 @@ -18,6 +18,7 @@ */ package org.apache.olingo.server.core.uri.parser; +import java.util.Collection; import java.util.Map; import org.apache.olingo.commons.api.edm.Edm; @@ -41,6 +42,7 @@ 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.UriResourceEntitySetImpl; 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; @@ -57,24 +59,58 @@ public class ExpandParser { private final Edm edm; private final OData odata; private final Map aliases; + private final Collection crossjoinEntitySetNames; - public ExpandParser(final Edm edm, final OData odata, final Map aliases) { + public ExpandParser(final Edm edm, final OData odata, final Map aliases, + final Collection crossjoinEntitySetNames) { this.edm = edm; this.odata = odata; this.aliases = aliases; + this.crossjoinEntitySetNames = crossjoinEntitySetNames; } 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); + // In the crossjoin case the start has to be an EntitySet name which will dictate the reference type + if (crossjoinEntitySetNames != null && !crossjoinEntitySetNames.isEmpty()) { + final ExpandItem item = parseCrossJoinItem(tokenizer); + expandOption.addExpandItem(item); + } else { + final ExpandItem item = parseItem(tokenizer, referencedType); + expandOption.addExpandItem(item); + } } while (tokenizer.next(TokenKind.COMMA)); return expandOption; } + private ExpandItem parseCrossJoinItem(UriTokenizer tokenizer) throws UriParserSemanticException { + ExpandItemImpl item = new ExpandItemImpl(); + if (tokenizer.next(TokenKind.STAR)) { + item.setIsStar(true); + } else if (tokenizer.next(TokenKind.ODataIdentifier)) { + String entitySetName = tokenizer.getText(); + if (crossjoinEntitySetNames.contains(entitySetName)) { + UriInfoImpl resource = new UriInfoImpl().setKind(UriInfoKind.resource); + final UriResourceEntitySetImpl entitySetResourceSegment = + new UriResourceEntitySetImpl(edm.getEntityContainer().getEntitySet(entitySetName)); + resource.addResourcePart(entitySetResourceSegment); + + item.setResourcePath(resource); + } else { + throw new UriParserSemanticException("Unknown crossjoin entity set.", + UriParserSemanticException.MessageKeys.UNKNOWN_PART, entitySetName); + } + } else { + throw new UriParserSemanticException("If the target resource is a crossjoin an entity set is " + + "needed as the starting point.", + UriParserSemanticException.MessageKeys.UNKNOWN_PART); + } + return item; + } + private ExpandItem parseItem(UriTokenizer tokenizer, final EdmStructuredType referencedType) throws UriParserException, UriValidationException { ExpandItemImpl item = new ExpandItemImpl(); @@ -167,6 +203,7 @@ public class ExpandParser { EdmStructuredType type = referencedType; String name = null; + while (tokenizer.next(TokenKind.ODataIdentifier)) { name = tokenizer.getText(); final EdmProperty property = referencedType.getStructuralProperty(name); @@ -217,7 +254,7 @@ public class ExpandParser { } else if (!forRef && !forCount && tokenizer.next(TokenKind.EXPAND)) { ParserHelper.requireNext(tokenizer, TokenKind.EQ); - systemQueryOption = new ExpandParser(edm, odata, aliases).parse(tokenizer, referencedType); + systemQueryOption = new ExpandParser(edm, odata, aliases, null).parse(tokenizer, referencedType); } else if (tokenizer.next(TokenKind.FILTER)) { ParserHelper.requireNext(tokenizer, TokenKind.EQ); 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 e1313c1a7..b4469d891 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 @@ -6,9 +6,9 @@ * 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 @@ -98,15 +98,15 @@ public class Parser { try { contextUriInfo.setQueryOption(parsedOption == null ? option : parsedOption); } catch (final ODataRuntimeException e) { - throw new UriParserSyntaxException( - parsedOption instanceof SystemQueryOption ? - "Double system query option!" : - "Alias already specified! Name: " + optionName, - e, - parsedOption instanceof SystemQueryOption ? - UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION : - UriParserSyntaxException.MessageKeys.DUPLICATED_ALIAS, - optionName); + throw new UriParserSyntaxException( + parsedOption instanceof SystemQueryOption ? + "Double system query option!" : + "Alias already specified! Name: " + optionName, + e, + parsedOption instanceof SystemQueryOption ? + UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION : + UriParserSyntaxException.MessageKeys.DUPLICATED_ALIAS, + optionName); } } @@ -209,7 +209,7 @@ public class Parser { parseOrderByOption(contextUriInfo.getOrderByOption(), contextType, contextUriInfo.getEntitySetNames(), contextUriInfo.getAliasMap()); parseExpandOption(contextUriInfo.getExpandOption(), contextType, - !contextUriInfo.getEntitySetNames().isEmpty() || contextUriInfo.getKind() == UriInfoKind.all, + contextUriInfo.getKind() == UriInfoKind.all, contextUriInfo.getEntitySetNames(), contextUriInfo.getAliasMap()); parseSelectOption(contextUriInfo.getSelectOption(), contextType, contextIsCollection); @@ -335,16 +335,18 @@ public class Parser { } } - private void parseExpandOption(ExpandOption expandOption, final EdmType contextType, final boolean isCrossjoinOrAll, - final Map aliases) throws UriParserException, UriValidationException { + private void parseExpandOption(ExpandOption expandOption, final EdmType contextType, final boolean isAll, + final List entitySetNames, final Map aliases) throws UriParserException, + UriValidationException { if (expandOption != null) { - if (!(contextType instanceof EdmStructuredType || isCrossjoinOrAll)) { + if (!(contextType instanceof EdmStructuredType || isAll + || (entitySetNames != null && !entitySetNames.isEmpty()))) { throw new UriValidationException("Expand is only allowed on structured types!", UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED, expandOption.getName()); } final String optionValue = expandOption.getText(); UriTokenizer expandTokenizer = new UriTokenizer(optionValue); - final ExpandOption option = new ExpandParser(edm, odata, aliases).parse(expandTokenizer, + final ExpandOption option = new ExpandParser(edm, odata, aliases, entitySetNames).parse(expandTokenizer, contextType instanceof EdmStructuredType ? (EdmStructuredType) contextType : null); checkOptionEOF(expandTokenizer, expandOption.getName(), optionValue); for (final ExpandItem item : option.getExpandItems()) { diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java index 32c56b469..64cf7578f 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java @@ -946,6 +946,27 @@ public class TestFullResourcePath { .is("< eq >"); } + @Test + public void crossjoinExpand() throws Exception { + testUri.run("$crossjoin(ESTwoPrim,ESAllPrim)", + "$expand=ESTwoPrim") + .goExpand() + .first().goPath().first().isEntitySet("ESTwoPrim"); + + testUri.run("$crossjoin(ESTwoPrim,ESAllPrim)", + "$expand=ESTwoPrim,ESAllPrim") + .goExpand() + .first().goPath().first().isEntitySet("ESTwoPrim") + .goUpExpandValidator().next().goPath().first().isEntitySet("ESAllPrim"); + + //TODO: Once crossjoin is implemented these tests should no longer result in errors +// testUri.run("$crossjoin(ESTwoPrim,ESAllPrim)", +// "$expand=ESAllPrim/NavPropertyETTwoPrimOne") +// .goExpand() +// .first().goPath().at(0).isEntitySet("ESAllPrim") +// .at(1).isNavProperty("NavPropertyETTwoPrimOne", new FullQualifiedName("Namespace1_Alias.ETTwoPrim"), false); + } + @Test public void crossjoinError() throws Exception { testUri.runEx("$crossjoin").isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);