You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by mt...@apache.org on 2019/11/18 23:45:09 UTC
[nifi] branch master updated: NIFI-6782: Added repeat() String EL
function
This is an automated email from the ASF dual-hosted git repository.
mthomsen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/master by this push:
new f1be730 NIFI-6782: Added repeat() String EL function
f1be730 is described below
commit f1be730c9470e56e6042be8418cb03a70845eb69
Author: Matthew Burgess <ma...@apache.org>
AuthorDate: Thu Oct 17 15:37:14 2019 -0400
NIFI-6782: Added repeat() String EL function
NIFI-6782: Fixed intermittent unit test failure
This closes #3825
Signed-off-by: Mike Thomsen <mt...@apache.org>
---
.../language/antlr/AttributeExpressionLexer.g | 1 +
.../language/antlr/AttributeExpressionParser.g | 2 +-
.../language/compile/ExpressionCompiler.java | 15 +++++
.../evaluation/functions/RepeatEvaluator.java | 74 ++++++++++++++++++++++
.../attribute/expression/language/TestQuery.java | 39 ++++++++++++
.../main/asciidoc/expression-language-guide.adoc | 42 ++++++++++++
6 files changed, 172 insertions(+), 1 deletion(-)
diff --git a/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionLexer.g b/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionLexer.g
index 23e59fe..1684412 100644
--- a/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionLexer.g
+++ b/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionLexer.g
@@ -182,6 +182,7 @@ JOIN : 'join';
TO_LITERAL : 'literal';
JSON_PATH : 'jsonPath';
JSON_PATH_DELETE : 'jsonPathDelete';
+REPEAT : 'repeat';
// 2 arg functions
SUBSTRING : 'substring';
diff --git a/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g b/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g
index 71e7ce9..b2e9039 100644
--- a/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g
+++ b/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g
@@ -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!;
diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/ExpressionCompiler.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/ExpressionCompiler.java
index 3f438fb..4624058 100644
--- a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/ExpressionCompiler.java
+++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/ExpressionCompiler.java
@@ -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");
diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/RepeatEvaluator.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/RepeatEvaluator.java
new file mode 100644
index 0000000..d605f0d
--- /dev/null
+++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/RepeatEvaluator.java
@@ -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;
+ }
+}
diff --git a/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java b/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java
index 3753ee8..d48129d 100644
--- a/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java
+++ b/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java
@@ -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);
}
diff --git a/nifi-docs/src/main/asciidoc/expression-language-guide.adoc b/nifi-docs/src/main/asciidoc/expression-language-guide.adoc
index 6445aa3..70fb9d9 100644
--- a/nifi-docs/src/main/asciidoc/expression-language-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/expression-language-guide.adoc
@@ -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