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/08/24 02:36:08 UTC

[nifi] branch master updated: NIFI-6500 (Original commit messages left to preserve authorship credit for multiple contributors)

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 bb9758b  NIFI-6500 (Original commit messages left to preserve authorship credit for multiple contributors)
bb9758b is described below

commit bb9758be2cc2f1dd3d58166025d55f37d770823d
Author: Alessandro D'Armiento <al...@agilelab.it>
AuthorDate: Tue Jul 30 15:05:32 2019 +0200

    NIFI-6500 (Original commit messages left to preserve authorship credit for multiple contributors)
    
    Expression Language now supports padding functions:
    - padLeft:
      -  attr:padLeft(Int n, Char c) will prepend to the `attr` attributes the `c` character until the size `n` is reached
      -  attr:padLeft(Int n) will prepend to the `attr` attributes the `'_'` character until the size `n` is reached
    - padRight:
      -  attr:padRight(Int n, Char c) will append to the `attr` attributes the `c` character until the size `n` is reached
      -  attr:padRight(Int n) will append to the `attr` attributes the `'_'` character until the size `n` is reached
    
    - In both cases, the padding function returns the `attr` `String` as is if its length is already equal of higher than the desired size `n`
    - Returns null if `attr` does not exist or in case desiredLenght is higher than Integer.MAX_INT
    
    Further test cases:
    - Returns null if the input string is null
    - Returns a string full of padding if the input string is empty
    Supports PaddingString instead of PaddingCharacter
    Apply suggestions from code review
    Applying style suggestions
    Co-Authored-By: Marco Gaido <ma...@gmail.com>
    style fixes
    style fixes
    Padding returns input string instead of null in case desired length is missing, is negative, or is overflowing
    Better tests
    doc update
    less verbose parser notation
    Doc and style fixes
    Fixed `StringEvaluator.evaluate()` after rebase
    Applying nitpicking suggestion
    Co-Authored-By: Marco Gaido <ma...@gmail.com>
    Fixed `PaddingEvaluator` constructor issue
    Removed unused import
    
    This closes #3615
    
    Signed-off-by: Mike Thomsen <mt...@apache.org>
---
 .../language/antlr/AttributeExpressionLexer.g      |  2 +
 .../language/antlr/AttributeExpressionParser.g     |  2 +-
 .../language/compile/ExpressionCompiler.java       | 26 ++++++++
 .../evaluation/functions/PadLeftEvaluator.java     | 37 ++++++++++++
 .../evaluation/functions/PadRightEvaluator.java    | 36 +++++++++++
 .../evaluation/functions/PaddingEvaluator.java     | 68 +++++++++++++++++++++
 .../attribute/expression/language/TestQuery.java   | 42 +++++++++++++
 .../main/asciidoc/expression-language-guide.adoc   | 70 ++++++++++++++++++++++
 8 files changed, 282 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 576210d..fb856b0 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
@@ -188,6 +188,8 @@ REPLACE	: 'replace';
 REPLACE_FIRST	: 'replaceFirst';
 REPLACE_ALL : 'replaceAll';
 IF_ELSE : 'ifElse';
+PAD_LEFT : 'padLeft';
+PAD_RIGHT : 'padRight';
 
 // 4 arg functions
 GET_DELIMITED_FIELD	: 'getDelimitedField';
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 f7e7b00..15f68e5 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) LPAREN! anyArg COMMA! anyArg RPAREN!) |
-			   ((SUBSTRING | FORMAT) LPAREN! anyArg (COMMA! anyArg)? RPAREN!);
+			   ((SUBSTRING | FORMAT | PAD_LEFT | PAD_RIGHT) LPAREN! anyArg (COMMA! anyArg)? RPAREN!);
 fiveArgString : GET_DELIMITED_FIELD LPAREN! anyArg (COMMA! anyArg (COMMA! anyArg (COMMA! anyArg (COMMA! anyArg)?)?)?)? RPAREN!;
 
 // functions that return Booleans
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 56d4571..63e3ba4 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
@@ -78,6 +78,8 @@ import org.apache.nifi.attribute.expression.language.evaluation.functions.NowEva
 import org.apache.nifi.attribute.expression.language.evaluation.functions.NumberToDateEvaluator;
 import org.apache.nifi.attribute.expression.language.evaluation.functions.OneUpSequenceEvaluator;
 import org.apache.nifi.attribute.expression.language.evaluation.functions.OrEvaluator;
