mirror of https://github.com/apache/nifi.git
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 <marcogaido91@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 <marcogaido91@gmail.com> Fixed `PaddingEvaluator` constructor issue Removed unused import This closes #3615 Signed-off-by: Mike Thomsen <mthomsen@apache.org>
This commit is contained in:
parent
0b74795578
commit
bb9758be2c
|
@ -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';
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue