[OLINGO-834] $expand parser in Java + clean-up
Signed-off-by: Christian Amend <christian.amend@sap.com>
This commit is contained in:
parent
8919d3ef11
commit
8925274c0b
|
@ -35,11 +35,8 @@ import org.apache.olingo.commons.api.edm.FullQualifiedName;
|
|||
import org.apache.olingo.commons.api.http.HttpHeader;
|
||||
import org.apache.olingo.commons.api.http.HttpStatusCode;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
// TODO
|
||||
@Ignore
|
||||
public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase {
|
||||
|
||||
private static final String ES_COMP_ALL_PRIM = "ESCompAllPrim";
|
||||
|
@ -212,7 +209,7 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase {
|
|||
sendRequest(ES_TWO_KEY_NAV, "PropertyComp/PropertyComp/PropertyBoolean eq not null");
|
||||
assertEquals(0, result.getBody().getEntities().size());
|
||||
|
||||
result = sendRequest(ES_TWO_KEY_NAV, "PropertyComp/PropertyComp/PropertyBoolean eq 0 add -(5 add null)");
|
||||
result = sendRequest(ES_TWO_KEY_NAV, "PropertyComp/PropertyComp/PropertyInt16 eq 0 add -(5 add null)");
|
||||
assertEquals(0, result.getBody().getEntities().size());
|
||||
}
|
||||
|
||||
|
@ -357,7 +354,7 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase {
|
|||
|
||||
@Test
|
||||
public void notOperator() {
|
||||
ODataRetrieveResponse<ClientEntitySet> result = sendRequest(ES_TWO_KEY_NAV, "not(PropertyInt16 eq 1)");
|
||||
ODataRetrieveResponse<ClientEntitySet> result = sendRequest(ES_TWO_KEY_NAV, "not (PropertyInt16 eq 1)");
|
||||
assertEquals(2, result.getBody().getEntities().size());
|
||||
|
||||
ClientEntity clientEntity = result.getBody().getEntities().get(0);
|
||||
|
|
|
@ -30,11 +30,8 @@ import org.apache.olingo.client.api.domain.ClientEntitySet;
|
|||
import org.apache.olingo.client.api.domain.ClientValuable;
|
||||
import org.apache.olingo.commons.api.http.HttpStatusCode;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
// TODO
|
||||
@Ignore
|
||||
public class OrderBySystemQueryITCase extends AbstractParamTecSvcITCase {
|
||||
|
||||
private static final String ES_TWO_PRIM = "ESTwoPrim";
|
||||
|
@ -74,7 +71,7 @@ public class OrderBySystemQueryITCase extends AbstractParamTecSvcITCase {
|
|||
|
||||
@Test
|
||||
public void multipleOrderBy() {
|
||||
final ODataRetrieveResponse<ClientEntitySet> response = sendRequest(ES_ALL_PRIM, "PropertyByte, PropertyInt16");
|
||||
final ODataRetrieveResponse<ClientEntitySet> response = sendRequest(ES_ALL_PRIM, "PropertyByte,PropertyInt16");
|
||||
assertEquals(3, response.getBody().getEntities().size());
|
||||
|
||||
ClientEntity clientEntity = response.getBody().getEntities().get(0);
|
||||
|
@ -90,7 +87,7 @@ public class OrderBySystemQueryITCase extends AbstractParamTecSvcITCase {
|
|||
@Test
|
||||
public void multipleOrderByDescending() {
|
||||
final ODataRetrieveResponse<ClientEntitySet> response =
|
||||
sendRequest(ES_ALL_PRIM, "PropertyByte, PropertyInt16 desc");
|
||||
sendRequest(ES_ALL_PRIM, "PropertyByte,PropertyInt16 desc");
|
||||
assertEquals(3, response.getBody().getEntities().size());
|
||||
|
||||
ClientEntity clientEntity = response.getBody().getEntities().get(0);
|
||||
|
|
|
@ -38,7 +38,6 @@ public interface ExpandItem {
|
|||
FilterOption getFilterOption();
|
||||
|
||||
/**
|
||||
* <b>CURRENTLY NOT SUPPORTED. WILL ALWAYS RETURN NULL</b>
|
||||
* @return Information of the option $search when used within $expand
|
||||
*/
|
||||
SearchOption getSearchOption();
|
||||
|
|
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.olingo.server.core.uri.parser;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.Edm;
|
||||
import org.apache.olingo.commons.api.edm.EdmEntityType;
|
||||
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
|
||||
import org.apache.olingo.commons.api.edm.EdmProperty;
|
||||
import org.apache.olingo.commons.api.edm.EdmStructuredType;
|
||||
import org.apache.olingo.commons.api.edm.FullQualifiedName;
|
||||
import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
|
||||
import org.apache.olingo.commons.api.ex.ODataRuntimeException;
|
||||
import org.apache.olingo.server.api.OData;
|
||||
import org.apache.olingo.server.api.uri.UriInfoKind;
|
||||
import org.apache.olingo.server.api.uri.UriResourceNavigation;
|
||||
import org.apache.olingo.server.api.uri.UriResourcePartTyped;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ExpandItem;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.LevelsExpandOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.SystemQueryOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.SystemQueryOptionKind;
|
||||
import org.apache.olingo.server.core.uri.UriInfoImpl;
|
||||
import org.apache.olingo.server.core.uri.UriResourceComplexPropertyImpl;
|
||||
import org.apache.olingo.server.core.uri.UriResourceCountImpl;
|
||||
import org.apache.olingo.server.core.uri.UriResourceNavigationPropertyImpl;
|
||||
import org.apache.olingo.server.core.uri.UriResourceRefImpl;
|
||||
import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
|
||||
import org.apache.olingo.server.core.uri.queryoption.CountOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.ExpandItemImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.ExpandOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.LevelsOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.SkipOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.TopOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.validator.UriValidationException;
|
||||
|
||||
public class ExpandParser {
|
||||
|
||||
private final Edm edm;
|
||||
private final OData odata;
|
||||
|
||||
public ExpandParser(final Edm edm, final OData odata) {
|
||||
this.edm = edm;
|
||||
this.odata = odata;
|
||||
}
|
||||
|
||||
public ExpandOption parse(UriTokenizer tokenizer, final EdmStructuredType referencedType)
|
||||
throws UriParserException, UriValidationException {
|
||||
ExpandOptionImpl expandOption = new ExpandOptionImpl();
|
||||
do {
|
||||
final ExpandItem item = parseItem(tokenizer, referencedType);
|
||||
expandOption.addExpandItem(item);
|
||||
} while (tokenizer.next(TokenKind.COMMA));
|
||||
|
||||
return expandOption;
|
||||
}
|
||||
|
||||
private ExpandItem parseItem(UriTokenizer tokenizer, final EdmStructuredType referencedType)
|
||||
throws UriParserException, UriValidationException {
|
||||
ExpandItemImpl item = new ExpandItemImpl();
|
||||
if (tokenizer.next(TokenKind.STAR)) {
|
||||
item.setIsStar(true);
|
||||
if (tokenizer.next(TokenKind.SLASH)) {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.REF);
|
||||
item.setIsRef(true);
|
||||
} else if (tokenizer.next(TokenKind.OPEN)) {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.LEVELS);
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.EQ);
|
||||
item.setSystemQueryOption((SystemQueryOption) parseLevels(tokenizer));
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
}
|
||||
|
||||
} else {
|
||||
final EdmStructuredType typeCast = parseTypeCast(tokenizer, referencedType);
|
||||
if (typeCast != null) {
|
||||
item.setTypeFilter(typeCast);
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.SLASH);
|
||||
}
|
||||
|
||||
UriInfoImpl resource = parseExpandPath(tokenizer, referencedType);
|
||||
|
||||
UriResourcePartTyped lastPart = (UriResourcePartTyped) resource.getLastResourcePart();
|
||||
|
||||
boolean hasSlash = false;
|
||||
if (tokenizer.next(TokenKind.SLASH)) {
|
||||
hasSlash = true;
|
||||
if (lastPart instanceof UriResourceNavigation) {
|
||||
UriResourceNavigationPropertyImpl navigationResource = (UriResourceNavigationPropertyImpl) lastPart;
|
||||
final EdmNavigationProperty navigationProperty = navigationResource.getProperty();
|
||||
final EdmStructuredType typeCastSuffix = parseTypeCast(tokenizer, navigationProperty.getType());
|
||||
if (typeCastSuffix != null) {
|
||||
if (navigationProperty.isCollection()) {
|
||||
navigationResource.setCollectionTypeFilter(typeCastSuffix);
|
||||
} else {
|
||||
navigationResource.setEntryTypeFilter(typeCastSuffix);
|
||||
}
|
||||
hasSlash = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final EdmStructuredType newReferencedType = (EdmStructuredType) lastPart.getType();
|
||||
final boolean newReferencedIsCollection = lastPart.isCollection();
|
||||
if (hasSlash || tokenizer.next(TokenKind.SLASH)) {
|
||||
if (tokenizer.next(TokenKind.REF)) {
|
||||
resource.addResourcePart(new UriResourceRefImpl());
|
||||
parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, true, false);
|
||||
} else {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.COUNT);
|
||||
resource.addResourcePart(new UriResourceCountImpl());
|
||||
parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, false, true);
|
||||
}
|
||||
} else {
|
||||
parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, false, false);
|
||||
}
|
||||
|
||||
item.setResourcePath(resource);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private EdmStructuredType parseTypeCast(UriTokenizer tokenizer, final EdmStructuredType referencedType)
|
||||
throws UriParserException {
|
||||
if (tokenizer.next(TokenKind.QualifiedName)) {
|
||||
final FullQualifiedName qualifiedName = new FullQualifiedName(tokenizer.getText());
|
||||
final EdmStructuredType type = referencedType instanceof EdmEntityType ?
|
||||
edm.getEntityType(qualifiedName) :
|
||||
edm.getComplexType(qualifiedName);
|
||||
if (type == null) {
|
||||
throw new UriParserSemanticException("Type '" + qualifiedName + "' not found.",
|
||||
UriParserSemanticException.MessageKeys.UNKNOWN_PART, qualifiedName.getFullQualifiedNameAsString());
|
||||
} else {
|
||||
if (!type.compatibleTo(referencedType)) {
|
||||
throw new UriParserSemanticException("The type cast '" + qualifiedName + "' is not compatible.",
|
||||
UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER, type.getName());
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private UriInfoImpl parseExpandPath(UriTokenizer tokenizer, final EdmStructuredType referencedType)
|
||||
throws UriParserException {
|
||||
UriInfoImpl resource = new UriInfoImpl().setKind(UriInfoKind.resource);
|
||||
|
||||
EdmStructuredType type = referencedType;
|
||||
String name = null;
|
||||
while (tokenizer.next(TokenKind.ODataIdentifier)) {
|
||||
name = tokenizer.getText();
|
||||
final EdmProperty property = referencedType.getStructuralProperty(name);
|
||||
if (property != null && property.getType().getKind() == EdmTypeKind.COMPLEX) {
|
||||
type = (EdmStructuredType) property.getType();
|
||||
UriResourceComplexPropertyImpl complexResource = new UriResourceComplexPropertyImpl(property);
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.SLASH);
|
||||
final EdmStructuredType typeCast = parseTypeCast(tokenizer, type);
|
||||
if (typeCast != null) {
|
||||
complexResource.setTypeFilter(typeCast);
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.SLASH);
|
||||
type = typeCast;
|
||||
}
|
||||
resource.addResourcePart(complexResource);
|
||||
}
|
||||
}
|
||||
|
||||
final EdmNavigationProperty navigationProperty = type.getNavigationProperty(name);
|
||||
if (navigationProperty == null) {
|
||||
// TODO: could also have been star after complex property (and maybe type cast)
|
||||
throw new UriParserSemanticException(
|
||||
"Navigation Property '" + name + "' not found in type '" + type.getFullQualifiedName() + "'.",
|
||||
UriParserSemanticException.MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE, type.getName(), name);
|
||||
} else {
|
||||
resource.addResourcePart(new UriResourceNavigationPropertyImpl(navigationProperty));
|
||||
}
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
private void parseOptions(UriTokenizer tokenizer,
|
||||
final EdmStructuredType referencedType, final boolean referencedIsCollection,
|
||||
ExpandItemImpl item,
|
||||
final boolean forRef, final boolean forCount) throws UriParserException, UriValidationException {
|
||||
if (tokenizer.next(TokenKind.OPEN)) {
|
||||
do {
|
||||
SystemQueryOption systemQueryOption;
|
||||
if (!forCount && tokenizer.next(TokenKind.COUNT)) {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.EQ);
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.BooleanValue);
|
||||
CountOptionImpl countOption = new CountOptionImpl();
|
||||
countOption.setText(tokenizer.getText());
|
||||
countOption.setValue(Boolean.parseBoolean(tokenizer.getText()));
|
||||
systemQueryOption = countOption;
|
||||
|
||||
} else if (!forRef && !forCount && tokenizer.next(TokenKind.EXPAND)) {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.EQ);
|
||||
systemQueryOption = new ExpandParser(edm, odata).parse(tokenizer, referencedType);
|
||||
|
||||
} else if (tokenizer.next(TokenKind.FILTER)) {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.EQ);
|
||||
systemQueryOption = new FilterParser(edm, odata).parse(tokenizer, referencedType, null);
|
||||
|
||||
} else if (!forRef && !forCount && tokenizer.next(TokenKind.LEVELS)) {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.EQ);
|
||||
systemQueryOption = (SystemQueryOption) parseLevels(tokenizer);
|
||||
|
||||
} else if (!forCount && tokenizer.next(TokenKind.ORDERBY)) {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.EQ);
|
||||
systemQueryOption = new OrderByParser(edm, odata).parse(tokenizer, referencedType, null);
|
||||
|
||||
} else if (tokenizer.next(TokenKind.SEARCH)) {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.EQ);
|
||||
systemQueryOption = new SearchParser().parse(tokenizer);
|
||||
|
||||
} else if (!forRef && !forCount && tokenizer.next(TokenKind.SELECT)) {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.EQ);
|
||||
systemQueryOption = new SelectParser(edm).parse(tokenizer, referencedType, referencedIsCollection);
|
||||
|
||||
} else if (!forCount && tokenizer.next(TokenKind.SKIP)) {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.EQ);
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.IntegerValue);
|
||||
final int value = ParserHelper.parseNonNegativeInteger(SystemQueryOptionKind.SKIP.toString(),
|
||||
tokenizer.getText(), true);
|
||||
SkipOptionImpl skipOption = new SkipOptionImpl();
|
||||
skipOption.setText(tokenizer.getText());
|
||||
skipOption.setValue(value);
|
||||
systemQueryOption = skipOption;
|
||||
|
||||
} else if (!forCount && tokenizer.next(TokenKind.TOP)) {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.EQ);
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.IntegerValue);
|
||||
final int value = ParserHelper.parseNonNegativeInteger(SystemQueryOptionKind.TOP.toString(),
|
||||
tokenizer.getText(), true);
|
||||
TopOptionImpl topOption = new TopOptionImpl();
|
||||
topOption.setText(tokenizer.getText());
|
||||
topOption.setValue(value);
|
||||
systemQueryOption = topOption;
|
||||
|
||||
} else {
|
||||
throw new UriParserSyntaxException("Allowed query option expected.",
|
||||
UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
try {
|
||||
item.setSystemQueryOption(systemQueryOption);
|
||||
} catch (final ODataRuntimeException e) {
|
||||
throw new UriParserSyntaxException("Double system query option '" + systemQueryOption.getName() + "'.", e,
|
||||
UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION, systemQueryOption.getName());
|
||||
}
|
||||
} while (tokenizer.next(TokenKind.SEMI));
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
private LevelsExpandOption parseLevels(UriTokenizer tokenizer) throws UriParserException {
|
||||
final LevelsOptionImpl option = new LevelsOptionImpl();
|
||||
if (tokenizer.next(TokenKind.MAX)) {
|
||||
option.setText(tokenizer.getText());
|
||||
option.setMax();
|
||||
} else {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.IntegerValue);
|
||||
option.setText(tokenizer.getText());
|
||||
option.setValue(
|
||||
ParserHelper.parseNonNegativeInteger(SystemQueryOptionKind.LEVELS.toString(), tokenizer.getText(), false));
|
||||
}
|
||||
return option;
|
||||
}
|
||||
}
|
|
@ -47,9 +47,6 @@ import org.apache.olingo.commons.api.edm.EdmType;
|
|||
import org.apache.olingo.commons.api.edm.EdmTypeDefinition;
|
||||
import org.apache.olingo.commons.api.edm.FullQualifiedName;
|
||||
import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
|
||||
import org.apache.olingo.commons.core.edm.primitivetype.EdmByte;
|
||||
import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
|
||||
import org.apache.olingo.commons.core.edm.primitivetype.EdmSByte;
|
||||
import org.apache.olingo.server.api.OData;
|
||||
import org.apache.olingo.server.api.uri.UriParameter;
|
||||
import org.apache.olingo.server.api.uri.UriResourceFunction;
|
||||
|
@ -161,7 +158,7 @@ public class ExpressionParser {
|
|||
|
||||
private static final Map<TokenKind, EdmPrimitiveTypeKind> tokenToPrimitiveType;
|
||||
static {
|
||||
/* Enum and null are not present in the map. These have to be handled differently */
|
||||
/* Enum and null are not present in the map. These have to be handled differently. */
|
||||
Map<TokenKind, EdmPrimitiveTypeKind> temp = new HashMap<TokenKind, EdmPrimitiveTypeKind>();
|
||||
temp.put(TokenKind.BooleanValue, EdmPrimitiveTypeKind.Boolean);
|
||||
temp.put(TokenKind.StringValue, EdmPrimitiveTypeKind.String);
|
||||
|
@ -242,9 +239,9 @@ public class ExpressionParser {
|
|||
}
|
||||
|
||||
private Expression parseExprRel() throws UriParserException, UriValidationException {
|
||||
if(tokenizer.next(TokenKind.IsofMethod)) {
|
||||
// The isof method is a terminal. So no further operators are allowed
|
||||
return parseIsOfMethod(TokenKind.IsofMethod);
|
||||
if (tokenizer.next(TokenKind.IsofMethod)) {
|
||||
// The isof method is a terminal. So no further operators are allowed.
|
||||
return parseIsOfOrCastMethod(MethodKind.ISOF);
|
||||
} else {
|
||||
Expression left = parseExprAdd();
|
||||
TokenKind operatorTokenKind = ParserHelper.next(tokenizer,
|
||||
|
@ -264,30 +261,25 @@ public class ExpressionParser {
|
|||
}
|
||||
}
|
||||
|
||||
private Expression parseIsOfMethod(final TokenKind lastToken) throws UriParserException, UriValidationException {
|
||||
if(lastToken == TokenKind.IsofMethod) {
|
||||
// The TokenKind 'IsOfMethod' consumes also the opening parenthesis
|
||||
|
||||
// The first parameter could be an expression or a type literal
|
||||
final List<Expression> parameters = new ArrayList<Expression>();
|
||||
private Expression parseIsOfOrCastMethod(final MethodKind kind) throws UriParserException, UriValidationException {
|
||||
// The TokenKind 'IsOfMethod' consumes also the opening parenthesis.
|
||||
// The first parameter could be an expression or a type literal.
|
||||
List<Expression> parameters = new ArrayList<Expression>();
|
||||
parameters.add(parseExpression());
|
||||
if (!(parameters.get(0) instanceof TypeLiteral)) {
|
||||
// The first parameter is not a type literal, so there must be a second parameter.
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
|
||||
parameters.add(parseExpression());
|
||||
if(!(parameters.get(0) instanceof TypeLiteral)) {
|
||||
// The first parameter is not a type literal, so there must be a second parameter
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
|
||||
parameters.add(parseExpression());
|
||||
|
||||
// The second parameter must be a type literal
|
||||
if(!(parameters.get(1) instanceof TypeLiteral)) {
|
||||
throw new UriParserSemanticException("Type literal extected",
|
||||
UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER);
|
||||
}
|
||||
|
||||
// The second parameter must be a type literal.
|
||||
if (!(parameters.get(1) instanceof TypeLiteral)) {
|
||||
throw new UriParserSemanticException("Type literal expected.",
|
||||
UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER);
|
||||
}
|
||||
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
return new MethodImpl(MethodKind.ISOF, parameters);
|
||||
} else {
|
||||
throw new UriParserSyntaxException("Unexpected token.", UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
return new MethodImpl(kind, parameters);
|
||||
}
|
||||
|
||||
private Expression parseExprAdd() throws UriParserException, UriValidationException {
|
||||
|
@ -296,9 +288,9 @@ public class ExpressionParser {
|
|||
// Null for everything other than ADD or SUB
|
||||
while (operatorTokenKind != null) {
|
||||
final Expression right = parseExprMul();
|
||||
checkAddSubTypes(left, right, operatorTokenKind == TokenKind.AddOperator);
|
||||
left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right,
|
||||
odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Double));
|
||||
final EdmType resultType = getAddSubTypeAndCheckLeftAndRight(left, right,
|
||||
operatorTokenKind == TokenKind.SubOperator);
|
||||
left = new BinaryImpl(left, tokenToBinaryOperator.get(operatorTokenKind), right, resultType);
|
||||
operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.AddOperator, TokenKind.SubOperator);
|
||||
}
|
||||
return left;
|
||||
|
@ -328,10 +320,7 @@ public class ExpressionParser {
|
|||
}
|
||||
|
||||
private Expression parseExprUnary() throws UriParserException, UriValidationException {
|
||||
final TokenKind operatorTokenKind = ParserHelper.next(tokenizer, TokenKind.MINUS, TokenKind.NotOperator,
|
||||
TokenKind.CastMethod);
|
||||
|
||||
if(operatorTokenKind == TokenKind.MINUS) {
|
||||
if (tokenizer.next(TokenKind.MinusOperator)) {
|
||||
final Expression expression = parseExprPrimary();
|
||||
checkType(expression,
|
||||
EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
|
||||
|
@ -339,37 +328,14 @@ public class ExpressionParser {
|
|||
EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double,
|
||||
EdmPrimitiveTypeKind.Duration);
|
||||
return new UnaryImpl(UnaryOperatorKind.MINUS, expression, getType(expression));
|
||||
} else if(operatorTokenKind == TokenKind.NotOperator) {
|
||||
} else if (tokenizer.next(TokenKind.NotOperator)) {
|
||||
final Expression expression = parseExprPrimary();
|
||||
checkType(expression, EdmPrimitiveTypeKind.Boolean);
|
||||
return new UnaryImpl(UnaryOperatorKind.NOT, expression, getType(expression));
|
||||
} else if(operatorTokenKind == TokenKind.CastMethod) {
|
||||
return parseCastMethod(operatorTokenKind);
|
||||
} else if (tokenizer.next(TokenKind.CastMethod)) {
|
||||
return parseIsOfOrCastMethod(MethodKind.CAST);
|
||||
} else {
|
||||
final Expression expression = parseExprPrimary();
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
|
||||
private Expression parseCastMethod(final TokenKind lastToken) throws UriParserException, UriValidationException {
|
||||
// The TokenKind 'CastMethod' consumes also the opening parenthesis
|
||||
if(lastToken == TokenKind.CastMethod) {
|
||||
final List<Expression> parameters = new ArrayList<Expression>();
|
||||
parameters.add(parseExpression());
|
||||
|
||||
if(!(parameters.get(0) instanceof TypeLiteral)) {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
|
||||
parameters.add(parseExpression());
|
||||
if(!(parameters.get(1) instanceof TypeLiteral)) {
|
||||
throw new UriParserSemanticException("Type literal extected",
|
||||
UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER);
|
||||
}
|
||||
}
|
||||
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
return new MethodImpl(MethodKind.CAST, parameters);
|
||||
} else {
|
||||
throw new UriParserSyntaxException("Unexpected token.", UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
return parseExprPrimary();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -433,22 +399,20 @@ public class ExpressionParser {
|
|||
private Expression parseMethod(final TokenKind nextMethod) throws UriParserException, UriValidationException {
|
||||
// The method token text includes the opening parenthesis so that method calls can be recognized unambiguously.
|
||||
// OData identifiers have to be considered after that.
|
||||
|
||||
final MethodKind methodKind = tokenToMethod.get(nextMethod);
|
||||
return new MethodImpl(methodKind, parseMethodParameters(methodKind));
|
||||
}
|
||||
|
||||
private Expression parsePrimitive(TokenKind nextPrimitive) throws UriParserException {
|
||||
private Expression parsePrimitive(final TokenKind primitiveTokenKind) throws UriParserException {
|
||||
final String primitiveValueLiteral = tokenizer.getText();
|
||||
if (nextPrimitive == TokenKind.EnumValue) {
|
||||
if (primitiveTokenKind == TokenKind.EnumValue) {
|
||||
return createEnumExpression(primitiveValueLiteral);
|
||||
} else {
|
||||
EdmPrimitiveTypeKind primitiveTypeKind = tokenToPrimitiveType.get(nextPrimitive);
|
||||
|
||||
if(primitiveTypeKind == EdmPrimitiveTypeKind.Int64) {
|
||||
EdmPrimitiveTypeKind primitiveTypeKind = tokenToPrimitiveType.get(primitiveTokenKind);
|
||||
if (primitiveTypeKind == EdmPrimitiveTypeKind.Int64) {
|
||||
primitiveTypeKind = determineIntegerType(primitiveValueLiteral);
|
||||
}
|
||||
|
||||
|
||||
final EdmPrimitiveType type = primitiveTypeKind == null ?
|
||||
// Null handling
|
||||
null :
|
||||
|
@ -472,12 +436,11 @@ public class ExpressionParser {
|
|||
} else {
|
||||
typeKind = EdmPrimitiveTypeKind.Int64;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
// This should never happen. Because the tokenizer has figured out that the literal is an integer
|
||||
throw new UriParserSyntaxException(intValueAsString + " is not an integer",
|
||||
} catch (final NumberFormatException e) {
|
||||
// This should never happen because the tokenizer has figured out that the literal is an integer.
|
||||
throw new UriParserSyntaxException("'" + intValueAsString + "' is not an integer.", e,
|
||||
UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
|
||||
return typeKind;
|
||||
}
|
||||
|
||||
|
@ -622,19 +585,19 @@ public class ExpressionParser {
|
|||
if (filterType == null) {
|
||||
filterType = edm.getComplexType(fullQualifiedName);
|
||||
}
|
||||
|
||||
if(filterType == null) {
|
||||
filterType = getEdmType(fullQualifiedName);
|
||||
|
||||
if (filterType == null) {
|
||||
filterType = getPrimitiveType(fullQualifiedName);
|
||||
}
|
||||
|
||||
if(filterType == null) {
|
||||
|
||||
if (filterType == null) {
|
||||
filterType = edm.getEnumType(fullQualifiedName);
|
||||
}
|
||||
|
||||
if(filterType == null) {
|
||||
|
||||
if (filterType == null) {
|
||||
filterType = edm.getTypeDefinition(fullQualifiedName);
|
||||
}
|
||||
|
||||
|
||||
if (filterType != null) {
|
||||
if (tokenizer.next(TokenKind.SLASH)) {
|
||||
// Leading type cast
|
||||
|
@ -642,30 +605,29 @@ public class ExpressionParser {
|
|||
startTypeFilter = filterType;
|
||||
|
||||
final TokenKind tokenKind = ParserHelper.next(tokenizer, TokenKind.QualifiedName, TokenKind.ODataIdentifier);
|
||||
parseMemberExpression(tokenKind, uriInfo, new UriResourceStartingTypeFilterImpl(filterType, false),
|
||||
false);
|
||||
parseMemberExpression(tokenKind, uriInfo, new UriResourceStartingTypeFilterImpl(filterType, false), false);
|
||||
} else {
|
||||
// Type literal
|
||||
return new TypeLiteralImpl(filterType);
|
||||
}
|
||||
} else {
|
||||
// Must be bound or unbound function.
|
||||
// Must be bound or unbound function.
|
||||
parseFunction(fullQualifiedName, uriInfo, referringType, true);
|
||||
}
|
||||
} else if (lastTokenKind == TokenKind.ODataIdentifier) {
|
||||
parseFirstMemberODataIdentifier(uriInfo);
|
||||
}
|
||||
|
||||
|
||||
return new MemberImpl(uriInfo, startTypeFilter);
|
||||
}
|
||||
|
||||
private EdmType getEdmType(final FullQualifiedName fullQualifiedName) {
|
||||
if(!fullQualifiedName.getNamespace().equals(EdmPrimitiveType.EDM_NAMESPACE)) {
|
||||
private EdmType getPrimitiveType(final FullQualifiedName fullQualifiedName) {
|
||||
if (EdmPrimitiveType.EDM_NAMESPACE.equals(fullQualifiedName.getNamespace())) {
|
||||
final EdmPrimitiveTypeKind primitiveTypeKind = EdmPrimitiveTypeKind.valueOf(fullQualifiedName.getName());
|
||||
return primitiveTypeKind == null ? null : odata.createPrimitiveTypeInstance(primitiveTypeKind);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
final EdmPrimitiveTypeKind primitiveTypeKind = EdmPrimitiveTypeKind.valueOfFQN(fullQualifiedName);
|
||||
return primitiveTypeKind == null ? null : EdmPrimitiveTypeFactory.getInstance(primitiveTypeKind);
|
||||
}
|
||||
|
||||
private void parseDollarRoot(UriInfoImpl uriInfo) throws UriParserException, UriValidationException {
|
||||
|
@ -694,7 +656,7 @@ public class ExpressionParser {
|
|||
parseSingleNavigationExpr(uriInfo, resource);
|
||||
}
|
||||
|
||||
private void parseDollarIt(UriInfoImpl uriInfo, EdmType referringType)
|
||||
private void parseDollarIt(UriInfoImpl uriInfo, final EdmType referringType)
|
||||
throws UriParserException, UriValidationException {
|
||||
UriResourceItImpl itResource = new UriResourceItImpl(referringType, false);
|
||||
uriInfo.addResourcePart(itResource);
|
||||
|
@ -759,16 +721,14 @@ public class ExpressionParser {
|
|||
if (edmEntityType != null) {
|
||||
if (allowTypeFilter) {
|
||||
setTypeFilter(lastResource, edmEntityType);
|
||||
|
||||
if(tokenizer.next(TokenKind.SLASH)) {
|
||||
final TokenKind nextTokenKind = ParserHelper.next(tokenizer, TokenKind.QualifiedName,
|
||||
TokenKind.ODataIdentifier);
|
||||
if(nextTokenKind == TokenKind.ODataIdentifier) {
|
||||
parsePropertyPathExpr(uriInfo, lastResource);
|
||||
} else if(nextTokenKind == TokenKind.QualifiedName) {
|
||||
|
||||
if (tokenizer.next(TokenKind.SLASH)) {
|
||||
if (tokenizer.next(TokenKind.QualifiedName)) {
|
||||
parseBoundFunction(fullQualifiedName, uriInfo, lastResource);
|
||||
} else if (tokenizer.next(TokenKind.ODataIdentifier)) {
|
||||
parsePropertyPathExpr(uriInfo, lastResource);
|
||||
} else {
|
||||
throw new UriParserSyntaxException("Extected OData Identifier or Full Qualified Name",
|
||||
throw new UriParserSyntaxException("Expected OData Identifier or Full Qualified Name.",
|
||||
UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
}
|
||||
|
@ -814,7 +774,9 @@ public class ExpressionParser {
|
|||
|
||||
if (property == null) {
|
||||
throw new UriParserSemanticException("Unknown property.",
|
||||
UriParserSemanticException.MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE, oDataIdentifier);
|
||||
UriParserSemanticException.MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE,
|
||||
lastType.getFullQualifiedName().getFullQualifiedNameAsString(),
|
||||
oDataIdentifier);
|
||||
}
|
||||
|
||||
if (property.getType() instanceof EdmComplexType) {
|
||||
|
@ -823,7 +785,9 @@ public class ExpressionParser {
|
|||
uriInfo.addResourcePart(complexResource);
|
||||
|
||||
if (property.isCollection()) {
|
||||
parseCollectionPathExpr(uriInfo, complexResource);
|
||||
if (tokenizer.next(TokenKind.SLASH)) {
|
||||
parseCollectionPathExpr(uriInfo, complexResource);
|
||||
}
|
||||
} else {
|
||||
parseComplexPathExpr(uriInfo, complexResource);
|
||||
}
|
||||
|
@ -847,7 +811,9 @@ public class ExpressionParser {
|
|||
uriInfo.addResourcePart(primitiveResource);
|
||||
|
||||
if (property.isCollection()) {
|
||||
parseCollectionPathExpr(uriInfo, primitiveResource);
|
||||
if (tokenizer.next(TokenKind.SLASH)) {
|
||||
parseCollectionPathExpr(uriInfo, primitiveResource);
|
||||
}
|
||||
} else {
|
||||
parseSinglePathExpr(uriInfo, primitiveResource);
|
||||
}
|
||||
|
@ -856,7 +822,6 @@ public class ExpressionParser {
|
|||
|
||||
private void parseSingleNavigationExpr(UriInfoImpl uriInfo, final UriResourcePartTyped lastResource)
|
||||
throws UriParserException, UriValidationException {
|
||||
// TODO: Is that correct?
|
||||
if (tokenizer.next(TokenKind.SLASH)) {
|
||||
final TokenKind tokenKind = ParserHelper.next(tokenizer, TokenKind.QualifiedName, TokenKind.ODataIdentifier);
|
||||
parseMemberExpression(tokenKind, uriInfo, lastResource, true);
|
||||
|
@ -865,8 +830,22 @@ public class ExpressionParser {
|
|||
|
||||
private void parseCollectionNavigationExpr(UriInfoImpl uriInfo, UriResourcePartTyped lastResource)
|
||||
throws UriParserException, UriValidationException {
|
||||
// TODO: Is type cast missing?
|
||||
if (tokenizer.next(TokenKind.OPEN)) {
|
||||
boolean hasSlash = false;
|
||||
if (tokenizer.next(TokenKind.SLASH)) {
|
||||
hasSlash = true;
|
||||
if (tokenizer.next(TokenKind.QualifiedName)) {
|
||||
final FullQualifiedName qualifiedName = new FullQualifiedName(tokenizer.getText());
|
||||
final EdmEntityType edmEntityType = edm.getEntityType(qualifiedName);
|
||||
if (edmEntityType == null) {
|
||||
parseBoundFunction(qualifiedName, uriInfo, lastResource);
|
||||
} else {
|
||||
setTypeFilter(lastResource, edmEntityType);
|
||||
}
|
||||
hasSlash = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasSlash && tokenizer.next(TokenKind.OPEN)) {
|
||||
if (lastResource instanceof UriResourceNavigation) {
|
||||
((UriResourceNavigationPropertyImpl) lastResource).setKeyPredicates(
|
||||
ParserHelper.parseNavigationKeyPredicate(tokenizer,
|
||||
|
@ -883,7 +862,10 @@ public class ExpressionParser {
|
|||
}
|
||||
parseSingleNavigationExpr(uriInfo, lastResource);
|
||||
}
|
||||
parseCollectionPathExpr(uriInfo, lastResource);
|
||||
|
||||
if (hasSlash || tokenizer.next(TokenKind.SLASH)) {
|
||||
parseCollectionPathExpr(uriInfo, lastResource);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseSinglePathExpr(UriInfoImpl uriInfo, final UriResourcePartTyped lastResource)
|
||||
|
@ -932,25 +914,23 @@ public class ExpressionParser {
|
|||
|
||||
private void parseCollectionPathExpr(UriInfoImpl uriInfo, final UriResourcePartTyped lastResource)
|
||||
throws UriParserException, UriValidationException {
|
||||
|
||||
if (tokenizer.next(TokenKind.SLASH)) {
|
||||
if (tokenizer.next(TokenKind.COUNT)) {
|
||||
uriInfo.addResourcePart(new UriResourceCountImpl());
|
||||
} else if (tokenizer.next(TokenKind.ANY)) {
|
||||
uriInfo.addResourcePart(parseLambdaRest(TokenKind.ANY, lastResource));
|
||||
} else if (tokenizer.next(TokenKind.ALL)) {
|
||||
uriInfo.addResourcePart(parseLambdaRest(TokenKind.ALL, lastResource));
|
||||
} else if (tokenizer.next(TokenKind.QualifiedName)) {
|
||||
final FullQualifiedName fullQualifiedName = new FullQualifiedName(tokenizer.getText());
|
||||
parseBoundFunction(fullQualifiedName, uriInfo, lastResource);
|
||||
}
|
||||
// The initial slash (see grammar) must have been checked and consumed by the caller.
|
||||
if (tokenizer.next(TokenKind.COUNT)) {
|
||||
uriInfo.addResourcePart(new UriResourceCountImpl());
|
||||
} else if (tokenizer.next(TokenKind.ANY)) {
|
||||
uriInfo.addResourcePart(parseLambdaRest(TokenKind.ANY, lastResource));
|
||||
} else if (tokenizer.next(TokenKind.ALL)) {
|
||||
uriInfo.addResourcePart(parseLambdaRest(TokenKind.ALL, lastResource));
|
||||
} else if (tokenizer.next(TokenKind.QualifiedName)) {
|
||||
final FullQualifiedName fullQualifiedName = new FullQualifiedName(tokenizer.getText());
|
||||
parseBoundFunction(fullQualifiedName, uriInfo, lastResource);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseFunction(final FullQualifiedName fullQualifiedName, UriInfoImpl uriInfo,
|
||||
final EdmType lastType, final boolean lastIsCollection) throws UriParserException, UriValidationException {
|
||||
|
||||
final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, true);
|
||||
final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, edm, referringType, true);
|
||||
final List<String> parameterNames = ParserHelper.getParameterNames(parameters);
|
||||
final EdmFunction boundFunction = edm.getBoundFunction(fullQualifiedName,
|
||||
lastType.getFullQualifiedName(), lastIsCollection, parameterNames);
|
||||
|
@ -966,18 +946,19 @@ public class ExpressionParser {
|
|||
return;
|
||||
}
|
||||
|
||||
throw new UriParserSemanticException("No function found.",
|
||||
throw new UriParserSemanticException("No function '" + fullQualifiedName + "' found.",
|
||||
UriParserSemanticException.MessageKeys.FUNCTION_NOT_FOUND, fullQualifiedName.getFullQualifiedNameAsString());
|
||||
}
|
||||
|
||||
private void parseBoundFunction(final FullQualifiedName fullQualifiedName, UriInfoImpl uriInfo,
|
||||
final UriResourcePartTyped lastResource) throws UriParserException, UriValidationException {
|
||||
final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, true);
|
||||
final EdmType type = lastResource.getType();
|
||||
final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, edm, referringType, true);
|
||||
final List<String> parameterNames = ParserHelper.getParameterNames(parameters);
|
||||
final EdmFunction boundFunction = edm.getBoundFunction(fullQualifiedName,
|
||||
lastResource.getType().getFullQualifiedName(), lastResource.isCollection(), parameterNames);
|
||||
type.getFullQualifiedName(), lastResource.isCollection(), parameterNames);
|
||||
if (boundFunction == null) {
|
||||
throw new UriParserSemanticException("Bound function not found.",
|
||||
throw new UriParserSemanticException("Bound function '" + fullQualifiedName + "' not found.",
|
||||
UriParserSemanticException.MessageKeys.FUNCTION_NOT_FOUND, fullQualifiedName.getFullQualifiedNameAsString());
|
||||
}
|
||||
parseFunctionRest(uriInfo, boundFunction, parameters);
|
||||
|
@ -995,19 +976,23 @@ public class ExpressionParser {
|
|||
if (function.isComposable()) {
|
||||
if (edmType instanceof EdmEntityType ) {
|
||||
if (isCollection) {
|
||||
parseCollectionNavigationExpr(uriInfo, functionResource);
|
||||
parseCollectionNavigationExpr(uriInfo, functionResource);
|
||||
} else {
|
||||
parseSingleNavigationExpr(uriInfo, functionResource);
|
||||
}
|
||||
} else if (edmType instanceof EdmComplexType) {
|
||||
if (isCollection) {
|
||||
parseCollectionPathExpr(uriInfo, functionResource);
|
||||
if (tokenizer.next(TokenKind.SLASH)) {
|
||||
parseCollectionPathExpr(uriInfo, functionResource);
|
||||
}
|
||||
} else {
|
||||
parseComplexPathExpr(uriInfo, functionResource);
|
||||
}
|
||||
} else if (edmType instanceof EdmPrimitiveType) {
|
||||
if (isCollection) {
|
||||
parseCollectionPathExpr(uriInfo, functionResource);
|
||||
if (tokenizer.next(TokenKind.SLASH)) {
|
||||
parseCollectionPathExpr(uriInfo, functionResource);
|
||||
}
|
||||
} else {
|
||||
parseSinglePathExpr(uriInfo, functionResource);
|
||||
}
|
||||
|
@ -1077,7 +1062,7 @@ public class ExpressionParser {
|
|||
TokenKind.YearMethod);
|
||||
}
|
||||
|
||||
private EdmType getType(final Expression expression) throws UriParserException {
|
||||
protected static EdmType getType(final Expression expression) throws UriParserException {
|
||||
EdmType type;
|
||||
if (expression instanceof Literal) {
|
||||
type = ((Literal) expression).getType();
|
||||
|
@ -1108,13 +1093,12 @@ public class ExpressionParser {
|
|||
return type;
|
||||
}
|
||||
|
||||
private boolean isType(final Expression expression, final EdmPrimitiveTypeKind... kinds) throws UriParserException {
|
||||
final EdmType expressionType = getType(expression);
|
||||
if (expressionType == null) {
|
||||
private boolean isType(final EdmType type, final EdmPrimitiveTypeKind... kinds) throws UriParserException {
|
||||
if (type == null) {
|
||||
return true;
|
||||
}
|
||||
for (final EdmPrimitiveTypeKind kind : kinds) {
|
||||
if (expressionType.equals(odata.createPrimitiveTypeInstance(kind))) {
|
||||
if (type.equals(odata.createPrimitiveTypeInstance(kind))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1122,12 +1106,13 @@ public class ExpressionParser {
|
|||
}
|
||||
|
||||
private void checkType(final Expression expression, final EdmPrimitiveTypeKind... kinds) throws UriParserException {
|
||||
if (!isType(expression, kinds)) {
|
||||
final EdmType type = getType(expression);
|
||||
if (!isType(type, kinds)) {
|
||||
throw new UriParserSemanticException("Incompatible type.",
|
||||
UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, // TODO: better message
|
||||
getType(expression) == null ?
|
||||
type == null ?
|
||||
"" :
|
||||
getType(expression).getFullQualifiedName().getFullQualifiedNameAsString());
|
||||
type.getFullQualifiedName().getFullQualifiedNameAsString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1137,21 +1122,21 @@ public class ExpressionParser {
|
|||
if (leftType == null || rightType == null || leftType.equals(rightType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Numeric promotion for Edm.Byte and Edm.SByte
|
||||
if((leftType instanceof EdmByte || leftType instanceof EdmSByte)
|
||||
&& (rightType instanceof EdmByte || rightType instanceof EdmSByte)) {
|
||||
if (isType(leftType, EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte)
|
||||
&& isType(rightType, EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (leftType.getKind() != EdmTypeKind.PRIMITIVE
|
||||
|| rightType.getKind() != EdmTypeKind.PRIMITIVE
|
||||
|| !(((EdmPrimitiveType) leftType).isCompatible((EdmPrimitiveType) rightType)
|
||||
|| ((EdmPrimitiveType) rightType).isCompatible((EdmPrimitiveType) leftType)))
|
||||
{
|
||||
|| ((EdmPrimitiveType) rightType).isCompatible((EdmPrimitiveType) leftType))) {
|
||||
throw new UriParserSemanticException("Incompatible types.",
|
||||
UriParserSemanticException.MessageKeys.TYPES_NOT_COMPATIBLE, leftType.getFullQualifiedName().toString(),
|
||||
rightType.getFullQualifiedName().toString());
|
||||
UriParserSemanticException.MessageKeys.TYPES_NOT_COMPATIBLE,
|
||||
leftType.getFullQualifiedName().getFullQualifiedNameAsString(),
|
||||
rightType.getFullQualifiedName().getFullQualifiedNameAsString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1164,12 +1149,12 @@ public class ExpressionParser {
|
|||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
private boolean isEnumType(final Expression expression) throws UriParserException {
|
||||
final EdmType expressionType = getType(expression);
|
||||
return expressionType == null
|
||||
|| expressionType.getKind() == EdmTypeKind.ENUM
|
||||
|| isType(expression,
|
||||
|| isType(expressionType,
|
||||
EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
|
||||
EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte);
|
||||
}
|
||||
|
@ -1208,44 +1193,69 @@ public class ExpressionParser {
|
|||
EdmPrimitiveTypeKind.Date, EdmPrimitiveTypeKind.TimeOfDay,
|
||||
EdmPrimitiveTypeKind.DateTimeOffset, EdmPrimitiveTypeKind.Duration);
|
||||
if (!(((EdmPrimitiveType) leftType).isCompatible((EdmPrimitiveType) rightType)
|
||||
|| ((EdmPrimitiveType) rightType).isCompatible((EdmPrimitiveType) leftType))) {
|
||||
|| ((EdmPrimitiveType) rightType).isCompatible((EdmPrimitiveType) leftType))) {
|
||||
throw new UriParserSemanticException("Incompatible types.",
|
||||
UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, ""); // TODO: better message
|
||||
UriParserSemanticException.MessageKeys.TYPES_NOT_COMPATIBLE,
|
||||
leftType.getFullQualifiedName().getFullQualifiedNameAsString(),
|
||||
rightType.getFullQualifiedName().getFullQualifiedNameAsString());
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAddSubTypes(final Expression left, final Expression right, final boolean isAdd)
|
||||
private EdmType getAddSubTypeAndCheckLeftAndRight(final Expression left, final Expression right, final boolean isSub)
|
||||
throws UriParserException {
|
||||
final EdmType leftType = getType(left);
|
||||
final EdmType rightType = getType(right);
|
||||
if (leftType == null || rightType == null
|
||||
|| isType(left,
|
||||
if (leftType == null || rightType == null) {
|
||||
return null;
|
||||
}
|
||||
if (isType(leftType,
|
||||
EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
|
||||
EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
|
||||
EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double)
|
||||
&& isType(right,
|
||||
&& isType(rightType,
|
||||
EdmPrimitiveTypeKind.Int16, EdmPrimitiveTypeKind.Int32, EdmPrimitiveTypeKind.Int64,
|
||||
EdmPrimitiveTypeKind.Byte, EdmPrimitiveTypeKind.SByte,
|
||||
EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double)) {
|
||||
return;
|
||||
// The result type must be able to handle the overflow,
|
||||
// so we return always a wider type than the types of the operands.
|
||||
if (isType(leftType, EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double)
|
||||
|| isType(rightType,
|
||||
EdmPrimitiveTypeKind.Decimal, EdmPrimitiveTypeKind.Single, EdmPrimitiveTypeKind.Double)) {
|
||||
return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Double);
|
||||
} else if (isType(leftType, EdmPrimitiveTypeKind.Int64) || isType(rightType, EdmPrimitiveTypeKind.Int64)) {
|
||||
return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Decimal);
|
||||
} else if (isType(leftType, EdmPrimitiveTypeKind.Int32) || isType(rightType, EdmPrimitiveTypeKind.Int32)) {
|
||||
return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Int64);
|
||||
} else if (isType(leftType, EdmPrimitiveTypeKind.Int16) || isType(rightType, EdmPrimitiveTypeKind.Int16)) {
|
||||
return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Int32);
|
||||
} else {
|
||||
return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Int16);
|
||||
}
|
||||
}
|
||||
if (isType(left, EdmPrimitiveTypeKind.DateTimeOffset)
|
||||
&& (isType(right, EdmPrimitiveTypeKind.Duration)
|
||||
|| isType(right, EdmPrimitiveTypeKind.DateTimeOffset) && !isAdd)) {
|
||||
return;
|
||||
if ((isType(leftType, EdmPrimitiveTypeKind.DateTimeOffset)
|
||||
|| isType(leftType, EdmPrimitiveTypeKind.Duration))
|
||||
&& isType(rightType, EdmPrimitiveTypeKind.Duration)) {
|
||||
return leftType;
|
||||
}
|
||||
if (isType(left, EdmPrimitiveTypeKind.Duration) && isType(right, EdmPrimitiveTypeKind.Duration)
|
||||
|| isType(left, EdmPrimitiveTypeKind.Date)
|
||||
&& (isType(right, EdmPrimitiveTypeKind.Duration) || isType(right, EdmPrimitiveTypeKind.Date) && !isAdd)) {
|
||||
return;
|
||||
if (isType(leftType, EdmPrimitiveTypeKind.Date) && isType(rightType, EdmPrimitiveTypeKind.Duration)) {
|
||||
return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.DateTimeOffset);
|
||||
}
|
||||
if (isSub
|
||||
&& (isType(leftType, EdmPrimitiveTypeKind.DateTimeOffset)
|
||||
&& isType(rightType, EdmPrimitiveTypeKind.DateTimeOffset)
|
||||
|| isType(leftType, EdmPrimitiveTypeKind.Date)
|
||||
&& isType(rightType, EdmPrimitiveTypeKind.Date))) {
|
||||
return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Duration);
|
||||
}
|
||||
throw new UriParserSemanticException("Incompatible types.",
|
||||
UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, ""); // TODO: better message
|
||||
UriParserSemanticException.MessageKeys.TYPES_NOT_COMPATIBLE,
|
||||
leftType.getFullQualifiedName().getFullQualifiedNameAsString(),
|
||||
rightType.getFullQualifiedName().getFullQualifiedNameAsString());
|
||||
}
|
||||
|
||||
private void checkStructuredTypeFilter(final EdmType type, final EdmType filterType)
|
||||
throws UriParserException {
|
||||
if (!(filterType instanceof EdmStructuredType && ((EdmStructuredType)filterType).compatibleTo(type))) {
|
||||
if (!(filterType instanceof EdmStructuredType && ((EdmStructuredType) filterType).compatibleTo(type))) {
|
||||
throw new UriParserSemanticException("Incompatible type filter.",
|
||||
UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER,
|
||||
filterType.getFullQualifiedName().getFullQualifiedNameAsString());
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.apache.olingo.server.core.uri.parser;
|
|||
import java.util.Collection;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.Edm;
|
||||
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
|
||||
import org.apache.olingo.commons.api.edm.EdmType;
|
||||
import org.apache.olingo.server.api.OData;
|
||||
import org.apache.olingo.server.api.uri.queryoption.FilterOption;
|
||||
|
@ -43,7 +44,13 @@ public class FilterParser {
|
|||
throws UriParserException, UriValidationException {
|
||||
final Expression filterExpression = new ExpressionParser(edm, odata)
|
||||
.parse(tokenizer, referencedType, crossjoinEntitySetNames);
|
||||
// TODO: Check that the expression is boolean.
|
||||
return new FilterOptionImpl().setExpression(filterExpression);
|
||||
final EdmType type = ExpressionParser.getType(filterExpression);
|
||||
if (type == null || type.equals(odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean))) {
|
||||
return new FilterOptionImpl().setExpression(filterExpression);
|
||||
} else {
|
||||
throw new UriParserSemanticException("Filter expressions must be boolean.",
|
||||
UriParserSemanticException.MessageKeys.TYPES_NOT_COMPATIBLE,
|
||||
"Edm.Boolean", type.getFullQualifiedName().getFullQualifiedNameAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,18 +18,11 @@
|
|||
*/
|
||||
package org.apache.olingo.server.core.uri.parser;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
import org.antlr.v4.runtime.ANTLRInputStream;
|
||||
import org.antlr.v4.runtime.BailErrorStrategy;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.Lexer;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.RecognitionException;
|
||||
import org.antlr.v4.runtime.atn.PredictionMode;
|
||||
import org.antlr.v4.runtime.misc.ParseCancellationException;
|
||||
import org.apache.olingo.commons.api.edm.Edm;
|
||||
import org.apache.olingo.commons.api.edm.EdmEntityContainer;
|
||||
import org.apache.olingo.commons.api.edm.EdmEntitySet;
|
||||
import org.apache.olingo.commons.api.edm.EdmStructuredType;
|
||||
import org.apache.olingo.commons.api.edm.EdmType;
|
||||
|
@ -52,14 +45,10 @@ import org.apache.olingo.server.api.uri.queryoption.SystemQueryOptionKind;
|
|||
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
|
||||
import org.apache.olingo.server.core.uri.UriInfoImpl;
|
||||
import org.apache.olingo.server.core.uri.UriResourceStartingTypeFilterImpl;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriLexer;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandItemsEOFContext;
|
||||
import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
|
||||
import org.apache.olingo.server.core.uri.parser.search.SearchParser;
|
||||
import org.apache.olingo.server.core.uri.queryoption.AliasQueryOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.CountOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.ExpandOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.FormatOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.IdOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.SkipOptionImpl;
|
||||
|
@ -77,8 +66,6 @@ public class Parser {
|
|||
private final Edm edm;
|
||||
private final OData odata;
|
||||
|
||||
private enum ParserEntryRules { ExpandItems }
|
||||
|
||||
public Parser(final Edm edm, final OData odata) {
|
||||
this.edm = edm;
|
||||
this.odata = odata;
|
||||
|
@ -87,8 +74,9 @@ public class Parser {
|
|||
public UriInfo parseUri(final String path, final String query, final String fragment)
|
||||
throws UriParserException, UriValidationException {
|
||||
|
||||
UriContext context = new UriContext();
|
||||
UriParseTreeVisitor uriParseTreeVisitor = new UriParseTreeVisitor(edm, context);
|
||||
UriInfoImpl contextUriInfo = new UriInfoImpl();
|
||||
Deque<EdmType> contextTypes = new ArrayDeque<EdmType>();
|
||||
boolean contextIsCollection = false;
|
||||
|
||||
final List<String> pathSegmentsDecoded = UriDecoder.splitAndDecodePath(path);
|
||||
final int numberOfSegments = pathSegmentsDecoded.size();
|
||||
|
@ -98,49 +86,46 @@ public class Parser {
|
|||
|
||||
if (firstSegment.isEmpty()) {
|
||||
ensureLastSegment(firstSegment, 0, numberOfSegments);
|
||||
context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.service);
|
||||
contextUriInfo.setKind(UriInfoKind.service);
|
||||
|
||||
} else if (firstSegment.equals("$batch")) {
|
||||
ensureLastSegment(firstSegment, 1, numberOfSegments);
|
||||
context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.batch);
|
||||
contextUriInfo.setKind(UriInfoKind.batch);
|
||||
|
||||
} else if (firstSegment.equals("$metadata")) {
|
||||
ensureLastSegment(firstSegment, 1, numberOfSegments);
|
||||
context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.metadata);
|
||||
context.contextUriInfo.setFragment(fragment);
|
||||
contextUriInfo.setKind(UriInfoKind.metadata);
|
||||
contextUriInfo.setFragment(fragment);
|
||||
|
||||
} else if (firstSegment.equals("$all")) {
|
||||
ensureLastSegment(firstSegment, 1, numberOfSegments);
|
||||
context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.all);
|
||||
contextUriInfo.setKind(UriInfoKind.all);
|
||||
// This loads nearly the whole schema, but sooner or later '$all' needs all entity sets anyway.
|
||||
for (final EdmEntitySet entitySet : edm.getEntityContainer().getEntitySets()) {
|
||||
context.contextTypes.push(entitySet.getEntityType());
|
||||
contextTypes.push(entitySet.getEntityType());
|
||||
}
|
||||
context.isCollection = true;
|
||||
contextIsCollection = true;
|
||||
|
||||
} else if (firstSegment.equals("$entity")) {
|
||||
if (numberOfSegments > 1) {
|
||||
final String typeCastSegment = pathSegmentsDecoded.get(1);
|
||||
ensureLastSegment(typeCastSegment, 2, numberOfSegments);
|
||||
context.contextUriInfo = new ResourcePathParser(edm).parseDollarEntityTypeCast(typeCastSegment);
|
||||
context.contextTypes.push(context.contextUriInfo.getEntityTypeCast());
|
||||
contextUriInfo = new ResourcePathParser(edm).parseDollarEntityTypeCast(typeCastSegment);
|
||||
contextTypes.push(contextUriInfo.getEntityTypeCast());
|
||||
} else {
|
||||
context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.entityId);
|
||||
contextUriInfo.setKind(UriInfoKind.entityId);
|
||||
// The type of the entity is not known until the $id query option has been parsed.
|
||||
// TODO: Set the type (needed for the evaluation of system query options).
|
||||
}
|
||||
context.isCollection = false;
|
||||
contextIsCollection = false;
|
||||
|
||||
} else if (firstSegment.startsWith("$crossjoin")) {
|
||||
ensureLastSegment(firstSegment, 1, numberOfSegments);
|
||||
context.contextUriInfo = new ResourcePathParser(edm).parseCrossjoinSegment(firstSegment);
|
||||
final EdmEntityContainer container = edm.getEntityContainer();
|
||||
for (final String name : context.contextUriInfo.getEntitySetNames()) {
|
||||
context.contextTypes.push(container.getEntitySet(name).getEntityType());
|
||||
}
|
||||
context.isCollection = true;
|
||||
contextUriInfo = new ResourcePathParser(edm).parseCrossjoinSegment(firstSegment);
|
||||
contextIsCollection = true;
|
||||
|
||||
} else {
|
||||
context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.resource);
|
||||
contextUriInfo.setKind(UriInfoKind.resource);
|
||||
final ResourcePathParser resourcePathParser = new ResourcePathParser(edm);
|
||||
int count = 0;
|
||||
UriResource lastSegment = null;
|
||||
|
@ -168,7 +153,7 @@ public class Parser {
|
|||
} else {
|
||||
lastSegment = segment;
|
||||
}
|
||||
context.contextUriInfo.addResourcePart(segment);
|
||||
contextUriInfo.addResourcePart(segment);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,9 +161,9 @@ public class Parser {
|
|||
final UriResourcePartTyped typed = (UriResourcePartTyped) lastSegment;
|
||||
final EdmType type = ParserHelper.getTypeInformation(typed);
|
||||
if (type != null) { // could be null for, e.g., actions without return type
|
||||
context.contextTypes.push(type);
|
||||
contextTypes.push(type);
|
||||
}
|
||||
context.isCollection = typed.isCollection();
|
||||
contextIsCollection = typed.isCollection();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,9 +176,10 @@ public class Parser {
|
|||
SystemQueryOption systemOption = null;
|
||||
if (optionName.equals(SystemQueryOptionKind.FILTER.toString())) {
|
||||
UriTokenizer filterTokenizer = new UriTokenizer(optionValue);
|
||||
// The Referring type could also be a primitive type not only a structured type
|
||||
systemOption = new FilterParser(edm, odata).parse(filterTokenizer, context.contextTypes.peek(),
|
||||
context.contextUriInfo.getEntitySetNames());
|
||||
// The referring type could be a primitive type or a structured type.
|
||||
systemOption = new FilterParser(edm, odata).parse(filterTokenizer,
|
||||
contextTypes.peek(),
|
||||
contextUriInfo.getEntitySetNames());
|
||||
checkOptionEOF(filterTokenizer, optionName, optionValue);
|
||||
|
||||
} else if (optionName.equals(SystemQueryOptionKind.FORMAT.toString())) {
|
||||
|
@ -211,19 +197,26 @@ public class Parser {
|
|||
systemOption = formatOption;
|
||||
|
||||
} else if (optionName.equals(SystemQueryOptionKind.EXPAND.toString())) {
|
||||
try {
|
||||
ExpandItemsEOFContext ctxExpandItems =
|
||||
(ExpandItemsEOFContext) parseRule(optionValue, ParserEntryRules.ExpandItems);
|
||||
systemOption = (ExpandOptionImpl) uriParseTreeVisitor.visitExpandItemsEOF(ctxExpandItems);
|
||||
} catch (final ParseCancellationException e) {
|
||||
throw e.getCause() instanceof UriParserException ?
|
||||
(UriParserException) e.getCause() :
|
||||
new UriParserSyntaxException("Syntax error", e, UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
if (contextTypes.peek() instanceof EdmStructuredType
|
||||
|| !contextUriInfo.getEntitySetNames().isEmpty()
|
||||
|| contextUriInfo.getKind() == UriInfoKind.entityId) { // TODO: Remove once the type has been set above.
|
||||
UriTokenizer expandTokenizer = new UriTokenizer(optionValue);
|
||||
systemOption = new ExpandParser(edm, odata).parse(expandTokenizer,
|
||||
contextTypes.peek() instanceof EdmStructuredType ? (EdmStructuredType) contextTypes.peek() : null);
|
||||
checkOptionEOF(expandTokenizer, optionName, optionValue);
|
||||
} else {
|
||||
throw new UriValidationException("Expand is only allowed on structured types!",
|
||||
UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED, optionName);
|
||||
}
|
||||
|
||||
} else if (optionName.equals(SystemQueryOptionKind.ID.toString())) {
|
||||
IdOptionImpl idOption = new IdOptionImpl();
|
||||
idOption.setText(optionValue);
|
||||
if (optionValue == null || optionValue.isEmpty()) {
|
||||
throw new UriParserSyntaxException("Illegal value of $id option!",
|
||||
UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
|
||||
optionName, optionValue);
|
||||
}
|
||||
idOption.setValue(optionValue);
|
||||
systemOption = idOption;
|
||||
|
||||
|
@ -234,10 +227,8 @@ public class Parser {
|
|||
} else if (optionName.equals(SystemQueryOptionKind.ORDERBY.toString())) {
|
||||
UriTokenizer orderByTokenizer = new UriTokenizer(optionValue);
|
||||
systemOption = new OrderByParser(edm, odata).parse(orderByTokenizer,
|
||||
context.contextTypes.peek() instanceof EdmStructuredType ?
|
||||
(EdmStructuredType) context.contextTypes.peek() :
|
||||
null,
|
||||
context.contextUriInfo.getEntitySetNames());
|
||||
contextTypes.peek() instanceof EdmStructuredType ? (EdmStructuredType) contextTypes.peek() : null,
|
||||
contextUriInfo.getEntitySetNames());
|
||||
checkOptionEOF(orderByTokenizer, optionName, optionValue);
|
||||
|
||||
} else if (optionName.equals(SystemQueryOptionKind.SEARCH.toString())) {
|
||||
|
@ -246,40 +237,31 @@ public class Parser {
|
|||
} else if (optionName.equals(SystemQueryOptionKind.SELECT.toString())) {
|
||||
UriTokenizer selectTokenizer = new UriTokenizer(optionValue);
|
||||
systemOption = new SelectParser(edm).parse(selectTokenizer,
|
||||
context.contextTypes.peek() instanceof EdmStructuredType ?
|
||||
(EdmStructuredType) context.contextTypes.peek() :
|
||||
null,
|
||||
context.isCollection);
|
||||
contextTypes.peek() instanceof EdmStructuredType ? (EdmStructuredType) contextTypes.peek() : null,
|
||||
contextIsCollection);
|
||||
checkOptionEOF(selectTokenizer, optionName, optionValue);
|
||||
|
||||
} else if (optionName.equals(SystemQueryOptionKind.SKIP.toString())) {
|
||||
SkipOptionImpl skipOption = new SkipOptionImpl();
|
||||
skipOption.setText(optionValue);
|
||||
try {
|
||||
skipOption.setValue(Integer.parseInt(optionValue));
|
||||
} catch (final NumberFormatException e) {
|
||||
throw new UriParserSyntaxException("Illegal value of $skip option!", e,
|
||||
UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
|
||||
optionName, optionValue);
|
||||
}
|
||||
skipOption.setValue(ParserHelper.parseNonNegativeInteger(optionName, optionValue, true));
|
||||
systemOption = skipOption;
|
||||
|
||||
} else if (optionName.equals(SystemQueryOptionKind.SKIPTOKEN.toString())) {
|
||||
SkipTokenOptionImpl skipTokenOption = new SkipTokenOptionImpl();
|
||||
skipTokenOption.setText(optionValue);
|
||||
if (optionValue == null || optionValue.isEmpty()) {
|
||||
throw new UriParserSyntaxException("Illegal value of $skiptoken option!",
|
||||
UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
|
||||
optionName, optionValue);
|
||||
}
|
||||
skipTokenOption.setValue(optionValue);
|
||||
systemOption = skipTokenOption;
|
||||
|
||||
} else if (optionName.equals(SystemQueryOptionKind.TOP.toString())) {
|
||||
TopOptionImpl topOption = new TopOptionImpl();
|
||||
topOption.setText(optionValue);
|
||||
try {
|
||||
topOption.setValue(Integer.parseInt(optionValue));
|
||||
} catch (final NumberFormatException e) {
|
||||
throw new UriParserSyntaxException("Illegal value of $top option!", e,
|
||||
UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
|
||||
optionName, optionValue);
|
||||
}
|
||||
topOption.setValue(ParserHelper.parseNonNegativeInteger(optionName, optionValue, true));
|
||||
systemOption = topOption;
|
||||
|
||||
} else if (optionName.equals(SystemQueryOptionKind.COUNT.toString())) {
|
||||
|
@ -299,14 +281,14 @@ public class Parser {
|
|||
UriParserSyntaxException.MessageKeys.UNKNOWN_SYSTEM_QUERY_OPTION, optionName);
|
||||
}
|
||||
try {
|
||||
context.contextUriInfo.setSystemQueryOption(systemOption);
|
||||
contextUriInfo.setSystemQueryOption(systemOption);
|
||||
} catch (final ODataRuntimeException e) {
|
||||
throw new UriParserSyntaxException("Double system query option!", e,
|
||||
UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION, optionName);
|
||||
}
|
||||
|
||||
} else if (optionName.startsWith(AT)) {
|
||||
if (context.contextUriInfo.getAlias(optionName) == null) {
|
||||
if (contextUriInfo.getAlias(optionName) == null) {
|
||||
// TODO: Create a proper alias-value parser that can parse also common expressions.
|
||||
Expression expression = null;
|
||||
UriTokenizer aliasTokenizer = new UriTokenizer(optionValue);
|
||||
|
@ -318,13 +300,13 @@ public class Parser {
|
|||
} else {
|
||||
UriTokenizer aliasValueTokenizer = new UriTokenizer(optionValue);
|
||||
expression = new ExpressionParser(edm, odata).parse(aliasValueTokenizer, null,
|
||||
context.contextUriInfo.getEntitySetNames());
|
||||
contextUriInfo.getEntitySetNames());
|
||||
if (!aliasValueTokenizer.next(TokenKind.EOF)) {
|
||||
throw new UriParserSyntaxException("Illegal value for alias '" + optionName + "'.",
|
||||
UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
}
|
||||
context.contextUriInfo.addAlias((AliasQueryOption) new AliasQueryOptionImpl()
|
||||
contextUriInfo.addAlias((AliasQueryOption) new AliasQueryOptionImpl()
|
||||
.setAliasValue(expression)
|
||||
.setName(optionName)
|
||||
.setText(NULL.equals(optionValue) ? null : optionValue));
|
||||
|
@ -334,11 +316,11 @@ public class Parser {
|
|||
}
|
||||
|
||||
} else {
|
||||
context.contextUriInfo.addCustomQueryOption((CustomQueryOption) option);
|
||||
contextUriInfo.addCustomQueryOption((CustomQueryOption) option);
|
||||
}
|
||||
}
|
||||
|
||||
return context.contextUriInfo;
|
||||
return contextUriInfo;
|
||||
}
|
||||
|
||||
private void ensureLastSegment(final String segment, final int pos, final int size)
|
||||
|
@ -362,102 +344,4 @@ public class Parser {
|
|||
optionName, optionValue);
|
||||
}
|
||||
}
|
||||
|
||||
private ParserRuleContext parseRule(final String input, final ParserEntryRules entryPoint)
|
||||
throws UriParserSyntaxException {
|
||||
UriParserParser parser = null;
|
||||
UriLexer lexer = null;
|
||||
ParserRuleContext ret = null;
|
||||
|
||||
// Use 2 stage approach to improve performance
|
||||
// see https://github.com/antlr/antlr4/issues/192
|
||||
|
||||
// stage = 1
|
||||
try {
|
||||
|
||||
// create parser
|
||||
lexer = new UriLexer(new ANTLRInputStream(input));
|
||||
parser = new UriParserParser(new CommonTokenStream(lexer));
|
||||
|
||||
// Set error strategy
|
||||
addStage1ErrorStrategy(parser);
|
||||
|
||||
// Set error collector
|
||||
addStage1ErrorListener(parser);
|
||||
|
||||
// user the faster LL parsing
|
||||
parser.getInterpreter().setPredictionMode(PredictionMode.SLL);
|
||||
|
||||
// parse
|
||||
switch (entryPoint) {
|
||||
case ExpandItems:
|
||||
lexer.mode(Lexer.DEFAULT_MODE);
|
||||
ret = parser.expandItemsEOF();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
} catch (ParseCancellationException hardException) {
|
||||
// stage = 2
|
||||
try {
|
||||
|
||||
// create parser
|
||||
lexer = new UriLexer(new ANTLRInputStream(input));
|
||||
parser = new UriParserParser(new CommonTokenStream(lexer));
|
||||
|
||||
// Set error strategy
|
||||
addStage2ErrorStrategy(parser);
|
||||
|
||||
// Set error collector
|
||||
addStage2ErrorListener(parser);
|
||||
|
||||
// Use the slower SLL parsing
|
||||
parser.getInterpreter().setPredictionMode(PredictionMode.LL);
|
||||
|
||||
// parse
|
||||
switch (entryPoint) {
|
||||
case ExpandItems:
|
||||
lexer.mode(Lexer.DEFAULT_MODE);
|
||||
ret = parser.expandItemsEOF();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
} catch (final RecognitionException weakException) {
|
||||
throw new UriParserSyntaxException("Error in syntax", weakException,
|
||||
UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
|
||||
// exceptionOnStage = 2;
|
||||
}
|
||||
} catch (final RecognitionException hardException) {
|
||||
throw new UriParserSyntaxException("Error in syntax", hardException,
|
||||
UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected void addStage1ErrorStrategy(final UriParserParser parser) {
|
||||
// Throw exception at first syntax error
|
||||
parser.setErrorHandler(new BailErrorStrategy());
|
||||
|
||||
}
|
||||
|
||||
protected void addStage2ErrorStrategy(final UriParserParser parser) {
|
||||
// Throw exception at first syntax error
|
||||
parser.setErrorHandler(new BailErrorStrategy());
|
||||
}
|
||||
|
||||
protected void addStage1ErrorListener(final UriParserParser parser) {
|
||||
// No error logging to System.out or System.err, only exceptions used (depending on ErrorStrategy)
|
||||
parser.removeErrorListeners();
|
||||
}
|
||||
|
||||
protected void addStage2ErrorListener(final UriParserParser parser) {
|
||||
// No error logging to System.out or System.err, only exceptions used (depending on ErrorStrategy)
|
||||
parser.removeErrorListeners();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.Edm;
|
||||
import org.apache.olingo.commons.api.edm.EdmEntityType;
|
||||
import org.apache.olingo.commons.api.edm.EdmKeyPropertyRef;
|
||||
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
|
||||
|
@ -36,6 +37,8 @@ import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
|
|||
import org.apache.olingo.server.api.OData;
|
||||
import org.apache.olingo.server.api.uri.UriParameter;
|
||||
import org.apache.olingo.server.api.uri.UriResourcePartTyped;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.Literal;
|
||||
import org.apache.olingo.server.core.ODataImpl;
|
||||
import org.apache.olingo.server.core.uri.UriParameterImpl;
|
||||
import org.apache.olingo.server.core.uri.UriResourceTypedImpl;
|
||||
|
@ -88,8 +91,9 @@ public class ParserHelper {
|
|||
TokenKind.EnumValue);
|
||||
}
|
||||
|
||||
protected static List<UriParameter> parseFunctionParameters(UriTokenizer tokenizer, final boolean withComplex)
|
||||
throws UriParserException {
|
||||
protected static List<UriParameter> parseFunctionParameters(UriTokenizer tokenizer,
|
||||
final Edm edm, final EdmType referringType, final boolean withComplex)
|
||||
throws UriParserException, UriValidationException {
|
||||
List<UriParameter> parameters = new ArrayList<UriParameter>();
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.OPEN);
|
||||
if (tokenizer.next(TokenKind.CLOSE)) {
|
||||
|
@ -115,6 +119,13 @@ public class ParserHelper {
|
|||
throw new UriParserSemanticException("A JSON array or object is not allowed as parameter value.",
|
||||
UriParserSemanticException.MessageKeys.COMPLEX_PARAMETER_IN_RESOURCE_PATH, tokenizer.getText());
|
||||
}
|
||||
} else if (withComplex) {
|
||||
final Expression expression = new ExpressionParser(edm, odata).parse(tokenizer, referringType, null);
|
||||
parameters.add(new UriParameterImpl().setName(name)
|
||||
.setText(expression instanceof Literal ?
|
||||
"null".equals(((Literal) expression).getText()) ? null : ((Literal) expression).getText() :
|
||||
null)
|
||||
.setExpression(expression instanceof Literal ? null : expression));
|
||||
} else if (nextPrimitiveValue(tokenizer) == null) {
|
||||
throw new UriParserSemanticException("Wrong parameter value.",
|
||||
UriParserSemanticException.MessageKeys.INVALID_KEY_VALUE, "");
|
||||
|
@ -387,4 +398,23 @@ public class ParserHelper {
|
|||
|
||||
return type;
|
||||
}
|
||||
|
||||
protected static int parseNonNegativeInteger(final String optionName, final String optionValue,
|
||||
final boolean zeroAllowed) throws UriParserException {
|
||||
int value;
|
||||
try {
|
||||
value = Integer.parseInt(optionValue);
|
||||
} catch (final NumberFormatException e) {
|
||||
throw new UriParserSyntaxException("Illegal value of '" + optionName + "' option!", e,
|
||||
UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
|
||||
optionName, optionValue);
|
||||
}
|
||||
if (value > 0 || value == 0 && zeroAllowed) {
|
||||
return value;
|
||||
} else {
|
||||
throw new UriParserSyntaxException("Illegal value of '" + optionName + "' option!",
|
||||
UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
|
||||
optionName, optionValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -261,30 +261,33 @@ public class ResourcePathParser {
|
|||
throws UriParserException, UriValidationException {
|
||||
final FullQualifiedName name = new FullQualifiedName(tokenizer.getText());
|
||||
requireTyped(previous, name.getFullQualifiedNameAsString());
|
||||
final UriResourcePartTyped previousTyped = (UriResourcePartTyped) previous;
|
||||
final EdmType previousTypeFilter = getPreviousTypeFilter(previousTyped);
|
||||
final EdmType previousType = previousTypeFilter == null ? previousTyped.getType() : previousTypeFilter;
|
||||
final EdmAction boundAction = edm.getBoundAction(name,
|
||||
previousType.getFullQualifiedName(),
|
||||
previousTyped.isCollection());
|
||||
if (boundAction != null) {
|
||||
ParserHelper.requireTokenEnd(tokenizer);
|
||||
return new UriResourceActionImpl(boundAction);
|
||||
}
|
||||
EdmStructuredType type = edm.getEntityType(name);
|
||||
if (type == null) {
|
||||
type = edm.getComplexType(name);
|
||||
}
|
||||
if (type != null) {
|
||||
return typeCast(name, type, previousTyped);
|
||||
}
|
||||
if (tokenizer.next(TokenKind.EOF)) {
|
||||
throw new UriParserSemanticException("Type '" + name.getFullQualifiedNameAsString() + "' not found.",
|
||||
UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, name.getFullQualifiedNameAsString());
|
||||
}
|
||||
return functionCall(null, name,
|
||||
previousType.getFullQualifiedName(),
|
||||
previousTyped.isCollection());
|
||||
final UriResourcePartTyped previousTyped = (UriResourcePartTyped) previous;
|
||||
final EdmType previousTypeFilter = getPreviousTypeFilter(previousTyped);
|
||||
final EdmType previousType = previousTypeFilter == null ? previousTyped.getType() : previousTypeFilter;
|
||||
|
||||
// We check for bound actions first because they cannot be followed by anything.
|
||||
final EdmAction boundAction =
|
||||
edm.getBoundAction(name, previousType.getFullQualifiedName(), previousTyped.isCollection());
|
||||
if (boundAction != null) {
|
||||
ParserHelper.requireTokenEnd(tokenizer);
|
||||
return new UriResourceActionImpl(boundAction);
|
||||
}
|
||||
|
||||
// Type casts can be syntactically indistinguishable from bound function calls in the case of additional keys.
|
||||
// But normally they are shorter, so they come next.
|
||||
final EdmStructuredType type = previousTyped.getType() instanceof EdmEntityType ?
|
||||
edm.getEntityType(name) :
|
||||
edm.getComplexType(name);
|
||||
if (type != null) {
|
||||
return typeCast(name, type, previousTyped);
|
||||
}
|
||||
if (tokenizer.next(TokenKind.EOF)) {
|
||||
throw new UriParserSemanticException("Type '" + name.getFullQualifiedNameAsString() + "' not found.",
|
||||
UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, name.getFullQualifiedNameAsString());
|
||||
}
|
||||
|
||||
// Now a bound function call is the only remaining option.
|
||||
return functionCall(null, name, previousType.getFullQualifiedName(), previousTyped.isCollection());
|
||||
}
|
||||
|
||||
private void requireTyped(final UriResource previous, final String forWhat) throws UriParserException {
|
||||
|
@ -317,8 +320,13 @@ public class ResourcePathParser {
|
|||
((UriResourceWithKeysImpl) previousTyped).setEntryTypeFilter(type);
|
||||
}
|
||||
if (tokenizer.next(TokenKind.OPEN)) {
|
||||
((UriResourceWithKeysImpl) previousTyped).setKeyPredicates(
|
||||
ParserHelper.parseKeyPredicate(tokenizer, (EdmEntityType) type, null));
|
||||
final List<UriParameter> keys = ParserHelper.parseKeyPredicate(tokenizer, (EdmEntityType) type, null);
|
||||
if (previousTyped.isCollection()) {
|
||||
((UriResourceWithKeysImpl) previousTyped).setKeyPredicates(keys);
|
||||
} else {
|
||||
throw new UriParserSemanticException("Key not allowed here.",
|
||||
UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
previousTypeFilter = ((UriResourceTypedImpl) previousTyped).getTypeFilter();
|
||||
|
@ -351,7 +359,7 @@ public class ResourcePathParser {
|
|||
private UriResource functionCall(final EdmFunctionImport edmFunctionImport,
|
||||
final FullQualifiedName boundFunctionName, final FullQualifiedName bindingParameterTypeName,
|
||||
final boolean isBindingParameterCollection) throws UriParserException, UriValidationException {
|
||||
final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, false);
|
||||
final List<UriParameter> parameters = ParserHelper.parseFunctionParameters(tokenizer, edm, null, false);
|
||||
final List<String> names = ParserHelper.getParameterNames(parameters);
|
||||
EdmFunction function = null;
|
||||
if (edmFunctionImport != null) {
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.olingo.server.core.uri.parser;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.SearchOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.search.SearchBinaryOperatorKind;
|
||||
import org.apache.olingo.server.api.uri.queryoption.search.SearchExpression;
|
||||
import org.apache.olingo.server.api.uri.queryoption.search.SearchTerm;
|
||||
import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
|
||||
import org.apache.olingo.server.core.uri.parser.search.SearchBinaryImpl;
|
||||
import org.apache.olingo.server.core.uri.parser.search.SearchParserException;
|
||||
import org.apache.olingo.server.core.uri.parser.search.SearchTermImpl;
|
||||
import org.apache.olingo.server.core.uri.parser.search.SearchUnaryImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.SearchOptionImpl;
|
||||
|
||||
/**
|
||||
* Parses search expressions according to the following (rewritten) grammar:
|
||||
* <pre>
|
||||
* SearchExpr ::= ExprOR
|
||||
* ExprOR ::= ExprAnd ('OR' ExprAnd)*
|
||||
* ExprAnd ::= Term ('AND'? Term)*
|
||||
* Term ::= ('NOT'? (Word | Phrase)) | ('(' SearchExpr ')')
|
||||
* </pre>
|
||||
*/
|
||||
public class SearchParser {
|
||||
|
||||
public SearchOption parse(UriTokenizer tokenizer) throws SearchParserException {
|
||||
SearchOptionImpl searchOption = new SearchOptionImpl();
|
||||
searchOption.setSearchExpression(processExprOr(tokenizer));
|
||||
return searchOption;
|
||||
}
|
||||
|
||||
private SearchExpression processExprOr(UriTokenizer tokenizer) throws SearchParserException {
|
||||
SearchExpression left = processExprAnd(tokenizer);
|
||||
|
||||
while (tokenizer.next(TokenKind.OrOperatorSearch)) {
|
||||
final SearchExpression right = processExprAnd(tokenizer);
|
||||
left = new SearchBinaryImpl(left, SearchBinaryOperatorKind.OR, right);
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
private SearchExpression processExprAnd(UriTokenizer tokenizer) throws SearchParserException {
|
||||
SearchExpression left = processTerm(tokenizer);
|
||||
|
||||
while (tokenizer.next(TokenKind.AndOperatorSearch)) { // Could be whitespace or whitespace-surrounded 'AND'.
|
||||
final SearchExpression right = processTerm(tokenizer);
|
||||
left = new SearchBinaryImpl(left, SearchBinaryOperatorKind.AND, right);
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
private SearchExpression processTerm(UriTokenizer tokenizer) throws SearchParserException {
|
||||
if (tokenizer.next(TokenKind.OPEN)) {
|
||||
final SearchExpression expr = processExprOr(tokenizer);
|
||||
if (!tokenizer.next(TokenKind.CLOSE)) {
|
||||
throw new SearchParserException("Missing close parenthesis after open parenthesis.",
|
||||
SearchParserException.MessageKeys.MISSING_CLOSE);
|
||||
}
|
||||
return expr;
|
||||
} else if (tokenizer.next(TokenKind.NotOperatorSearch)) {
|
||||
return processNot(tokenizer);
|
||||
} else if (tokenizer.next(TokenKind.Word)) {
|
||||
return new SearchTermImpl(tokenizer.getText());
|
||||
} else if (tokenizer.next(TokenKind.Phrase)) {
|
||||
return processPhrase(tokenizer);
|
||||
} else {
|
||||
throw new SearchParserException("Expected PHRASE or WORD not found.",
|
||||
SearchParserException.MessageKeys.EXPECTED_DIFFERENT_TOKEN, "PHRASE, WORD", "");
|
||||
}
|
||||
}
|
||||
|
||||
private SearchExpression processNot(UriTokenizer tokenizer) throws SearchParserException {
|
||||
if (tokenizer.next(TokenKind.Word)) {
|
||||
return new SearchUnaryImpl(new SearchTermImpl(tokenizer.getText()));
|
||||
} else if (tokenizer.next(TokenKind.Phrase)) {
|
||||
return new SearchUnaryImpl(processPhrase(tokenizer));
|
||||
} else {
|
||||
throw new SearchParserException("NOT must be followed by a term.",
|
||||
SearchParserException.MessageKeys.INVALID_NOT_OPERAND, "");
|
||||
}
|
||||
}
|
||||
|
||||
private SearchTerm processPhrase(UriTokenizer tokenizer) {
|
||||
final String literal = tokenizer.getText();
|
||||
return new SearchTermImpl(literal.substring(1, literal.length() - 1)
|
||||
.replace("\\\"", "\"")
|
||||
.replace("\\\\", "\\"));
|
||||
}
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
/*
|
||||
* 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.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.EdmType;
|
||||
import org.apache.olingo.server.core.uri.UriInfoImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.ExpandItemImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.SelectItemImpl;
|
||||
|
||||
/**
|
||||
* UriContext object used for holding information for URI parsing.
|
||||
*
|
||||
*/
|
||||
public class UriContext {
|
||||
|
||||
public static class LambdaVariable {
|
||||
public String name;
|
||||
public EdmType type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hold all currently allowed lambda variables
|
||||
* As lambda functions can be nested there may be more than one allowed lambda variables at a time while parsing a
|
||||
* $filter or $orderby expressions.
|
||||
*/
|
||||
public Deque<LambdaVariable> allowedLambdaVariables;
|
||||
/**
|
||||
* Used to stack type information for nested $expand, $filter query options and other cases.
|
||||
*/
|
||||
public Deque<EdmType> contextTypes;
|
||||
|
||||
/** Whether the context types are collections. */
|
||||
public boolean isCollection;
|
||||
|
||||
// CHECKSTYLE:OFF (Maven checkstyle)
|
||||
/**
|
||||
* Set within method
|
||||
* {@link org.apache.olingo.server.core.uri.antlr.UriParserBaseVisitor#visitExpandItem(org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandItemContext ctx)}
|
||||
* and
|
||||
* {@link org.apache.olingo.server.core.uri.antlr.UriParserBaseVisitor#visitExpandPathExtension(org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandPathExtensionContext ctx)}
|
||||
* to allow nodes
|
||||
* deeper in the expand tree at
|
||||
* {@link org.apache.olingo.server.core.uri.antlr.UriParserBaseVisitor#visitExpandPathExtension(org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandPathExtensionContext ctx)}
|
||||
* appending path
|
||||
* segments to the currently processed {@link ExpandItemImpl}.
|
||||
*/
|
||||
public ExpandItemImpl contextExpandItemPath;
|
||||
// CHECKSTYLE:ON (Maven checkstyle)
|
||||
|
||||
//CHECKSTYLE:OFF (Maven checkstyle)
|
||||
/**
|
||||
* Set to true in method {@link UriParseTreeVisitor#visitExpandPath} right before
|
||||
* calling {@link org.apache.olingo.server.core.uri.parser.UriParseTreeVisitor#readResourcePathSegment}
|
||||
* After reading the path the variable is set back to false
|
||||
*
|
||||
* readResourcePathSegment handles all navigation properties, it depends on the context if key predicates are allowed or not.
|
||||
* In case of expand
|
||||
*/
|
||||
public boolean contextVisitExpandResourcePath;
|
||||
//CHECKSTYLE:ON (Maven checkstyle)
|
||||
|
||||
// CHECKSTYLE:OFF (Maven checkstyle)
|
||||
/**
|
||||
* Set within method
|
||||
* {@link org.apache.olingo.server.core.uri.antlr.UriParserBaseVisitor#visitSelectItem(org.apache.olingo.server.core.uri.antlr.UriParserParser.SelectItemContext ctx)}
|
||||
* to allow
|
||||
* nodes
|
||||
* deeper in the expand tree at
|
||||
* {@link org.apache.olingo.server.core.uri.antlr.UriParserBaseVisitor#visitSelectSegment(org.apache.olingo.server.core.uri.antlr.UriParserParser.SelectSegmentContext ctx)}
|
||||
* appending path segments to the
|
||||
* currently processed {@link SelectItemImpl}.
|
||||
*/
|
||||
public SelectItemImpl contextSelectItem;
|
||||
// CHECKSTYLE:ON (Maven checkstyle)
|
||||
/**
|
||||
* Stores the currently processed UriInfo objects. There is one URI Info object for the resource path
|
||||
* and one for each new first member access within $filter and $orderBy options.
|
||||
*/
|
||||
public UriInfoImpl contextUriInfo;
|
||||
public boolean contextReadingFunctionParameters;
|
||||
|
||||
/**
|
||||
* Set to true if the parser operates on query part.
|
||||
*/
|
||||
public boolean contextReadingQueryPart;
|
||||
|
||||
public UriContext() {
|
||||
|
||||
contextExpandItemPath = null;
|
||||
contextReadingFunctionParameters = false;
|
||||
contextSelectItem = null;
|
||||
contextTypes = new ArrayDeque<EdmType>();
|
||||
allowedLambdaVariables = new ArrayDeque<UriContext.LambdaVariable>();
|
||||
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -40,6 +40,15 @@ public class UriTokenizer {
|
|||
ROOT,
|
||||
IT,
|
||||
|
||||
EXPAND,
|
||||
FILTER,
|
||||
LEVELS,
|
||||
ORDERBY,
|
||||
SEARCH,
|
||||
SELECT,
|
||||
SKIP,
|
||||
TOP,
|
||||
|
||||
ANY,
|
||||
ALL,
|
||||
|
||||
|
@ -53,8 +62,9 @@ public class UriTokenizer {
|
|||
EQ,
|
||||
STAR,
|
||||
PLUS,
|
||||
MINUS,
|
||||
|
||||
NULL,
|
||||
MAX,
|
||||
|
||||
// variable-value tokens (convention: mixed case)
|
||||
ODataIdentifier,
|
||||
|
@ -76,6 +86,13 @@ public class UriTokenizer {
|
|||
|
||||
jsonArrayOrObject,
|
||||
|
||||
Word,
|
||||
Phrase,
|
||||
|
||||
OrOperatorSearch,
|
||||
AndOperatorSearch,
|
||||
NotOperatorSearch,
|
||||
|
||||
OrOperator,
|
||||
AndOperator,
|
||||
EqualsOperator,
|
||||
|
@ -90,6 +107,7 @@ public class UriTokenizer {
|
|||
MulOperator,
|
||||
DivOperator,
|
||||
ModOperator,
|
||||
MinusOperator,
|
||||
NotOperator,
|
||||
|
||||
CastMethod,
|
||||
|
@ -161,6 +179,10 @@ public class UriTokenizer {
|
|||
boolean found = false;
|
||||
final int previousIndex = index;
|
||||
switch (allowedTokenKind) {
|
||||
case EOF:
|
||||
found = index >= parseString.length();
|
||||
break;
|
||||
|
||||
// Constants
|
||||
case REF:
|
||||
found = nextConstant("$ref");
|
||||
|
@ -181,6 +203,31 @@ public class UriTokenizer {
|
|||
found = nextConstant("$it");
|
||||
break;
|
||||
|
||||
case EXPAND:
|
||||
found = nextConstant("$expand");
|
||||
break;
|
||||
case FILTER:
|
||||
found = nextConstant("$filter");
|
||||
break;
|
||||
case LEVELS:
|
||||
found = nextConstant("$levels");
|
||||
break;
|
||||
case ORDERBY:
|
||||
found = nextConstant("$orderby");
|
||||
break;
|
||||
case SEARCH:
|
||||
found = nextConstant("$search");
|
||||
break;
|
||||
case SELECT:
|
||||
found = nextConstant("$select");
|
||||
break;
|
||||
case SKIP:
|
||||
found = nextConstant("$skip");
|
||||
break;
|
||||
case TOP:
|
||||
found = nextConstant("$top");
|
||||
break;
|
||||
|
||||
case ANY:
|
||||
found = nextConstant("any");
|
||||
break;
|
||||
|
@ -218,14 +265,12 @@ public class UriTokenizer {
|
|||
case PLUS:
|
||||
found = nextCharacter('+');
|
||||
break;
|
||||
case MINUS:
|
||||
found = nextMinus();
|
||||
break;
|
||||
|
||||
case NULL:
|
||||
found = nextConstant("null");
|
||||
break;
|
||||
case EOF:
|
||||
found = index >= parseString.length();
|
||||
case MAX:
|
||||
found = nextConstant("max");
|
||||
break;
|
||||
|
||||
// Identifiers
|
||||
|
@ -282,6 +327,25 @@ public class UriTokenizer {
|
|||
found = nextJsonArrayOrObject();
|
||||
break;
|
||||
|
||||
// Search
|
||||
case Word:
|
||||
found = nextWord();
|
||||
break;
|
||||
case Phrase:
|
||||
found = nextPhrase();
|
||||
break;
|
||||
|
||||
// Operators in Search Expressions
|
||||
case OrOperatorSearch:
|
||||
found = nextBinaryOperator("OR");
|
||||
break;
|
||||
case AndOperatorSearch:
|
||||
found = nextAndOperatorSearch();
|
||||
break;
|
||||
case NotOperatorSearch:
|
||||
found = nextUnaryOperator("NOT");
|
||||
break;
|
||||
|
||||
// Operators
|
||||
case OrOperator:
|
||||
found = nextBinaryOperator("or");
|
||||
|
@ -325,8 +389,12 @@ public class UriTokenizer {
|
|||
case ModOperator:
|
||||
found = nextBinaryOperator("mod");
|
||||
break;
|
||||
case MinusOperator:
|
||||
// To avoid unnecessary minus operators for negative numbers, we have to check what follows the minus sign.
|
||||
found = nextCharacter('-') && !nextDigit() && !nextConstant("INF");
|
||||
break;
|
||||
case NotOperator:
|
||||
found = nextConstant("not") && nextWhitespace();
|
||||
found = nextUnaryOperator("not");
|
||||
break;
|
||||
|
||||
// Methods
|
||||
|
@ -444,27 +512,6 @@ public class UriTokenizer {
|
|||
return found;
|
||||
}
|
||||
|
||||
private boolean nextMinus() {
|
||||
if(parseString.startsWith("-", index)) {
|
||||
final int lastGoodIndex = index;
|
||||
|
||||
if(nextDoubleValue()) {
|
||||
index = lastGoodIndex;
|
||||
return false;
|
||||
} else if(nextDecimalValue()) {
|
||||
index = lastGoodIndex;
|
||||
return false;
|
||||
} else if(nextIntegerValue(true)) {
|
||||
index = lastGoodIndex;
|
||||
return false;
|
||||
} else {
|
||||
index++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves past the given string constant if found; otherwise leaves the index unchanged.
|
||||
* @return whether the constant has been found at the current index
|
||||
|
@ -569,34 +616,6 @@ public class UriTokenizer {
|
|||
}
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves past the given whitespace-surrounded operator constant if found;
|
||||
* otherwise leaves the index unchanged.
|
||||
* @return whether the operator has been found at the current index
|
||||
*/
|
||||
private boolean nextBinaryOperator(final String operator) {
|
||||
return nextWhitespace() && nextConstant(operator) && nextWhitespace();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves past the given method name and its immediately following opening parenthesis if found;
|
||||
* otherwise leaves the index unchanged.
|
||||
* @return whether the method has been found at the current index
|
||||
*/
|
||||
private boolean nextMethod(final String methodName) {
|
||||
return nextConstant(methodName) && nextCharacter('(');
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves past (required) whitespace and the given suffix name if found;
|
||||
* otherwise leaves the index unchanged.
|
||||
* @return whether the suffix has been found at the current index
|
||||
*/
|
||||
private boolean nextSuffix(final String suffixName) {
|
||||
return nextWhitespace() && nextConstant(suffixName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves past an OData identifier if found; otherwise leaves the index unchanged.
|
||||
* @return whether an OData identifier has been found at the current index
|
||||
|
@ -650,6 +669,38 @@ public class UriTokenizer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves past the given whitespace-surrounded operator constant if found.
|
||||
* @return whether the operator has been found at the current index
|
||||
*/
|
||||
private boolean nextBinaryOperator(final String operator) {
|
||||
return nextWhitespace() && nextConstant(operator) && nextWhitespace();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves past the given whitespace-suffixed operator constant if found.
|
||||
* @return whether the operator has been found at the current index
|
||||
*/
|
||||
private boolean nextUnaryOperator(final String operator) {
|
||||
return nextConstant(operator) && nextWhitespace();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves past the given method name and its immediately following opening parenthesis if found.
|
||||
* @return whether the method has been found at the current index
|
||||
*/
|
||||
private boolean nextMethod(final String methodName) {
|
||||
return nextConstant(methodName) && nextCharacter('(');
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves past (required) whitespace and the given suffix name if found.
|
||||
* @return whether the suffix has been found at the current index
|
||||
*/
|
||||
private boolean nextSuffix(final String suffixName) {
|
||||
return nextWhitespace() && nextConstant(suffixName);
|
||||
}
|
||||
|
||||
private boolean nextParameterAliasName() {
|
||||
return nextCharacter('@') && nextODataIdentifier();
|
||||
}
|
||||
|
@ -978,4 +1029,52 @@ public class UriTokenizer {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean nextAndOperatorSearch() {
|
||||
if (nextWhitespace()) {
|
||||
final int lastGoodIndex = index;
|
||||
if (nextUnaryOperator("OR")) {
|
||||
return false;
|
||||
} else if (!(nextUnaryOperator("AND"))) {
|
||||
index = lastGoodIndex;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean nextWord() {
|
||||
int count = 0;
|
||||
while (index < parseString.length()) {
|
||||
final int code = parseString.codePointAt(index);
|
||||
if (Character.isUnicodeIdentifierStart(code)) {
|
||||
count++;
|
||||
// Unicode characters outside of the Basic Multilingual Plane are represented as two Java characters.
|
||||
index += Character.isSupplementaryCodePoint(code) ? 2 : 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
final String word = parseString.substring(index - count, index);
|
||||
return count > 0 && !("OR".equals(word) || "AND".equals(word) || "NOT".equals(word));
|
||||
}
|
||||
|
||||
private boolean nextPhrase() {
|
||||
if (nextCharacter('"')) {
|
||||
do {
|
||||
if (nextCharacter('\\')) {
|
||||
if (!(nextCharacter('\\') || nextCharacter('"'))) {
|
||||
return false;
|
||||
}
|
||||
} else if (nextCharacter('"')) {
|
||||
return true;
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
} while (index < parseString.length());
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,4 +41,8 @@ public class AliasImpl implements Alias {
|
|||
return visitor.visitAlias(parameterName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return parameterName;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,4 +53,10 @@ public class EnumerationImpl implements Enumeration {
|
|||
public <T> T accept(final ExpressionVisitor<T> visitor) throws ExpressionVisitException, ODataApplicationException {
|
||||
return visitor.visitEnum(type, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return type == null ? null :
|
||||
type.getFullQualifiedName().getFullQualifiedNameAsString() + getValues();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,4 +40,9 @@ public class LambdaRefImpl implements LambdaRef {
|
|||
public <T> T accept(final ExpressionVisitor<T> visitor) throws ExpressionVisitException, ODataApplicationException {
|
||||
return visitor.visitLambdaReference(variableText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return variableText;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,4 +41,9 @@ public class TypeLiteralImpl implements TypeLiteral {
|
|||
public <T> T accept(final ExpressionVisitor<T> visitor) throws ExpressionVisitException, ODataApplicationException {
|
||||
return visitor.visitTypeLiteral(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return type == null ? null : type.getFullQualifiedName().getFullQualifiedNameAsString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,11 +81,11 @@ UriParserSemanticException.COMPLEX_PROPERTY_OF_ENTITY_TYPE_EXPECTED=A complex pr
|
|||
UriParserSemanticException.NOT_FOR_ENTITY_TYPE=Not allowed for entity type.
|
||||
UriParserSemanticException.PREVIOUS_PART_TYPED=The previous part is typed.
|
||||
UriParserSemanticException.RESOURCE_NOT_FOUND=Cannot find EntitySet, Singleton, ActionImport or FunctionImport with name '%1$s'.
|
||||
UriParserSemanticException.NOT_IMPLEMENTED=%1$s is not implemented!
|
||||
UriParserSemanticException.NAMESPACE_NOT_ALLOWED_AT_FIRST_ELEMENT=Namespace is not allowed for Entity Sets, Singeltons, Action Imports and Function Imports. Found '%1$s'.
|
||||
UriParserSemanticException.COMPLEX_PARAMETER_IN_RESOURCE_PATH=Complex parameters must not appear in resource path segments. Found: '%1$s'.
|
||||
UriParserSemanticException.NOT_IMPLEMENTED='%1$s' is not implemented!
|
||||
UriParserSemanticException.NAMESPACE_NOT_ALLOWED_AT_FIRST_ELEMENT=Namespace is not allowed for Entity Sets, Singletons, Action Imports and Function Imports; found '%1$s'.
|
||||
UriParserSemanticException.COMPLEX_PARAMETER_IN_RESOURCE_PATH=Complex parameters must not appear in resource path segments; found: '%1$s'.
|
||||
UriParserSemanticException.FUNCTION_IMPORT_NOT_ALLOWED=Function Imports are not allowed in $filter or $orderby. Found: '%1$s'.
|
||||
UriParserSemanticException.TYPES_NOT_COMPATIBLE=Types are not compatible. Left type: '%1$s', right type: '%1$s'.
|
||||
UriParserSemanticException.TYPES_NOT_COMPATIBLE=The types '%1$s' and '%2$s' are not compatible.
|
||||
|
||||
UriValidationException.UNSUPPORTED_QUERY_OPTION=The query option '%1$s' is not supported.
|
||||
UriValidationException.UNSUPPORTED_URI_KIND=The URI kind '%1$s' is not supported.
|
||||
|
|
|
@ -22,9 +22,11 @@ import static org.junit.Assert.assertEquals;
|
|||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.Edm;
|
||||
import org.apache.olingo.server.api.OData;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
|
||||
import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
|
||||
|
@ -260,9 +262,17 @@ public class ExpressionParserTest {
|
|||
expression = parseMethod(TokenKind.SubstringMethod, "'abc'", "1");
|
||||
assertEquals("{substring ['abc', 1]}", expression.toString());
|
||||
|
||||
assertEquals("{cast [Edm.SByte]}", parseMethod(TokenKind.CastMethod, "Edm.SByte").toString());
|
||||
assertEquals("{cast [42, Edm.SByte]}", parseMethod(TokenKind.CastMethod, "42", "Edm.SByte").toString());
|
||||
|
||||
assertEquals("{isof [Edm.SByte]}", parseMethod(TokenKind.IsofMethod, "Edm.SByte").toString());
|
||||
assertEquals("{isof [42, Edm.SByte]}", parseMethod(TokenKind.IsofMethod, "42", "Edm.SByte").toString());
|
||||
|
||||
wrongExpression("substring('abc')");
|
||||
wrongExpression("substring('abc',1,2,3)");
|
||||
wrongExpression("substring(1,2)");
|
||||
wrongExpression("cast(1,2)");
|
||||
wrongExpression("isof(Edm.Int16,2)");
|
||||
}
|
||||
|
||||
private Expression parseMethod(TokenKind kind, String... parameters)
|
||||
|
@ -288,7 +298,7 @@ public class ExpressionParserTest {
|
|||
private Expression parseExpression(final String expressionString)
|
||||
throws UriParserException, UriValidationException {
|
||||
UriTokenizer tokenizer = new UriTokenizer(expressionString);
|
||||
Expression expression = new ExpressionParser(null, odata).parse(tokenizer, null, null);
|
||||
Expression expression = new ExpressionParser(mock(Edm.class), odata).parse(tokenizer, null, null);
|
||||
assertNotNull(expression);
|
||||
assertTrue(tokenizer.next(TokenKind.EOF));
|
||||
return expression;
|
||||
|
@ -296,7 +306,7 @@ public class ExpressionParserTest {
|
|||
|
||||
private void wrongExpression(final String expressionString) {
|
||||
try {
|
||||
new ExpressionParser(null, odata).parse(new UriTokenizer(expressionString), null, null);
|
||||
new ExpressionParser(mock(Edm.class), odata).parse(new UriTokenizer(expressionString), null, null);
|
||||
fail("Expected exception not thrown.");
|
||||
} catch (final UriParserException e) {
|
||||
assertNotNull(e);
|
||||
|
|
|
@ -76,7 +76,7 @@ public class UriTokenizerTest {
|
|||
assertTrue(tokenizer.next(TokenKind.STAR));
|
||||
assertTrue(tokenizer.next(TokenKind.SLASH));
|
||||
assertTrue(tokenizer.next(TokenKind.PLUS));
|
||||
assertTrue(tokenizer.next(TokenKind.MINUS));
|
||||
assertTrue(tokenizer.next(TokenKind.MinusOperator));
|
||||
assertTrue(tokenizer.next(TokenKind.EOF));
|
||||
|
||||
tokenizer = new UriTokenizer("any(a:true) or all(b:false)");
|
||||
|
@ -96,6 +96,45 @@ public class UriTokenizerTest {
|
|||
assertTrue(tokenizer.next(TokenKind.EOF));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void systemQueryOptions() {
|
||||
UriTokenizer tokenizer = new UriTokenizer("$expand=*;$filter=true;$levels=max;$orderby=false");
|
||||
assertTrue(tokenizer.next(TokenKind.EXPAND));
|
||||
assertTrue(tokenizer.next(TokenKind.EQ));
|
||||
assertTrue(tokenizer.next(TokenKind.STAR));
|
||||
assertTrue(tokenizer.next(TokenKind.SEMI));
|
||||
assertTrue(tokenizer.next(TokenKind.FILTER));
|
||||
assertTrue(tokenizer.next(TokenKind.EQ));
|
||||
assertTrue(tokenizer.next(TokenKind.BooleanValue));
|
||||
assertTrue(tokenizer.next(TokenKind.SEMI));
|
||||
assertTrue(tokenizer.next(TokenKind.LEVELS));
|
||||
assertTrue(tokenizer.next(TokenKind.EQ));
|
||||
assertTrue(tokenizer.next(TokenKind.MAX));
|
||||
assertTrue(tokenizer.next(TokenKind.SEMI));
|
||||
assertTrue(tokenizer.next(TokenKind.ORDERBY));
|
||||
assertTrue(tokenizer.next(TokenKind.EQ));
|
||||
assertTrue(tokenizer.next(TokenKind.BooleanValue));
|
||||
assertTrue(tokenizer.next(TokenKind.EOF));
|
||||
|
||||
tokenizer = new UriTokenizer("$search=A;$select=*;$skip=1;$top=2");
|
||||
assertTrue(tokenizer.next(TokenKind.SEARCH));
|
||||
assertTrue(tokenizer.next(TokenKind.EQ));
|
||||
assertTrue(tokenizer.next(TokenKind.ODataIdentifier));
|
||||
assertTrue(tokenizer.next(TokenKind.SEMI));
|
||||
assertTrue(tokenizer.next(TokenKind.SELECT));
|
||||
assertTrue(tokenizer.next(TokenKind.EQ));
|
||||
assertTrue(tokenizer.next(TokenKind.STAR));
|
||||
assertTrue(tokenizer.next(TokenKind.SEMI));
|
||||
assertTrue(tokenizer.next(TokenKind.SKIP));
|
||||
assertTrue(tokenizer.next(TokenKind.EQ));
|
||||
assertTrue(tokenizer.next(TokenKind.IntegerValue));
|
||||
assertTrue(tokenizer.next(TokenKind.SEMI));
|
||||
assertTrue(tokenizer.next(TokenKind.TOP));
|
||||
assertTrue(tokenizer.next(TokenKind.EQ));
|
||||
assertTrue(tokenizer.next(TokenKind.IntegerValue));
|
||||
assertTrue(tokenizer.next(TokenKind.EOF));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void identifier() {
|
||||
assertTrue(new UriTokenizer("name").next(TokenKind.ODataIdentifier));
|
||||
|
@ -390,11 +429,11 @@ public class UriTokenizerTest {
|
|||
assertTrue(tokenizer.next(TokenKind.IntegerValue));
|
||||
assertTrue(tokenizer.next(TokenKind.EOF));
|
||||
|
||||
tokenizer = new UriTokenizer("1ne 2");
|
||||
tokenizer = new UriTokenizer("-1ne 2");
|
||||
assertTrue(tokenizer.next(TokenKind.IntegerValue));
|
||||
assertFalse(tokenizer.next(TokenKind.NotEqualsOperator));
|
||||
|
||||
tokenizer = new UriTokenizer("1 ne2");
|
||||
tokenizer = new UriTokenizer("1 ne-2");
|
||||
assertTrue(tokenizer.next(TokenKind.IntegerValue));
|
||||
assertFalse(tokenizer.next(TokenKind.NotEqualsOperator));
|
||||
|
||||
|
@ -404,6 +443,11 @@ public class UriTokenizerTest {
|
|||
assertTrue(tokenizer.next(TokenKind.IntegerValue));
|
||||
assertTrue(tokenizer.next(TokenKind.EOF));
|
||||
|
||||
assertTrue(new UriTokenizer("-x").next(TokenKind.MinusOperator));
|
||||
assertFalse(new UriTokenizer("-1").next(TokenKind.MinusOperator));
|
||||
assertFalse(new UriTokenizer("-INF").next(TokenKind.MinusOperator));
|
||||
assertFalse(new UriTokenizer("+").next(TokenKind.MinusOperator));
|
||||
|
||||
assertFalse(new UriTokenizer("nottrue").next(TokenKind.NotOperator));
|
||||
assertFalse(new UriTokenizer("no true").next(TokenKind.NotOperator));
|
||||
|
||||
|
@ -484,6 +528,38 @@ public class UriTokenizerTest {
|
|||
wrongToken(TokenKind.DescSuffix, " desc", 'D');
|
||||
}
|
||||
|
||||
@Test
|
||||
public void search() {
|
||||
UriTokenizer tokenizer = new UriTokenizer("a AND b OR NOT \"c\" d");
|
||||
assertTrue(tokenizer.next(TokenKind.Word));
|
||||
assertTrue(tokenizer.next(TokenKind.AndOperatorSearch));
|
||||
assertTrue(tokenizer.next(TokenKind.Word));
|
||||
assertFalse(tokenizer.next(TokenKind.AndOperatorSearch));
|
||||
assertTrue(tokenizer.next(TokenKind.OrOperatorSearch));
|
||||
assertTrue(tokenizer.next(TokenKind.NotOperatorSearch));
|
||||
assertTrue(tokenizer.next(TokenKind.Phrase));
|
||||
assertTrue(tokenizer.next(TokenKind.AndOperatorSearch));
|
||||
assertTrue(tokenizer.next(TokenKind.Word));
|
||||
assertFalse(tokenizer.next(TokenKind.AndOperatorSearch));
|
||||
assertFalse(tokenizer.next(TokenKind.Word));
|
||||
assertFalse(tokenizer.next(TokenKind.Phrase));
|
||||
assertTrue(tokenizer.next(TokenKind.EOF));
|
||||
|
||||
assertTrue(new UriTokenizer("\"a\\\\x\\\"\"").next(TokenKind.Phrase));
|
||||
assertFalse(new UriTokenizer("\"a\\\"").next(TokenKind.Phrase));
|
||||
assertFalse(new UriTokenizer("\"a\\x\"").next(TokenKind.Phrase));
|
||||
wrongToken(TokenKind.Phrase, "\"a\"", '\\');
|
||||
|
||||
final String outsideBmpLetter = String.valueOf(Character.toChars(0x10330));
|
||||
assertTrue(new UriTokenizer("\"" + outsideBmpLetter + "\"").next(TokenKind.Phrase));
|
||||
|
||||
assertTrue(new UriTokenizer(outsideBmpLetter).next(TokenKind.Word));
|
||||
assertFalse(new UriTokenizer("1").next(TokenKind.Word));
|
||||
assertFalse(new UriTokenizer("AND").next(TokenKind.Word));
|
||||
assertFalse(new UriTokenizer("OR").next(TokenKind.Word));
|
||||
assertFalse(new UriTokenizer("NOT").next(TokenKind.Word));
|
||||
}
|
||||
|
||||
private void wrongToken(final TokenKind kind, final String value, final char disturbCharacter) {
|
||||
assertFalse(new UriTokenizer(disturbCharacter + value).next(kind));
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ public class SearchParserAndTokenizerTest {
|
|||
assertQuery("a AND b AND c").resultsIn("{{'a' AND 'b'} AND 'c'}");
|
||||
assertQuery("a OR b").resultsIn("{'a' OR 'b'}");
|
||||
assertQuery("a OR b OR c").resultsIn("{{'a' OR 'b'} OR 'c'}");
|
||||
|
||||
|
||||
assertQuery("NOT a NOT b").resultsIn("{{NOT 'a'} AND {NOT 'b'}}");
|
||||
assertQuery("NOT a AND NOT b").resultsIn("{{NOT 'a'} AND {NOT 'b'}}");
|
||||
assertQuery("NOT a OR NOT b").resultsIn("{{NOT 'a'} OR {NOT 'b'}}");
|
||||
|
@ -59,16 +59,16 @@ public class SearchParserAndTokenizerTest {
|
|||
assertQuery("a AND (b OR c)").resultsIn("{'a' AND {'b' OR 'c'}}");
|
||||
assertQuery("(a OR b) AND NOT c").resultsIn("{{'a' OR 'b'} AND {NOT 'c'}}");
|
||||
assertQuery("(a OR B) AND (c OR d AND NOT e OR (f))")
|
||||
.resultsIn("{{'a' OR 'B'} AND {{'c' OR {'d' AND {NOT 'e'}}} OR 'f'}}");
|
||||
.resultsIn("{{'a' OR 'B'} AND {{'c' OR {'d' AND {NOT 'e'}}} OR 'f'}}");
|
||||
assertQuery("(a OR B) (c OR d NOT e OR (f))")
|
||||
.resultsIn("{{'a' OR 'B'} AND {{'c' OR {'d' AND {NOT 'e'}}} OR 'f'}}");
|
||||
.resultsIn("{{'a' OR 'B'} AND {{'c' OR {'d' AND {NOT 'e'}}} OR 'f'}}");
|
||||
assertQuery("((((a))))").resultsIn("'a'");
|
||||
assertQuery("((((a)))) ((((a))))").resultsIn("{'a' AND 'a'}");
|
||||
assertQuery("((((a)))) OR ((((a))))").resultsIn("{'a' OR 'a'}");
|
||||
assertQuery("((((((a)))) ((((c))) OR (((C)))) ((((a))))))").resultsIn("{{'a' AND {'c' OR 'C'}} AND 'a'}");
|
||||
assertQuery("((((\"a\")))) OR ((((\"a\"))))").resultsIn("{'a' OR 'a'}");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void parseImplicitAnd() throws Exception {
|
||||
assertQuery("a b").resultsIn("{'a' AND 'b'}");
|
||||
|
@ -103,7 +103,7 @@ public class SearchParserAndTokenizerTest {
|
|||
assertQuery("((a AND b OR c)").resultsIn(SearchParserException.MessageKeys.MISSING_CLOSE);
|
||||
assertQuery("a AND (b OR c").resultsIn(SearchParserException.MessageKeys.MISSING_CLOSE);
|
||||
assertQuery("(a AND ((b OR c)").resultsIn(SearchParserException.MessageKeys.MISSING_CLOSE);
|
||||
|
||||
|
||||
assertQuery("NOT NOT a").resultsIn(SearchParserException.MessageKeys.INVALID_NOT_OPERAND);
|
||||
assertQuery("NOT (a)").resultsIn(SearchParserException.MessageKeys.TOKENIZER_EXCEPTION);
|
||||
}
|
||||
|
@ -186,7 +186,6 @@ public class SearchParserAndTokenizerTest {
|
|||
// <Input>http://serviceRoot/Products?$search=blue</Input>
|
||||
assertQuery("blue").resultsIn("'blue'");
|
||||
|
||||
|
||||
// below cases can not be tested here
|
||||
// <TestCase Name="5.1.7 Search - on entity container" Rule="odataUri">
|
||||
// <Input>http://serviceRoot/Model.Container/$all?$search=blue</Input>
|
||||
|
@ -194,68 +193,47 @@ public class SearchParserAndTokenizerTest {
|
|||
// <Input>http://serviceRoot/$all?$search=blue</Input>
|
||||
}
|
||||
|
||||
|
||||
private static Validator assertQuery(String searchQuery) {
|
||||
return Validator.init(searchQuery);
|
||||
return new Validator(searchQuery);
|
||||
}
|
||||
|
||||
private static class Validator {
|
||||
private boolean log;
|
||||
private final String searchQuery;
|
||||
|
||||
private Validator(String searchQuery) {
|
||||
this.searchQuery = searchQuery;
|
||||
}
|
||||
|
||||
private static Validator init(String searchQuery) {
|
||||
return new Validator(searchQuery);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Validator withLogging() {
|
||||
log = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void resultsIn(SearchParserException.MessageKey key)
|
||||
throws SearchTokenizerException {
|
||||
private void resultsIn(SearchParserException.MessageKey key) throws SearchTokenizerException {
|
||||
try {
|
||||
resultsIn(searchQuery);
|
||||
} catch (SearchParserException e) {
|
||||
Assert.assertEquals("SearchParserException with unexpected message '" + e.getMessage() +
|
||||
"' was thrown.", key, e.getMessageKey());
|
||||
if(log) {
|
||||
System.out.println("Caught SearchParserException with message key " +
|
||||
e.getMessageKey() + " and message " + e.getMessage());
|
||||
}
|
||||
return;
|
||||
}
|
||||
Assert.fail("SearchParserException with message key " + key.getKey() + " was not thrown.");
|
||||
}
|
||||
|
||||
|
||||
public void resultsInExpectedTerm(final String actualToken) throws SearchTokenizerException {
|
||||
try {
|
||||
resultsIn(searchQuery);
|
||||
} catch(SearchParserException e) {
|
||||
} catch (SearchParserException e) {
|
||||
Assert.assertEquals(SearchParserException.MessageKeys.EXPECTED_DIFFERENT_TOKEN, e.getMessageKey());
|
||||
Assert.assertEquals("Expected PHRASE||WORD found: " + actualToken, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void resultsIn(String expectedSearchExpression) throws SearchTokenizerException, SearchParserException {
|
||||
final SearchExpression searchExpression = getSearchExpression();
|
||||
Assert.assertEquals(expectedSearchExpression, searchExpression.toString());
|
||||
}
|
||||
|
||||
private SearchExpression getSearchExpression() throws SearchParserException, SearchTokenizerException {
|
||||
SearchParser tokenizer = new SearchParser();
|
||||
SearchOption result = tokenizer.parse(searchQuery);
|
||||
SearchOption result = new SearchParser().parse(searchQuery);
|
||||
Assert.assertNotNull(result);
|
||||
final SearchExpression searchExpression = result.getSearchExpression();
|
||||
Assert.assertNotNull(searchExpression);
|
||||
if (log) {
|
||||
System.out.println(searchExpression);
|
||||
}
|
||||
return searchExpression;
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -18,16 +18,22 @@
|
|||
*/
|
||||
package org.apache.olingo.server.core.uri.antlr;
|
||||
|
||||
import org.antlr.v4.runtime.Lexer;
|
||||
import org.apache.olingo.server.core.uri.testutil.TokenValidator;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.apache.olingo.server.core.uri.parser.UriTokenizer;
|
||||
import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests originally written for the ANTLR lexer.
|
||||
*/
|
||||
public class TestLexer {
|
||||
|
||||
private TokenValidator test = null;
|
||||
|
||||
private static final String cPCT_ENCODED = "%45%46%47" + "%22" + "%5C";// last two chars are not in
|
||||
// cPCT_ENCODED_UNESCAPED
|
||||
// The last two chars are not in cPCT_ENCODED_UNESCAPED.
|
||||
private static final String cPCT_ENCODED = "%45%46%47" + "%22" + "%5C";
|
||||
private static final String cUNRESERVED = "ABCabc123-._~";
|
||||
private static final String cOTHER_DELIMS = "!()*+,;";
|
||||
private static final String cSUB_DELIMS = "$&'=" + cOTHER_DELIMS;
|
||||
|
@ -39,265 +45,279 @@ public class TestLexer {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
// test.log(1).run("ESAllPrim?$orderby=PropertyDouble eq 3.5E+38");
|
||||
}
|
||||
|
||||
// ;------------------------------------------------------------------------------
|
||||
// ; 0. URI
|
||||
// ;------------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void testUnary() {
|
||||
test.run("- a eq a").isAllInput();
|
||||
public void unary() {
|
||||
test.run("-a eq a").has(TokenKind.MinusOperator, TokenKind.ODataIdentifier, TokenKind.EqualsOperator,
|
||||
TokenKind.ODataIdentifier).isInput();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUriTokens() {
|
||||
test.globalMode(UriLexer.MODE_QUERY);
|
||||
test.run("#").isText("#").isType(UriLexer.FRAGMENT);
|
||||
test.run("$count").isText("$count").isType(UriLexer.COUNT);
|
||||
test.run("$ref").isText("$ref").isType(UriLexer.REF);
|
||||
test.run("$value").isText("$value").isType(UriLexer.VALUE);
|
||||
public void uriTokens() {
|
||||
// test.run("#").isType(TokenKind.FRAGMENT).isInput();
|
||||
test.run("$count").has(TokenKind.COUNT).isInput();
|
||||
test.run("$ref").has(TokenKind.REF).isInput();
|
||||
test.run("$value").has(TokenKind.VALUE).isInput();
|
||||
}
|
||||
|
||||
// ;------------------------------------------------------------------------------
|
||||
// ; 2. Query Options
|
||||
// ;------------------------------------------------------------------------------
|
||||
@Test
|
||||
public void testQueryOptionsTokens() {
|
||||
public void queryOptionsTokens() {
|
||||
test.run("$skip=1").has(TokenKind.SKIP, TokenKind.EQ, TokenKind.IntegerValue).isInput();
|
||||
test.run("$skip=2").has(TokenKind.SKIP, TokenKind.EQ, TokenKind.IntegerValue).isInput();
|
||||
test.run("$skip=123").has(TokenKind.SKIP, TokenKind.EQ, TokenKind.IntegerValue).isInput();
|
||||
|
||||
test.globalMode(UriLexer.MODE_QUERY);
|
||||
test.run("$skip=1").isAllText("$skip=1").isType(UriLexer.SKIP_QO);
|
||||
test.run("$skip=2").isAllText("$skip=2").isType(UriLexer.SKIP_QO);
|
||||
test.run("$skip=123").isAllText("$skip=123").isType(UriLexer.SKIP_QO);
|
||||
test.run("$top=1").has(TokenKind.TOP, TokenKind.EQ, TokenKind.IntegerValue).isInput();
|
||||
test.run("$top=2").has(TokenKind.TOP, TokenKind.EQ, TokenKind.IntegerValue).isInput();
|
||||
test.run("$top=123").has(TokenKind.TOP, TokenKind.EQ, TokenKind.IntegerValue).isInput();
|
||||
|
||||
test.run("$top=1").isAllText("$top=1").isType(UriLexer.TOP);
|
||||
test.run("$top=2").isAllText("$top=2").isType(UriLexer.TOP);
|
||||
test.run("$top=123").isAllText("$top=123").isType(UriLexer.TOP);
|
||||
test.run("$levels=1").has(TokenKind.LEVELS, TokenKind.EQ, TokenKind.IntegerValue).isInput();
|
||||
test.run("$levels=2").has(TokenKind.LEVELS, TokenKind.EQ, TokenKind.IntegerValue).isInput();
|
||||
test.run("$levels=123").has(TokenKind.LEVELS, TokenKind.EQ, TokenKind.IntegerValue).isInput();
|
||||
test.run("$levels=max").has(TokenKind.LEVELS, TokenKind.EQ, TokenKind.MAX).isInput();
|
||||
|
||||
test.run("$levels=1").isAllText("$levels=1").isType(UriLexer.LEVELS);
|
||||
test.run("$levels=2").isAllText("$levels=2").isType(UriLexer.LEVELS);
|
||||
test.run("$levels=123").isAllText("$levels=123").isType(UriLexer.LEVELS);
|
||||
test.run("$levels=max").isAllText("$levels=max").isType(UriLexer.LEVELS);
|
||||
// test.run("$format=atom").has(TokenKind.FORMAT, TokenKind.EQ, TokenKind.ODataIdentifier).isInput();
|
||||
// test.run("$format=json").has(TokenKind.FORMAT, TokenKind.EQ, TokenKind.ODataIdentifier).isInput();
|
||||
// test.run("$format=xml").has(TokenKind.FORMAT,, TokenKind.EQ, TokenKind.ODataIdentifier).isInput();
|
||||
// test.run("$format=abc/def").has(TokenKind.FORMAT, TokenKind.EQ,
|
||||
// TokenKind.ODataIdentifier, TokenKind.SLASH, TokenKind.ODataIdentifier).isInput();
|
||||
|
||||
test.run("$format=atom").isAllText("$format=atom").isType(UriLexer.FORMAT);
|
||||
test.run("$format=json").isAllText("$format=json").isType(UriLexer.FORMAT);
|
||||
test.run("$format=xml").isAllText("$format=xml").isType(UriLexer.FORMAT);
|
||||
test.run("$format=abc/def").isAllText("$format=abc/def").isType(UriLexer.FORMAT);
|
||||
// test.run("$id=123").has(TokenKind.ID, TokenKind.EQ, TokenKind.IntegerValue).isInput();
|
||||
// test.run("$id=ABC").has(TokenKind.ID, TokenKind.EQ, TokenKind.ODataIdentifier).isInput();
|
||||
|
||||
test.run("$id=123").isAllText("$id=123").isType(UriLexer.ID);
|
||||
test.run("$id=ABC").isAllText("$id=ABC").isType(UriLexer.ID);
|
||||
// test.run("$skiptoken=ABC").has(TokenKind.SKIPTOKEN, TokenKind.EQ, TokenKind.ODataIdentifier).isInput();
|
||||
// test.run("$skiptoken=ABC").has(TokenKind.SKIPTOKEN, TokenKind.EQ, TokenKind.ODataIdentifier).isInput();
|
||||
|
||||
test.run("$skiptoken=ABC").isAllText("$skiptoken=ABC").isType(UriLexer.SKIPTOKEN);
|
||||
test.run("$skiptoken=ABC").isAllText("$skiptoken=ABC").isType(UriLexer.SKIPTOKEN);
|
||||
|
||||
test.run("$search=\"ABC\"").isAllText("$search=\"ABC\"").isType(UriLexer.SEARCH);
|
||||
test.run("$search=ABC").isAllText("$search=ABC").isType(UriLexer.SEARCH);
|
||||
test.run("$search=\"A%20B%20C\"").isAllText("$search=\"A%20B%20C\"").isType(UriLexer.SEARCH);
|
||||
test.run("$search=Test Test").isAllText("$search=Test Test").isType(UriLexer.SEARCH);
|
||||
test.run("$search=Test&$filter=ABC eq 1").isAllText("$search=Test&$filter=ABC eq 1").isType(UriLexer.SEARCH);
|
||||
test.run("$search=\"ABC\"").has(TokenKind.SEARCH, TokenKind.EQ, TokenKind.Phrase).isInput();
|
||||
test.run("$search=ABC").has(TokenKind.SEARCH, TokenKind.EQ, TokenKind.Word).isInput();
|
||||
test.run("$search=\"A%20B%20C\"").has(TokenKind.SEARCH, TokenKind.EQ, TokenKind.Phrase).isInput();
|
||||
test.run("$search=Test Test").has(TokenKind.SEARCH, TokenKind.EQ, TokenKind.Word,
|
||||
TokenKind.AndOperatorSearch, TokenKind.Word).isInput();
|
||||
test.run("$search=Test&$filter=ABC eq 1").has(TokenKind.SEARCH, TokenKind.EQ, TokenKind.Word);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testQueryOptionsDefaultMode() {
|
||||
// First set query mode, than use expand(switches to default mode) and use nested system query options
|
||||
test.globalMode(UriLexer.MODE_QUERY);
|
||||
test.run("$expand=ABC($skip=1)").isAllText("$expand=ABC($skip=1)").at(4).isType(UriLexer.SKIP_QO);
|
||||
test.run("$expand=ABC($skip=2)").isAllText("$expand=ABC($skip=2)").at(4).isType(UriLexer.SKIP_QO);
|
||||
test.run("$expand=ABC($skip=123)").isAllText("$expand=ABC($skip=123)").at(4).isType(UriLexer.SKIP_QO);
|
||||
test.run("$expand=ABC($search=abc)").isAllText("$expand=ABC($search=abc)").at(4).isType(UriLexer.SEARCH_INLINE);
|
||||
test.run("$expand=ABC($search=\"123\")").isAllText("$expand=ABC($search=\"123\")")
|
||||
.at(4).isType(UriLexer.SEARCH_INLINE)
|
||||
.at(6).isType(UriLexer.SEARCHPHRASE);
|
||||
test.run("$expand=ABC($top=1)").isAllText("$expand=ABC($top=1)").at(4).isType(UriLexer.TOP);
|
||||
test.run("$expand=ABC($top=2)").isAllText("$expand=ABC($top=2)").at(4).isType(UriLexer.TOP);
|
||||
test.run("$expand=ABC($top=123)").isAllText("$expand=ABC($top=123)").at(4).isType(UriLexer.TOP);
|
||||
|
||||
test.run("$expand=ABC($expand=DEF($skip=1))").isAllText("$expand=ABC($expand=DEF($skip=1))")
|
||||
.at(8).isType(UriLexer.SKIP_QO);
|
||||
test.run("$expand=ABC($expand=DEF($skip=2))").isAllText("$expand=ABC($expand=DEF($skip=2))")
|
||||
.at(8).isType(UriLexer.SKIP_QO);
|
||||
test.run("$expand=ABC($expand=DEF($skip=123))").isAllText("$expand=ABC($expand=DEF($skip=123))")
|
||||
.at(8).isType(UriLexer.SKIP_QO);
|
||||
|
||||
test.run("$expand=ABC($expand=DEF($top=1))").isAllText("$expand=ABC($expand=DEF($top=1))")
|
||||
.at(8).isType(UriLexer.TOP);
|
||||
test.run("$expand=ABC($expand=DEF($top=2))").isAllText("$expand=ABC($expand=DEF($top=2))")
|
||||
.at(8).isType(UriLexer.TOP);
|
||||
test.run("$expand=ABC($expand=DEF($top=123))").isAllText("$expand=ABC($expand=DEF($top=123))")
|
||||
.at(8).isType(UriLexer.TOP);
|
||||
test.run("$expand=ABC($expand=DEF($search=Test Test))").isAllText("$expand=ABC($expand=DEF($search=Test Test))")
|
||||
.at(8).isType(UriLexer.SEARCH_INLINE)
|
||||
.at(10).isType(UriLexer.SEARCHWORD)
|
||||
.at(12).isType(UriLexer.SEARCHWORD);
|
||||
public void queryOptionsDefaultMode() {
|
||||
test.run("$expand=ABC($skip=1)").has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
|
||||
TokenKind.OPEN, TokenKind.SKIP, TokenKind.EQ, TokenKind.IntegerValue, TokenKind.CLOSE).isInput();
|
||||
test.run("$expand=ABC($skip=123)").has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
|
||||
TokenKind.OPEN, TokenKind.SKIP, TokenKind.EQ, TokenKind.IntegerValue, TokenKind.CLOSE).isInput();
|
||||
test.run("$expand=ABC($search=abc)").has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
|
||||
TokenKind.OPEN, TokenKind.SEARCH, TokenKind.EQ, TokenKind.Word, TokenKind.CLOSE).isInput();
|
||||
test.run("$expand=ABC($search=\"123\")").has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
|
||||
TokenKind.OPEN, TokenKind.SEARCH, TokenKind.EQ, TokenKind.Phrase, TokenKind.CLOSE).isInput();
|
||||
test.run("$expand=ABC($top=1)").has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
|
||||
TokenKind.OPEN, TokenKind.TOP, TokenKind.EQ, TokenKind.IntegerValue, TokenKind.CLOSE).isInput();
|
||||
test.run("$expand=ABC($top=123)").has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
|
||||
TokenKind.OPEN, TokenKind.TOP, TokenKind.EQ, TokenKind.IntegerValue, TokenKind.CLOSE).isInput();
|
||||
|
||||
test.run("$expand=ABC($expand=DEF($skip=1))").has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
|
||||
TokenKind.OPEN, TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
|
||||
TokenKind.OPEN, TokenKind.SKIP, TokenKind.EQ, TokenKind.IntegerValue, TokenKind.CLOSE, TokenKind.CLOSE)
|
||||
.isInput();
|
||||
test.run("$expand=ABC($expand=DEF($skip=123))").has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
|
||||
TokenKind.OPEN, TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
|
||||
TokenKind.OPEN, TokenKind.SKIP, TokenKind.EQ, TokenKind.IntegerValue, TokenKind.CLOSE, TokenKind.CLOSE)
|
||||
.isInput();
|
||||
|
||||
test.run("$expand=ABC($expand=DEF($top=1))").has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
|
||||
TokenKind.OPEN, TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
|
||||
TokenKind.OPEN, TokenKind.TOP, TokenKind.EQ, TokenKind.IntegerValue, TokenKind.CLOSE, TokenKind.CLOSE)
|
||||
.isInput();
|
||||
test.run("$expand=ABC($expand=DEF($top=123))").has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
|
||||
TokenKind.OPEN, TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
|
||||
TokenKind.OPEN, TokenKind.TOP, TokenKind.EQ, TokenKind.IntegerValue, TokenKind.CLOSE, TokenKind.CLOSE)
|
||||
.isInput();
|
||||
|
||||
test.run("$expand=ABC($expand=DEF($search=Test Test))")
|
||||
.has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
|
||||
TokenKind.OPEN, TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
|
||||
TokenKind.OPEN, TokenKind.SEARCH, TokenKind.EQ, TokenKind.Word,
|
||||
TokenKind.AndOperatorSearch, TokenKind.Word, TokenKind.CLOSE, TokenKind.CLOSE)
|
||||
.isInput();
|
||||
test.run("$expand=ABC($expand=DEF($search=\"Test\" \"Test\"))")
|
||||
.isAllText("$expand=ABC($expand=DEF($search=\"Test\" \"Test\"))")
|
||||
.at(8).isType(UriLexer.SEARCH_INLINE)
|
||||
.at(10).isType(UriLexer.SEARCHPHRASE)
|
||||
.at(12).isType(UriLexer.SEARCHPHRASE);
|
||||
.has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
|
||||
TokenKind.OPEN, TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
|
||||
TokenKind.OPEN, TokenKind.SEARCH, TokenKind.EQ, TokenKind.Phrase,
|
||||
TokenKind.AndOperatorSearch, TokenKind.Phrase, TokenKind.CLOSE, TokenKind.CLOSE)
|
||||
.isInput();
|
||||
test.run("$expand=ABC($expand=DEF($search=\"Test\" \"Test\";$filter=PropertyInt16 eq 0;$orderby=PropertyInt16))")
|
||||
.isAllText("$expand=ABC($expand=DEF($search=\"Test\" \"Test\";$filter=PropertyInt16 " +
|
||||
"eq 0;$orderby=PropertyInt16))")
|
||||
.at(8).isType(UriLexer.SEARCH_INLINE)
|
||||
.at(10).isType(UriLexer.SEARCHPHRASE)
|
||||
.at(12).isType(UriLexer.SEARCHPHRASE)
|
||||
.at(13).isType(UriLexer.SEMI)
|
||||
.at(14).isType(UriLexer.FILTER)
|
||||
.at(22).isType(UriLexer.ORDERBY);
|
||||
}
|
||||
|
||||
// ;------------------------------------------------------------------------------
|
||||
// ; 4. Expressions
|
||||
// ;------------------------------------------------------------------------------
|
||||
@Test
|
||||
public void testQueryExpressions() {
|
||||
test.globalMode(Lexer.DEFAULT_MODE);
|
||||
|
||||
test.run("$it").isText("$it").isType(UriLexer.IT);
|
||||
|
||||
test.run("$filter=contains(").at(2).isText("contains(").isType(UriLexer.CONTAINS_WORD);
|
||||
|
||||
test.run("$filter=containsabc").at(2).isText("containsabc")
|
||||
.isType(UriLexer.ODATAIDENTIFIER); // test that this is a ODI
|
||||
|
||||
test.run("$filter=startswith(").at(2).isText("startswith(").isType(UriLexer.STARTSWITH_WORD);
|
||||
test.run("$filter=endswith(").at(2).isText("endswith(").isType(UriLexer.ENDSWITH_WORD);
|
||||
test.run("$filter=length(").at(2).isText("length(").isType(UriLexer.LENGTH_WORD);
|
||||
test.run("$filter=indexof(").at(2).isText("indexof(").isType(UriLexer.INDEXOF_WORD);
|
||||
test.run("$filter=substring(").at(2).isText("substring(").isType(UriLexer.SUBSTRING_WORD);
|
||||
test.run("$filter=tolower(").at(2).isText("tolower(").isType(UriLexer.TOLOWER_WORD);
|
||||
test.run("$filter=toupper(").at(2).isText("toupper(").isType(UriLexer.TOUPPER_WORD);
|
||||
test.run("$filter=trim(").at(2).isText("trim(").isType(UriLexer.TRIM_WORD);
|
||||
test.run("$filter=concat(").at(2).isText("concat(").isType(UriLexer.CONCAT_WORD);
|
||||
|
||||
.has(TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
|
||||
TokenKind.OPEN, TokenKind.EXPAND, TokenKind.EQ, TokenKind.ODataIdentifier,
|
||||
TokenKind.OPEN, TokenKind.SEARCH, TokenKind.EQ, TokenKind.Phrase,
|
||||
TokenKind.AndOperatorSearch, TokenKind.Phrase, TokenKind.SEMI,
|
||||
TokenKind.FILTER, TokenKind.EQ, TokenKind.ODataIdentifier, TokenKind.EqualsOperator,
|
||||
TokenKind.IntegerValue, TokenKind.SEMI,
|
||||
TokenKind.ORDERBY, TokenKind.EQ, TokenKind.ODataIdentifier, TokenKind.CLOSE, TokenKind.CLOSE)
|
||||
.isInput();
|
||||
}
|
||||
|
||||
// ;------------------------------------------------------------------------------
|
||||
// ; 7. Literal Data Values
|
||||
// ;------------------------------------------------------------------------------
|
||||
@Test
|
||||
public void queryExpressions() {
|
||||
test.run("$it").has(TokenKind.IT).isText("$it");
|
||||
|
||||
test.run("$filter=contains(").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.ContainsMethod).isText("contains(");
|
||||
|
||||
test.run("$filter=containsabc").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.ODataIdentifier)
|
||||
.isText("containsabc");
|
||||
|
||||
test.run("$filter=startswith(").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.StartswithMethod)
|
||||
.isText("startswith(");
|
||||
test.run("$filter=endswith(").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.EndswithMethod).isText("endswith(");
|
||||
test.run("$filter=length(").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.LengthMethod).isText("length(");
|
||||
test.run("$filter=indexof(").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.IndexofMethod).isText("indexof(");
|
||||
test.run("$filter=substring(").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.SubstringMethod).isText("substring(");
|
||||
test.run("$filter=tolower(").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.TolowerMethod).isText("tolower(");
|
||||
test.run("$filter=toupper(").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.ToupperMethod).isText("toupper(");
|
||||
test.run("$filter=trim(").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.TrimMethod).isText("trim(");
|
||||
test.run("$filter=concat(").has(TokenKind.FILTER, TokenKind.EQ, TokenKind.ConcatMethod).isText("concat(");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLiteralDataValues() {
|
||||
test.globalMode(Lexer.DEFAULT_MODE);
|
||||
public void literalDataValues() {
|
||||
// null
|
||||
test.run("null").isInput().isType(UriLexer.NULLVALUE);
|
||||
test.run("null").has(TokenKind.NULL).isInput();
|
||||
|
||||
// binary
|
||||
test.run("binary'ABCD'").isInput().isType(UriLexer.BINARY);
|
||||
test.run("BiNaRy'ABCD'").isInput().isType(UriLexer.BINARY);
|
||||
test.run("binary'ABCD'").has(TokenKind.BinaryValue).isInput();
|
||||
test.run("BiNaRy'ABCD'").has(TokenKind.BinaryValue).isInput();
|
||||
|
||||
// boolean
|
||||
test.run("true").isInput().isType(UriLexer.TRUE);
|
||||
test.run("false").isInput().isType(UriLexer.FALSE);
|
||||
test.run("TrUe").isInput().isType(UriLexer.BOOLEAN);
|
||||
test.run("FaLsE").isInput().isType(UriLexer.BOOLEAN);
|
||||
test.run("true").has(TokenKind.BooleanValue).isInput();
|
||||
test.run("false").has(TokenKind.BooleanValue).isInput();
|
||||
test.run("TrUe").has(TokenKind.BooleanValue).isInput();
|
||||
test.run("FaLsE").has(TokenKind.BooleanValue).isInput();
|
||||
|
||||
// Lexer rule INT
|
||||
test.run("123").isInput().isType(UriLexer.INT);
|
||||
test.run("123456789").isInput().isType(UriLexer.INT);
|
||||
test.run("+123").isInput().isType(UriLexer.INT);
|
||||
test.run("+123456789").isInput().isType(UriLexer.INT);
|
||||
test.run("-123").isInput().isType(UriLexer.INT);
|
||||
test.run("-123456789").isInput().isType(UriLexer.INT);
|
||||
test.run("123").has(TokenKind.IntegerValue).isInput();
|
||||
test.run("123456789").has(TokenKind.IntegerValue).isInput();
|
||||
test.run("+123").has(TokenKind.IntegerValue).isInput();
|
||||
test.run("+123456789").has(TokenKind.IntegerValue).isInput();
|
||||
test.run("-123").has(TokenKind.IntegerValue).isInput();
|
||||
test.run("-123456789").has(TokenKind.IntegerValue).isInput();
|
||||
|
||||
// Lexer rule DECIMAL
|
||||
test.run("0.1").isInput().isType(UriLexer.DECIMAL);
|
||||
test.run("1.1").isInput().isType(UriLexer.DECIMAL);
|
||||
test.run("+0.1").isInput().isType(UriLexer.DECIMAL);
|
||||
test.run("+1.1").isInput().isType(UriLexer.DECIMAL);
|
||||
test.run("-0.1").isInput().isType(UriLexer.DECIMAL);
|
||||
test.run("-1.1").isInput().isType(UriLexer.DECIMAL);
|
||||
test.run("0.1").has(TokenKind.DecimalValue).isInput();
|
||||
test.run("1.1").has(TokenKind.DecimalValue).isInput();
|
||||
test.run("+0.1").has(TokenKind.DecimalValue).isInput();
|
||||
test.run("+1.1").has(TokenKind.DecimalValue).isInput();
|
||||
test.run("-0.1").has(TokenKind.DecimalValue).isInput();
|
||||
test.run("-1.1").has(TokenKind.DecimalValue).isInput();
|
||||
|
||||
// Lexer rule EXP
|
||||
test.run("1.1e+1").isInput().isType(UriLexer.DECIMAL);
|
||||
test.run("1.1e-1").isInput().isType(UriLexer.DECIMAL);
|
||||
test.run("1.1e+1").has(TokenKind.DoubleValue).isInput();
|
||||
test.run("1.1e-1").has(TokenKind.DoubleValue).isInput();
|
||||
|
||||
test.run("NaN").isInput().isType(UriLexer.NANINFINITY);
|
||||
test.run("-INF").isInput().isType(UriLexer.NANINFINITY);
|
||||
test.run("INF").isInput().isType(UriLexer.NANINFINITY);
|
||||
test.run("NaN").has(TokenKind.DoubleValue).isInput();
|
||||
test.run("-INF").has(TokenKind.DoubleValue).isInput();
|
||||
test.run("INF").has(TokenKind.DoubleValue).isInput();
|
||||
|
||||
// Lexer rule GUID
|
||||
test.run("1234ABCD-12AB-23CD-45EF-123456780ABC").isInput().isType(UriLexer.GUID);
|
||||
test.run("1234ABCD-12AB-23CD-45EF-123456780ABC").isInput().isType(UriLexer.GUID);
|
||||
test.run("1234ABCD-12AB-23CD-45EF-123456780ABC").has(TokenKind.GuidValue).isInput();
|
||||
test.run("1234ABCD-12AB-23CD-45EF-123456780ABC").has(TokenKind.GuidValue).isInput();
|
||||
|
||||
// Lexer rule DATE
|
||||
test.run("2013-11-15").isInput().isType(UriLexer.DATE);
|
||||
test.run("2013-11-15").has(TokenKind.DateValue).isInput();
|
||||
|
||||
// Lexer rule DATETIMEOFFSET
|
||||
test.run("2013-11-15T13:35Z").isInput().isType(UriLexer.DATETIMEOFFSET);
|
||||
test.run("2013-11-15T13:35:10Z").isInput().isType(UriLexer.DATETIMEOFFSET);
|
||||
test.run("2013-11-15T13:35:10.1234Z").isInput().isType(UriLexer.DATETIMEOFFSET);
|
||||
test.run("2013-11-15T13:35Z").has(TokenKind.DateTimeOffsetValue).isInput();
|
||||
test.run("2013-11-15T13:35:10Z").has(TokenKind.DateTimeOffsetValue).isInput();
|
||||
test.run("2013-11-15T13:35:10.1234Z").has(TokenKind.DateTimeOffsetValue).isInput();
|
||||
|
||||
test.run("2013-11-15T13:35:10.1234+01:30").isInput().isType(UriLexer.DATETIMEOFFSET);
|
||||
test.run("2013-11-15T13:35:10.1234-01:12").isInput().isType(UriLexer.DATETIMEOFFSET);
|
||||
test.run("2013-11-15T13:35:10.1234+01:30").has(TokenKind.DateTimeOffsetValue).isInput();
|
||||
test.run("2013-11-15T13:35:10.1234-01:12").has(TokenKind.DateTimeOffsetValue).isInput();
|
||||
|
||||
test.run("2013-11-15T13:35Z").isInput().isType(UriLexer.DATETIMEOFFSET);
|
||||
test.run("2013-11-15T13:35Z").has(TokenKind.DateTimeOffsetValue).isInput();
|
||||
|
||||
// Lexer rule DURATION
|
||||
test.run("duration'PT67S'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'PT67.89S'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'PT67S'").has(TokenKind.DurationValue).isInput();
|
||||
test.run("duration'PT67.89S'").has(TokenKind.DurationValue).isInput();
|
||||
|
||||
test.run("duration'PT5M'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'PT5M67S'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'PT5M67.89S'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'PT5M'").has(TokenKind.DurationValue).isInput();
|
||||
test.run("duration'PT5M67S'").has(TokenKind.DurationValue).isInput();
|
||||
test.run("duration'PT5M67.89S'").has(TokenKind.DurationValue).isInput();
|
||||
|
||||
test.run("duration'PT4H'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'PT4H67S'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'PT4H67.89S'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'PT4H5M'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'PT4H5M67S'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'PT4H5M67.89S'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'PT4H'").has(TokenKind.DurationValue).isInput();
|
||||
test.run("duration'PT4H67S'").has(TokenKind.DurationValue).isInput();
|
||||
test.run("duration'PT4H67.89S'").has(TokenKind.DurationValue).isInput();
|
||||
test.run("duration'PT4H5M'").has(TokenKind.DurationValue).isInput();
|
||||
test.run("duration'PT4H5M67S'").has(TokenKind.DurationValue).isInput();
|
||||
test.run("duration'PT4H5M67.89S'").has(TokenKind.DurationValue).isInput();
|
||||
|
||||
test.run("duration'P3D'");
|
||||
test.run("duration'P3DT67S'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'P3DT67.89S'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'P3DT5M'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'P3DT5M67S'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'P3DT5M67.89S'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'P3DT4H'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'P3DT4H67S'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'P3DT4H67.89S'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'P3DT4H5M'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'P3DT4H5M67S'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'P3DT4H5M67.89S'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("duration'P3D'").has(TokenKind.DurationValue).isInput();
|
||||
test.run("duration'P3DT67S'").has(TokenKind.DurationValue).isInput();
|
||||
test.run("duration'P3DT67.89S'").has(TokenKind.DurationValue).isInput();
|
||||
test.run("duration'P3DT5M'").has(TokenKind.DurationValue).isInput();
|
||||
test.run("duration'P3DT5M67S'").has(TokenKind.DurationValue).isInput();
|
||||
test.run("duration'P3DT5M67.89S'").has(TokenKind.DurationValue).isInput();
|
||||
test.run("duration'P3DT4H'").has(TokenKind.DurationValue).isInput();
|
||||
test.run("duration'P3DT4H67S'").has(TokenKind.DurationValue).isInput();
|
||||
test.run("duration'P3DT4H67.89S'").has(TokenKind.DurationValue).isInput();
|
||||
test.run("duration'P3DT4H5M'").has(TokenKind.DurationValue).isInput();
|
||||
test.run("duration'P3DT4H5M67S'").has(TokenKind.DurationValue).isInput();
|
||||
test.run("duration'P3DT4H5M67.89S'").has(TokenKind.DurationValue).isInput();
|
||||
|
||||
test.run("DuRaTiOn'P3DT4H5M67.89S'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("DuRaTiOn'-P3DT4H5M67.89S'").isInput().isType(UriLexer.DURATION);
|
||||
test.run("DuRaTiOn'P3DT4H5M67.89S'").has(TokenKind.DurationValue).isInput();
|
||||
test.run("DuRaTiOn'-P3DT4H5M67.89S'").has(TokenKind.DurationValue).isInput();
|
||||
|
||||
test.run("20:00").isInput().isType(UriLexer.TIMEOFDAY);
|
||||
test.run("20:15:01").isInput().isType(UriLexer.TIMEOFDAY);
|
||||
test.run("20:15:01.02").isInput().isType(UriLexer.TIMEOFDAY);
|
||||
test.run("20:00").has(TokenKind.TimeOfDayValue).isInput();
|
||||
test.run("20:15:01").has(TokenKind.TimeOfDayValue).isInput();
|
||||
test.run("20:15:01.02").has(TokenKind.TimeOfDayValue).isInput();
|
||||
|
||||
test.run("20:15:01.02").isInput().isType(UriLexer.TIMEOFDAY);
|
||||
test.run("20:15:01.02").has(TokenKind.TimeOfDayValue).isInput();
|
||||
|
||||
// String
|
||||
test.run("'ABC'").isText("'ABC'").isType(UriLexer.STRING);
|
||||
test.run("'A%20C'").isInput().isType(UriLexer.STRING);
|
||||
test.run("'%20%20%20ABC'").isInput().isType(UriLexer.STRING);
|
||||
|
||||
test.run("'ABC'").has(TokenKind.StringValue).isInput();
|
||||
test.run("'A%20C'").has(TokenKind.StringValue).isInput();
|
||||
test.run("'%20%20%20ABC'").has(TokenKind.StringValue).isInput();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDelims() {
|
||||
String reserved = "/";
|
||||
test.globalMode(UriLexer.MODE_QUERY);
|
||||
public void delims() {
|
||||
final String reserved = "/";
|
||||
// Test lexer rule UNRESERVED
|
||||
test.run("$format=A/" + cUNRESERVED).isAllInput().isType(UriLexer.FORMAT);
|
||||
test.run("$format=A/" + cUNRESERVED + reserved).isType(UriLexer.FORMAT).at(4).isText(cUNRESERVED);
|
||||
// test.run("$format=A/" + cUNRESERVED).has(TokenKind.FORMAT).isInput();
|
||||
// test.run("$format=A/" + cUNRESERVED + reserved).has(TokenKind.FORMAT).isText(cUNRESERVED);
|
||||
// Test lexer rule PCT_ENCODED
|
||||
test.run("$format=A/" + cPCT_ENCODED).isAllInput().isType(UriLexer.FORMAT);
|
||||
test.run("$format=A/" + cPCT_ENCODED + reserved).isType(UriLexer.FORMAT).at(4).isText(cPCT_ENCODED);
|
||||
// test.run("$format=A/" + cPCT_ENCODED).has(TokenKind.FORMAT).isInput();
|
||||
// test.run("$format=A/" + cPCT_ENCODED + reserved).has(TokenKind.FORMAT).isText(cPCT_ENCODED);
|
||||
// Test lexer rule SUB_DELIMS
|
||||
test.run("$format=A/" + cSUB_DELIMS).isAllInput().isType(UriLexer.FORMAT);
|
||||
test.run("$format=A/" + cSUB_DELIMS + reserved).isType(UriLexer.FORMAT).at(4).isText("$");
|
||||
// test.run("$format=A/" + cSUB_DELIMS).has(TokenKind.FORMAT).isInput();
|
||||
// test.run("$format=A/" + cSUB_DELIMS + reserved).has(TokenKind.FORMAT).isText("$");
|
||||
// Test lexer rule PCHAR rest
|
||||
test.run("$format=A/:@").isAllText("$format=A/:@").isType(UriLexer.FORMAT);
|
||||
test.run("$format=A/:@" + reserved).isType(UriLexer.FORMAT).at(4).isText(":@");
|
||||
// test.run("$format=A/:@").has(TokenKind.FORMAT).isInput();
|
||||
// test.run("$format=A/:@" + reserved).has(TokenKind.FORMAT).isText(":@");
|
||||
// Test lexer rule PCHAR all
|
||||
test.run("$format=" + cPCHAR + "/" + cPCHAR).isAllInput().isType(UriLexer.FORMAT);
|
||||
|
||||
// test.run("$format=" + cPCHAR + "/" + cPCHAR).has(TokenKind.FORMAT).isInput();
|
||||
}
|
||||
|
||||
public class TokenValidator {
|
||||
|
||||
private String input = null;
|
||||
private UriTokenizer tokenizer = null;
|
||||
private String curText = null;
|
||||
|
||||
public TokenValidator run(final String uri) {
|
||||
input = uri;
|
||||
tokenizer = new UriTokenizer(uri);
|
||||
curText = "";
|
||||
return this;
|
||||
}
|
||||
|
||||
public TokenValidator has(final TokenKind... expected) {
|
||||
for (final TokenKind kind : expected) {
|
||||
assertTrue(tokenizer.next(kind));
|
||||
curText += tokenizer.getText();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public TokenValidator isText(final String expected) {
|
||||
assertEquals(expected, tokenizer.getText());
|
||||
return this;
|
||||
}
|
||||
|
||||
public TokenValidator isInput() {
|
||||
assertEquals(input, curText);
|
||||
assertTrue(tokenizer.next(TokenKind.EOF));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ import org.apache.olingo.server.api.edmx.EdmxReference;
|
|||
import org.apache.olingo.server.api.uri.UriInfoKind;
|
||||
import org.apache.olingo.server.api.uri.UriResourceKind;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind;
|
||||
import org.apache.olingo.server.core.uri.parser.UriParserSyntaxException;
|
||||
import org.apache.olingo.server.core.uri.parser.UriParserSemanticException;
|
||||
import org.apache.olingo.server.core.uri.testutil.FilterValidator;
|
||||
import org.apache.olingo.server.core.uri.testutil.ResourceValidator;
|
||||
import org.apache.olingo.server.core.uri.testutil.TestUriValidator;
|
||||
|
@ -67,8 +67,7 @@ public class TestUriParserImpl {
|
|||
+ "," + PropertyDateTimeOffset + "," + PropertyDuration + "," + PropertyGuid + "," + PropertyTimeOfDay;
|
||||
|
||||
@Test
|
||||
public void testBoundFunctionImport_VarParameters() {
|
||||
|
||||
public void boundFunctionImport_VarParameters() {
|
||||
// no input
|
||||
testRes.run("ESKeyNav(1)/olingo.odata.test1.BFCETKeyNavRTETKeyNav()")
|
||||
.at(0).isUriPathInfoKind(UriResourceKind.entitySet)
|
||||
|
@ -89,9 +88,8 @@ public class TestUriParserImpl {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionBound_varReturnType() {
|
||||
|
||||
String esTwoKeyNav = "ESTwoKeyNav(PropertyInt16=1,PropertyString='ABC')";
|
||||
public void functionBound_varReturnType() {
|
||||
final String esTwoKeyNav = "ESTwoKeyNav(PropertyInt16=1,PropertyString='ABC')";
|
||||
|
||||
// returning primitive
|
||||
testRes.run("ESTwoKeyNav/olingo.odata.test1.BFCESTwoKeyNavRTString()")
|
||||
|
@ -151,8 +149,7 @@ public class TestUriParserImpl {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void runActionImport_VarReturnType() {
|
||||
|
||||
public void actionImport_VarReturnType() {
|
||||
testRes.run(ContainerProvider.AIRT_STRING).isKind(UriInfoKind.resource)
|
||||
.first()
|
||||
.isActionImport(ContainerProvider.AIRT_STRING)
|
||||
|
@ -189,7 +186,6 @@ public class TestUriParserImpl {
|
|||
|
||||
@Test
|
||||
public void count() {
|
||||
|
||||
// count entity set
|
||||
testRes.run("ESAllPrim/$count")
|
||||
.at(0)
|
||||
|
@ -216,7 +212,7 @@ public class TestUriParserImpl {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void runCrossJoin() throws Exception {
|
||||
public void crossJoin() throws Exception {
|
||||
testUri.run("$crossjoin(ESAllKey)")
|
||||
.isKind(UriInfoKind.crossjoin)
|
||||
.isCrossJoinEntityList(Arrays.asList("ESAllKey"));
|
||||
|
@ -226,29 +222,15 @@ public class TestUriParserImpl {
|
|||
.isCrossJoinEntityList(Arrays.asList("ESAllKey", "ESTwoPrim"));
|
||||
}
|
||||
|
||||
@Test(expected = UriValidationException.class)
|
||||
public void testEntityFailOnValidation1() throws Exception {
|
||||
@Test
|
||||
public void entityFailOnValidation() throws Exception {
|
||||
// simple entity set; with qualifiedentityTypeName; with filter
|
||||
testUri.run("$entity/olingo.odata.test1.ETTwoPrim", "$filter=PropertyInt16 eq 123&$id=ESAllKey")
|
||||
.isIdText("ESAllKey")
|
||||
.goFilter().is("<<PropertyInt16> eq <123>>");
|
||||
}
|
||||
|
||||
@Test(expected = UriParserSyntaxException.class)
|
||||
public void testEntityFailOnValidation2() throws Exception {
|
||||
// simple entity set; with qualifiedentityTypeName; with 2xformat(before and after), expand, filter
|
||||
testUri.run("$entity/olingo.odata.test1.ETTwoPrim",
|
||||
"$format=xml&$expand=*&abc=123&$id=ESBase&xyz=987&$filter=PropertyInt16 eq 123&$format=atom&$select=*")
|
||||
.isFormatText("atom")
|
||||
.isCustomParameter(0, "abc", "123")
|
||||
.isIdText("ESBase")
|
||||
.isCustomParameter(1, "xyz", "987")
|
||||
.isSelectItemStar(0);
|
||||
testUri.runEx("$entity/olingo.odata.test1.ETTwoPrim", "$filter=PropertyInt16 eq 123&$id=ESAllKey")
|
||||
.isExValidation(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEntity() throws Exception {
|
||||
|
||||
public void entity() throws Exception {
|
||||
// simple entity set
|
||||
testUri.run("$entity", "$id=ESAllPrim").isKind(UriInfoKind.entityId)
|
||||
.isKind(UriInfoKind.entityId)
|
||||
|
@ -311,14 +293,11 @@ public class TestUriParserImpl {
|
|||
.isKind(UriInfoKind.entityId)
|
||||
.isEntityType(EntityTypeProvider.nameETBase)
|
||||
.isIdText("ESTwoPrim")
|
||||
.isExpandText("*")
|
||||
.goExpand().first().isSegmentStar();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void entitySet() throws Exception {
|
||||
|
||||
// plain entity set
|
||||
testRes.run("ESAllPrim")
|
||||
.isEntitySet("ESAllPrim")
|
||||
|
@ -342,7 +321,7 @@ public class TestUriParserImpl {
|
|||
.isKeyPredicate(1, "PropertyString", "'ABC'");
|
||||
|
||||
// with all keys
|
||||
testRes.run("ESAllKey(" + encode(allKeys) + ")")
|
||||
testRes.run("ESAllKey(" + allKeys + ")")
|
||||
.isEntitySet("ESAllKey")
|
||||
.isKeyPredicate(0, "PropertyString", "'ABC'")
|
||||
.isKeyPredicate(1, "PropertyInt16", "1")
|
||||
|
@ -360,10 +339,7 @@ public class TestUriParserImpl {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testEntitySet_NavigationProperty() {
|
||||
|
||||
// plain entity set ...
|
||||
|
||||
public void entitySet_NavigationProperty() {
|
||||
// with navigation property
|
||||
testRes.run("ESKeyNav(1)/NavPropertyETTwoKeyNavOne")
|
||||
.at(0)
|
||||
|
@ -467,10 +443,7 @@ public class TestUriParserImpl {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testEntitySet_Property() {
|
||||
|
||||
// plain entity set ...
|
||||
|
||||
public void entitySet_Property() {
|
||||
// with property
|
||||
testRes.run("ESAllPrim(1)/PropertyString")
|
||||
.at(0)
|
||||
|
@ -499,8 +472,7 @@ public class TestUriParserImpl {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testEntitySet_TypeFilter() {
|
||||
|
||||
public void entitySet_TypeFilter() {
|
||||
// filter
|
||||
testRes.run("ESTwoPrim/olingo.odata.test1.ETBase")
|
||||
.at(0)
|
||||
|
@ -556,57 +528,53 @@ public class TestUriParserImpl {
|
|||
.at(1)
|
||||
.isPrimitiveProperty("AdditionalPropertyString_5", PropertyProvider.nameString, false)
|
||||
.isType(PropertyProvider.nameString);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unary() throws Exception {
|
||||
testFilter.runOnETAllPrim("not PropertyBoolean").isCompr("<not <PropertyBoolean>>");
|
||||
testFilter.runOnETAllPrim("-PropertyInt16 eq PropertyInt16").isCompr("<<- <PropertyInt16>> eq <PropertyInt16>>");
|
||||
testFilter.runOnETAllPrim("not PropertyBoolean").is("<not <PropertyBoolean>>");
|
||||
testFilter.runOnETAllPrim("-PropertyInt16 eq PropertyInt16").is("<<- <PropertyInt16>> eq <PropertyInt16>>");
|
||||
}
|
||||
|
||||
// TODO: Use correct types.
|
||||
@Test
|
||||
@Ignore
|
||||
public void filterComplexMixedPriority() throws Exception {
|
||||
testFilter.runOnETAllPrim("PropertyInt16 or PropertyInt32 and PropertyInt64")
|
||||
.isCompr("<<PropertyInt16> or <<PropertyInt32> and <PropertyInt64>>>");
|
||||
testFilter.runOnETAllPrim("PropertyInt16 or PropertyInt32 and PropertyInt64 eq PropertyByte")
|
||||
.isCompr("<<PropertyInt16> or <<PropertyInt32> and <<PropertyInt64> eq <PropertyByte>>>>");
|
||||
testFilter.runOnETAllPrim("PropertyInt16 or PropertyInt32 eq PropertyInt64 and PropertyByte")
|
||||
.isCompr("<<PropertyInt16> or <<<PropertyInt32> eq <PropertyInt64>> and <PropertyByte>>>");
|
||||
testFilter.runOnETAllPrim("PropertyInt16 or PropertyInt32 eq PropertyInt64 and PropertyByte eq PropertySByte")
|
||||
.isCompr("<<PropertyInt16> or <<<PropertyInt32> eq <PropertyInt64>> "
|
||||
testFilter.runOnETAllPrim("PropertyBoolean or true and false")
|
||||
.is("<<PropertyBoolean> or <<true> and <false>>>");
|
||||
testFilter.runOnETAllPrim("PropertyBoolean or true and PropertyInt64 eq PropertyByte")
|
||||
.is("<<PropertyBoolean> or <<true> and <<PropertyInt64> eq <PropertyByte>>>>");
|
||||
testFilter.runOnETAllPrim("PropertyBoolean or PropertyInt32 eq PropertyInt64 and true")
|
||||
.is("<<PropertyBoolean> or <<<PropertyInt32> eq <PropertyInt64>> and <true>>>");
|
||||
testFilter.runOnETAllPrim("PropertyBoolean or PropertyInt32 eq PropertyInt64 and PropertyByte eq PropertySByte")
|
||||
.is("<<PropertyBoolean> or <<<PropertyInt32> eq <PropertyInt64>> "
|
||||
+ "and <<PropertyByte> eq <PropertySByte>>>>");
|
||||
testFilter.runOnETAllPrim("PropertyInt16 eq PropertyInt32 or PropertyInt64 and PropertyByte")
|
||||
.isCompr("<<<PropertyInt16> eq <PropertyInt32>> or <<PropertyInt64> and <PropertyByte>>>");
|
||||
testFilter.runOnETAllPrim("PropertyInt16 eq PropertyInt32 or PropertyInt64 and PropertyByte eq PropertySByte")
|
||||
.isCompr("<<<PropertyInt16> eq <PropertyInt32>> "
|
||||
+ "or <<PropertyInt64> and <<PropertyByte> eq <PropertySByte>>>>");
|
||||
testFilter.runOnETAllPrim("PropertyInt16 eq PropertyInt32 or PropertyInt64 eq PropertyByte and PropertySByte")
|
||||
.isCompr("<<<PropertyInt16> eq <PropertyInt32>> "
|
||||
+ "or <<<PropertyInt64> eq <PropertyByte>> and <PropertySByte>>>");
|
||||
testFilter.runOnETAllPrim("PropertyInt16 eq PropertyInt32 or PropertyBoolean and true")
|
||||
.is("<<<PropertyInt16> eq <PropertyInt32>> or <<PropertyBoolean> and <true>>>");
|
||||
testFilter.runOnETAllPrim("PropertyInt16 eq PropertyInt32 or PropertyBoolean and PropertyByte eq PropertySByte")
|
||||
.is("<<<PropertyInt16> eq <PropertyInt32>> "
|
||||
+ "or <<PropertyBoolean> and <<PropertyByte> eq <PropertySByte>>>>");
|
||||
testFilter.runOnETAllPrim("PropertyInt16 eq PropertyInt32 or PropertyInt64 eq PropertyByte and PropertyBoolean")
|
||||
.is("<<<PropertyInt16> eq <PropertyInt32>> "
|
||||
+ "or <<<PropertyInt64> eq <PropertyByte>> and <PropertyBoolean>>>");
|
||||
testFilter.runOnETAllPrim("PropertyInt16 eq PropertyInt32 or PropertyInt64 eq PropertyByte "
|
||||
+ "and PropertySByte eq PropertyDecimal")
|
||||
.isCompr("<<<PropertyInt16> eq <PropertyInt32>> or <<<PropertyInt64> eq <PropertyByte>> "
|
||||
.is("<<<PropertyInt16> eq <PropertyInt32>> or <<<PropertyInt64> eq <PropertyByte>> "
|
||||
+ "and <<PropertySByte> eq <PropertyDecimal>>>>");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterSimpleSameBinaryBinaryBinaryPriority() throws Exception {
|
||||
testFilter.runOnETAllPrim("1 add 2 add 3 add 4").isCompr("<<< <1> add <2>> add <3>> add <4>>");
|
||||
testFilter.runOnETAllPrim("1 add 2 add 3 div 4").isCompr("<< <1> add <2>> add <<3> div <4>>>");
|
||||
testFilter.runOnETAllPrim("1 add 2 div 3 add 4").isCompr("<< <1> add <<2> div <3>>> add <4>>");
|
||||
testFilter.runOnETAllPrim("1 add 2 div 3 div 4").isCompr("< <1> add <<<2> div <3>> div <4>>>");
|
||||
testFilter.runOnETAllPrim("1 div 2 add 3 add 4").isCompr("<<< <1> div <2>> add <3>> add <4>>");
|
||||
testFilter.runOnETAllPrim("1 div 2 add 3 div 4").isCompr("<< <1> div <2>> add <<3> div <4>>>");
|
||||
testFilter.runOnETAllPrim("1 div 2 div 3 add 4").isCompr("<<< <1> div <2>> div <3>> add <4>>");
|
||||
testFilter.runOnETAllPrim("1 div 2 div 3 div 4").isCompr("<<< <1> div <2>> div <3>> div <4>>");
|
||||
testFilter.runOnETAllPrim("1 add 2 add 3 add 4 ge 0").isCompr("<<<< <1> add <2>> add <3>> add <4>> ge <0>>");
|
||||
testFilter.runOnETAllPrim("1 add 2 add 3 div 4 ge 0").isCompr("<<< <1> add <2>> add <<3> div <4>>> ge <0>>");
|
||||
testFilter.runOnETAllPrim("1 add 2 div 3 add 4 ge 0").isCompr("<<< <1> add <<2> div <3>>> add <4>> ge <0>>");
|
||||
testFilter.runOnETAllPrim("1 add 2 div 3 div 4 ge 0").isCompr("<< <1> add <<<2> div <3>> div <4>>> ge <0>>");
|
||||
testFilter.runOnETAllPrim("1 div 2 add 3 add 4 ge 0").isCompr("<<<< <1> div <2>> add <3>> add <4>> ge <0>>");
|
||||
testFilter.runOnETAllPrim("1 div 2 add 3 div 4 ge 0").isCompr("<<< <1> div <2>> add <<3> div <4>>> ge <0>>");
|
||||
testFilter.runOnETAllPrim("1 div 2 div 3 add 4 ge 0").isCompr("<<<< <1> div <2>> div <3>> add <4>> ge <0>>");
|
||||
testFilter.runOnETAllPrim("1 div 2 div 3 div 4 ge 0").isCompr("<<<< <1> div <2>> div <3>> div <4>> ge <0>>");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionImport_VarParameters() {
|
||||
|
||||
public void functionImport_VarParameters() {
|
||||
// no input
|
||||
testRes.run("FINRTInt16()")
|
||||
.isFunctionImport("FINRTInt16")
|
||||
|
@ -627,7 +595,7 @@ public class TestUriParserImpl {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionImport_VarReturning() {
|
||||
public void functionImport_VarReturning() {
|
||||
// returning primitive
|
||||
testRes.run("FINRTInt16()")
|
||||
.isFunctionImport("FINRTInt16")
|
||||
|
@ -666,8 +634,7 @@ public class TestUriParserImpl {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionImportChain() {
|
||||
|
||||
public void functionImportChain() {
|
||||
// test chain; returning single complex
|
||||
testRes.run("FICRTCTAllPrimTwoParam(ParameterString='ABC',ParameterInt16=1)/PropertyInt16")
|
||||
.at(0)
|
||||
|
@ -710,12 +677,10 @@ public class TestUriParserImpl {
|
|||
.isKeyPredicate(1, "PropertyString", "'ABC'")
|
||||
.at(1)
|
||||
.isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMetaData() throws Exception {
|
||||
|
||||
public void metaData() throws Exception {
|
||||
// Parsing the fragment may be used if a uri has to be parsed on the consumer side.
|
||||
// On the producer side this feature is currently not supported, so the context fragment
|
||||
// part is only available as text.
|
||||
|
@ -879,12 +844,12 @@ public class TestUriParserImpl {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testRef() throws Exception {
|
||||
public void ref() throws Exception {
|
||||
testUri.run("ESKeyNav(1)/NavPropertyETTwoKeyNavOne/$ref");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleton() {
|
||||
public void singleton() {
|
||||
// plain singleton
|
||||
testRes.run("SINav")
|
||||
.isSingleton("SINav")
|
||||
|
@ -892,10 +857,7 @@ public class TestUriParserImpl {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testNavigationProperty() {
|
||||
|
||||
// plain entity set ...
|
||||
|
||||
public void navigationProperty() {
|
||||
// with navigation property
|
||||
testRes.run("ESKeyNav(1)/NavPropertyETTwoKeyNavOne")
|
||||
.at(0).isEntitySet("ESKeyNav")
|
||||
|
@ -969,10 +931,7 @@ public class TestUriParserImpl {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSingleton_Property() {
|
||||
|
||||
// plain singleton ...
|
||||
|
||||
public void singleton_Property() {
|
||||
// with property
|
||||
testRes.run("SINav/PropertyInt16")
|
||||
.at(0)
|
||||
|
@ -1002,72 +961,51 @@ public class TestUriParserImpl {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testValue() throws Exception {
|
||||
public void value() throws Exception {
|
||||
testUri.run("ESAllPrim(1)/PropertyString/$value");
|
||||
}
|
||||
|
||||
@Test(expected = UriValidationException.class)
|
||||
public void testMemberStartingWithCastFailOnValidation1() throws Exception {
|
||||
@Test
|
||||
public void memberStartingWithCastFailOnValidation1() throws Exception {
|
||||
// on EntityType entry
|
||||
testUri.run("ESTwoKeyNav(ParameterInt16=1,PropertyString='ABC')",
|
||||
testUri.runEx("ESTwoKeyNav(Property16=1,PropertyString='ABC')",
|
||||
"$filter=olingo.odata.test1.ETBaseTwoKeyNav/PropertyDate")
|
||||
.goFilter().root().isMember()
|
||||
.isMemberStartType(EntityTypeProvider.nameETBaseTwoKeyNav).goPath()
|
||||
// .at(0)
|
||||
// .isUriPathInfoKind(UriResourceKind.startingTypeFilter)
|
||||
// .isType(EntityTypeProvider.nameETTwoKeyNav, false)
|
||||
// .isTypeFilterOnEntry(EntityTypeProvider.nameETBaseTwoKeyNav)
|
||||
.at(0).isType(PropertyProvider.nameDate);
|
||||
}
|
||||
|
||||
@Test(expected = UriValidationException.class)
|
||||
public void testMemberStartingWithCastFailOnValidation2() throws Exception {
|
||||
testUri.run("FICRTCTTwoPrimTwoParam(ParameterInt16=1,ParameterString='2')",
|
||||
"$filter=olingo.odata.test1.CTBase/AdditionalPropString")
|
||||
.goFilter().root().isMember()
|
||||
.isMemberStartType(ComplexTypeProvider.nameCTBase).goPath()
|
||||
// .at(0)
|
||||
// .isUriPathInfoKind(UriResourceKind.startingTypeFilter)
|
||||
// .isType(ComplexTypeProvider.nameCTTwoPrim, false)
|
||||
// .isTypeFilterOnEntry(ComplexTypeProvider.nameCTBase)
|
||||
.at(0).isType(PropertyProvider.nameString);
|
||||
.isExValidation(UriValidationException.MessageKeys.INVALID_KEY_PROPERTY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMemberStartingWithCast() throws Exception {
|
||||
public void memberStartingWithCastFailOnValidation2() throws Exception {
|
||||
testUri.runEx("FICRTCTTwoPrimTwoParam(ParameterInt16=1,ParameterString='2')",
|
||||
"$filter=olingo.odata.test1.CTBase/AdditionalPropString")
|
||||
.isExSemantic(UriParserSemanticException.MessageKeys.TYPES_NOT_COMPATIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void memberStartingWithCast() throws Exception {
|
||||
// on EntityType collection
|
||||
testUri.run("ESTwoKeyNav", "$filter=olingo.odata.test1.ETBaseTwoKeyNav/PropertyDate")
|
||||
.goFilter().root().isMember()
|
||||
testFilter.runOnETTwoKeyNav("olingo.odata.test1.ETBaseTwoKeyNav/PropertyDate eq null")
|
||||
.left()
|
||||
.isMember()
|
||||
.isMemberStartType(EntityTypeProvider.nameETBaseTwoKeyNav).goPath()
|
||||
// .at(0)
|
||||
// .isUriPathInfoKind(UriResourceKind.startingTypeFilter)
|
||||
// .isType(EntityTypeProvider.nameETTwoKeyNav, true)
|
||||
// .isTypeFilterOnCollection(EntityTypeProvider.nameETBaseTwoKeyNav)
|
||||
.at(0).isType(PropertyProvider.nameDate);
|
||||
|
||||
// on Complex collection
|
||||
testUri.run("FICRTCollCTTwoPrimTwoParam(ParameterInt16=1,ParameterString='2')",
|
||||
"$filter=olingo.odata.test1.CTBase/AdditionalPropString")
|
||||
.goFilter().root().isMember()
|
||||
"$filter=olingo.odata.test1.CTBase/AdditionalPropString eq null")
|
||||
.goFilter().left().isMember()
|
||||
.isMemberStartType(ComplexTypeProvider.nameCTBase).goPath()
|
||||
// .at(0)
|
||||
// .isUriPathInfoKind(UriResourceKind.startingTypeFilter)
|
||||
// .isType(ComplexTypeProvider.nameCTTwoPrim, true)
|
||||
// .isTypeFilterOnCollection(ComplexTypeProvider.nameCTBase)
|
||||
.at(0).isType(PropertyProvider.nameString);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComplexTypeCastFollowingAsCollection() throws Exception {
|
||||
public void complexTypeCastFollowingAsCollection() throws Exception {
|
||||
testUri.run("FICRTCollCTTwoPrimTwoParam(ParameterInt16=1,ParameterString='2')/olingo.odata.test1.CTBase");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlias() throws Exception {
|
||||
testUri.run("ESAllPrim", "$filter=PropertyInt16 eq @p1&@p1=1")
|
||||
.goFilter().is("<<PropertyInt16> eq <@p1>>");
|
||||
public void alias() throws Exception {
|
||||
testFilter.runOnETAllPrim("PropertyInt16 eq @p1&@p1=1")
|
||||
.is("<<PropertyInt16> eq <@p1>>");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1087,26 +1025,22 @@ public class TestUriParserImpl {
|
|||
@Test
|
||||
public void customQueryOption() throws Exception {
|
||||
testUri.run("ESTwoKeyNav", "custom")
|
||||
.isCustomParameter(0, "custom", "");
|
||||
.isCustomParameter(0, "custom", "");
|
||||
testUri.run("ESTwoKeyNav", "custom=ABC")
|
||||
.isCustomParameter(0, "custom", "ABC");
|
||||
.isCustomParameter(0, "custom", "ABC");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Geo types are not supported yet.")
|
||||
public void geo() throws Exception {
|
||||
testFilter.runOnETAllPrim("geo.distance(PropertySByte,PropertySByte)")
|
||||
.is("<geo.distance(<PropertySByte>,<PropertySByte>)>")
|
||||
.isMethod(MethodKind.GEODISTANCE, 2);
|
||||
.is("<geo.distance(<PropertySByte>,<PropertySByte>)>")
|
||||
.isMethod(MethodKind.GEODISTANCE, 2);
|
||||
testFilter.runOnETAllPrim("geo.length(PropertySByte)")
|
||||
.is("<geo.length(<PropertySByte>)>")
|
||||
.isMethod(MethodKind.GEOLENGTH, 1);
|
||||
.is("<geo.length(<PropertySByte>)>")
|
||||
.isMethod(MethodKind.GEOLENGTH, 1);
|
||||
testFilter.runOnETAllPrim("geo.intersects(PropertySByte,PropertySByte)")
|
||||
.is("<geo.intersects(<PropertySByte>,<PropertySByte>)>")
|
||||
.isMethod(MethodKind.GEOINTERSECTS, 2);
|
||||
}
|
||||
|
||||
private final String encode(final String uriPart) {
|
||||
return uriPart.replaceAll(":", "%3A");
|
||||
.is("<geo.intersects(<PropertySByte>,<PropertySByte>)>")
|
||||
.isMethod(MethodKind.GEOINTERSECTS, 2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,12 +18,18 @@
|
|||
*/
|
||||
package org.apache.olingo.server.core.uri.parser;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.Edm;
|
||||
import org.apache.olingo.commons.api.edm.EdmEntityContainer;
|
||||
import org.apache.olingo.commons.api.edm.EdmEntitySet;
|
||||
import org.apache.olingo.commons.api.edm.EdmEntityType;
|
||||
import org.apache.olingo.commons.api.edm.EdmKeyPropertyRef;
|
||||
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
|
||||
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
|
||||
import org.apache.olingo.commons.api.edm.EdmProperty;
|
||||
import org.apache.olingo.commons.api.edm.FullQualifiedName;
|
||||
import org.apache.olingo.server.api.OData;
|
||||
import org.apache.olingo.server.api.uri.UriInfoKind;
|
||||
import org.apache.olingo.server.core.uri.testutil.TestUriValidator;
|
||||
import org.junit.Test;
|
||||
|
@ -33,17 +39,57 @@ import static org.junit.Assert.assertEquals;
|
|||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* All Tests which involves the <code>Parser</code> implementation
|
||||
* (and with that also the <code>UriParseTreeVisitor</code>).
|
||||
* Tests of the <code>Parser</code> implementation that require mocking of the EDM.
|
||||
*/
|
||||
public class ParserTest {
|
||||
|
||||
@Test
|
||||
public void navPropertySameNameAsEntitySet() throws Exception {
|
||||
final String namespace = "namespace";
|
||||
final String entityTypeName = "ETNavProp";
|
||||
final FullQualifiedName nameETNavProp = new FullQualifiedName(namespace, entityTypeName);
|
||||
final String entitySetName = "ESNavProp";
|
||||
final String keyPropertyName = "a";
|
||||
EdmProperty keyProperty = Mockito.mock(EdmProperty.class);
|
||||
Mockito.when(keyProperty.getType())
|
||||
.thenReturn(OData.newInstance().createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Byte));
|
||||
EdmKeyPropertyRef keyPropertyRef = Mockito.mock(EdmKeyPropertyRef.class);
|
||||
Mockito.when(keyPropertyRef.getName()).thenReturn(keyPropertyName);
|
||||
Mockito.when(keyPropertyRef.getProperty()).thenReturn(keyProperty);
|
||||
EdmNavigationProperty navProperty = Mockito.mock(EdmNavigationProperty.class);
|
||||
Mockito.when(navProperty.getName()).thenReturn(entitySetName);
|
||||
Mockito.when(navProperty.isCollection()).thenReturn(true);
|
||||
EdmEntityType entityType = Mockito.mock(EdmEntityType.class);
|
||||
Mockito.when(entityType.getFullQualifiedName()).thenReturn(nameETNavProp);
|
||||
Mockito.when(entityType.getKeyPredicateNames()).thenReturn(Collections.singletonList(keyPropertyName));
|
||||
Mockito.when(entityType.getKeyPropertyRefs()).thenReturn(Collections.singletonList(keyPropertyRef));
|
||||
Mockito.when(entityType.getNavigationProperty(entitySetName)).thenReturn(navProperty);
|
||||
Mockito.when(navProperty.getType()).thenReturn(entityType);
|
||||
EdmEntitySet entitySet = Mockito.mock(EdmEntitySet.class);
|
||||
Mockito.when(entitySet.getName()).thenReturn(entitySetName);
|
||||
Mockito.when(entitySet.getEntityType()).thenReturn(entityType);
|
||||
EdmEntityContainer container = Mockito.mock(EdmEntityContainer.class);
|
||||
Mockito.when(container.getEntitySet(entitySetName)).thenReturn(entitySet);
|
||||
Edm mockedEdm = Mockito.mock(Edm.class);
|
||||
Mockito.when(mockedEdm.getEntityContainer()).thenReturn(container);
|
||||
new TestUriValidator().setEdm(mockedEdm)
|
||||
.run("ESNavProp(1)/ESNavProp(2)/ESNavProp(3)/ESNavProp")
|
||||
.goPath()
|
||||
.at(0).isEntitySet(entitySetName)
|
||||
.at(0).isKeyPredicate(0, keyPropertyName, "1")
|
||||
.at(1).isNavProperty(entitySetName, nameETNavProp, false)
|
||||
.at(1).isKeyPredicate(0, keyPropertyName, "2")
|
||||
.at(2).isNavProperty(entitySetName, nameETNavProp, false)
|
||||
.at(2).isKeyPredicate(0, keyPropertyName, "3")
|
||||
.at(3).isNavProperty(entitySetName, nameETNavProp, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for EntitySet and NavigationProperty with same name defined in metadata.
|
||||
* (related to Olingo issue OLINGO-741)
|
||||
*/
|
||||
@Test
|
||||
public void parseEntitySetAndNavigationPropertyWithSameName() throws Exception {
|
||||
public void expandNavigationPropertyWithSameNameAsEntitySet() throws Exception {
|
||||
TestUriValidator testUri = new TestUriValidator();
|
||||
|
||||
Edm mockEdm = Mockito.mock(Edm.class);
|
||||
|
@ -60,7 +106,7 @@ public class ParserTest {
|
|||
Mockito.when(typeCategory.getNamespace()).thenReturn("NS");
|
||||
Mockito.when(esCategory.getEntityType()).thenReturn(typeCategory);
|
||||
Mockito.when(productsNavigation.getName()).thenReturn("Products");
|
||||
Mockito.when(typeCategory.getProperty("Products")).thenReturn(productsNavigation);
|
||||
Mockito.when(typeCategory.getNavigationProperty("Products")).thenReturn(productsNavigation);
|
||||
Mockito.when(container.getEntitySet("Category")).thenReturn(esCategory);
|
||||
Mockito.when(container.getEntitySet("Products")).thenReturn(esProduct);
|
||||
Mockito.when(productsType.getFullQualifiedName()).thenReturn(nameProducts);
|
||||
|
@ -122,7 +168,7 @@ public class ParserTest {
|
|||
.isType(new FullQualifiedName("NS", "Category"), false);
|
||||
fail("Expected exception was not thrown.");
|
||||
} catch (final UriParserException e) {
|
||||
assertEquals("NavigationProperty 'Category' not found in type 'NS.Products'", e.getMessage());
|
||||
assertEquals("Navigation Property 'Category' not found in type 'NS.Products'.", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@
|
|||
package org.apache.olingo.server.core.uri.testutil;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
|
@ -151,12 +152,6 @@ public class ExpandValidator implements TestValidator {
|
|||
return this;
|
||||
}
|
||||
|
||||
public ExpandValidator isSelectText(final String text) {
|
||||
final QueryOption option = expandItem.getSelectOption();
|
||||
assertEquals(text, option.getText());
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExpandValidator isSelectItemStar(final int index) {
|
||||
SelectOption select = expandItem.getSelectOption();
|
||||
SelectItem item = select.getSelectItems().get(index);
|
||||
|
@ -171,12 +166,6 @@ public class ExpandValidator implements TestValidator {
|
|||
return this;
|
||||
}
|
||||
|
||||
public ExpandValidator isFilterOptionText(final String text) {
|
||||
QueryOption option = expandItem.getFilterOption();
|
||||
assertEquals(text, option.getText());
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExpandValidator isFilterSerialized(final String serialized) {
|
||||
FilterOption filter = expandItem.getFilterOption();
|
||||
|
||||
|
@ -201,7 +190,13 @@ public class ExpandValidator implements TestValidator {
|
|||
}
|
||||
|
||||
public ExpandValidator isExpandStartType(final FullQualifiedName fullName) {
|
||||
assertNotNull(expandItem.getStartTypeFilter());
|
||||
assertEquals(fullName, expandItem.getStartTypeFilter().getFullQualifiedName());
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExpandValidator isSearchSerialized(final String serialized) {
|
||||
assertEquals(serialized, expandItem.getSearchOption().getSearchExpression().toString());
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,13 +46,16 @@ import org.apache.olingo.server.api.uri.queryoption.expression.Member;
|
|||
import org.apache.olingo.server.api.uri.queryoption.expression.Method;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.TypeLiteral;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.Unary;
|
||||
import org.apache.olingo.server.core.uri.UriResourceFunctionImpl;
|
||||
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.parser.UriParserSemanticException;
|
||||
import org.apache.olingo.server.core.uri.parser.UriParserSyntaxException;
|
||||
import org.apache.olingo.server.core.uri.queryoption.expression.BinaryImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.expression.MemberImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.expression.MethodImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.expression.UnaryImpl;
|
||||
import org.apache.olingo.server.core.uri.validator.UriValidationException;
|
||||
|
||||
public class FilterValidator implements TestValidator {
|
||||
|
@ -127,7 +130,7 @@ public class FilterValidator implements TestValidator {
|
|||
}
|
||||
|
||||
public FilterValidator runOrderByOnETTwoKeyNavEx(final String orderBy) throws UriParserException {
|
||||
return runUriOrderByEx("ESTwoKeyNav", "$orderby=" + orderBy.trim());
|
||||
return runUriEx("ESTwoKeyNav", "$orderby=" + orderBy.trim());
|
||||
}
|
||||
|
||||
public FilterValidator runOnETTwoKeyNav(final String filter) throws UriParserException, UriValidationException {
|
||||
|
@ -225,19 +228,6 @@ public class FilterValidator implements TestValidator {
|
|||
return this;
|
||||
}
|
||||
|
||||
public FilterValidator runUriOrderByEx(final String path, final String query) {
|
||||
exception = null;
|
||||
try {
|
||||
new Parser(edm, odata).parseUri(path, query, null);
|
||||
fail("Expected exception not thrown.");
|
||||
} catch (final UriParserException e) {
|
||||
exception = e;
|
||||
} catch (final UriValidationException e) {
|
||||
exception = e;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// --- Navigation ---
|
||||
|
||||
public ExpandValidator goUpToExpandValidator() {
|
||||
|
@ -274,8 +264,8 @@ public class FilterValidator implements TestValidator {
|
|||
// --- Validation ---
|
||||
|
||||
/**
|
||||
* Validates the serialized filterTree against a given filterString
|
||||
* The given expected filterString is compressed before to allow better readable code in the unit tests
|
||||
* Validates the serialized filterTree against a given filterString.
|
||||
* The given expected filterString is compressed before to allow better readable code in the unit tests.
|
||||
* @param toBeCompr
|
||||
* @return {@link FilterValidator}
|
||||
*/
|
||||
|
@ -310,14 +300,19 @@ public class FilterValidator implements TestValidator {
|
|||
EdmType actualType = null;
|
||||
|
||||
if (curExpression instanceof Member) {
|
||||
Member member = (Member) curExpression;
|
||||
actualType = member.getType();
|
||||
actualType = ((Member) curExpression).getType();
|
||||
} else if (curExpression instanceof TypeLiteral) {
|
||||
TypeLiteral typeLiteral = (TypeLiteral) curExpression;
|
||||
actualType = typeLiteral.getType();
|
||||
actualType = ((TypeLiteral) curExpression).getType();
|
||||
} else if (curExpression instanceof Literal) {
|
||||
Literal typeLiteral = (Literal) curExpression;
|
||||
actualType = typeLiteral.getType();
|
||||
actualType = ((Literal) curExpression).getType();
|
||||
} else if (curExpression instanceof Enumeration) {
|
||||
actualType = ((Enumeration) curExpression).getType();
|
||||
} else if (curExpression instanceof Unary) {
|
||||
actualType = ((UnaryImpl) curExpression).getType();
|
||||
} else if (curExpression instanceof Binary) {
|
||||
actualType = ((BinaryImpl) curExpression).getType();
|
||||
} else if (curExpression instanceof Method) {
|
||||
actualType = ((MethodImpl) curExpression).getType();
|
||||
}
|
||||
|
||||
if (actualType == null) {
|
||||
|
@ -349,7 +344,6 @@ public class FilterValidator implements TestValidator {
|
|||
|
||||
curExpression = ((Binary) curExpression).getRightOperand();
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
public FilterValidator isLiteral(final String literalText) {
|
||||
|
@ -361,9 +355,9 @@ public class FilterValidator implements TestValidator {
|
|||
assertEquals(literalText, actualLiteralText);
|
||||
return this;
|
||||
}
|
||||
|
||||
public FilterValidator isLiteralType(EdmType edmType) {
|
||||
if(!(curExpression instanceof Literal)) {
|
||||
|
||||
public FilterValidator isLiteralType(final EdmType edmType) {
|
||||
if (!(curExpression instanceof Literal)) {
|
||||
fail("Current expression is not a literal");
|
||||
}
|
||||
|
||||
|
@ -374,7 +368,7 @@ public class FilterValidator implements TestValidator {
|
|||
}
|
||||
|
||||
public FilterValidator isNullLiteralType() {
|
||||
if(!(curExpression instanceof Literal)) {
|
||||
if (!(curExpression instanceof Literal)) {
|
||||
fail("Current expression is not a literal");
|
||||
}
|
||||
|
||||
|
@ -495,12 +489,4 @@ public class FilterValidator implements TestValidator {
|
|||
assertEquals(messageKey, exception.getMessageKey());
|
||||
return this;
|
||||
}
|
||||
|
||||
public FilterValidator isNull() {
|
||||
return isLiteral("null");
|
||||
}
|
||||
|
||||
public FilterValidator isTrue() {
|
||||
return isLiteral("true");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,6 +140,20 @@ public class TestUriValidator implements TestValidator {
|
|||
return this;
|
||||
}
|
||||
|
||||
public TestUriValidator isSelectItemStar(final int index) {
|
||||
final SelectOption select = uriInfo.getSelectOption();
|
||||
SelectItem item = select.getSelectItems().get(index);
|
||||
assertTrue(item.isStar());
|
||||
return this;
|
||||
}
|
||||
|
||||
public TestUriValidator isSelectItemAllOp(final int index, final FullQualifiedName fqn) {
|
||||
final SelectOption select = uriInfo.getSelectOption();
|
||||
SelectItem item = select.getSelectItems().get(index);
|
||||
assertEquals(fqn, item.getAllOperationsInSchemaNameSpace());
|
||||
return this;
|
||||
}
|
||||
|
||||
// Validation
|
||||
public TestUriValidator isKind(final UriInfoKind kind) {
|
||||
assertEquals(kind, uriInfo.getKind());
|
||||
|
@ -202,16 +216,6 @@ public class TestUriValidator implements TestValidator {
|
|||
return this;
|
||||
}
|
||||
|
||||
public TestUriValidator isExpandText(final String text) {
|
||||
assertEquals(text, uriInfo.getExpandOption().getText());
|
||||
return this;
|
||||
}
|
||||
|
||||
public TestUriValidator isSelectText(final String text) {
|
||||
assertEquals(text, uriInfo.getSelectOption().getText());
|
||||
return this;
|
||||
}
|
||||
|
||||
public TestUriValidator isFormatText(final String text) {
|
||||
assertEquals(text, uriInfo.getFormatOption().getText());
|
||||
return this;
|
||||
|
@ -234,18 +238,4 @@ public class TestUriValidator implements TestValidator {
|
|||
assertEquals(fullName, uriInfo.getEntityTypeCast().getFullQualifiedName());
|
||||
return this;
|
||||
}
|
||||
|
||||
public TestUriValidator isSelectItemStar(final int index) {
|
||||
final SelectOption select = uriInfo.getSelectOption();
|
||||
SelectItem item = select.getSelectItems().get(index);
|
||||
assertTrue(item.isStar());
|
||||
return this;
|
||||
}
|
||||
|
||||
public TestUriValidator isSelectItemAllOp(final int index, final FullQualifiedName fqn) {
|
||||
final SelectOption select = uriInfo.getSelectOption();
|
||||
SelectItem item = select.getSelectItems().get(index);
|
||||
assertEquals(fqn, item.getAllOperationsInSchemaNameSpace());
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,127 +0,0 @@
|
|||
/*
|
||||
* 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.testutil;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.antlr.v4.runtime.ANTLRInputStream;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriLexer;
|
||||
|
||||
public class TokenValidator {
|
||||
|
||||
private String input = null;
|
||||
private List<? extends Token> tokens = null;
|
||||
private Token curToken = null;
|
||||
private Exception curException = null;
|
||||
|
||||
private int startMode;
|
||||
|
||||
// --- Execution ---
|
||||
|
||||
public TokenValidator run(final String uri) {
|
||||
input = uri;
|
||||
tokens = parseInput(uri);
|
||||
first();
|
||||
return this;
|
||||
}
|
||||
|
||||
// --- Navigation ---
|
||||
|
||||
// navigate within the tokenlist
|
||||
public TokenValidator first() {
|
||||
try {
|
||||
curToken = tokens.get(0);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
curToken = null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public TokenValidator last() {
|
||||
curToken = tokens.get(tokens.size() - 1);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TokenValidator at(final int index) {
|
||||
try {
|
||||
curToken = tokens.get(index);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
curToken = null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// --- Validation ---
|
||||
|
||||
public TokenValidator isText(final String expected) {
|
||||
assertEquals(expected, curToken.getText());
|
||||
return this;
|
||||
}
|
||||
|
||||
public TokenValidator isAllText(final String expected) {
|
||||
String actual = "";
|
||||
|
||||
for (Token curToken : tokens) {
|
||||
actual += curToken.getText();
|
||||
}
|
||||
assertEquals(expected, actual);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TokenValidator isAllInput() {
|
||||
String actual = "";
|
||||
|
||||
for (Token curToken : tokens) {
|
||||
actual += curToken.getText();
|
||||
}
|
||||
assertEquals(input, actual);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TokenValidator isInput() {
|
||||
assertEquals(input, curToken.getText());
|
||||
return this;
|
||||
}
|
||||
|
||||
public TokenValidator isType(final int expected) {
|
||||
assertEquals(UriLexer.VOCABULARY.getDisplayName(expected), UriLexer.VOCABULARY.getDisplayName(curToken.getType()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public TokenValidator isExType(final Class<?> exClass) {
|
||||
assertEquals(exClass, curException.getClass());
|
||||
return this;
|
||||
}
|
||||
|
||||
public void globalMode(final int mode) {
|
||||
startMode = mode;
|
||||
}
|
||||
|
||||
// --- Helper ---
|
||||
|
||||
private List<? extends Token> parseInput(final String input) {
|
||||
ANTLRInputStream inputStream = new ANTLRInputStream(input);
|
||||
UriLexer lexer = new UriLexer(inputStream);
|
||||
lexer.mode(startMode);
|
||||
return lexer.getAllTokens();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue