Repository: nifi
Updated Branches:
  refs/heads/master 368ea7a2d -> 1d74b5d3c


NIFI-2791 Adding 'math' expression language function

This closes #1157.


Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/1d74b5d3
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/1d74b5d3
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/1d74b5d3

Branch: refs/heads/master
Commit: 1d74b5d3cea7cc844ebb97bb4af1d3c9110ed487
Parents: 368ea7a
Author: jpercivall <[email protected]>
Authored: Thu Oct 20 10:58:53 2016 -0400
Committer: Pierre Villard <[email protected]>
Committed: Tue Oct 25 19:45:38 2016 +0200

----------------------------------------------------------------------
 .../language/antlr/AttributeExpressionLexer.g   |   1 +
 .../language/antlr/AttributeExpressionParser.g  |   5 +-
 .../attribute/expression/language/Query.java    |  18 +++
 .../evaluation/functions/MathEvaluator.java     | 161 +++++++++++++++++++
 .../expression/language/TestQuery.java          |  55 +++++++
 .../asciidoc/expression-language-guide.adoc     |  31 +++-
 .../webapp/js/jquery/nfeditor/languages/nfel.js |   8 +-
 7 files changed, 273 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/1d74b5d3/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionLexer.g
----------------------------------------------------------------------
diff --git 
a/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionLexer.g
 
b/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionLexer.g
index 12704dd..cf76808 100644
--- 
a/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionLexer.g
+++ 
b/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionLexer.g
@@ -166,6 +166,7 @@ PLUS : 'plus';
 MINUS : 'minus';
 MULTIPLY : 'multiply';
 DIVIDE : 'divide';
+MATH : 'math';
 TO_RADIX : 'toRadix';
 OR : 'or';
 AND : 'and';

http://git-wip-us.apache.org/repos/asf/nifi/blob/1d74b5d3/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g
----------------------------------------------------------------------
diff --git 
a/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g
 
b/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g
index 64644c6..5542fb0 100644
--- 
a/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g
+++ 
b/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g
@@ -95,10 +95,11 @@ 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!);
+oneOrTwoArgNum : MATH LPAREN! anyArg (COMMA! anyArg)? RPAREN!;
 
 stringFunctionRef : zeroArgString | oneArgString | twoArgString | 
fiveArgString;
 booleanFunctionRef : zeroArgBool | oneArgBool | multiArgBool;
-numberFunctionRef : zeroArgNum | oneArgNum;
+numberFunctionRef : zeroArgNum | oneArgNum | oneOrTwoArgNum;
 
 anyArg : WHOLE_NUMBER | DECIMAL | numberFunctionRef | STRING_LITERAL | 
zeroArgString | oneArgString | twoArgString | fiveArgString | booleanLiteral | 
zeroArgBool | oneArgBool | multiArgBool | expression;
 stringArg : STRING_LITERAL | zeroArgString | oneArgString | twoArgString | 
expression;
@@ -128,7 +129,7 @@ functionCall : functionRef ->
 
 booleanLiteral : TRUE | FALSE;
 zeroArgStandaloneFunction : (IP | UUID | NOW | NEXT_INT | HOSTNAME | RANDOM) 
LPAREN! RPAREN!;
-oneArgStandaloneFunction : (TO_LITERAL^ LPAREN! anyArg RPAREN!) |
+oneArgStandaloneFunction : ((TO_LITERAL | MATH)^ LPAREN! anyArg RPAREN!) |
                            (HOSTNAME^ LPAREN! booleanLiteral RPAREN!);
 standaloneFunction : zeroArgStandaloneFunction | oneArgStandaloneFunction;
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/1d74b5d3/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/Query.java
----------------------------------------------------------------------
diff --git 
a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/Query.java
 
b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/Query.java
index e8b1f31..49c7518 100644
--- 
a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/Query.java
+++ 
b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/Query.java
@@ -62,6 +62,7 @@ import 
org.apache.nifi.attribute.expression.language.evaluation.functions.Length
 import 
org.apache.nifi.attribute.expression.language.evaluation.functions.LessThanEvaluator;
 import 
org.apache.nifi.attribute.expression.language.evaluation.functions.LessThanOrEqualEvaluator;
 import 
org.apache.nifi.attribute.expression.language.evaluation.functions.MatchesEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.functions.MathEvaluator;
 import 
org.apache.nifi.attribute.expression.language.evaluation.functions.MinusEvaluator;
 import 
org.apache.nifi.attribute.expression.language.evaluation.functions.ModEvaluator;
 import 
org.apache.nifi.attribute.expression.language.evaluation.functions.MultiplyEvaluator;
@@ -122,6 +123,7 @@ import org.antlr.runtime.CharStream;
 import org.antlr.runtime.CommonTokenStream;
 import org.antlr.runtime.tree.Tree;
 
+import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.MATH;
 import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.ALL_ATTRIBUTES;
 import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.ALL_DELINEATED_VALUES;
 import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.ALL_MATCHING_ATTRIBUTES;
@@ -736,6 +738,13 @@ public class Query {
             case RANDOM: {
                 return new RandomNumberGeneratorEvaluator();
             }
+            case MATH: {
+                if (tree.getChildCount() == 1) {
+                    return addToken(new MathEvaluator(null, 
toStringEvaluator(buildEvaluator(tree.getChild(0))), null), "math");
+                } else {
+                    throw new 
AttributeExpressionLanguageParsingException("Call to math() as the subject must 
take exactly 1 parameter");
+                }
+            }
             default:
                 throw new 
AttributeExpressionLanguageParsingException("Unexpected token: " + 
tree.toString());
         }
@@ -1247,6 +1256,15 @@ public class Query {
             case DIVIDE: {
                 return addToken(new 
DivideEvaluator(toNumberEvaluator(subjectEvaluator), 
toNumberEvaluator(argEvaluators.get(0))), "divide");
             }
+            case MATH: {
+                if (argEvaluators.size() == 1) {
+                    return addToken(new 
MathEvaluator(toNumberEvaluator(subjectEvaluator), 
toStringEvaluator(argEvaluators.get(0)), null), "math");
+                } else if (argEvaluators.size() == 2){
+                    return addToken(new 
MathEvaluator(toNumberEvaluator(subjectEvaluator), 
toStringEvaluator(argEvaluators.get(0)), 
toNumberEvaluator(argEvaluators.get(1))), "math");
+                } else {
+                    throw new 
AttributeExpressionLanguageParsingException("math() function takes 1 or 2 
arguments");
+                }
+            }
             case RANDOM : {
                 return addToken(new RandomNumberGeneratorEvaluator(), 
"random");
             }

http://git-wip-us.apache.org/repos/asf/nifi/blob/1d74b5d3/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/MathEvaluator.java
----------------------------------------------------------------------
diff --git 
a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/MathEvaluator.java
 
b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/MathEvaluator.java
new file mode 100644
index 0000000..46b4109
--- /dev/null
+++ 
b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/MathEvaluator.java
@@ -0,0 +1,161 @@
+/*
+ * 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.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.exception.AttributeExpressionLanguageException;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Map;
+
+public class MathEvaluator extends NumberEvaluator {
+
+    private final Evaluator<Number> subject;
+    private final Evaluator<String> methodName;
+    private final Evaluator<Number> optionalArg;
+
+    public MathEvaluator(final Evaluator<Number> subject, final 
Evaluator<String> methodName, final Evaluator<Number> optionalArg) {
+        this.subject = subject;
+        this.methodName = methodName;
+        this.optionalArg = optionalArg;
+    }
+
+    @Override
+    public QueryResult<Number> evaluate(final Map<String, String> attributes) {
+        final String methodNamedValue = 
methodName.evaluate(attributes).getValue();
+        if (methodNamedValue == null) {
+            return new NumberQueryResult(null);
+        }
+
+        final Number subjectValue;
+        if(subject != null) {
+            subjectValue = subject.evaluate(attributes).getValue();
+            if(subjectValue == null){
+                return new NumberQueryResult(null);
+            }
+        } else {
+            subjectValue = null;
+        }
+
+        final Number optionalArgValue;
+        if(optionalArg != null) {
+            optionalArgValue = optionalArg.evaluate(attributes).getValue();
+
+            if(optionalArgValue == null) {
+                return new NumberQueryResult(null);
+            }
+        } else {
+            optionalArgValue = null;
+        }
+
+        try {
+            Number executionValue = null;
+
+            if (subjectValue == null){
+                Method method;
+                try {
+                    method = Math.class.getMethod(methodNamedValue);
+                } catch (NoSuchMethodException subjectlessNoMethodException) {
+                    throw new AttributeExpressionLanguageException("Cannot 
evaluate 'math' function because no subjectless method was found with the 
name:'" +
+                            methodNamedValue + "'", 
subjectlessNoMethodException);
+                }
+
+                if(method == null) {
+                    throw new AttributeExpressionLanguageException("Cannot 
evaluate 'math' function because no subjectless method was found with the 
name:'" + methodNamedValue + "'");
+                }
+
+                executionValue = (Number) method.invoke(null);
+
+            } else if(optionalArg == null) {
+                boolean subjectIsDecimal = subjectValue instanceof Double;
+                Method method;
+                try {
+                    method = Math.class.getMethod(methodNamedValue, 
subjectIsDecimal ? double.class : long.class);
+                } catch (NoSuchMethodException noOptionalNoMethodException){
+                    throw new AttributeExpressionLanguageException("Cannot 
evaluate 'math' function because no method was found matching the passed 
parameters:" +
+                            " name:'" + methodNamedValue + "', one argument of 
type: '" + (subjectIsDecimal ? "double" : "long")+"'", 
noOptionalNoMethodException);
+                }
+
+                if(method == null) {
+                    throw new AttributeExpressionLanguageException("Cannot 
evaluate 'math' function because no method was found matching the passed 
parameters:" +
+                            " name:'" + methodNamedValue + "', one argument of 
type: '" + (subjectIsDecimal ? "double" : "long")+"'");
+                }
+
+                if (subjectIsDecimal){
+                    executionValue = (Number) method.invoke(null, 
subjectValue.doubleValue());
+                } else {
+                    executionValue = (Number) method.invoke(null, 
subjectValue.longValue());
+                }
+
+            } else {
+                boolean subjectIsDecimal = subjectValue instanceof Double;
+                boolean optionalArgIsDecimal = optionalArgValue instanceof 
Double;
+                Method method;
+                boolean convertOptionalToInt = false;
+
+                try {
+                    method = Math.class.getMethod(methodNamedValue, 
subjectIsDecimal ? double.class : long.class, optionalArgIsDecimal ? 
double.class : long.class);
+                } catch (NoSuchMethodException withOptionalNoMethodException) {
+
+                    if (!optionalArgIsDecimal) {
+                        try {
+                            method = Math.class.getMethod(methodNamedValue, 
subjectIsDecimal ? double.class : long.class, int.class);
+                        } catch (NoSuchMethodException 
withOptionalInnerNoMethodException) {
+                            throw new 
AttributeExpressionLanguageException("Cannot evaluate 'math' function because 
no method was found matching the passed parameters: " + "name:'" +
+                                    methodNamedValue + "', first argument 
type: '" + (subjectIsDecimal ? "double" : "long") + "', second argument type:  
'long'", withOptionalInnerNoMethodException);
+                        }
+                        convertOptionalToInt = true;
+
+                    } else {
+                        throw new AttributeExpressionLanguageException("Cannot 
evaluate 'math' function because no method was found matching the passed 
parameters: " + "name:'" +
+                                methodNamedValue + "', first argument type: '" 
+ (subjectIsDecimal ? "double" : "long") + "', second argument type:  
'double'", withOptionalNoMethodException);
+                    }
+                }
+
+                if(method == null) {
+                    throw new AttributeExpressionLanguageException("Cannot 
evaluate 'math' function because no method was found matching the passed 
parameters: " +
+                            "name:'" + methodNamedValue + "', first argument 
type: '" + (subjectIsDecimal ? "double" : "long") + "', second argument type:  
'"
+                            + (optionalArgIsDecimal ? "double" : "long") + 
"'");
+                }
+
+                if (optionalArgIsDecimal) {
+                    executionValue = (Number) method.invoke(null, 
subjectValue, optionalArgValue.doubleValue());
+                } else {
+                    if (convertOptionalToInt) {
+                        executionValue = (Number) method.invoke(null, 
subjectValue, optionalArgValue.intValue());
+                    } else {
+                        executionValue = (Number) method.invoke(null, 
subjectValue, optionalArgValue.longValue());
+                    }
+                }
+            }
+
+            return new NumberQueryResult(executionValue);
+        } catch (IllegalAccessException | InvocationTargetException e) {
+            throw new AttributeExpressionLanguageException("Unable to 
calculate math function value", e);
+        }
+    }
+
+    @Override
+    public Evaluator<?> getSubjectEvaluator() {
+        return subject;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/1d74b5d3/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java
----------------------------------------------------------------------
diff --git 
a/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java
 
b/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java
index eee88f7..f124f76 100644
--- 
a/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java
+++ 
b/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.hamcrest.Matchers.greaterThan;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
 
 import java.io.BufferedInputStream;
 import java.io.IOException;
@@ -814,6 +815,60 @@ public class TestQuery {
     }
 
     @Test
+    public void testMathFunction() {
+        final Map<String, String> attributes = new HashMap<>();
+        attributes.put("one", "1");
+        attributes.put("two", "2");
+        attributes.put("oneDecimal", "1.5");
+        attributes.put("twoDecimal", "2.3");
+        attributes.put("negative", "-64");
+        attributes.put("negativeDecimal", "-64.1");
+
+        // Test that errors relating to not finding methods are properly 
handled
+        try {
+            verifyEquals("${math('rand'):toNumber()}", attributes, 0L);
+            fail();
+        } catch (AttributeExpressionLanguageException expected) {
+            assertEquals("Cannot evaluate 'math' function because no 
subjectless method was found with the name:'rand'", expected.getMessage());
+        }
+        try {
+            verifyEquals("${negativeDecimal:math('absolute')}", attributes, 
0L);
+            fail();
+        } catch (AttributeExpressionLanguageException expected) {
+            assertEquals("Cannot evaluate 'math' function because no method 
was found matching the passed parameters: name:'absolute', one argument of 
type: 'double'", expected.getMessage());
+        }
+        try {
+            verifyEquals("${oneDecimal:math('power', ${two:toDecimal()})}", 
attributes, 0L);
+            fail();
+        } catch (AttributeExpressionLanguageException expected) {
+            assertEquals("Cannot evaluate 'math' function because no method 
was found matching the passed parameters: name:'power', " +
+                    "first argument type: 'double', second argument type:  
'double'", expected.getMessage());
+        }
+        try {
+            verifyEquals("${oneDecimal:math('power', ${two})}", attributes, 
0L);
+            fail();
+        } catch (AttributeExpressionLanguageException expected) {
+            assertEquals("Cannot evaluate 'math' function because no method 
was found matching the passed parameters: name:'power', " +
+                    "first argument type: 'double', second argument type:  
'long'", expected.getMessage());
+        }
+
+        // Can only verify that it runs. ToNumber() will verify that it 
produced a number greater than or equal to 0.0 and less than 1.0
+        verifyEquals("${math('random'):toNumber()}", attributes, 0L);
+
+        verifyEquals("${negative:math('abs')}", attributes, 64L);
+        verifyEquals("${negativeDecimal:math('abs')}", attributes, 64.1D);
+
+        verifyEquals("${negative:math('max', ${two})}", attributes, 2L);
+        verifyEquals("${negativeDecimal:math('max', ${twoDecimal})}", 
attributes, 2.3D);
+
+        verifyEquals("${oneDecimal:math('pow', ${two:toDecimal()})}", 
attributes, Math.pow(1.5,2));
+        verifyEquals("${oneDecimal:math('scalb', ${two})}", attributes, 
Math.scalb(1.5,2));
+
+        
verifyEquals("${negative:math('abs'):toDecimal():math('cbrt'):math('max', 
${two:toDecimal():math('pow',${oneDecimal}):mod(${two})})}", attributes,
+                Math.max(Math.cbrt(Math.abs(-64)), Math.pow(2,1.5)%2));
+    }
+
+    @Test
     public void testMathLiteralOperations() {
         final Map<String, String> attributes = new HashMap<>();
         attributes.put("ten", "10.1");

http://git-wip-us.apache.org/repos/asf/nifi/blob/1d74b5d3/nifi-docs/src/main/asciidoc/expression-language-guide.adoc
----------------------------------------------------------------------
diff --git a/nifi-docs/src/main/asciidoc/expression-language-guide.adoc 
b/nifi-docs/src/main/asciidoc/expression-language-guide.adoc
index 1f296f6..90205e8 100644
--- a/nifi-docs/src/main/asciidoc/expression-language-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/expression-language-guide.adoc
@@ -1200,9 +1200,9 @@ Each of the following functions will encode a string 
according the rules of the
 
 *Return Type*: [.returnType]#String#
 
-*Examples*: We can Base64-Encoded an attribute named "payload" by using the 
Expression 
+*Examples*: We can Base64-Encoded an attribute named "payload" by using the 
Expression
           `${payload:base64Encode()}` If the attribute payload had a value of 
"admin:admin"
-           then the Expression  `${payload:base64Encode()}` will return 
"YWRtaW46YWRtaW4=". 
+           then the Expression  `${payload:base64Encode()}` will return 
"YWRtaW46YWRtaW4=".
 
 
 
@@ -1657,6 +1657,33 @@ Divide. This is to preserve backwards compatibility and 
to not force rounding er
 
 *Examples*: ${random():mod(10):plus(1)} returns random number between 1 and 10 
inclusive.
 
+[.function]
+=== math
+
+*Description*: [.description]#ADVANCED FEATURE. This expression is designed to 
be used by advanced users only. It utilizes Java Reflection to run arbitrary 
java.lang.Math static methods. The exact API will depend on the version of Java 
you are running. The Java 8 API can be found here: 
https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html
+ +
+In order to run the correct method, the parameter types must be correct. The 
Expression Language "Number" (whole number) type is interpreted as a Java 
"long". The "Decimal" type is interpreted as a Java "double". Running the 
desired method may require calling "toNumber()" or "toDecimal()" in order to 
"cast" the value to the desired type. This also is important to remember when 
cascading "math()" calls since the return type depends on the method that was 
run.#
+
+*Subject Type*: [.subject .subjectless]#Subjectless, Number or Decimal 
(depending on the desired method to run)#
+
+*Arguments*:
+       - [.argName]#_Method_# : [.argDesc]#The name of the Java Math method to 
run#
+       - [.argName]#_Optional Argument_# : [.argDesc]#Optional argument that 
acts as the second parameter to the method.#
+
+*Return Type*: [.returnType]#Number or Decimal (depending on method run)#
+
+*Examples*:
+
+       - ${math("random")} runs Math.random().
+
+       - ${literal(2):toDecimal:math("pow", 2.5)} runs Math.pow(2D,2.5D).
+
+       - ${literal(64):toDouble():math("cbrt"):toNumber():math("max", 5)} runs 
Math.max((Double.valueOf(Math.cbrt(64D))).longValue(), 5L). Note that the 
toDecimal() is needed because "cbrt" takes a "double" as input and the "64" 
will get interpreted as a long. The "toDecimal()" call is necessary to 
correctly call the method. that the "toNumber()" call is necessary because 
"cbrt" returns a double and the "max" method is must have parameters of the 
same type and "5" is interpreted as a long.
+
+       - ${literal(5.4):math("scalb", 2)} runs Math.scalb(5.4, 2). This 
example is important because NiFi EL treats all whole numbers as "longs" and 
there is no concept of an "int". "scalb" takes a second parameter of an "int" 
and it is not overloaded to accept longs so it could not be run without special 
type handling. In the instance where the Java method cannot be found using 
parameters of type "double" and "long" the "math()" EL function will attempt to 
find a Java method with the same name but parameters of "double" and "int".
+
+       - ${first:toDecimal():math("pow", ${second:toDecimal()})} where 
attributes evaluate to "first" = 2.5 and "second" = 2. This example runs 
Math.pow(2.5D, 2D). The explicit calls to toDecimal() are important because of 
the dynamic nature of EL. When creating the flow, the user is unaware if the 
expression language values will be able to be interpreted as a whole number or 
not. In this example without the explicit calls "toDecimal" the "math" function 
would attempt to run a Java method "pow" with types "double" and "long" (which 
doesn't exist).
+
 [[dates]]
 == Date Manipulation
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/1d74b5d3/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/nfeditor/languages/nfel.js
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/nfeditor/languages/nfel.js
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/nfeditor/languages/nfel.js
index dc4cc68..8f90f20 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/nfeditor/languages/nfel.js
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/nfeditor/languages/nfel.js
@@ -64,13 +64,17 @@ nf.nfel = (function() {
             var returnType = elFunction.find('span.returnType').text();
             
             var subject;
+            var subjectSpan = subject = elFunction.find('span.subject');
             var subjectless = elFunction.find('span.subjectless');
             
-            // determine if this function is subjectless
+            // Determine if this function supports running subjectless
             if (subjectless.length) {
                 subjectlessFunctions.push(name);
                 subject = '<span class="unset">None</span>';
-            } else {
+            }
+
+            // Determine if this function supports running with a subject
+            if (subjectSpan.length) {
                 functions.push(name);
                 subject = elFunction.find('span.subject').text();
             }

Reply via email to