+import org.apache.nifi.attribute.expression.language.evaluation.functions.PadLeftEvaluator;
+import org.apache.nifi.attribute.expression.language.evaluation.functions.PadRightEvaluator;
 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;
@@ -190,6 +192,8 @@ import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpre
 import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.NOT_NULL;
 import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.NOW;
 import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.OR;
+import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.PAD_LEFT;
+import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.PAD_RIGHT;
 import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.PARAMETER_REFERENCE;
 import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.PLUS;
 import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.PREPEND;
@@ -657,6 +661,28 @@ public class ExpressionCompiler {
                     toStringEvaluator(argEvaluators.get(0), "first argument to replaceAll"),
                     toStringEvaluator(argEvaluators.get(1), "second argument to replaceAll")), "replaceAll");
             }
+            case PAD_LEFT: {
+                if (argEvaluators.size() == 1) {
+                    return addToken(new PadLeftEvaluator(toStringEvaluator(subjectEvaluator),
+                        toWholeNumberEvaluator(argEvaluators.get(0), "desired string length")),
+                        "padLeft");
+                } else {
+                    return addToken(new PadLeftEvaluator(toStringEvaluator(subjectEvaluator),
+                        toWholeNumberEvaluator(argEvaluators.get(0), "desired string length"),
+                        toStringEvaluator(argEvaluators.get(1), "padding string")), "padLeft");
+                }
+            }
+            case PAD_RIGHT: {
+                if (argEvaluators.size() == 1) {
+                    return addToken(new PadRightEvaluator(toStringEvaluator(subjectEvaluator),
+                        toWholeNumberEvaluator(argEvaluators.get(0), "desired string length")),
+                        "padRight");
+                } else {
+                    return addToken(new PadRightEvaluator(toStringEvaluator(subjectEvaluator),
+                        toWholeNumberEvaluator(argEvaluators.get(0), "desired string length"),
+                        toStringEvaluator(argEvaluators.get(1), "padding string")), "padRight");
+                }
+            }
             case APPEND: {
                 verifyArgCount(argEvaluators, 1, "append");
                 return addToken(new AppendEvaluator(toStringEvaluator(subjectEvaluator),
diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/PadLeftEvaluator.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/PadLeftEvaluator.java
new file mode 100644
index 0000000..6212abe
--- /dev/null
+++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/PadLeftEvaluator.java
@@ -0,0 +1,37 @@
+/*
+ * 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.evaluation.Evaluator;
+
+
+public class PadLeftEvaluator extends PaddingEvaluator {
+
+    public PadLeftEvaluator(Evaluator<String> subject, Evaluator<Long> desiredLength, Evaluator<String> pad) {
+        super(subject, desiredLength, pad);
+    }
+
+    public PadLeftEvaluator(Evaluator<String> subject, Evaluator<Long> desiredLength) {
+        super(subject, desiredLength, null);
+    }
+
+    @Override
+    protected String doPad(String subjectValue, int desiredLengthValue, String padValue) {
+        return StringUtils.leftPad(subjectValue, desiredLengthValue, padValue);
+    }
+}
diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/PadRightEvaluator.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/PadRightEvaluator.java
new file mode 100644
index 0000000..7730e69
--- /dev/null
+++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/PadRightEvaluator.java
@@ -0,0 +1,36 @@
+/*
+ * 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.evaluation.Evaluator;
+
+public class PadRightEvaluator extends PaddingEvaluator {
+
+    public PadRightEvaluator(Evaluator<String> subject, Evaluator<Long> desiredLength, Evaluator<String> pad) {
+        super(subject, desiredLength, pad);
+    }
+
+    public PadRightEvaluator(Evaluator<String> subject, Evaluator<Long> desiredLength) {
+        super(subject, desiredLength, null);
+    }
+
+    @Override
+    protected String doPad(String subjectValue, int desiredLengthValue, String padValue) {
+        return StringUtils.rightPad(subjectValue, desiredLengthValue, padValue);
+    }
+}
diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/PaddingEvaluator.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/PaddingEvaluator.java
new file mode 100644
index 0000000..8de94fa
--- /dev/null
+++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/PaddingEvaluator.java
@@ -0,0 +1,68 @@
+/*
+ * 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.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.StringQueryResult;
+import org.apache.nifi.attribute.expression.language.evaluation.StringEvaluator;
+
+abstract class PaddingEvaluator extends StringEvaluator {
+
+    public static final String DEFAULT_PADDING_STRING = "_";
+
+    private final Evaluator<String> subject;
+    private final Evaluator<Long> desiredLength;
+    private final Evaluator<String> pad;
+
+    PaddingEvaluator(final Evaluator<String> subject, final Evaluator<Long> desiredLength, final Evaluator<String> pad) {
+        this.subject = subject;
+        this.desiredLength = desiredLength;
+        this.pad = pad;
+    }
+
+    @Override
+    public QueryResult<String> evaluate(EvaluationContext evaluationContext) {
+        final String subjectValue = subject.evaluate(evaluationContext).getValue();
+        if (subjectValue == null) {
+            return new StringQueryResult(null);
+        }
+        final Long desiredLengthValue = desiredLength.evaluate(evaluationContext).getValue();
+        if (desiredLengthValue == null || desiredLengthValue > Integer.MAX_VALUE || desiredLengthValue <= 0) {
+            return new StringQueryResult(subjectValue);
+        }
+
+        String padValue = DEFAULT_PADDING_STRING;
+        if (pad != null) {
+            String s = pad.evaluate(evaluationContext).getValue();
+            if (s != null && !s.isEmpty()) {
+                padValue = s;
+            }
+        }
+
+        return new StringQueryResult(doPad(subjectValue, desiredLengthValue.intValue(), padValue));
+    }
+
+    protected abstract String doPad(String subjectValue, int desiredLengthValue, String padValue);
+
+    @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 4080ca0..d971807 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
@@ -74,6 +74,10 @@ public class TestQuery {
         assertValid("${literal(3)}");
         assertValid("${random()}");
         assertValid("${getStateValue('the_count')}");
+        assertValid("${attr:padLeft(10, '#')}");
+        assertValid("${attr:padRight(10, '#')}");
+        assertValid("${attr:padLeft(10)}");
+        assertValid("${attr:padRight(10)}");
         // left here because it's convenient for looking at the output
         //System.out.println(Query.compile("").evaluate(null));
     }
@@ -1902,6 +1906,44 @@ public class TestQuery {
         verifyEquals("${thread()}", attributes, "main");
     }
 
+    @Test
+    public void testPadLeft() {
+        final Map<String, String> attributes = new HashMap<>();
+        attributes.put("attr", "hello");
+        attributes.put("emptyString", "");
+        attributes.put("nullString", null);
+
+        verifyEquals("${attr:padLeft(10, '@')}", attributes, "@@@@@hello");
+        verifyEquals("${attr:padLeft(10)}", attributes, "_____hello");
+        verifyEquals("${attr:padLeft(10, \"xy\")}", attributes, "xyxyxhello");
+        verifyEquals("${attr:padLeft(10, \"aVeryLongPaddingString\")}", attributes, "aVeryhello");
+        verifyEquals("${attr:padLeft(1, \"a\")}", attributes, "hello");
+        verifyEquals("${attr:padLeft(-10, \"a\")}", attributes, "hello");
+        verifyEquals("${emptyString:padLeft(10, '@')}", attributes, "@@@@@@@@@@");
+        verifyEquals("${attr:padLeft(9999999999, \"abc\")}", attributes, "hello");
+        verifyEmpty("${nonExistingAttr:padLeft(10, \"abc\")}", attributes);
+        verifyEmpty("${nullString:padLeft(10, \"@\")}", attributes);
+    }
+
+    @Test
+    public void testPadRight() {
+        final Map<String, String> attributes = new HashMap<>();
+        attributes.put("attr", "hello");
+        attributes.put("emptyString", "");
+        attributes.put("nullString", null);
+
+        verifyEquals("${attr:padRight(10, '@')}", attributes, "hello@@@@@");
+        verifyEquals("${attr:padRight(10)}", attributes, "hello_____");
+        verifyEquals("${attr:padRight(10, \"xy\")}", attributes, "helloxyxyx");
+        verifyEquals("${attr:padRight(10, \"aVeryLongPaddingString\")}", attributes, "helloaVery");
+        verifyEquals("${attr:padRight(1, \"a\")}", attributes, "hello");
+        verifyEquals("${attr:padRight(-10, \"a\")}", attributes, "hello");
+        verifyEquals("${emptyString:padRight(10, '@')}", attributes, "@@@@@@@@@@");
+        verifyEquals("${attr:padRight(9999999999, \"abc\")}", attributes, "hello");
+        verifyEmpty("${nonExistingAttr:padRight(10, \"abc\")}", attributes);
+        verifyEmpty("${nullString:padRight(10, \"@\")}", attributes);
+    }
+
     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 364b5ca..c6949c4 100644
--- a/nifi-docs/src/main/asciidoc/expression-language-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/expression-language-guide.adoc
@@ -977,6 +977,76 @@ Expressions will provide the following results:
 
 
 
+[.function]
+=== padLeft
+
+*Description*: [.description]#The `padLeft` function prepends the given padding string (or `'_'`, if nothing is provided) to the argument `String` until the passed desired length is reached.
+
+It returns the argument as is if its length is already equal or higher than the desired length, if the padding string is `null`, and if the desired length is either negative or greater than `Integer.MAX_VALUE`.
+It returns `null` if the argument string is not a valid attribute.
+
+*Subject Type*: [.subject]#String#
+
+*Arguments*:
+
+- [.argName]#_DesiredLength_# : [.argDesc]#The integer value to pad to.#
+- [.argName]#_PaddingString_# : [.argDesc]#The optional string to pad with. `"_"` will be used if a `PaddingString` is not provided. If the `PaddingString` is not an exact multiple of the actual pad size, it will be trimmed to fit in `DesiredLength`.#
+
+*Return Type*: [.returnType]#String#
+
+*Examples*: If the "greetings" attribute has the value "hello", then the following
+Expressions will provide the following results:
+
+
+
+.PadLeft Examples
+|=======================================================================================
+| Expression | Value
+| `${greetings:padLeft(10)}` | `\_____hello`
+| `${greetings:padLeft(10, '@')}` | `@@@@@hello`
+| `${greetings:padLeft(10, 'xy')}` | `xyxyxhello`
+| `${greetings:padLeft(10, 'aVeryLongPaddingString')}` | `aVeryhello`
+|=======================================================================================
+
+
+
+
+
+[.function]
+=== padRight
+
+*Description*: [.description]#The `padRight` function appends the given padding string (or `'_'`, if nothing is provided) to the argument `String` until the passed desired length is reached.
+
+It returns the argument as is if its length is already equal or higher than the desired length, if the padding string is `null`, and if the desired length is either negative or greater than `Integer.MAX_VALUE`.
+It returns `null` if the argument string is not a valid attribute.
+
+*Subject Type*: [.subject]#String#
+
+*Arguments*:
+
+- [.argName]#_DesiredLength_# : [.argDesc]#The integer value to pad to.#
+- [.argName]#_PaddingString_# : [.argDesc]#The optional string to pad with. `"_"` will be used if a `PaddingString` is not provided. If the `PaddingString` is not an exact multiple of the actual pad size, it will be trimmed to fit in `DesiredLength`.#
+
+*Return Type*: [.returnType]#String#
+
+*Examples*: If the "greetings" attribute has the value "hello", then the following
+Expressions will provide the following results:
+
+
+
+.PadLeft Examples
+|=======================================================================================
+| Expression | Value
+| `${greetings:padRight(10)}` | `hello\_____`
+| `${greetings:padRight(10, '@')}` | `hello@@@@@`
+| `${greetings:padRight(10, 'xy')}` | `helloxyxyx`
+| `${greetings:padLeft(10, 'aVeryLongPaddingString')}` | `helloaVery`
+|=======================================================================================
+
+
+
+
+
 
 [.function]
 === replaceNull