NIFI-1662 adding Expression Language decimal support

This commit is contained in:
jpercivall 2016-09-13 17:40:58 -04:00 committed by Matt Burgess
parent 9b08f23b24
commit 94ab999026
35 changed files with 820 additions and 328 deletions

View File

@ -77,6 +77,6 @@ public interface AttributeExpression {
public static enum ResultType {
STRING, BOOLEAN, NUMBER, DATE;
STRING, BOOLEAN, WHOLE_NUMBER, DATE, DECIMAL, NUMBER;
}
}

View File

@ -77,7 +77,7 @@ COLON : ':';
COMMA : ',';
DOT : '.';
SEMICOLON : ';';
NUMBER : ('0'..'9')+;
WHOLE_NUMBER : ('0'..'9')+;
TRUE : 'true';
FALSE : 'false';
@ -112,6 +112,7 @@ IS_NULL : 'isNull';
IS_EMPTY : 'isEmpty';
NOT_NULL : 'notNull';
TO_NUMBER : 'toNumber';
TO_DECIMAL : 'toDecimal';
URL_ENCODE : 'urlEncode';
URL_DECODE : 'urlDecode';
NOT : 'not';

View File

@ -90,8 +90,8 @@ oneArgBool : ((FIND | MATCHES | EQUALS_IGNORE_CASE) LPAREN! anyArg RPAREN!) |
multiArgBool : (IN) LPAREN! anyArg (COMMA! anyArg)* RPAREN!;
// functions that return Numbers
zeroArgNum : (LENGTH | TO_NUMBER | COUNT) LPAREN! RPAREN!;
// functions that return Numbers (whole or decimal)
zeroArgNum : (LENGTH | TO_NUMBER | TO_DECIMAL | COUNT) LPAREN! RPAREN!;
oneArgNum : ((INDEX_OF | LAST_INDEX_OF) LPAREN! anyArg RPAREN!) |
(TO_DATE LPAREN! anyArg? RPAREN!) |
((MOD | PLUS | MINUS | MULTIPLY | DIVIDE) LPAREN! anyArg RPAREN!);
@ -100,7 +100,7 @@ stringFunctionRef : zeroArgString | oneArgString | twoArgString | fiveArgString;
booleanFunctionRef : zeroArgBool | oneArgBool | multiArgBool;
numberFunctionRef : zeroArgNum | oneArgNum;
anyArg : NUMBER | numberFunctionRef | STRING_LITERAL | zeroArgString | oneArgString | twoArgString | fiveArgString | booleanLiteral | zeroArgBool | oneArgBool | multiArgBool | expression;
anyArg : WHOLE_NUMBER | numberFunctionRef | STRING_LITERAL | zeroArgString | oneArgString | twoArgString | fiveArgString | booleanLiteral | zeroArgBool | oneArgBool | multiArgBool | expression;
stringArg : STRING_LITERAL | zeroArgString | oneArgString | twoArgString | expression;
functionRef : stringFunctionRef | booleanFunctionRef | numberFunctionRef;

View File

@ -1,90 +0,0 @@
/*
* 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.
*/
ALL_ATTRIBUTES=4
ALL_DELINEATED_VALUES=5
ALL_MATCHING_ATTRIBUTES=6
AND=7
ANY_ATTRIBUTE=8
ANY_DELINEATED_VALUE=9
ANY_MATCHING_ATTRIBUTE=10
APPEND=11
ATTRIBUTE_NAME=12
CEIL=13
COLON=14
COMMA=15
CONTAINS=16
DIVIDE=17
DOLLAR=18
DOT=19
ENDS_WITH=20
EQUALS=21
EQUALS_IGNORE_CASE=22
FALSE=23
FIND=24
FLOOR=25
FORMAT=26
GREATER_THAN=27
GREATER_THAN_OR_EQUAL=28
HOSTNAME=29
INDEX_OF=30
IP=31
IS_NULL=32
LAST_INDEX_OF=33
LBRACE=34
LENGTH=35
LESS_THAN=36
LESS_THAN_OR_EQUAL=37
LPAREN=38
MATCHES=39
MINUS=40
MOD=41
MULTIPLY=42
NEXT_INT=43
NOT=44
NOT_NULL=45
NOW=46
NUMBER=47
OR=48
PLUS=49
PREPEND=50
RBRACE=51
REPLACE=52
REPLACE_ALL=53
REPLACE_NULL=54
RPAREN=55
SEMICOLON=56
STARTS_WITH=57
STRING_LITERAL=58
SUBSTRING=59
SUBSTRING_AFTER=60
SUBSTRING_AFTER_LAST=61
SUBSTRING_BEFORE=62
SUBSTRING_BEFORE_LAST=63
TO_DATE=64
TO_LOWER=65
TO_NUMBER=66
TO_RADIX=67
TO_STRING=68
TO_UPPER=69
TRIM=70
TRUE=71
URL_DECODE=72
URL_ENCODE=73
UUID=74
WHITESPACE=75
BASE64_ENCODE=76
BASE64_DECODE=77

View File

