You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by pv...@apache.org on 2016/10/25 17:45:58 UTC

nifi git commit: NIFI-2791 Adding 'math' expression language function

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 <jo...@yahoo.com>
Authored: Thu Oct 20 10:58:53 2016 -0400
Committer: Pierre Villard <pi...@gmail.com>
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();
             }