mirror of
https://github.com/apache/nifi.git
synced 2025-03-04 00:19:44 +00:00
NIFI-6782: Added repeat() String EL function
NIFI-6782: Fixed intermittent unit test failure This closes #3825 Signed-off-by: Mike Thomsen <mthomsen@apache.org>
This commit is contained in:
parent
c72a5618c0
commit
f1be730c94
@ -182,6 +182,7 @@ JOIN : 'join';
|
||||
TO_LITERAL : 'literal';
|
||||
JSON_PATH : 'jsonPath';
|
||||
JSON_PATH_DELETE : 'jsonPathDelete';
|
||||
REPEAT : 'repeat';
|
||||
|
||||
// 2 arg functions
|
||||
SUBSTRING : 'substring';
|
||||
|
@ -79,7 +79,7 @@ oneArgString : ((SUBSTRING_BEFORE | SUBSTRING_BEFORE_LAST | SUBSTRING_AFTER | SU
|
||||
PREPEND | APPEND | STARTS_WITH | ENDS_WITH | CONTAINS | JOIN | JSON_PATH | JSON_PATH_DELETE | FROM_RADIX) LPAREN! anyArg RPAREN!) |
|
||||
(TO_RADIX LPAREN! anyArg (COMMA! anyArg)? RPAREN!);
|
||||
twoArgString : ((REPLACE | REPLACE_FIRST | REPLACE_ALL | IF_ELSE | JSON_PATH_SET | JSON_PATH_ADD) LPAREN! anyArg COMMA! anyArg RPAREN!) |
|
||||
((SUBSTRING | FORMAT | PAD_LEFT | PAD_RIGHT) LPAREN! anyArg (COMMA! anyArg)? RPAREN!);
|
||||
((SUBSTRING | FORMAT | PAD_LEFT | PAD_RIGHT | REPEAT) LPAREN! anyArg (COMMA! anyArg)? RPAREN!);
|
||||
threeArgString: ((JSON_PATH_PUT) LPAREN! anyArg COMMA! anyArg COMMA! anyArg RPAREN!);
|
||||
fiveArgString : GET_DELIMITED_FIELD LPAREN! anyArg (COMMA! anyArg (COMMA! anyArg (COMMA! anyArg (COMMA! anyArg)?)?)?)? RPAREN!;
|
||||
|
||||
|
@ -89,6 +89,7 @@ import org.apache.nifi.attribute.expression.language.evaluation.functions.PadRig
|
||||
import org.apache.nifi.attribute.expression.language.evaluation.functions.PlusEvaluator;
|
||||
import org.apache.nifi.attribute.expression.language.evaluation.functions.PrependEvaluator;
|
||||
import org.apache.nifi.attribute.expression.language.evaluation.functions.RandomNumberGeneratorEvaluator;
|
||||
import org.apache.nifi.attribute.expression.language.evaluation.functions.RepeatEvaluator;
|
||||
import org.apache.nifi.attribute.expression.language.evaluation.functions.ReplaceAllEvaluator;
|
||||
import org.apache.nifi.attribute.expression.language.evaluation.functions.ReplaceEmptyEvaluator;
|
||||
import org.apache.nifi.attribute.expression.language.evaluation.functions.ReplaceEvaluator;
|
||||
@ -208,6 +209,7 @@ import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpre
|
||||
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.PLUS;
|
||||
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.PREPEND;
|
||||
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.RANDOM;
|
||||
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.REPEAT;
|
||||
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.REPLACE;
|
||||
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.REPLACE_ALL;
|
||||
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.REPLACE_EMPTY;
|
||||
@ -721,6 +723,19 @@ public class ExpressionCompiler {
|
||||
throw new AttributeExpressionLanguageParsingException("substring() function can take either 1 or 2 arguments but cannot take " + numArgs + " arguments");
|
||||
}
|
||||
}
|
||||
case REPEAT: {
|
||||
final int numArgs = argEvaluators.size();
|
||||
if (numArgs == 1) {
|
||||
return addToken(new RepeatEvaluator(toStringEvaluator(subjectEvaluator),
|
||||
toWholeNumberEvaluator(argEvaluators.get(0), "first argument to repeat")), "repeat");
|
||||
} else if (numArgs == 2) {
|
||||
return addToken(new RepeatEvaluator(toStringEvaluator(subjectEvaluator),
|
||||
toWholeNumberEvaluator(argEvaluators.get(0), "first argument to repeat"),
|
||||
toWholeNumberEvaluator(argEvaluators.get(1), "second argument to repeat")), "repeat");
|
||||
} else {
|
||||
throw new AttributeExpressionLanguageParsingException("repeat() function can take either 1 or 2 arguments but cannot take " + numArgs + " arguments");
|
||||
}
|
||||
}
|
||||
case JOIN: {
|
||||
verifyArgCount(argEvaluators, 1, "join");
|
||||
return addToken(new JoinEvaluator(toStringEvaluator(subjectEvaluator), toStringEvaluator(argEvaluators.get(0))), "join");
|
||||
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.nifi.attribute.expression.language.evaluation.functions;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.attribute.expression.language.EvaluationContext;
|
||||
import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
|
||||
import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
|
||||
import org.apache.nifi.attribute.expression.language.evaluation.StringEvaluator;
|
||||
import org.apache.nifi.attribute.expression.language.evaluation.StringQueryResult;
|
||||
import org.apache.nifi.attribute.expression.language.exception.AttributeExpressionLanguageException;
|
||||
|
||||
public class RepeatEvaluator extends StringEvaluator {
|
||||
|
||||
private final Evaluator<String> subject;
|
||||
private final Evaluator<Long> minRepeats;
|
||||
private final Evaluator<Long> maxRepeats;
|
||||
|
||||
public RepeatEvaluator(final Evaluator<String> subject, final Evaluator<Long> minRepeats, final Evaluator<Long> maxRepeats) {
|
||||
this.subject = subject;
|
||||
this.minRepeats = minRepeats;
|
||||
this.maxRepeats = maxRepeats;
|
||||
}
|
||||
|
||||
public RepeatEvaluator(final Evaluator<String> subject, final Evaluator<Long> minRepeats) {
|
||||
this.subject = subject;
|
||||
this.minRepeats = minRepeats;
|
||||
this.maxRepeats = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryResult<String> evaluate(final EvaluationContext evaluationContext) {
|
||||
final String subjectValue = subject.evaluate(evaluationContext).getValue();
|
||||
if (subjectValue == null) {
|
||||
return new StringQueryResult("");
|
||||
}
|
||||
final int firstRepeatValue = minRepeats.evaluate(evaluationContext).getValue().intValue();
|
||||
if (maxRepeats == null) {
|
||||
if (firstRepeatValue <= 0) {
|
||||
throw new AttributeExpressionLanguageException("Number of repeats must be > 0");
|
||||
}
|
||||
return new StringQueryResult(StringUtils.repeat(subjectValue, firstRepeatValue));
|
||||
} else {
|
||||
if (firstRepeatValue <= 0) {
|
||||
throw new AttributeExpressionLanguageException("Minimum number of repeats must be > 0");
|
||||
}
|
||||
final int maxRepeatCount = maxRepeats.evaluate(evaluationContext).getValue().intValue();
|
||||
if (firstRepeatValue > maxRepeatCount) {
|
||||
throw new AttributeExpressionLanguageException("Min repeats must not be greater than max repeats");
|
||||
}
|
||||
final int randomRepeatCount = ((int) (Math.random() * (maxRepeatCount - firstRepeatValue + 1))) + firstRepeatValue;
|
||||
return new StringQueryResult(StringUtils.repeat(subjectValue, randomRepeatCount));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Evaluator<?> getSubjectEvaluator() {
|
||||
return subject;
|
||||
}
|
||||
}
|
@ -2098,6 +2098,45 @@ public class TestQuery {
|
||||
verifyEmpty("${nullString:padRight(10, \"@\")}", attributes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRepeat() {
|
||||
final Map<String, String> attributes = new HashMap<>();
|
||||
attributes.put("str", "abc");
|
||||
|
||||
verifyEquals("${not_exist:repeat(1, 2)}", attributes, "");
|
||||
verifyEquals("${str:repeat(1, 1)}", attributes, "abc");
|
||||
|
||||
// Custom verify because the result could be one of multiple options
|
||||
String multipleResultExpression = "${str:repeat(1, 3)}";
|
||||
String multipleResultExpectedResult1 = "abc";
|
||||
String multipleResultExpectedResult2 = "abcabc";
|
||||
String multipleResultExpectedResult3 = "abcabcabc";
|
||||
List<String> multipleResultExpectedResults = Arrays.asList(multipleResultExpectedResult1, multipleResultExpectedResult2, multipleResultExpectedResult3);
|
||||
Query.validateExpression(multipleResultExpression, false);
|
||||
final String actualResult = Query.evaluateExpressions(multipleResultExpression, attributes, null, null, ParameterLookup.EMPTY);
|
||||
assertTrue(multipleResultExpectedResults.contains(actualResult));
|
||||
|
||||
verifyEquals("${str:repeat(4)}", attributes, "abcabcabcabc");
|
||||
try {
|
||||
verifyEquals("${str:repeat(-1)}", attributes, "");
|
||||
fail("Should have failed on numRepeats < 0");
|
||||
} catch(AttributeExpressionLanguageException aele) {
|
||||
// Do nothing, it is expected
|
||||
}
|
||||
try {
|
||||
verifyEquals("${str:repeat(0)}", attributes, "");
|
||||
fail("Should have failed on numRepeats = 0");
|
||||
} catch(AttributeExpressionLanguageException aele) {
|
||||
// Do nothing, it is expected
|
||||
}
|
||||
try {
|
||||
verifyEquals("${str:repeat(2,1)}", attributes, "");
|
||||
fail("Should have failed on minRepeats > maxRepeats");
|
||||
} catch(AttributeExpressionLanguageException aele) {
|
||||
// Do nothing, it is expected
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyEquals(final String expression, final Map<String, String> attributes, final Object expectedResult) {
|
||||
verifyEquals(expression,attributes, null, ParameterLookup.EMPTY, expectedResult);
|
||||
}
|
||||
|
@ -1123,6 +1123,48 @@ Expressions will provide the following results:
|
||||
then the Expression `${query:evaluateELString()}` will return SELECT * FROM TABLE WHERE ID = 20
|
||||
|
||||
|
||||
|
||||
|
||||
[.function]
|
||||
=== repeat
|
||||
|
||||
*Description*:
|
||||
[.description]#Returns a string that is the Subject repeated a random number of times between _min repeats_ and
|
||||
_max repeats_. If _max repeats_ is not supplied, it will return the Subject repeated exactly _min repeats_ times.#
|
||||
|
||||
[.description]#The _min repeats_ and _max repeats_ must be positive numbers, with _max repeats_ greater than or equal
|
||||
to _min repeats_#
|
||||
|
||||
[.description]#If either _min repeats_ or _max repeats_ is not a number, this function call will result
|
||||
in an error.#
|
||||
|
||||
|
||||
*Subject Type*: [.subject]#String#
|
||||
|
||||
*Arguments*:
|
||||
|
||||
- [.argName]#_min repeats_# : [.argDesc]#The minimum number (inclusive) of times to repeat the subject, or the exact number
|
||||
of times to repeat the subject if _max repeats_ is not supplied.#
|
||||
- [.argName]#_max repeats_# : [.argDesc]#The maximum number (inclusive) of times to repeat the subject.#
|
||||
|
||||
*Return Type*: [.returnType]#String#
|
||||
|
||||
*Examples*:
|
||||
|
||||
If we have an attribute named "str" with the value "abc",
|
||||
then the following Expressions will result in the following values:
|
||||
|
||||
.Repeat Examples
|
||||
|================================================================
|
||||
| Expression | Value
|
||||
| `${str:repeat(1)}` | `abc`
|
||||
| `${str:repeat(2)}` | `abcabc`
|
||||
| `${str:repeat(1,2)}` | `abc` or `abcabc` (at random)
|
||||
| `${str:repeat( ${str:length()} )}` | `abc` or `abcabc` or `abcabcabc` (at random)
|
||||
|================================================================
|
||||
|
||||
|
||||
|
||||
[[encode]]
|
||||
== Encode/Decode Functions
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user