blob: 8a493fff6c549904eed7058e457a2beb3820d40e [file] [log] [blame]
/*
* 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.Collection;
import java.util.Map;
import org.apache.olingo.commons.api.edm.Edm;
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.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.AliasQueryOption;
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.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;
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;
private final Map<String, AliasQueryOption> aliases;
private final Collection<String> crossjoinEntitySetNames;
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 {
// 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();
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 = ParserHelper.parseTypeCast(tokenizer, edm, referencedType);
if (typeCast != null) {
item.setTypeFilter(typeCast);
ParserHelper.requireNext(tokenizer, TokenKind.SLASH);
}
UriInfoImpl resource = parseExpandPath(tokenizer, edm, referencedType, item);
UriResourcePartTyped lastPart = (UriResourcePartTyped) resource.getLastResourcePart();
boolean hasSlash = false;
EdmStructuredType typeCastSuffix = null;
if (tokenizer.next(TokenKind.SLASH)) {
hasSlash = true;
if (lastPart instanceof UriResourceNavigation) {
UriResourceNavigationPropertyImpl navigationResource = (UriResourceNavigationPropertyImpl) lastPart;
final EdmNavigationProperty navigationProperty = navigationResource.getProperty();
typeCastSuffix = ParserHelper.parseTypeCast(tokenizer, edm, navigationProperty.getType());
if (typeCastSuffix != null) {
if (navigationProperty.isCollection()) {
navigationResource.setCollectionTypeFilter(typeCastSuffix);
} else {
navigationResource.setEntryTypeFilter(typeCastSuffix);
}
hasSlash = false;
}
}
}
final EdmStructuredType newReferencedType = typeCastSuffix != null ? typeCastSuffix
: (EdmStructuredType) lastPart.getType();
final boolean newReferencedIsCollection = lastPart.isCollection();
if (hasSlash || tokenizer.next(TokenKind.SLASH)) {
if (tokenizer.next(TokenKind.REF)) {
resource.addResourcePart(new UriResourceRefImpl());
item.setIsRef(true);
parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, true, false);
} else {
ParserHelper.requireNext(tokenizer, TokenKind.COUNT);
resource.addResourcePart(new UriResourceCountImpl());
item.setCountPath(true);
parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, false, true);
}
} else {
parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, false, false);
}
item.setResourcePath(resource);
}
return item;
}
protected static UriInfoImpl parseExpandPath(UriTokenizer tokenizer, final Edm edm,
final EdmStructuredType referencedType, ExpandItemImpl item) 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 = ParserHelper.parseTypeCast(tokenizer, edm, 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) {
if (tokenizer.next(TokenKind.STAR)) {
item.setIsStar(true);
} else {
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, aliases, null).parse(tokenizer, referencedType);
} else if (tokenizer.next(TokenKind.FILTER)) {
ParserHelper.requireNext(tokenizer, TokenKind.EQ);
systemQueryOption = new FilterParser(edm, odata).parse(tokenizer, referencedType, null, aliases);
} 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, aliases);
} 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;
}
}