[OLINGO-904] Allow expand on crossjoin with entity set level

Future changes still needed to support navigation/options afterwards
This commit is contained in:
Christian Amend 2016-03-18 14:35:06 +01:00
parent 8406c58b7f
commit e5d1e823c5
3 changed files with 82 additions and 22 deletions

View File

@ -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<String, AliasQueryOption> aliases;
private final Collection<String> crossjoinEntitySetNames;
public ExpandParser(final Edm edm, final OData odata, final Map<String, AliasQueryOption> aliases) {
public ExpandParser(final Edm edm, final OData odata, final Map<String, AliasQueryOption> aliases,
final Collection<String> 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);

View File

@ -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<String, AliasQueryOption> aliases) throws UriParserException, UriValidationException {
private void parseExpandOption(ExpandOption expandOption, final EdmType contextType, final boolean isAll,
final List<String> entitySetNames, final Map<String, AliasQueryOption> 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()) {

View File

@ -946,6 +946,27 @@ public class TestFullResourcePath {
.is("<<ESTwoPrim/PropertyString> eq <ESMixPrimCollComp/PropertyComp/PropertyString>>");
}
@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);