@ -28,18 +28,18 @@ import org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionPa
import org.apache.nifi.attribute.expression.language.evaluation.BooleanEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.DateEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberEvaluator;
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.cast.BooleanCastEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.cast.DateCastEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.cast.DecimalCastEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.cast.NumberCastEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.cast.StringCastEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.cast.WholeNumberCastEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.functions.AndEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.functions.AppendEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.functions.AttributeEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.functions.ContainsEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.functions.DateToNumberEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.functions.DivideEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.functions.EndsWithEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.functions.EqualsEvaluator;
@ -87,7 +87,6 @@ import org.apache.nifi.attribute.expression.language.evaluation.functions.Substr
import org.apache.nifi.attribute.expression.language.evaluation.functions.SubstringBeforeLastEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.functions.SubstringEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.functions.ToLowerEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.functions.ToNumberEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.functions.ToRadixEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.functions.ToStringEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.functions.ToUpperEvaluator;
@ -98,9 +97,9 @@ import org.apache.nifi.attribute.expression.language.evaluation.functions.Base64
import org.apache.nifi.attribute.expression.language.evaluation.functions.Base64EncodeEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.functions.UuidEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.literals.BooleanLiteralEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.literals.NumberLiteralEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.literals.StringLiteralEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.literals.ToLiteralEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.literals.WholeNumberLiteralEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.reduce.CountEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.reduce.JoinEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.reduce.ReduceEvaluator;
@ -166,7 +165,8 @@ import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpre
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.NOT;
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.NUMBER;
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.TO_DECIMAL;
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.WHOLE_NUMBER;
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.OR;
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.PLUS;
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.PREPEND;
@ -674,8 +674,8 @@ public class Query {
case ATTR_NAME: {
return newStringLiteralEvaluator(tree.getChild(0).getText());
}
case NUMBER: {
return new NumberLiteralEvaluator(tree.getText());
case WHOLE_NUMBER: {
return new WholeNumberLiteralEvaluator(tree.getText());
}
case STRING_LITERAL: {
return newStringLiteralEvaluator(tree.getText());
@ -880,22 +880,63 @@ public class Query {
return toBooleanEvaluator(evaluator, null);
}
private static Evaluator<Long> toNumberEvaluator(final Evaluator<?> evaluator) {
private static Evaluator<Long> toWholeNumberEvaluator(final Evaluator<?> evaluator) {
return toWholeNumberEvaluator(evaluator, null);
}
@SuppressWarnings("unchecked")
private static Evaluator<Long> toWholeNumberEvaluator(final Evaluator<?> evaluator, final String location) {
switch (evaluator.getResultType()) {
case WHOLE_NUMBER:
return (Evaluator<Long>) evaluator;
case STRING:
case DATE:
case DECIMAL:
case NUMBER:
return addToken(new WholeNumberCastEvaluator(evaluator), evaluator.getToken());
default:
throw new AttributeExpressionLanguageParsingException("Cannot implicitly convert Data Type " + evaluator.getResultType() + " to " + ResultType.WHOLE_NUMBER
+ (location == null ? "" : " at location [" + location + "]"));
}
}
private static Evaluator<Double> toDecimalEvaluator(final Evaluator<?> evaluator) {
return toDecimalEvaluator(evaluator, null);
}
@SuppressWarnings("unchecked")
private static Evaluator<Double> toDecimalEvaluator(final Evaluator<?> evaluator, final String location) {
switch (evaluator.getResultType()) {
case DECIMAL:
return (Evaluator<Double>) evaluator;
case WHOLE_NUMBER:
case STRING:
case DATE:
case NUMBER:
return addToken(new DecimalCastEvaluator(evaluator), evaluator.getToken());
default:
throw new AttributeExpressionLanguageParsingException("Cannot implicitly convert Data Type " + evaluator.getResultType() + " to " + ResultType.DECIMAL
+ (location == null ? "" : " at location [" + location + "]"));
}
}
private static Evaluator<Number> toNumberEvaluator(final Evaluator<?> evaluator) {
return toNumberEvaluator(evaluator, null);
}
@SuppressWarnings("unchecked")
private static Evaluator<Long> toNumberEvaluator(final Evaluator<?> evaluator, final String location) {
private static Evaluator<Number> toNumberEvaluator(final Evaluator<?> evaluator, final String location) {
switch (evaluator.getResultType()) {
case NUMBER:
return (Evaluator<Long>) evaluator;
return (Evaluator<Number>) evaluator;
case STRING:
return addToken(new NumberCastEvaluator(evaluator), evaluator.getToken());
case DATE:
return addToken(new DateToNumberEvaluator((DateEvaluator) evaluator), evaluator.getToken());
case DECIMAL:
case WHOLE_NUMBER:
return addToken(new NumberCastEvaluator(evaluator), evaluator.getToken());
default:
throw new AttributeExpressionLanguageParsingException("Cannot implicitly convert Data Type " + evaluator.getResultType() + " to " + ResultType.NUMBER
+ (location == null ? "" : " at location [" + location + "]"));
throw new AttributeExpressionLanguageParsingException("Cannot implicitly convert Data Type " + evaluator.getResultType() + " to " + ResultType.WHOLE_NUMBER
+ (location == null ? "" : " at location [" + location + "]"));
}
}
@ -1046,11 +1087,11 @@ public class Query {
final int numArgs = argEvaluators.size();
if (numArgs == 1) {
return addToken(new SubstringEvaluator(toStringEvaluator(subjectEvaluator),
toNumberEvaluator(argEvaluators.get(0), "first argument to substring")), "substring");
toWholeNumberEvaluator(argEvaluators.get(0), "first argument to substring")), "substring");
} else if (numArgs == 2) {
return addToken(new SubstringEvaluator(toStringEvaluator(subjectEvaluator),
toNumberEvaluator(argEvaluators.get(0), "first argument to substring"),
toNumberEvaluator(argEvaluators.get(1), "second argument to substring")), "substring");
toWholeNumberEvaluator(argEvaluators.get(0), "first argument to substring"),
toWholeNumberEvaluator(argEvaluators.get(1), "second argument to substring")), "substring");
} else {
throw new AttributeExpressionLanguageParsingException("substring() function can take either 1 or 2 arguments but cannot take " + numArgs + " arguments");
}
@ -1142,29 +1183,48 @@ public class Query {
}
case TO_DATE: {
if (argEvaluators.isEmpty()) {
return addToken(new NumberToDateEvaluator(toNumberEvaluator(subjectEvaluator)), "toDate");
return addToken(new NumberToDateEvaluator(toWholeNumberEvaluator(subjectEvaluator)), "toDate");
} else if (subjectEvaluator.getResultType() == ResultType.STRING) {
return addToken(new StringToDateEvaluator(toStringEvaluator(subjectEvaluator), toStringEvaluator(argEvaluators.get(0))), "toDate");
} else {
return addToken(new NumberToDateEvaluator(toNumberEvaluator(subjectEvaluator)), "toDate");
return addToken(new NumberToDateEvaluator(toWholeNumberEvaluator(subjectEvaluator)), "toDate");
}
}
case TO_NUMBER: {
verifyArgCount(argEvaluators, 0, "toNumber");
switch (subjectEvaluator.getResultType()) {
case STRING:
return addToken(new ToNumberEvaluator((StringEvaluator) subjectEvaluator), "toNumber");
case WHOLE_NUMBER:
case DECIMAL:
case NUMBER:
case DATE:
return addToken(new DateToNumberEvaluator((DateEvaluator) subjectEvaluator), "toNumber");
return addToken(toWholeNumberEvaluator(subjectEvaluator), "toNumber");
default:
throw new AttributeExpressionLanguageParsingException(subjectEvaluator + " returns type " + subjectEvaluator.getResultType() + " but expected to get " + ResultType.STRING);
throw new AttributeExpressionLanguageParsingException(subjectEvaluator + " returns type " + subjectEvaluator.getResultType() + " but expected to get " + ResultType.STRING +
", " + ResultType.DECIMAL + ", or " + ResultType.DATE);
}
}
case TO_DECIMAL: {
verifyArgCount(argEvaluators, 0, "toDecimal");
switch (subjectEvaluator.getResultType()) {
case WHOLE_NUMBER:
case DECIMAL:
case STRING:
case NUMBER:
case DATE:
return addToken(toDecimalEvaluator(subjectEvaluator), "toDecimal");
default:
throw new AttributeExpressionLanguageParsingException(subjectEvaluator + " returns type " + subjectEvaluator.getResultType() + " but expected to get " + ResultType.STRING +
", " + ResultType.WHOLE_NUMBER + ", or " + ResultType.DATE);
}
}
case TO_RADIX: {
if (argEvaluators.size() == 1) {
return addToken(new ToRadixEvaluator((NumberEvaluator) subjectEvaluator, toNumberEvaluator(argEvaluators.get(0))), "toRadix");
return addToken(new ToRadixEvaluator(toWholeNumberEvaluator(subjectEvaluator),
toWholeNumberEvaluator(argEvaluators.get(0))), "toRadix");
} else {
return addToken(new ToRadixEvaluator((NumberEvaluator) subjectEvaluator, toNumberEvaluator(argEvaluators.get(0)), toNumberEvaluator(argEvaluators.get(1))), "toRadix");
return addToken(new ToRadixEvaluator(toWholeNumberEvaluator(subjectEvaluator),
toWholeNumberEvaluator(argEvaluators.get(0)), toWholeNumberEvaluator(argEvaluators.get(1))), "toRadix");
}
}
case MOD: {
@ -1211,24 +1271,24 @@ public class Query {
if (argEvaluators.size() == 1) {
// Only a single argument - the index to return.
return addToken(new GetDelimitedFieldEvaluator(toStringEvaluator(subjectEvaluator),
toNumberEvaluator(argEvaluators.get(0), "first argument of getDelimitedField")), "getDelimitedField");
toWholeNumberEvaluator(argEvaluators.get(0), "first argument of getDelimitedField")), "getDelimitedField");
} else if (argEvaluators.size() == 2) {
// two arguments - index and delimiter.
return addToken(new GetDelimitedFieldEvaluator(toStringEvaluator(subjectEvaluator),
toNumberEvaluator(argEvaluators.get(0), "first argument of getDelimitedField"),
toWholeNumberEvaluator(argEvaluators.get(0), "first argument of getDelimitedField"),
toStringEvaluator(argEvaluators.get(1), "second argument of getDelimitedField")),
"getDelimitedField");
} else if (argEvaluators.size() == 3) {
// 3 arguments - index, delimiter, quote char.
return addToken(new GetDelimitedFieldEvaluator(toStringEvaluator(subjectEvaluator),
toNumberEvaluator(argEvaluators.get(0), "first argument of getDelimitedField"),
toWholeNumberEvaluator(argEvaluators.get(0), "first argument of getDelimitedField"),
toStringEvaluator(argEvaluators.get(1), "second argument of getDelimitedField"),
toStringEvaluator(argEvaluators.get(2), "third argument of getDelimitedField")),
"getDelimitedField");
} else if (argEvaluators.size() == 4) {
// 4 arguments - index, delimiter, quote char, escape char
return addToken(new GetDelimitedFieldEvaluator(toStringEvaluator(subjectEvaluator),
toNumberEvaluator(argEvaluators.get(0), "first argument of getDelimitedField"),
toWholeNumberEvaluator(argEvaluators.get(0), "first argument of getDelimitedField"),
toStringEvaluator(argEvaluators.get(1), "second argument of getDelimitedField"),
toStringEvaluator(argEvaluators.get(2), "third argument of getDelimitedField"),
toStringEvaluator(argEvaluators.get(3), "fourth argument of getDelimitedField")),
@ -1236,7 +1296,7 @@ public class Query {
} else {
// 5 arguments - index, delimiter, quote char, escape char, strip escape/quote chars flag
return addToken(new GetDelimitedFieldEvaluator(toStringEvaluator(subjectEvaluator),
toNumberEvaluator(argEvaluators.get(0), "first argument of getDelimitedField"),
toWholeNumberEvaluator(argEvaluators.get(0), "first argument of getDelimitedField"),
toStringEvaluator(argEvaluators.get(1), "second argument of getDelimitedField"),
toStringEvaluator(argEvaluators.get(2), "third argument of getDelimitedField"),
toStringEvaluator(argEvaluators.get(3), "fourth argument of getDelimitedField"),

View File

@ -0,0 +1,43 @@
/*
* 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;
import org.apache.nifi.expression.AttributeExpression.ResultType;
public abstract class DecimalEvaluator implements Evaluator<Double> {
private String token;
@Override
public ResultType getResultType() {
return ResultType.DECIMAL;
}
@Override
public int getEvaluationsRemaining() {
return 0;
}
@Override
public String getToken() {
return token;
}
@Override
public void setToken(final String token) {
this.token = token;
}
}

View File

@ -14,32 +14,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.attribute.expression.language.evaluation.functions;
package org.apache.nifi.attribute.expression.language.evaluation;
import java.util.Map;
import org.apache.nifi.expression.AttributeExpression.ResultType;
import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberQueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
public class DecimalQueryResult implements QueryResult<Double> {
public class ToNumberEvaluator extends NumberEvaluator {
private final Double value;
private final Evaluator<String> subject;
public ToNumberEvaluator(final Evaluator<String> subject) {
this.subject = subject;
public DecimalQueryResult(final Double value) {
this.value = value;
}
@Override
public QueryResult<Long> evaluate(final Map<String, String> attributes) {
final String subjectValue = subject.evaluate(attributes).getValue();
return new NumberQueryResult(subjectValue == null || subjectValue.trim().isEmpty() ? null : Long.valueOf(subjectValue));
public Double getValue() {
return value;
}
@Override
public Evaluator<?> getSubjectEvaluator() {
return subject;
public ResultType getResultType() {
return ResultType.DECIMAL;
}
@Override
public String toString() {
return String.valueOf(getValue());
}
}

View File

@ -18,7 +18,7 @@ package org.apache.nifi.attribute.expression.language.evaluation;
import org.apache.nifi.expression.AttributeExpression.ResultType;
public abstract class NumberEvaluator implements Evaluator<Long> {
public abstract class NumberEvaluator implements Evaluator<Number> {
private String token;
@Override

View File

@ -18,16 +18,16 @@ package org.apache.nifi.attribute.expression.language.evaluation;
import org.apache.nifi.expression.AttributeExpression.ResultType;
public class NumberQueryResult implements QueryResult<Long> {
public class NumberQueryResult implements QueryResult<Number> {
private final Long value;
private final Number value;
public NumberQueryResult(final Long value) {
public NumberQueryResult(final Number value) {
this.value = value;
}
@Override
public Long getValue() {
public Number getValue() {
return value;
}

View File

@ -0,0 +1,43 @@
/*
* 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;
import org.apache.nifi.expression.AttributeExpression.ResultType;
public abstract class WholeNumberEvaluator implements Evaluator<Long> {
private String token;
@Override
public ResultType getResultType() {
return ResultType.WHOLE_NUMBER;
}
@Override
public int getEvaluationsRemaining() {
return 0;
}
@Override
public String getToken() {
return token;
}
@Override
public void setToken(final String token) {
this.token = token;
}
}

View File

@ -0,0 +1,43 @@
/*
* 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;
import org.apache.nifi.expression.AttributeExpression.ResultType;
public class WholeNumberQueryResult implements QueryResult<Long> {
private final Long value;
public WholeNumberQueryResult(final Long value) {
this.value = value;
}
@Override
public Long getValue() {
return value;
}
@Override
public ResultType getResultType() {
return ResultType.WHOLE_NUMBER;
}
@Override
public String toString() {
return String.valueOf(getValue());
}
}

View File

@ -27,6 +27,7 @@ import java.util.regex.Pattern;
import org.apache.nifi.attribute.expression.language.evaluation.DateEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.DateQueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberQueryResult;
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.exception.AttributeExpressionLanguageException;
@ -102,8 +103,14 @@ public class DateCastEvaluator extends DateEvaluator {
throw new AttributeExpressionLanguageException("Could not implicitly convert input to DATE: " + value);
}
}
case NUMBER:
case WHOLE_NUMBER:
return new DateQueryResult(new Date((Long) result.getValue()));
case DECIMAL:
Double resultDouble = (Double) result.getValue();
return new DateQueryResult(new Date(resultDouble.longValue()));
case NUMBER:
final Number numberValue = ((NumberQueryResult) result).getValue();
return new DateQueryResult(new Date(numberValue.longValue()));
default:
return new DateQueryResult(null);
}

View File

@ -0,0 +1,86 @@
/*
* 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.cast;
import org.apache.nifi.attribute.expression.language.evaluation.DateQueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.DecimalEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.DecimalQueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberQueryResult;
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.WholeNumberQueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.util.NumberParsing;
import org.apache.nifi.attribute.expression.language.exception.AttributeExpressionLanguageParsingException;
import org.apache.nifi.expression.AttributeExpression.ResultType;
import java.util.Map;
public class DecimalCastEvaluator extends DecimalEvaluator {
private final Evaluator<?> subjectEvaluator;
public DecimalCastEvaluator(final Evaluator<?> subjectEvaluator) {
if (subjectEvaluator.getResultType() == ResultType.BOOLEAN) {
throw new AttributeExpressionLanguageParsingException("Cannot implicitly convert Data Type " + subjectEvaluator.getResultType() + " to " + ResultType.DECIMAL);
}
this.subjectEvaluator = subjectEvaluator;
}
@Override
public QueryResult<Double> evaluate(final Map<String, String> attributes) {
final QueryResult<?> result = subjectEvaluator.evaluate(attributes);
if (result.getValue() == null) {
return new DecimalQueryResult(null);
}
switch (result.getResultType()) {
case DECIMAL:
return (DecimalQueryResult) result;
case STRING:
final String trimmed = ((StringQueryResult) result).getValue().trim();
NumberParsing.ParseResultType parseType = NumberParsing.parse(trimmed);
switch (parseType){
case DECIMAL:
return new DecimalQueryResult(Double.valueOf(trimmed));
case WHOLE_NUMBER:
final Long resultValue = Long.valueOf(trimmed);
return new DecimalQueryResult(resultValue.doubleValue());
case NOT_NUMBER:
default:
return new DecimalQueryResult(null);
}
case DATE:
Long timestamp = ((DateQueryResult) result).getValue().getTime();
return new DecimalQueryResult(timestamp.doubleValue());
case WHOLE_NUMBER:
final Long resultValue = ((WholeNumberQueryResult) result).getValue();
return new DecimalQueryResult(resultValue.doubleValue());
case NUMBER:
final Number numberValue = ((NumberQueryResult) result).getValue();
return new DecimalQueryResult(numberValue.doubleValue());
default:
return new DecimalQueryResult(null);
}
}
@Override
public Evaluator<?> getSubjectEvaluator() {
return subjectEvaluator;
}
}

View File

@ -16,32 +16,33 @@
*/
package org.apache.nifi.attribute.expression.language.evaluation.cast;
import java.util.Map;
import java.util.regex.Pattern;
import org.apache.nifi.attribute.expression.language.evaluation.DateQueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.DecimalQueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberQueryResult;
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.WholeNumberQueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.util.NumberParsing;
import org.apache.nifi.attribute.expression.language.exception.AttributeExpressionLanguageParsingException;
import org.apache.nifi.expression.AttributeExpression.ResultType;
import java.util.Map;
public class NumberCastEvaluator extends NumberEvaluator {
private final Evaluator<?> subjectEvaluator;
private static final Pattern NUMBER_PATTERN = Pattern.compile("-?\\d+");
public NumberCastEvaluator(final Evaluator<?> subjectEvaluator) {
if (subjectEvaluator.getResultType() == ResultType.BOOLEAN) {
throw new AttributeExpressionLanguageParsingException("Cannot implicitly convert Data Type " + subjectEvaluator.getResultType() + " to " + ResultType.NUMBER);
throw new AttributeExpressionLanguageParsingException("Cannot implicitly convert Data Type " + subjectEvaluator.getResultType() + " to " + ResultType.WHOLE_NUMBER);
}
this.subjectEvaluator = subjectEvaluator;
}
@Override
public QueryResult<Long> evaluate(final Map<String, String> attributes) {
public QueryResult<Number> evaluate(final Map<String, String> attributes) {
final QueryResult<?> result = subjectEvaluator.evaluate(attributes);
if (result.getValue() == null) {
return new NumberQueryResult(null);
@ -50,12 +51,24 @@ public class NumberCastEvaluator extends NumberEvaluator {
switch (result.getResultType()) {
case NUMBER:
return (NumberQueryResult) result;
case WHOLE_NUMBER:
Long longValue = ((WholeNumberQueryResult) result).getValue();
return new NumberQueryResult(longValue);
case DECIMAL:
Double doubleValue = ((DecimalQueryResult) result).getValue();
return new NumberQueryResult(doubleValue);
case STRING:
final String trimmed = ((StringQueryResult) result).getValue().trim();
if (NUMBER_PATTERN.matcher(trimmed).matches()) {
return new NumberQueryResult(Long.valueOf(trimmed));
} else {
return new NumberQueryResult(null);
NumberParsing.ParseResultType parseType = NumberParsing.parse(trimmed);
switch (parseType){
case DECIMAL:
return new NumberQueryResult(Double.valueOf(trimmed));
case WHOLE_NUMBER:
final Long resultValue = Long.valueOf(trimmed);
return new NumberQueryResult(Long.valueOf(trimmed));
case NOT_NUMBER:
default:
return new NumberQueryResult(null);
}
case DATE:
return new NumberQueryResult(((DateQueryResult) result).getValue().getTime());

View File

@ -0,0 +1,85 @@
/*
* 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.cast;
import java.util.Map;
import org.apache.nifi.attribute.expression.language.evaluation.DateQueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.DecimalQueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberQueryResult;
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.WholeNumberEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.WholeNumberQueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.util.NumberParsing;
import org.apache.nifi.attribute.expression.language.exception.AttributeExpressionLanguageParsingException;
import org.apache.nifi.expression.AttributeExpression.ResultType;
public class WholeNumberCastEvaluator extends WholeNumberEvaluator {
private final Evaluator<?> subjectEvaluator;
public WholeNumberCastEvaluator(final Evaluator<?> subjectEvaluator) {
if (subjectEvaluator.getResultType() == ResultType.BOOLEAN) {
throw new AttributeExpressionLanguageParsingException("Cannot implicitly convert Data Type " + subjectEvaluator.getResultType() + " to " + ResultType.WHOLE_NUMBER);
}
this.subjectEvaluator = subjectEvaluator;
}
@Override
public QueryResult<Long> evaluate(final Map<String, String> attributes) {
final QueryResult<?> result = subjectEvaluator.evaluate(attributes);
if (result.getValue() == null) {
return new WholeNumberQueryResult(null);
}
switch (result.getResultType()) {
case WHOLE_NUMBER:
return (WholeNumberQueryResult) result;
case STRING:
final String trimmed = ((StringQueryResult) result).getValue().trim();
NumberParsing.ParseResultType parseType = NumberParsing.parse(trimmed);
switch (parseType){
case DECIMAL:
final Double resultValue = Double.valueOf(trimmed);
return new WholeNumberQueryResult(resultValue.longValue());
case WHOLE_NUMBER:
return new WholeNumberQueryResult(Long.valueOf(trimmed));
case NOT_NUMBER:
default:
return new WholeNumberQueryResult(null);
}
case DATE:
return new WholeNumberQueryResult(((DateQueryResult) result).getValue().getTime());
case DECIMAL:
final Double resultValue = ((DecimalQueryResult) result).getValue();
return new WholeNumberQueryResult(resultValue.longValue());
case NUMBER:
final Number numberValue = ((NumberQueryResult) result).getValue();
return new WholeNumberQueryResult(numberValue.longValue());
default:
return new WholeNumberQueryResult(null);
}
}
@Override
public Evaluator<?> getSubjectEvaluator() {
return subjectEvaluator;
}
}

View File

@ -1,50 +0,0 @@
/*
* 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 java.util.Date;
import java.util.Map;
import org.apache.nifi.attribute.expression.language.evaluation.DateEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberQueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
public class DateToNumberEvaluator extends NumberEvaluator {
private final DateEvaluator subjectEvaluator;
public DateToNumberEvaluator(final DateEvaluator subjectEvaluator) {
this.subjectEvaluator = subjectEvaluator;
}
@Override
public QueryResult<Long> evaluate(final Map<String, String> attributes) {
final QueryResult<Date> subjectResult = subjectEvaluator.evaluate(attributes);
if (subjectResult.getValue() == null) {
return new NumberQueryResult(null);
}
return new NumberQueryResult(subjectResult.getValue().getTime());
}
@Override
public Evaluator<?> getSubjectEvaluator() {
return subjectEvaluator;
}
}

View File

@ -25,27 +25,32 @@ import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
public class DivideEvaluator extends NumberEvaluator {
private final Evaluator<Long> subject;
private final Evaluator<Long> divideValue;
private final Evaluator<Number> subject;
private final Evaluator<Number> divideValue;
public DivideEvaluator(final Evaluator<Long> subject, final Evaluator<Long> divideValue) {
public DivideEvaluator(final Evaluator<Number> subject, final Evaluator<Number> divideValue) {
this.subject = subject;
this.divideValue = divideValue;
}
@Override
public QueryResult<Long> evaluate(final Map<String, String> attributes) {
final Long subjectValue = subject.evaluate(attributes).getValue();
public QueryResult<Number> evaluate(final Map<String, String> attributes) {
final Number subjectValue = subject.evaluate(attributes).getValue();
if (subjectValue == null) {
return new NumberQueryResult(null);
}
final Long divide = divideValue.evaluate(attributes).getValue();
final Number divide = divideValue.evaluate(attributes).getValue();
if (divide == null) {
return new NumberQueryResult(null);
}
final long result = subjectValue / divide;
final Number result;
if (subjectValue instanceof Double || divide instanceof Double){
result = subjectValue.doubleValue() / divide.doubleValue();
} else {
result = subjectValue.longValue() / divide.longValue();
}
return new NumberQueryResult(result);
}

View File

@ -25,27 +25,31 @@ import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
public class GreaterThanEvaluator extends BooleanEvaluator {
private final Evaluator<Long> subject;
private final Evaluator<Long> comparison;
private final Evaluator<Number> subject;
private final Evaluator<Number> comparison;
public GreaterThanEvaluator(final Evaluator<Long> subject, final Evaluator<Long> comparison) {
public GreaterThanEvaluator(final Evaluator<Number> subject, final Evaluator<Number> comparison) {
this.subject = subject;
this.comparison = comparison;
}
@Override
public QueryResult<Boolean> evaluate(final Map<String, String> attributes) {
final Long subjectValue = subject.evaluate(attributes).getValue();
final Number subjectValue = subject.evaluate(attributes).getValue();
if (subjectValue == null) {
return new BooleanQueryResult(false);
}
final Long comparisonValue = comparison.evaluate(attributes).getValue();
final Number comparisonValue = comparison.evaluate(attributes).getValue();
if (comparisonValue == null) {
return new BooleanQueryResult(false);
}
return new BooleanQueryResult(subjectValue > comparisonValue);
if (subjectValue instanceof Double || comparisonValue instanceof Double){
return new BooleanQueryResult(subjectValue.doubleValue() > comparisonValue.doubleValue());
} else {
return new BooleanQueryResult(subjectValue.longValue() > comparisonValue.longValue());
}
}
@Override

View File

@ -25,27 +25,31 @@ import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
public class GreaterThanOrEqualEvaluator extends BooleanEvaluator {
private final Evaluator<Long> subject;
private final Evaluator<Long> comparison;
private final Evaluator<Number> subject;
private final Evaluator<Number> comparison;
public GreaterThanOrEqualEvaluator(final Evaluator<Long> subject, final Evaluator<Long> comparison) {
public GreaterThanOrEqualEvaluator(final Evaluator<Number> subject, final Evaluator<Number> comparison) {
this.subject = subject;
this.comparison = comparison;
}
@Override
public QueryResult<Boolean> evaluate(final Map<String, String> attributes) {
final Long subjectValue = subject.evaluate(attributes).getValue();
final Number subjectValue = subject.evaluate(attributes).getValue();
if (subjectValue == null) {
return new BooleanQueryResult(false);
}
final Long comparisonValue = comparison.evaluate(attributes).getValue();
final Number comparisonValue = comparison.evaluate(attributes).getValue();
if (comparisonValue == null) {
return new BooleanQueryResult(false);
}
return new BooleanQueryResult(subjectValue >= comparisonValue);
if (subjectValue instanceof Double || comparisonValue instanceof Double){
return new BooleanQueryResult(subjectValue.doubleValue() >= comparisonValue.doubleValue());
} else {
return new BooleanQueryResult(subjectValue.longValue() >= comparisonValue.longValue());
}
}
@Override

View File

@ -19,11 +19,11 @@ package org.apache.nifi.attribute.expression.language.evaluation.functions;
import java.util.Map;
import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberQueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.WholeNumberEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.WholeNumberQueryResult;
public class IndexOfEvaluator extends NumberEvaluator {
public class IndexOfEvaluator extends WholeNumberEvaluator {
private final Evaluator<String> subject;
private final Evaluator<String> indexEvaluator;
@ -37,11 +37,11 @@ public class IndexOfEvaluator extends NumberEvaluator {
public QueryResult<Long> evaluate(final Map<String, String> attributes) {
final String subjectValue = subject.evaluate(attributes).getValue();
if (subjectValue == null) {
return new NumberQueryResult(-1L);
return new WholeNumberQueryResult(-1L);
}
final String indexEvalValue = indexEvaluator.evaluate(attributes).getValue();
return new NumberQueryResult((long) subjectValue.indexOf(indexEvalValue));
return new WholeNumberQueryResult((long) subjectValue.indexOf(indexEvalValue));
}
@Override

View File

@ -19,11 +19,11 @@ package org.apache.nifi.attribute.expression.language.evaluation.functions;
import java.util.Map;
import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberQueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.WholeNumberEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.WholeNumberQueryResult;
public class LastIndexOfEvaluator extends NumberEvaluator {
public class LastIndexOfEvaluator extends WholeNumberEvaluator {
private final Evaluator<String> subject;
private final Evaluator<String> indexEvaluator;
@ -37,11 +37,11 @@ public class LastIndexOfEvaluator extends NumberEvaluator {
public QueryResult<Long> evaluate(final Map<String, String> attributes) {
final String subjectValue = subject.evaluate(attributes).getValue();
if (subjectValue == null) {
return new NumberQueryResult(-1L);
return new WholeNumberQueryResult(-1L);
}
final String indexEvalValue = indexEvaluator.evaluate(attributes).getValue();
return new NumberQueryResult((long) subjectValue.lastIndexOf(indexEvalValue));
return new WholeNumberQueryResult((long) subjectValue.lastIndexOf(indexEvalValue));
}
@Override

View File

@ -19,11 +19,11 @@ package org.apache.nifi.attribute.expression.language.evaluation.functions;
import java.util.Map;
import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberQueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.WholeNumberEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.WholeNumberQueryResult;
public class LengthEvaluator extends NumberEvaluator {
public class LengthEvaluator extends WholeNumberEvaluator {
private final Evaluator<String> subject;
@ -34,7 +34,7 @@ public class LengthEvaluator extends NumberEvaluator {
@Override
public QueryResult<Long> evaluate(final Map<String, String> attributes) {
final String subjectValue = subject.evaluate(attributes).getValue();
return new NumberQueryResult((long) (subjectValue == null ? 0 : subjectValue.length()));
return new WholeNumberQueryResult((long) (subjectValue == null ? 0 : subjectValue.length()));
}
@Override

View File

@ -25,27 +25,31 @@ import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
public class LessThanEvaluator extends BooleanEvaluator {
private final Evaluator<Long> subject;
private final Evaluator<Long> comparison;
private final Evaluator<Number> subject;
private final Evaluator<Number> comparison;
public LessThanEvaluator(final Evaluator<Long> subject, final Evaluator<Long> comparison) {
public LessThanEvaluator(final Evaluator<Number> subject, final Evaluator<Number> comparison) {
this.subject = subject;
this.comparison = comparison;
}
@Override
public QueryResult<Boolean> evaluate(final Map<String, String> attributes) {
final Long subjectValue = subject.evaluate(attributes).getValue();
final Number subjectValue = subject.evaluate(attributes).getValue();
if (subjectValue == null) {
return new BooleanQueryResult(false);
}
final Long comparisonValue = comparison.evaluate(attributes).getValue();
final Number comparisonValue = comparison.evaluate(attributes).getValue();
if (comparisonValue == null) {
return new BooleanQueryResult(false);
}
return new BooleanQueryResult(subjectValue < comparisonValue);
if (subjectValue instanceof Double || comparisonValue instanceof Double){
return new BooleanQueryResult(subjectValue.doubleValue() < comparisonValue.doubleValue());
} else {
return new BooleanQueryResult(subjectValue.longValue() < comparisonValue.longValue());
}
}
@Override

View File

@ -25,27 +25,31 @@ import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
public class LessThanOrEqualEvaluator extends BooleanEvaluator {
private final Evaluator<Long> subject;
private final Evaluator<Long> comparison;
private final Evaluator<Number> subject;
private final Evaluator<Number> comparison;
public LessThanOrEqualEvaluator(final Evaluator<Long> subject, final Evaluator<Long> comparison) {
public LessThanOrEqualEvaluator(final Evaluator<Number> subject, final Evaluator<Number> comparison) {
this.subject = subject;
this.comparison = comparison;
}
@Override
public QueryResult<Boolean> evaluate(final Map<String, String> attributes) {
final Long subjectValue = subject.evaluate(attributes).getValue();
final Number subjectValue = subject.evaluate(attributes).getValue();
if (subjectValue == null) {
return new BooleanQueryResult(false);
}
final Long comparisonValue = comparison.evaluate(attributes).getValue();
final Number comparisonValue = comparison.evaluate(attributes).getValue();
if (comparisonValue == null) {
return new BooleanQueryResult(false);
}
return new BooleanQueryResult(subjectValue <= comparisonValue);
if (subjectValue instanceof Double || comparisonValue instanceof Double){
return new BooleanQueryResult(subjectValue.doubleValue() <= comparisonValue.doubleValue());
} else {
return new BooleanQueryResult(subjectValue.longValue() <= comparisonValue.longValue());
}
}
@Override

View File

@ -25,27 +25,32 @@ import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
public class MinusEvaluator extends NumberEvaluator {
private final Evaluator<Long> subject;
private final Evaluator<Long> minusValue;
private final Evaluator<Number> subject;
private final Evaluator<Number> minusValue;
public MinusEvaluator(final Evaluator<Long> subject, final Evaluator<Long> minusValue) {
public MinusEvaluator(final Evaluator<Number> subject, final Evaluator<Number> minusValue) {
this.subject = subject;
this.minusValue = minusValue;
}
@Override
public QueryResult<Long> evaluate(final Map<String, String> attributes) {
final Long subjectValue = subject.evaluate(attributes).getValue();
public QueryResult<Number> evaluate(final Map<String, String> attributes) {
final Number subjectValue = subject.evaluate(attributes).getValue();
if (subjectValue == null) {
return new NumberQueryResult(null);
}
final Long minus = minusValue.evaluate(attributes).getValue();
final Number minus = minusValue.evaluate(attributes).getValue();
if (minus == null) {
return new NumberQueryResult(null);
}
final long result = subjectValue - minus;
final Number result;
if (subjectValue instanceof Double || minus instanceof Double){
result = subjectValue.doubleValue() - minus.doubleValue();
} else {
result = subjectValue.longValue() - minus.longValue();
}
return new NumberQueryResult(result);
}

View File

@ -25,27 +25,32 @@ import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
public class ModEvaluator extends NumberEvaluator {
private final Evaluator<Long> subject;
private final Evaluator<Long> modValue;
private final Evaluator<Number> subject;
private final Evaluator<Number> modValue;
public ModEvaluator(final Evaluator<Long> subject, final Evaluator<Long> modValue) {
public ModEvaluator(final Evaluator<Number> subject, final Evaluator<Number> modValue) {
this.subject = subject;
this.modValue = modValue;
}
@Override
public QueryResult<Long> evaluate(final Map<String, String> attributes) {
final Long subjectValue = subject.evaluate(attributes).getValue();
public QueryResult<Number> evaluate(final Map<String, String> attributes) {
final Number subjectValue = subject.evaluate(attributes).getValue();
if (subjectValue == null) {
return new NumberQueryResult(null);
}
final Long mod = modValue.evaluate(attributes).getValue();
final Number mod = modValue.evaluate(attributes).getValue();
if (mod == null) {
return new NumberQueryResult(null);
}
final long result = subjectValue % mod;
final Number result;
if (subjectValue instanceof Double || mod instanceof Double){
result = subjectValue.doubleValue() % mod.doubleValue();
} else {
result = subjectValue.longValue() % mod.longValue();
}
return new NumberQueryResult(result);
}

View File

@ -25,27 +25,32 @@ import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
public class MultiplyEvaluator extends NumberEvaluator {
private final Evaluator<Long> subject;
private final Evaluator<Long> multiplyValue;
private final Evaluator<Number> subject;
private final Evaluator<Number> multiplyValue;
public MultiplyEvaluator(final Evaluator<Long> subject, final Evaluator<Long> multiplyValue) {
public MultiplyEvaluator(final Evaluator<Number> subject, final Evaluator<Number> multiplyValue) {
this.subject = subject;
this.multiplyValue = multiplyValue;
}
@Override
public QueryResult<Long> evaluate(final Map<String, String> attributes) {
final Long subjectValue = subject.evaluate(attributes).getValue();
public QueryResult<Number> evaluate(final Map<String, String> attributes) {
final Number subjectValue = subject.evaluate(attributes).getValue();
if (subjectValue == null) {
return new NumberQueryResult(null);
}
final Long multiply = multiplyValue.evaluate(attributes).getValue();
final Number multiply = multiplyValue.evaluate(attributes).getValue();
if (multiply == null) {
return new NumberQueryResult(null);
}
final long result = subjectValue * multiply;
final Number result;
if (subjectValue instanceof Double || multiply instanceof Double){
result = subjectValue.doubleValue() * multiply.doubleValue();
} else {
result = subjectValue.longValue() * multiply.longValue();
}
return new NumberQueryResult(result);
}

View File

@ -20,17 +20,17 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberQueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.WholeNumberEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.WholeNumberQueryResult;
public class OneUpSequenceEvaluator extends NumberEvaluator {
public class OneUpSequenceEvaluator extends WholeNumberEvaluator {
private static final AtomicLong value = new AtomicLong(0L);
@Override
public QueryResult<Long> evaluate(final Map<String, String> attributes) {
return new NumberQueryResult(value.getAndIncrement());
return new WholeNumberQueryResult(value.getAndIncrement());
}
@Override

View File

@ -25,27 +25,32 @@ import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
public class PlusEvaluator extends NumberEvaluator {
private final Evaluator<Long> subject;
private final Evaluator<Long> plusValue;
private final Evaluator<Number> subject;
private final Evaluator<Number> plusValue;
public PlusEvaluator(final Evaluator<Long> subject, final Evaluator<Long> plusValue) {
public PlusEvaluator(final Evaluator<Number> subject, final Evaluator<Number> plusValue) {
this.subject = subject;
this.plusValue = plusValue;
}
@Override
public QueryResult<Long> evaluate(final Map<String, String> attributes) {
final Long subjectValue = subject.evaluate(attributes).getValue();
public QueryResult<Number> evaluate(final Map<String, String> attributes) {
final Number subjectValue = subject.evaluate(attributes).getValue();
if (subjectValue == null) {
return new NumberQueryResult(null);
}
final Long plus = plusValue.evaluate(attributes).getValue();
final Number plus = plusValue.evaluate(attributes).getValue();
if (plus == null) {
return new NumberQueryResult(null);
}
final long result = subjectValue + plus;
final Number result;
if (subjectValue instanceof Double || plus instanceof Double){
result = subjectValue.doubleValue() + plus.doubleValue();
} else {
result = subjectValue.longValue() + plus.longValue();
}
return new NumberQueryResult(result);
}

View File

@ -20,18 +20,18 @@ import java.util.Map;
import java.util.Random;
import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberQueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.WholeNumberEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.WholeNumberQueryResult;
public class RandomNumberGeneratorEvaluator extends NumberEvaluator {
public class RandomNumberGeneratorEvaluator extends WholeNumberEvaluator {
private static final Random RNG = new Random();
@Override
public QueryResult<Long> evaluate(final Map<String, String> attributes) {
return new NumberQueryResult(Math.abs(RNG.nextLong()));
return new WholeNumberQueryResult(Math.abs(RNG.nextLong()));
}
@Override

View File

@ -19,21 +19,21 @@ package org.apache.nifi.attribute.expression.language.evaluation.literals;
import java.util.Map;
import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberQueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.WholeNumberEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.WholeNumberQueryResult;
public class NumberLiteralEvaluator extends NumberEvaluator {
public class WholeNumberLiteralEvaluator extends WholeNumberEvaluator {
private final long literal;
public NumberLiteralEvaluator(final String value) {
public WholeNumberLiteralEvaluator(final String value) {
this.literal = Long.parseLong(value);
}
@Override
public QueryResult<Long> evaluate(final Map<String, String> attributes) {
return new NumberQueryResult(literal);
return new WholeNumberQueryResult(literal);
}
@Override

View File

@ -19,12 +19,12 @@ package org.apache.nifi.attribute.expression.language.evaluation.reduce;
import java.util.Map;
import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.NumberQueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.WholeNumberEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.WholeNumberQueryResult;
import org.apache.nifi.expression.AttributeExpression.ResultType;
public class CountEvaluator extends NumberEvaluator implements ReduceEvaluator<Long> {
public class CountEvaluator extends WholeNumberEvaluator implements ReduceEvaluator<Long> {
private final Evaluator<?> subjectEvaluator;
private long count = 0L;
@ -37,15 +37,15 @@ public class CountEvaluator extends NumberEvaluator implements ReduceEvaluator<L
public QueryResult<Long> evaluate(final Map<String, String> attributes) {
final QueryResult<?> result = subjectEvaluator.evaluate(attributes);
if (result.getValue() == null) {
return new NumberQueryResult(count);
return new WholeNumberQueryResult(count);
}
if (result.getResultType() == ResultType.BOOLEAN && ((Boolean) result.getValue()).equals(Boolean.FALSE)) {
return new NumberQueryResult(count);
return new WholeNumberQueryResult(count);
}
count++;
return new NumberQueryResult(count);
return new WholeNumberQueryResult(count);
}
@Override

View File

@ -0,0 +1,84 @@
/*
* 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.util;
import java.util.regex.Pattern;
public class NumberParsing {
private static final Pattern NUMBER_PATTERN = Pattern.compile("-?\\d+");
public static enum ParseResultType {
NOT_NUMBER, WHOLE_NUMBER, DECIMAL;
}
private static final String Digits = "(\\p{Digit}+)";
// Double regex according to Oracle documentation: http://docs.oracle.com/javase/6/docs/api/java/lang/Double.html#valueOf%28java.lang.String%29
private static final String HexDigits = "(\\p{XDigit}+)";
// an exponent is 'e' or 'E' followed by an optionally
// signed decimal integer.
private static final String Exp = "[eE][+-]?"+Digits;
private static final String fpRegex =
("[\\x00-\\x20]*"+ // Optional leading "whitespace"
"[+-]?(" + // Optional sign character
"NaN|" + // "NaN" string
"Infinity|" + // "Infinity" string
// A decimal floating-point string representing a finite positive
// number without a leading sign has at most five basic pieces:
// Digits . Digits ExponentPart FloatTypeSuffix
//
// Since this method allows integer-only strings as input
// in addition to strings of floating-point literals, the
// two sub-patterns below are simplifications of the grammar
// productions from the Java Language Specification, 2nd
// edition, section 3.10.2.
// Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt
"((("+Digits+"(\\.)?("+Digits+"?)("+Exp+")?)|"+
// . Digits ExponentPart_opt FloatTypeSuffix_opt
"(\\.("+Digits+")("+Exp+")?)|"+
// Hexadecimal strings
"((" +
// 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt
"(0[xX]" + HexDigits + "(\\.)?)|" +
// 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt
"(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" +
")[pP][+-]?" + Digits + "))" +
"[fFdD]?))" +
"[\\x00-\\x20]*");// Optional trailing "whitespace"
private static final Pattern DOUBLE_PATTERN = Pattern.compile(fpRegex);
private NumberParsing(){
}
public static ParseResultType parse(String input){
if (NUMBER_PATTERN.matcher(input).matches()) {
return ParseResultType.WHOLE_NUMBER;
} else if (DOUBLE_PATTERN.matcher(input).matches()) {
return ParseResultType.DECIMAL;
} else {
return ParseResultType.NOT_NUMBER;
}
}
}

View File

@ -37,6 +37,7 @@ import java.util.Locale;
import java.util.Map;
import org.apache.nifi.attribute.expression.language.Query.Range;
import org.apache.nifi.attribute.expression.language.evaluation.NumberQueryResult;
import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
import org.apache.nifi.attribute.expression.language.exception.AttributeExpressionLanguageException;
import org.apache.nifi.attribute.expression.language.exception.AttributeExpressionLanguageParsingException;
@ -194,7 +195,7 @@ public class TestQuery {
attributes.put("dateTime", "2013/11/18 10:22:27.678");
final QueryResult<?> result = query.evaluate(attributes);
assertEquals(ResultType.NUMBER, result.getResultType());
assertEquals(ResultType.WHOLE_NUMBER, result.getResultType());
assertEquals(1384788147678L, result.getValue());
}
@ -491,7 +492,7 @@ public class TestQuery {
assertEquals(3, types.size());
assertEquals(ResultType.BOOLEAN, types.get(0));
assertEquals(ResultType.STRING, types.get(1));
assertEquals(ResultType.NUMBER, types.get(2));
assertEquals(ResultType.WHOLE_NUMBER, types.get(2));
}
@Test
@ -768,7 +769,7 @@ public class TestQuery {
}
@Test
public void testMathOperations() {
public void testMathWholeNumberOperations() {
final Map<String, String> attributes = new HashMap<>();
attributes.put("one", "1");
attributes.put("two", "2");
@ -777,7 +778,46 @@ public class TestQuery {
attributes.put("five", "5");
attributes.put("hundred", "100");
verifyEquals("${hundred:toNumber():multiply(2):divide(3):plus(1):mod(5)}", attributes, 2L);
verifyEquals("${hundred:toNumber():multiply(${two}):divide(${three}):plus(${one}):mod(${five})}", attributes, 2L);
}
@Test
public void testMathDecimalOperations() {
final Map<String, String> attributes = new HashMap<>();
attributes.put("first", "1.5");
attributes.put("second", "12.3");
attributes.put("third", "3");
attributes.put("fourth", "4.201");
attributes.put("fifth", "5.1");
attributes.put("hundred", "100");
// The expected resulted is calculated instead of a set number due to the inaccuracy of double arithmetic
verifyEquals("${hundred:toNumber():multiply(${second}):divide(${third}):plus(${first}):mod(${fifth})}", attributes, (((100 * 12.3) / 3) + 1.5) %5.1);
}
@Test
public void testMathResultInterpretation() {
final Map<String, String> attributes = new HashMap<>();
attributes.put("ten", "10.1");
attributes.put("two", "2.2");
// The expected resulted is calculated instead of a set number due to the inaccuracy of double arithmetic
verifyEquals("${ten:divide(${two:plus(3)}):toNumber()}", attributes, (Double.valueOf(10.1 / (2.2 + 3)).longValue()));
// The expected resulted is calculated instead of a set number due to the inaccuracy of double arithmetic
verifyEquals("${ten:divide(${two:plus(3)}):toDecimal()}", attributes, (10.1 / (2.2 + 3)));
verifyEquals("${ten:divide(${two:plus(3)}):toDate():format(\"SSS\")}", attributes, "001");
}
@Test
public void testMathLiteralOperations() {
final Map<String, String> attributes = new HashMap<>();
attributes.put("ten", "10.1");
attributes.put("two", "2.2");
// The expected resulted is calculated instead of a set number due to the inaccuracy of double arithmetic
verifyEquals("${literal(5):toNumber():multiply(${two:plus(1)})}", attributes, 5*3.2);
}
@Test
@ -900,7 +940,7 @@ public class TestQuery {
}
@Test
public void testMathOperators() {
public void testMathWholeNumberOperators() {
final Map<String, String> attributes = new HashMap<>();
attributes.put("abc", "1234");
attributes.put("xyz", "4132");
@ -909,6 +949,62 @@ public class TestQuery {
verifyEquals("${xyz:toNumber():gt( ${abc:toNumber()} )}", attributes, true);
}
@Test
public void testMathDecimalOperators() {
final Map<String, String> attributes = new HashMap<>();
attributes.put("one", "1.1");
attributes.put("two", "2.2");
attributes.put("one_2", "1.1");
verifyEquals("${one:lt(${two})}", attributes, true);
verifyEquals("${one:lt(${one_2})}", attributes, false);
verifyEquals("${two:lt(${one})}", attributes, false);
verifyEquals("${one:le(${two})}", attributes, true);
verifyEquals("${one:le(${one_2})}", attributes, true);
verifyEquals("${two:le(${one_2})}", attributes, false);
verifyEquals("${one:ge(${two})}", attributes, false);
verifyEquals("${one:ge(${one_2})}", attributes, true);
verifyEquals("${two:ge(${one_2})}", attributes, true);
verifyEquals("${one:gt(${two})}", attributes, false);
verifyEquals("${one:gt(${one_2})}", attributes, false);
verifyEquals("${two:gt(${one})}", attributes, true);
}
@Test
public void testMathNumberDecimalConversion() {
final Map<String, String> attributes = new HashMap<>();
attributes.put("xyz", "1.332");
attributes.put("hello", "world!");
verifyEquals("${xyz:toNumber()}", attributes, 1L);
attributes.put("xyz", "2");
attributes.put("hello", "world!");
verifyEquals("${xyz:toDecimal()}", attributes, 2D);
}
@Test
public void testLiteral() {
final Map<String, String> attributes = new HashMap<>();
verifyEquals("${literal(5)}", attributes, "5");
verifyEquals("${literal(\"5\")}", attributes, "5");
verifyEquals("${literal(5):toNumber()}", attributes, 5L);
verifyEquals("${literal(5):toDecimal()}", attributes, 5D);
// Unquoted doubles are not due to more complicated parsing
verifyEquals("${literal(\"5.5\")}", attributes, "5.5");
verifyEquals("${literal(\"5.5\"):toNumber()}", attributes, 5L);
verifyEquals("${literal(\"5.5\"):toDecimal()}", attributes, 5.5D);
}
@Test
public void testAllMatchingAttributes() {
final Map<String, String> attributes = new HashMap<>();
@ -1170,12 +1266,12 @@ public class TestQuery {
@Test
public void testToNumberFunctionReturnsNumberType() {
assertEquals(ResultType.NUMBER, Query.getResultType("${header.size:toNumber()}"));
assertEquals(ResultType.WHOLE_NUMBER, Query.getResultType("${header.size:toNumber()}"));
}
@Test
public void testRandomFunctionReturnsNumberType() {
assertEquals(ResultType.NUMBER, Query.getResultType("${random()}"));
assertEquals(ResultType.WHOLE_NUMBER, Query.getResultType("${random()}"));
}
@Test
@ -1377,8 +1473,20 @@ public class TestQuery {
final Query query = Query.compile(expression);
final QueryResult<?> result = query.evaluate(attributes);
if (expectedResult instanceof Number) {
assertEquals(ResultType.NUMBER, result.getResultType());
if (expectedResult instanceof Long) {
if (ResultType.NUMBER.equals(result.getResultType())) {
final Number resultNumber = ((NumberQueryResult) result).getValue();
assertTrue(resultNumber instanceof Long);
} else {
assertEquals(ResultType.WHOLE_NUMBER, result.getResultType());
}
} else if(expectedResult instanceof Double) {
if (ResultType.NUMBER.equals(result.getResultType())) {
final Number resultNumber = ((NumberQueryResult) result).getValue();
assertTrue(resultNumber instanceof Double);
} else {
assertEquals(ResultType.DECIMAL, result.getResultType());
}
} else if (expectedResult instanceof Boolean) {
assertEquals(ResultType.BOOLEAN, result.getResultType());
} else {

View File

@ -189,16 +189,20 @@ Language supports four different data types:
- *String*: A String is a sequence of characters that can consist of numbers, letters, white space, and
special characters.
- *Number*: A Number is an integer comprised of one or more digits (`0` through `9`). The Expression Language
does not provide support for fractional numbers. When converting to numbers from Date data types, they are represented as
- *Number*: A Number is an whole number comprised of one or more digits (`0` through `9`). When converting to numbers from Date data types, they are represented as
the number of milliseconds since midnight GMT on January 1, 1970.
- *Decimal*: A Decimal is a numeric value that can support decimals and larger values with minimal loss of precision. More precisely it
is a double-precision 64-bit IEEE 754 floating point. Due to this minimal loss of precision this data type should not be used for
very precise values, such as currency. For more documentation on the range of values stored in this data type
refer to this https://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.2.3[link]. Decimals cannot be expressed as un-quoted characters
when inputting a literal Decimal to an Expression Language function. They must be input as Strings using quotes, like so: "1.1".
- *Date*: A Date is an object that holds a Date and Time. Utilizing the <<dates>> and <<type_cast>> functions this data
type can be converted to/from Strings and numbers. If the whole Expression Language expression is evaluated to be a
date then it will be converted to a String with the format: "<Day of Week> <Month> <Day of Month> <Hour>:<Minute>:<Second> <Time Zone> <Year>".
Also expressed as "E MMM dd HH:mm:ss z yyyy" in Java SimpleDateFormat format. For example: "Wed Dec 31 12:00:04 UTC 2016".
- *Boolean*: A Boolean is one of either `true` or `false`.
All attributes are considered to be of type String.
After evaluating expression language functions, all attributes are stored as of type String.
The Expression Language is generally able to automatically coerce a value of one data type to the appropriate
data type for a function. However, functions do exist to manually coerce a value into a specific data type.
@ -1496,6 +1500,10 @@ An empty subject value or a subject value with an invalid JSON document results
[[numbers]]
== Mathematical Operations and Numeric Manipulation
For those functions that support Decimal and Number (whole number) types, the return value type depends on the input types. If either the
subject or argument are a Decimal then the result will be a Decimal. If both values are Numbers then the result will be a Number. This includes
Divide. This is to preserve backwards compatibility and to not force rounding errors.
[.function]
=== plus
@ -1503,13 +1511,13 @@ An empty subject value or a subject value with an invalid JSON document results
*Description*: [.description]#Adds a numeric value to the Subject. If either the argument or the Subject cannot be
coerced into a Number, returns `null`.#
*Subject Type*: [.subject]#Number#
*Subject Type*: [.subject]#Number or Decimal#
*Arguments*:
- [.argName]#_Operand_# : [.argDesc]#The value to add to the Subject#
*Return Type*: [.returnType]#Number#
*Return Type*: [.returnType]#Number or Decimal (depending on input types)#
*Examples*: If the "fileSize" attribute has a value of 100, then the Expression `${fileSize:plus(1000)}`
will return the value `1100`.
@ -1523,13 +1531,13 @@ An empty subject value or a subject value with an invalid JSON document results
*Description*: [.description]#Subtracts a numeric value from the Subject.#
*Subject Type*: [.subject]#Number#
*Subject Type*: [.subject]#Number or Decimal#
*Arguments*:
- [.argName]#_Operand_# : [.argDesc]#The value to subtract from the Subject#
*Return Type*: [.returnType]#Number#
*Return Type*: [.returnType]#Number or Decimal (depending on input types)#
*Examples*: If the "fileSize" attribute has a value of 100, then the Expression `${fileSize:minus(100)}`
will return the value `0`.
@ -1543,13 +1551,13 @@ An empty subject value or a subject value with an invalid JSON document results
*Description*: [.description]#Multiplies a numeric value by the Subject and returns the product.#
*Subject Type*: [.subject]#Number#
*Subject Type*: [.subject]#Number or Decimal#
*Arguments*:
- [.argName]#_Operand_# : [.argDesc]#The value to multiple the Subject by#
*Return Type*: [.returnType]#Number#
*Return Type*: [.returnType]#Number or Decimal (depending on input types)#
*Examples*: If the "fileSize" attribute has a value of 100, then the Expression `${fileSize:multiply(1024)}`
will return the value `102400`.
@ -1560,15 +1568,15 @@ An empty subject value or a subject value with an invalid JSON document results
[.function]
=== divide
*Description*: [.description]#Divides a numeric value by the Subject and returns the result, rounded down to the nearest integer.#
*Description*: [.description]#Divides the Subject by a numeric value and returns the result.#
*Subject Type*: [.subject]#Number#
*Subject Type*: [.subject]#Number or Decimal#
*Arguments*:
- [.argName]#_Operand_# : [.argDesc]#The value to add divide the Subject by#
- [.argName]#_Operand_# : [.argDesc]#The value to divide the Subject by#
*Return Type*: [.returnType]#Number#
*Return Type*: [.returnType]#Number or Decimal (depending on input types)#
*Examples*: If the "fileSize" attribute has a value of 100, then the Expression `${fileSize:divide(12)}`
will return the value `8`.
@ -1582,13 +1590,13 @@ An empty subject value or a subject value with an invalid JSON document results
*Description*: [.description]#Performs a modular division of the Subject by the argument. That is, this function will divide
the Subject by the value of the argument and return not the quotient but rather the remainder.#
*Subject Type*: [.subject]#Number#
*Subject Type*: [.subject]#Number or Decimal#
*Arguments*:
- [.argName]#_Operand_# : [.argDesc]#The value to divide the Subject by#
*Return Type*: [.returnType]#Number#
*Return Type*: [.returnType]#Number or Decimal (depending on input types)#
*Examples*: If the "fileSize" attribute has a value of 100, then the Expression `${fileSize:mod(12)}`
will return the value `4`.
@ -1602,7 +1610,9 @@ An empty subject value or a subject value with an invalid JSON document results
*Description*: [.description]#Converts the Subject from a Base 10 number to a different Radix (or number base). An optional
second argument can be used to indicate the minimum number of characters to be used. If the converted value
has fewer than this number of characters, the number will be padded with leading zeroes.#
has fewer than this number of characters, the number will be padded with leading zeroes.
If a decimal is passed as the subject, it will first be converted to a whole number and then processed.#
*Subject Type*: [.subject]#Number#
@ -1632,7 +1642,7 @@ An empty subject value or a subject value with an invalid JSON document results
[.function]
=== random
*Description*: [.description]#Returns a random number ( 0 to 2^63 - 1) using an insecure random number generator.#
*Description*: [.description]#Returns a random whole number ( 0 to 2^63 - 1) using an insecure random number generator.#
*Subject Type*: [.subjectless]#No subject#
@ -1761,22 +1771,32 @@ manipulate the value.
[.function]
=== toNumber
*Description*: [.description]#Coerces the Subject into a Number#
*Subject Type*: [.subject]#String#
*Subject Type*: [.subject]#String, Decimal, or Date#
*Arguments*: No arguments
*Return Type*: [.returnType]#Number#
*Examples*: The Expression `${fileSize:toNumber()}` converts the String attribute value of "fileSize" to a number.
*Examples*: The Expression `${fileSize:toNumber()}` converts the attribute value of "fileSize" to a number.
[.function]
=== toDecimal
*Description*: [.description]#Coerces the Subject into a Decimal#
*Subject Type*: [.subject]#String, Whole Number or Date#
*Arguments*: No arguments
*Return Type*: [.returnType]#Decimal#
*Examples*: The Expression `${fileSize:toDecimal()}` converts the attribute value of "fileSize" to a decimal.