[OLINGO-1526]Support for Compute aggregate method
This commit is contained in:
parent
3de463a5b1
commit
a3d679aacd
|
@ -28,7 +28,7 @@ import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
|
|||
* Represents an aggregate expression.
|
||||
* @see Aggregate
|
||||
*/
|
||||
public interface AggregateExpression {
|
||||
public interface AggregateExpression extends Expression {
|
||||
|
||||
/** Standard aggregation method. */
|
||||
public enum StandardMethod { SUM, MIN, MAX, AVERAGE, COUNT_DISTINCT }
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.List;
|
|||
import org.apache.olingo.commons.api.edm.EdmEnumType;
|
||||
import org.apache.olingo.commons.api.edm.EdmType;
|
||||
import org.apache.olingo.server.api.ODataApplicationException;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.AggregateExpression;
|
||||
|
||||
/**
|
||||
* Generic interface to define expression visitors with arbitrary return types.
|
||||
|
@ -37,7 +38,7 @@ public interface ExpressionVisitor<T> {
|
|||
* @param left Application return value of left sub tree
|
||||
* @param right Application return value of right sub tree
|
||||
* @return Application return value of type T
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occured
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occurred
|
||||
* @throws ODataApplicationException Thrown by the application
|
||||
*/
|
||||
T visitBinaryOperator(BinaryOperatorKind operator, T left, T right)
|
||||
|
@ -48,7 +49,7 @@ public interface ExpressionVisitor<T> {
|
|||
* @param operator Operator kind
|
||||
* @param operand return value of sub tree
|
||||
* @return Application return value of type T
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occured
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occurred
|
||||
* @throws ODataApplicationException Thrown by the application
|
||||
*/
|
||||
T visitUnaryOperator(UnaryOperatorKind operator, T operand)
|
||||
|
@ -71,7 +72,7 @@ public interface ExpressionVisitor<T> {
|
|||
* @param lambdaVariable Variable name used lambda variable
|
||||
* @param expression Lambda expression
|
||||
* @return Application return value of type T
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occured
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occurred
|
||||
* @throws ODataApplicationException Thrown by the application
|
||||
*/
|
||||
T visitLambdaExpression(String lambdaFunction, String lambdaVariable, Expression expression)
|
||||
|
@ -81,7 +82,7 @@ public interface ExpressionVisitor<T> {
|
|||
* Called for each traversed {@link Literal} expression
|
||||
* @param literal Literal
|
||||
* @return Application return value of type T
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occured
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occurred
|
||||
* @throws ODataApplicationException Thrown by the application
|
||||
*/
|
||||
T visitLiteral(Literal literal) throws ExpressionVisitException, ODataApplicationException;
|
||||
|
@ -91,7 +92,7 @@ public interface ExpressionVisitor<T> {
|
|||
* @param member UriInfoResource object describing the whole path used to access an data value
|
||||
* (this includes for example the usage of $root and $it inside the URI)
|
||||
* @return Application return value of type T
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occured
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occurred
|
||||
* @throws ODataApplicationException Thrown by the application
|
||||
*/
|
||||
T visitMember(Member member) throws ExpressionVisitException, ODataApplicationException;
|
||||
|
@ -100,7 +101,7 @@ public interface ExpressionVisitor<T> {
|
|||
* Called for each traversed {@link Alias} expression
|
||||
* @param aliasName Name of the alias
|
||||
* @return Application return value of type T
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occured
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occurred
|
||||
* @throws ODataApplicationException Thrown by the application
|
||||
*/
|
||||
T visitAlias(String aliasName) throws ExpressionVisitException, ODataApplicationException;
|
||||
|
@ -109,7 +110,7 @@ public interface ExpressionVisitor<T> {
|
|||
* Called for each traversed {@link TypeLiteral} expression
|
||||
* @param type EdmType
|
||||
* @return Application return value of type T
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occured
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occurred
|
||||
* @throws ODataApplicationException Thrown by the application
|
||||
*/
|
||||
T visitTypeLiteral(EdmType type) throws ExpressionVisitException, ODataApplicationException;
|
||||
|
@ -118,7 +119,7 @@ public interface ExpressionVisitor<T> {
|
|||
* Called for each traversed {@link LambdaRef}
|
||||
* @param variableName Name of the used lambda variable
|
||||
* @return Application return value of type T
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occured
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occurred
|
||||
* @throws ODataApplicationException Thrown by the application
|
||||
*/
|
||||
T visitLambdaReference(String variableName) throws ExpressionVisitException, ODataApplicationException;
|
||||
|
@ -128,7 +129,7 @@ public interface ExpressionVisitor<T> {
|
|||
* @param type Type used in the URI before the enumeration values
|
||||
* @param enumValues List of enumeration values
|
||||
* @return Application return value of type T
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occured
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occurred
|
||||
* @throws ODataApplicationException Thrown by the application
|
||||
*/
|
||||
T visitEnum(EdmEnumType type, List<String> enumValues) throws ExpressionVisitException, ODataApplicationException;
|
||||
|
@ -139,10 +140,22 @@ public interface ExpressionVisitor<T> {
|
|||
* @param left Application return value of left sub tree
|
||||
* @param right Application return value of right sub tree
|
||||
* @return Application return value of type T
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occured
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occurred
|
||||
* @throws ODataApplicationException Thrown by the application
|
||||
*/
|
||||
T visitBinaryOperator(BinaryOperatorKind operator, T left, List<T> right)
|
||||
throws ExpressionVisitException, ODataApplicationException;
|
||||
|
||||
/**
|
||||
* Called for each traversed {@link AggregateExpression}
|
||||
* @param aggregateExpr the aggregate expression
|
||||
* @return Application return value of type T
|
||||
* @throws ExpressionVisitException Thrown if an exception while traversing occurred
|
||||
* @throws ODataApplicationException Thrown by the application
|
||||
*/
|
||||
default T visitComputeAggregate(AggregateExpression aggregateExpr)
|
||||
throws ExpressionVisitException, ODataApplicationException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ package org.apache.olingo.server.api.uri.queryoption.expression;
|
|||
* For the semantic of these methods please see the ODATA specification for URL conventions
|
||||
*/
|
||||
public enum MethodKind {
|
||||
COMPUTE_AGGREGATE("aggregate"),
|
||||
CONTAINS("contains"),
|
||||
STARTSWITH("startswith"),
|
||||
ENDSWITH("endswith"),
|
||||
|
|
|
@ -222,79 +222,84 @@ public class ApplyParser {
|
|||
throws UriParserException, UriValidationException {
|
||||
AggregateImpl aggregate = new AggregateImpl();
|
||||
do {
|
||||
aggregate.addExpression(parseAggregateExpr(referencedType));
|
||||
aggregate.addExpression(parseAggregateExpr(referencedType, Requirement.REQUIRED));
|
||||
} while (tokenizer.next(TokenKind.COMMA));
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
return aggregate;
|
||||
}
|
||||
|
||||
public AggregateExpression parseAggregateMethodCallExpr(UriTokenizer tokenizer, EdmStructuredType referringType)
|
||||
throws UriParserException, UriValidationException {
|
||||
this.tokenizer = tokenizer;
|
||||
|
||||
private AggregateExpression parseAggregateExpr(EdmStructuredType referencedType)
|
||||
return parseAggregateExpr(referringType, Requirement.FORBIDDEN);
|
||||
}
|
||||
|
||||
private AggregateExpression parseAggregateExpr(EdmStructuredType referencedType, Requirement aliasRequired)
|
||||
throws UriParserException, UriValidationException {
|
||||
AggregateExpressionImpl aggregateExpression = new AggregateExpressionImpl();
|
||||
tokenizer.saveState();
|
||||
AggregateExpressionImpl aggregateExpression = new AggregateExpressionImpl();
|
||||
tokenizer.saveState();
|
||||
|
||||
// First try is checking for a (potentially empty) path prefix and the things that could follow it.
|
||||
UriInfoImpl uriInfo = new UriInfoImpl();
|
||||
final String identifierLeft = parsePathPrefix(uriInfo, referencedType);
|
||||
if (identifierLeft != null) {
|
||||
customAggregate(referencedType, aggregateExpression, uriInfo);
|
||||
} else if (tokenizer.next(TokenKind.OPEN)) {
|
||||
final UriResource lastResourcePart = uriInfo.getLastResourcePart();
|
||||
if (lastResourcePart == null) {
|
||||
throw new UriParserSyntaxException("Invalid 'aggregateExpr' syntax.",
|
||||
UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
aggregateExpression.setPath(uriInfo);
|
||||
DynamicStructuredType inlineType = new DynamicStructuredType((EdmStructuredType)
|
||||
ParserHelper.getTypeInformation((UriResourcePartTyped) lastResourcePart));
|
||||
aggregateExpression.setInlineAggregateExpression(parseAggregateExpr(inlineType));
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
} else if (tokenizer.next(TokenKind.COUNT)) {
|
||||
uriInfo.addResourcePart(new UriResourceCountImpl());
|
||||
aggregateExpression.setPath(uriInfo);
|
||||
final String alias = parseAsAlias(referencedType, true);
|
||||
aggregateExpression.setAlias(alias);
|
||||
((DynamicStructuredType) referencedType).addProperty(
|
||||
createDynamicProperty(alias,
|
||||
// The OData standard mandates Edm.Decimal (with no decimals), although counts are always integer.
|
||||
odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Decimal)));
|
||||
} else {
|
||||
// No legitimate continuation of a path prefix has been found.
|
||||
// First try is checking for a (potentially empty) path prefix and the things that could follow it.
|
||||
UriInfoImpl uriInfo = new UriInfoImpl();
|
||||
final String identifierLeft = parsePathPrefix(uriInfo, referencedType);
|
||||
if (identifierLeft != null) {
|
||||
customAggregate(referencedType, aggregateExpression, uriInfo);
|
||||
} else if (tokenizer.next(TokenKind.OPEN)) {
|
||||
final UriResource lastResourcePart = uriInfo.getLastResourcePart();
|
||||
if (lastResourcePart == null) {
|
||||
throw new UriParserSyntaxException("Invalid 'aggregateExpr' syntax.",
|
||||
UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
aggregateExpression.setPath(uriInfo);
|
||||
DynamicStructuredType inlineType = new DynamicStructuredType((EdmStructuredType)
|
||||
ParserHelper.getTypeInformation((UriResourcePartTyped) lastResourcePart));
|
||||
aggregateExpression.setInlineAggregateExpression(parseAggregateExpr(inlineType, aliasRequired));
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
} else if (tokenizer.next(TokenKind.COUNT)) {
|
||||
uriInfo.addResourcePart(new UriResourceCountImpl());
|
||||
aggregateExpression.setPath(uriInfo);
|
||||
final String alias = parseAsAlias(referencedType, aliasRequired);
|
||||
if (alias != null) {
|
||||
aggregateExpression.setAlias(alias);
|
||||
((DynamicStructuredType) referencedType).addProperty(
|
||||
createDynamicProperty(alias,
|
||||
// The OData standard mandates Edm.Decimal (with no decimals),
|
||||
// although counts are always integer.
|
||||
odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Decimal)));
|
||||
}
|
||||
} else {
|
||||
// No legitimate continuation of a path prefix has been found.
|
||||
|
||||
// Second try is checking for a common expression.
|
||||
tokenizer.returnToSavedState();
|
||||
final Expression expression = new ExpressionParser(edm, odata)
|
||||
.parse(tokenizer, referencedType, crossjoinEntitySetNames, aliases);
|
||||
aggregateExpression.setExpression(expression);
|
||||
parseAggregateWith(aggregateExpression);
|
||||
if (aggregateExpression.getStandardMethod() == null && aggregateExpression.getCustomMethod() == null) {
|
||||
if (tokenizer.next(TokenKind.AsOperator)) {
|
||||
throw new UriParserSyntaxException("Invalid 'aggregateExpr' syntax.",
|
||||
UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
customAggregateNamedAsProperty(referencedType, aggregateExpression, uriInfo);
|
||||
|
||||
return aggregateExpression;
|
||||
}
|
||||
final String alias = parseAsAlias(referencedType, true);
|
||||
aggregateExpression.setAlias(alias);
|
||||
DynamicProperty dynamicProperty = createDynamicProperty(alias,
|
||||
// Determine the type for standard methods; there is no way to do this for custom methods.
|
||||
getTypeForAggregateMethod(aggregateExpression.getStandardMethod(),
|
||||
ExpressionParser.getType(expression)));
|
||||
if (aggregateExpression.getStandardMethod() == StandardMethod.SUM
|
||||
|| aggregateExpression.getStandardMethod() == StandardMethod.AVERAGE) {
|
||||
//by default a property with no precision/scale defaults to a 0 scale
|
||||
//this does not work for sum/average in general
|
||||
dynamicProperty.setScale(Integer.MAX_VALUE);
|
||||
}
|
||||
((DynamicStructuredType) referencedType).addProperty(
|
||||
dynamicProperty);
|
||||
parseAggregateFrom(aggregateExpression, referencedType);
|
||||
}
|
||||
// Second try is checking for a common expression.
|
||||
tokenizer.returnToSavedState();
|
||||
final Expression expression = new ExpressionParser(edm, odata)
|
||||
.parse(tokenizer, referencedType, crossjoinEntitySetNames, aliases);
|
||||
aggregateExpression.setExpression(expression);
|
||||
parseAggregateWith(aggregateExpression);
|
||||
if (aggregateExpression.getStandardMethod() == null && aggregateExpression.getCustomMethod() == null) {
|
||||
if (tokenizer.next(TokenKind.AsOperator)) {
|
||||
throw new UriParserSyntaxException("Invalid 'aggregateExpr' syntax.",
|
||||
UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
customAggregateNamedAsProperty(referencedType, aggregateExpression, uriInfo);
|
||||
|
||||
return aggregateExpression;
|
||||
}
|
||||
final String alias = parseAsAlias(referencedType, aliasRequired);
|
||||
if(alias != null) {
|
||||
aggregateExpression.setAlias(alias);
|
||||
((DynamicStructuredType) referencedType).addProperty(
|
||||
createDynamicProperty(alias,
|
||||
// Determine the type for standard methods; there is no way to do this for custom methods.
|
||||
getTypeForAggregateMethod(aggregateExpression.getStandardMethod(),
|
||||
ExpressionParser.getType(expression))));
|
||||
}
|
||||
parseAggregateFrom(aggregateExpression, referencedType);
|
||||
}
|
||||
|
||||
return aggregateExpression;
|
||||
}
|
||||
return aggregateExpression;
|
||||
}
|
||||
|
||||
private void customAggregate(EdmStructuredType referencedType, AggregateExpressionImpl aggregateExpression,
|
||||
UriInfoImpl uriInfo) throws UriParserException {
|
||||
|
@ -306,7 +311,7 @@ public class ApplyParser {
|
|||
// allowed and have no type.
|
||||
uriInfo.addResourcePart(new UriResourcePrimitivePropertyImpl(createDynamicProperty(customAggregate, null)));
|
||||
aggregateExpression.setPath(uriInfo);
|
||||
final String alias = parseAsAlias(referencedType, false);
|
||||
final String alias = parseAsAlias(referencedType, Requirement.OPTIONAL);
|
||||
if (alias != null) {
|
||||
aggregateExpression.setAlias(alias);
|
||||
((DynamicStructuredType) referencedType).addProperty(createDynamicProperty(alias, null));
|
||||
|
@ -361,22 +366,31 @@ public class ApplyParser {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
enum Requirement {
|
||||
REQUIRED, OPTIONAL, FORBIDDEN
|
||||
}
|
||||
|
||||
private String parseAsAlias(final EdmStructuredType referencedType, final boolean isRequired)
|
||||
private String parseAsAlias(final EdmStructuredType referencedType, final Requirement requirement)
|
||||
throws UriParserException {
|
||||
if (tokenizer.next(TokenKind.AsOperator)) {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.ODataIdentifier);
|
||||
final String name = tokenizer.getText();
|
||||
if (referencedType.getProperty(name) != null) {
|
||||
throw new UriParserSemanticException("Alias '" + name + "' is already a property.",
|
||||
UriParserSemanticException.MessageKeys.IS_PROPERTY, name);
|
||||
}
|
||||
return name;
|
||||
} else if (isRequired) {
|
||||
throw new UriParserSyntaxException("Expected asAlias not found.", UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (tokenizer.next(TokenKind.AsOperator)) {
|
||||
if(requirement == Requirement.FORBIDDEN) {
|
||||
throw new UriParserSyntaxException("Unexpected as alias found.",
|
||||
UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.ODataIdentifier);
|
||||
final String name = tokenizer.getText();
|
||||
if (referencedType.getProperty(name) != null) {
|
||||
throw new UriParserSemanticException("Alias '" + name + "' is already a property.",
|
||||
UriParserSemanticException.MessageKeys.IS_PROPERTY, name);
|
||||
}
|
||||
return name;
|
||||
} else if (requirement == Requirement.REQUIRED) {
|
||||
throw new UriParserSyntaxException("Expected as alias not found.",
|
||||
UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void parseAggregateFrom(AggregateExpressionImpl aggregateExpression,
|
||||
final EdmStructuredType referencedType) throws UriParserException {
|
||||
|
@ -403,7 +417,7 @@ public class ApplyParser {
|
|||
throw new UriParserSemanticException("Compute expressions must return primitive values.",
|
||||
UriParserSemanticException.MessageKeys.ONLY_FOR_PRIMITIVE_TYPES, "compute");
|
||||
}
|
||||
final String alias = parseAsAlias(referencedType, true);
|
||||
final String alias = parseAsAlias(referencedType, Requirement.REQUIRED);
|
||||
((DynamicStructuredType) referencedType).addProperty(createDynamicProperty(alias, expressionType));
|
||||
compute.addExpression(new ComputeExpressionImpl()
|
||||
.setExpression(expression)
|
||||
|
|
|
@ -54,6 +54,7 @@ import org.apache.olingo.server.api.uri.UriResourceLambdaVariable;
|
|||
import org.apache.olingo.server.api.uri.UriResourceNavigation;
|
||||
import org.apache.olingo.server.api.uri.UriResourcePartTyped;
|
||||
import org.apache.olingo.server.api.uri.queryoption.AliasQueryOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.AggregateExpression;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.Alias;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.Binary;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind;
|
||||
|
@ -123,6 +124,7 @@ public class ExpressionParser {
|
|||
private static final Map<TokenKind, MethodKind> tokenToMethod;
|
||||
static {
|
||||
Map<TokenKind, MethodKind> temp = new EnumMap<>(TokenKind.class);
|
||||
temp.put(TokenKind.AggregateTrafo, MethodKind.COMPUTE_AGGREGATE);
|
||||
temp.put(TokenKind.CeilingMethod, MethodKind.CEILING);
|
||||
temp.put(TokenKind.ConcatMethod, MethodKind.CONCAT);
|
||||
temp.put(TokenKind.ContainsMethod, MethodKind.CONTAINS);
|
||||
|
@ -660,8 +662,16 @@ public class ExpressionParser {
|
|||
case CAST:
|
||||
case ISOF:
|
||||
break;
|
||||
|
||||
case COMPUTE_AGGREGATE:
|
||||
ApplyParser ap = new ApplyParser(edm, odata);
|
||||
AggregateExpression aggrExpr = ap.parseAggregateMethodCallExpr(tokenizer,
|
||||
(EdmStructuredType) referringType);
|
||||
parameters.add(aggrExpr);
|
||||
|
||||
}
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,10 +23,13 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.FullQualifiedName;
|
||||
import org.apache.olingo.server.api.ODataApplicationException;
|
||||
import org.apache.olingo.server.api.uri.UriInfo;
|
||||
import org.apache.olingo.server.api.uri.UriResource;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.AggregateExpression;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitException;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitor;
|
||||
|
||||
/**
|
||||
* Represents an aggregate expression.
|
||||
|
@ -110,4 +113,10 @@ public class AggregateExpressionImpl implements AggregateExpression {
|
|||
this.alias = alias;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(ExpressionVisitor<T> visitor) throws ExpressionVisitException, ODataApplicationException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,8 +112,11 @@ public class MethodImpl implements Method {
|
|||
case ISOF:
|
||||
kind = EdmPrimitiveTypeKind.Boolean;
|
||||
break;
|
||||
case COMPUTE_AGGREGATE:
|
||||
kind = null;
|
||||
}
|
||||
return new ODataImpl().createPrimitiveTypeInstance(kind);
|
||||
|
||||
return kind == null ? null : new ODataImpl().createPrimitiveTypeInstance(kind);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -127,22 +127,53 @@ public class ApplyParserTest {
|
|||
|
||||
@Test
|
||||
public void customAggregate() throws Exception {
|
||||
parse("ESTwoKeyNav", "aggregate(customAggregate)")
|
||||
.is(Aggregate.class)
|
||||
.goAggregate(0)
|
||||
.noExpression()
|
||||
.noInlineAggregateExpression()
|
||||
.goPath().first().isUriPathInfoKind(UriResourceKind.primitiveProperty);
|
||||
parse("ESTwoKeyNav", "aggregate(customAggregate)").is(Aggregate.class).goAggregate(0).noExpression()
|
||||
.noInlineAggregateExpression().goPath().first().isUriPathInfoKind(UriResourceKind.primitiveProperty);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customAggregateNamedAsProperty() throws Exception {
|
||||
parse("ESTwoKeyNav", "aggregate(PropertyInt16)")
|
||||
.is(Aggregate.class)
|
||||
.goAggregate(0)
|
||||
.noExpression()
|
||||
.noInlineAggregateExpression()
|
||||
.goPath().first().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
|
||||
parse("ESTwoKeyNav", "aggregate(PropertyInt16)").is(Aggregate.class).goAggregate(0).noExpression()
|
||||
.noInlineAggregateExpression().goPath().first()
|
||||
.isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterByCustomAggregate() throws Exception {
|
||||
ApplyValidator validator = parse("ESTwoKeyNav", "filter(aggregate(customAggregate) gt 5)");
|
||||
FilterValidator filter = validator.is(Filter.class).goFilter().isBinary(BinaryOperatorKind.GT);
|
||||
AggregateValidator aggregate = aggregate(filter.root().left());
|
||||
aggregate.goPath().first().isUriPathInfoKind(UriResourceKind.primitiveProperty);
|
||||
filter.root().right().isLiteral("5");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterByCountAggregate() throws Exception {
|
||||
ApplyValidator validator = parse("ESTwoKeyNav", "filter(aggregate($count) ge 9)");
|
||||
FilterValidator filter = validator.is(Filter.class).goFilter().isBinary(BinaryOperatorKind.GE);
|
||||
AggregateValidator aggregate = aggregate(filter.root().left());
|
||||
aggregate.goPath().first().isCount();
|
||||
filter.root().right().isLiteral("9");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterByTransformationAggregate() throws Exception {
|
||||
ApplyValidator validator = parse("ESTwoKeyNav", "filter(aggregate(PropertyInt16 with min) ne 3)");
|
||||
FilterValidator filter = validator.is(Filter.class).goFilter().isBinary(BinaryOperatorKind.NE);
|
||||
AggregateValidator aggregate = aggregate(filter.root().left());
|
||||
aggregate.isStandardMethod(StandardMethod.MIN)
|
||||
.goExpression().goPath().first().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
|
||||
filter.root().right().isLiteral("3");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterByCustomAggregateNamedAsProperty() throws Exception {
|
||||
ApplyValidator validator = parse("ESTwoKeyNav", "filter(aggregate(PropertyString) eq 'Evgeny')");
|
||||
FilterValidator filter = validator.is(Filter.class).goFilter().isBinary(BinaryOperatorKind.EQ);
|
||||
AggregateValidator aggregate = aggregate(filter.root().left());
|
||||
aggregate.goPath().first()
|
||||
.isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
|
||||
filter.root().right().isLiteral("'Evgeny'");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -660,6 +691,14 @@ public class ApplyParserTest {
|
|||
return previous;
|
||||
}
|
||||
}
|
||||
|
||||
private AggregateValidator aggregate(FilterValidator filter) {
|
||||
filter.isMethod(MethodKind.COMPUTE_AGGREGATE, 1);
|
||||
filter.goParameter(0);
|
||||
|
||||
return new AggregateValidator((AggregateExpression) filter.getExpression(), filter);
|
||||
}
|
||||
|
||||
|
||||
private final class AggregateValidator implements TestValidator {
|
||||
|
||||
|
@ -670,7 +709,7 @@ public class ApplyParserTest {
|
|||
this.aggregateExpression = aggregateExpression;
|
||||
this.previous = previous;
|
||||
}
|
||||
|
||||
|
||||
public AggregateValidator isStandardMethod(final AggregateExpression.StandardMethod method) {
|
||||
assertNotNull(aggregateExpression);
|
||||
assertEquals(method, aggregateExpression.getStandardMethod());
|
||||
|
|
|
@ -91,6 +91,10 @@ public class FilterValidator implements TestValidator {
|
|||
rootExpression = curExpression = expression;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Expression getExpression() {
|
||||
return curExpression;
|
||||
}
|
||||
|
||||
// --- Execution ---
|
||||
|
||||
|
@ -375,7 +379,7 @@ public class FilterValidator implements TestValidator {
|
|||
assertTrue("Current expression not a member", curExpression instanceof Member);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public FilterValidator isMemberStartType(final FullQualifiedName fullName) {
|
||||
isMember();
|
||||
Member member = (Member) curExpression;
|
||||
|
|
Loading…
Reference in New Issue