Skip to content

Commit

Permalink
#1081: remove requirement of passing knowledge about "exists" functio…
Browse files Browse the repository at this point in the history
…n in PipelineFunctionFilter

Signed-off-by: Florian Fendt <Florian.Fendt@bosch.io>
  • Loading branch information
ffendt committed Jun 17, 2021
1 parent f736fb6 commit a44e91f
Show file tree
Hide file tree
Showing 4 changed files with 272 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,13 @@
package org.eclipse.ditto.internal.models.placeholders;


import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

import org.eclipse.ditto.internal.models.placeholders.filter.FilterFunction;
Expand Down Expand Up @@ -48,12 +47,12 @@ public Signature getSignature() {
public PipelineElement apply(final PipelineElement value, final String paramsIncludingParentheses,
final ExpressionResolver expressionResolver) {

final Map<String, String> parameters = parseAndResolve(paramsIncludingParentheses, expressionResolver);
final Parameters parameters = parseAndResolve(paramsIncludingParentheses, expressionResolver);

return value.onResolved(valueThatShouldBeFilteredConditionally -> {

final Optional<FilterFunction> rqlFunctionOpt =
FilterFunctions.fromName(parameters.get(RqlFunctionParam.NAME));
FilterFunctions.fromName(parameters.getRqlFunction());
final boolean shouldKeepValue = rqlFunctionOpt
.map(rqlFunction -> applyRqlFunction(parameters, rqlFunction))
.orElse(false);
Expand All @@ -68,61 +67,38 @@ public PipelineElement apply(final PipelineElement value, final String paramsInc
});
}

private Boolean applyRqlFunction(final Map<String, String> parameters, final FilterFunction rqlFunction) {
if (rqlFunction == FilterFunctions.EXISTS) {
final String filterValue = parameters.get(FilterValueParam.NAME);
return rqlFunction.apply(filterValue);
} else {
final String filterValue = parameters.get(FilterValueParam.NAME);
final String comparedValue = parameters.get(ComparedValueParam.NAME);
return rqlFunction.apply(filterValue, comparedValue);
}
private Boolean applyRqlFunction(final Parameters parameters, final FilterFunction rqlFunction) {
return parameters.getComparedValueParam()
.map(comparedValue -> rqlFunction.apply(parameters.getFilterValue(), comparedValue))
.orElseGet(() -> rqlFunction.apply(parameters.getFilterValue()));
}

private Map<String, String> parseAndResolve(final String paramsIncludingParentheses,
private Parameters parseAndResolve(final String paramsIncludingParentheses,
final ExpressionResolver expressionResolver) {

final boolean hasComparedValue = hasComparedValue(paramsIncludingParentheses);
final List<PipelineElement> parameterElements = getPipelineElements(paramsIncludingParentheses,
expressionResolver, hasComparedValue);
final List<PipelineElement> parameterElements =
PipelineFunctionParameterResolverFactory.forDoubleOrTripleStringOrPlaceholderParameter()
.apply(paramsIncludingParentheses, expressionResolver, this);

final Map<String, String> parameters = new HashMap<>();
final ParametersBuilder parametersBuilder = new ParametersBuilder();

final PipelineElement filterValueParamElement = parameterElements.get(0);
final String filterValueParam = filterValueParamElement.toOptional().orElse("");
parameters.put(FilterValueParam.NAME, filterValueParam);
parametersBuilder.withFilterValue(filterValueParam);

final PipelineElement rqlFunctionParamElement = parameterElements.get(1);
final String rqlFunctionParam = rqlFunctionParamElement.toOptional().orElseThrow(() ->
PlaceholderFunctionSignatureInvalidException.newBuilder(paramsIncludingParentheses, this)
.build());
parameters.put(RqlFunctionParam.NAME, rqlFunctionParam);
parametersBuilder.withRqlFunction(rqlFunctionParam);

if (hasComparedValue) {
if (parameterElements.size() > 2) {
final PipelineElement comparedValueParamElement = parameterElements.get(2);
final String comparedValueParam = comparedValueParamElement.toOptional().orElse("");
parameters.put(ComparedValueParam.NAME, comparedValueParam);
parametersBuilder.withComparedValue(comparedValueParam);
}

return parameters;
}

private boolean hasComparedValue(final String paramsIncludingParentheses) {
final Pattern pattern =
Pattern.compile(PipelineFunctionParameterResolverFactory.ParameterResolver.EXISTS_FUNCTION);
final Matcher matcher = pattern.matcher(paramsIncludingParentheses);
return !matcher.matches();
}

private List<PipelineElement> getPipelineElements(final String paramsIncludingParentheses,
final ExpressionResolver expressionResolver, final boolean hasComparedValue) {
if (hasComparedValue) {
return PipelineFunctionParameterResolverFactory.forTripleStringOrPlaceholderParameter()
.apply(paramsIncludingParentheses, expressionResolver, this);
} else {
return PipelineFunctionParameterResolverFactory.forDoubleStringOrPlaceholderParameter()
.apply(paramsIncludingParentheses, expressionResolver, this);
}
return parametersBuilder.build();
}

/**
Expand Down Expand Up @@ -236,4 +212,62 @@ public String getDescription() {

}

@Immutable
private static final class Parameters {
private final String filterValue;
private final String rqlFunction;
@Nullable
private final String comparedValueParam;


private Parameters(
final String filterValue,
final String rqlFunction,
@Nullable final String comparedValueParam) {
this.filterValue = checkNotNull(filterValue, "filterValue");
this.rqlFunction = checkNotNull(rqlFunction, "rqlFuntion");
this.comparedValueParam = comparedValueParam;
}

private String getFilterValue() {
return filterValue;
}

private String getRqlFunction() {
return rqlFunction;
}

private Optional<String> getComparedValueParam() {
return Optional.ofNullable(comparedValueParam);
}

}

private static final class ParametersBuilder {
private String filterValue;
private String rqlFunction;
private String comparedValue;

ParametersBuilder withFilterValue(final String filterValue) {
this.filterValue = filterValue;
return this;
}

ParametersBuilder withRqlFunction(final String rqlFunction) {
this.rqlFunction = rqlFunction;
return this;
}

ParametersBuilder withComparedValue(@Nullable final String comparedValue) {
this.comparedValue = comparedValue;
return this;
}

Parameters build() {
return new Parameters(filterValue, rqlFunction, comparedValue);
}

}


}
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ final class PipelineFunctionParameterResolverFactory {
private static final ParameterResolver TRIPLE_STRING_OR_PLACEHOLDER_PARAMETER_RESOLVER =
new ParameterResolver(3, true);

private static final ParameterResolver DOUBLE_STRING_OR_PLACEHOLDER_PARAMETER_RESOLVER =
new ParameterResolver(2, true);
private static final ParameterResolver DOUBLE_OR_TRIPLE_STRING_OR_PLACEHOLDER_PARAMETER_RESOLVER =
new ParameterResolver(2, 1, true);


/**
Expand Down Expand Up @@ -94,15 +94,16 @@ static ParameterResolver forTripleStringOrPlaceholderParameter() {
}

/**
* Get a parameter resolver that resolves 2 parameters that each could be either a string constant or a placeholder.
* Get a parameter resolver that resolves 2 or 3 parameters that each could be either a string constant or a placeholder.
* <p>
* E.g.
* <ul>
* <li>("value", 'otherValue', 'optionalThirdValue')</li>
* <li>("value", 'otherValue')</li>
* </ul>
*/
static ParameterResolver forDoubleStringOrPlaceholderParameter() {
return DOUBLE_STRING_OR_PLACEHOLDER_PARAMETER_RESOLVER;
static ParameterResolver forDoubleOrTripleStringOrPlaceholderParameter() {
return DOUBLE_OR_TRIPLE_STRING_OR_PLACEHOLDER_PARAMETER_RESOLVER;
}

private PipelineFunctionParameterResolverFactory() {
Expand Down Expand Up @@ -143,30 +144,52 @@ static class ParameterResolver {
private static final String OPEN_PARENTHESIS = "\\(";
private static final String CLOSED_PARENTHESIS = "\\)";

static final String EXISTS_FUNCTION =
OPEN_PARENTHESIS + ".*"+ PARAMETER_SEPARATOR + "\\s*'exists'\\s*" + CLOSED_PARENTHESIS;

private final Pattern pattern;
private final int numberOfParameters;
private final int requiredParameters;
private final int optionalParameters;

private ParameterResolver(final int numberOfParameters, final boolean allowPlaceholders) {
this(numberOfParameters, 0, allowPlaceholders);
}

private ParameterResolver(final int requiredParameters, final int optionalParameters, final boolean allowPlaceholders) {

this.requiredParameters = requiredParameters;
this.optionalParameters = optionalParameters;

this.numberOfParameters = numberOfParameters;
final StringBuilder patternBuilder = new StringBuilder(OPEN_PARENTHESIS);

for (int parameterIndex = 0; parameterIndex < numberOfParameters; parameterIndex++) {
if (parameterIndex > 0) {
patternBuilder.append(PARAMETER_SEPARATOR);
}
final String parameterPattern = buildParameterPattern(parameterIndex, allowPlaceholders);
patternBuilder.append(parameterPattern);
}
patternBuilder.append(buildParameterPatterns(0, requiredParameters, allowPlaceholders, false));
patternBuilder.append(buildParameterPatterns(requiredParameters, optionalParameters, allowPlaceholders, true));

patternBuilder.append(CLOSED_PARENTHESIS);

pattern = Pattern.compile(patternBuilder.toString());
}

private static String buildParameterPatterns(final int startIndex, final int amount,
final boolean allowPlaceholders, final boolean optionalParameters) {
final StringBuilder parameters = new StringBuilder();
for (int parameterIndex = startIndex; parameterIndex < startIndex + amount; parameterIndex++) {
final StringBuilder singleParam = new StringBuilder();
if (optionalParameters) {
singleParam.append("(?:");
}

if (parameterIndex > 0) {
singleParam.append(PARAMETER_SEPARATOR);
}
final String parameterPattern = buildParameterPattern(parameterIndex, allowPlaceholders);
singleParam.append(parameterPattern);

if (optionalParameters) {
singleParam.append(")?");
}
parameters.append(singleParam);
}
return parameters.toString();
}

private static String buildParameterPattern(final int parameterIndex, final boolean allowPlaceholders) {
final String stringConstantParameterPattern = STRING_CONSTANT_PATTERN.format(new Object[]{parameterIndex});
if (allowPlaceholders) {
Expand Down Expand Up @@ -219,23 +242,34 @@ public List<PipelineElement> apply(final String paramsIncludingParentheses,
final Matcher matcher = this.pattern.matcher(paramsIncludingParentheses);

if (matcher.matches()) {
final ArrayList<PipelineElement> parameters = new ArrayList<>(numberOfParameters);
for (int parameterIndex = 0; parameterIndex < numberOfParameters; parameterIndex++) {
final PipelineElement resolvedParameter =
apply(matcher, parameterIndex, resolver).orElseThrow(() -> {
throw PlaceholderFunctionSignatureInvalidException.newBuilder(
paramsIncludingParentheses,
pipelineFunction).build();
});
parameters.add(parameterIndex, resolvedParameter);
}
final ArrayList<PipelineElement> parameters = new ArrayList<>(requiredParameters);
parameters.addAll(extractParameters(matcher, resolver, paramsIncludingParentheses, pipelineFunction,
0, requiredParameters, false));
parameters.addAll(extractParameters(matcher, resolver, paramsIncludingParentheses, pipelineFunction,
requiredParameters, optionalParameters, true));
return parameters;
}

throw PlaceholderFunctionSignatureInvalidException.newBuilder(paramsIncludingParentheses, pipelineFunction)
.build();
}

private List<PipelineElement> extractParameters(final Matcher matcher, final ExpressionResolver resolver,
final String paramsIncludingParentheses, final PipelineFunction pipelineFunction,
final int startIndex, final int amount, final boolean optional) {
final List<PipelineElement> parameters = new ArrayList<>(amount);
for (int parameterIndex = startIndex; parameterIndex < startIndex + amount; parameterIndex++) {
final Optional<PipelineElement> applied = apply(matcher, parameterIndex, resolver);
if (!optional && applied.isEmpty()) {
throw PlaceholderFunctionSignatureInvalidException.newBuilder(
paramsIncludingParentheses,
pipelineFunction).build();
}
applied.ifPresent(parameters::add);
}
return parameters;
}

}

static class SingleParameterResolver {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ public interface FilterFunction {
* @param parameters the parameters on which the decision should be made.
* @return true if condition succeeds, false if not.
*/
boolean apply(final String... parameters);
boolean apply(String... parameters);

}
Loading

0 comments on commit a44e91f

Please sign in to comment.