http://git-wip-us.apache.org/repos/asf/nifi/blob/5cd8e93b/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 4b1ce59..fe2c060 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
@@ -16,209 +16,21 @@
  */
 package org.apache.nifi.attribute.expression.language;
 
-import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionLexer;
-import 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser;
-import 
org.apache.nifi.attribute.expression.language.evaluation.BooleanEvaluator;
-import org.apache.nifi.attribute.expression.language.evaluation.DateEvaluator;
+import org.antlr.runtime.tree.Tree;
+import 
org.apache.nifi.attribute.expression.language.compile.CompiledExpression;
+import 
org.apache.nifi.attribute.expression.language.compile.ExpressionCompiler;
 import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
 import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
-import 
org.apache.nifi.attribute.expression.language.evaluation.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.DivideEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.EndsWithEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.EqualsEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.EqualsIgnoreCaseEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.CharSequenceTranslatorEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.FindEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.FormatEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.FromRadixEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.GetDelimitedFieldEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.GetStateVariableEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.GreaterThanEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.GreaterThanOrEqualEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.HostnameEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.IPEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.IfElseEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.InEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.IndexOfEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.IsEmptyEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.IsNullEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.JsonPathEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.LastIndexOfEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.LengthEvaluator;
-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;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.NotEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.NotNullEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.NowEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.NumberToDateEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.OneUpSequenceEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.OrEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.PlusEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.PrependEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.RandomNumberGeneratorEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.ReplaceAllEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.ReplaceEmptyEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.ReplaceEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.ReplaceFirstEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.ReplaceNullEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.StartsWithEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.StringToDateEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.SubstringAfterEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.SubstringAfterLastEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.SubstringBeforeEvaluator;
-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.ToRadixEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.ToStringEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.ToUpperEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.TrimEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.UrlDecodeEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.UrlEncodeEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.functions.Base64DecodeEvaluator;
-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.DecimalLiteralEvaluator;
-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;
-import 
org.apache.nifi.attribute.expression.language.evaluation.selection.AllAttributesEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.selection.AnyAttributeEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.selection.DelineatedAttributeEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.selection.IteratingEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.selection.MultiAttributeEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.selection.MultiMatchAttributeEvaluator;
-import 
org.apache.nifi.attribute.expression.language.evaluation.selection.MultiNamedAttributeEvaluator;
-import 
org.apache.nifi.attribute.expression.language.exception.AttributeExpressionLanguageException;
 import 
org.apache.nifi.attribute.expression.language.exception.AttributeExpressionLanguageParsingException;
 import org.apache.nifi.expression.AttributeExpression.ResultType;
 import org.apache.nifi.expression.AttributeValueDecorator;
-import org.apache.nifi.flowfile.FlowFile;
 import org.apache.nifi.processor.exception.ProcessException;
-import org.antlr.runtime.ANTLRStringStream;
-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.FROM_RADIX;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.IF_ELSE;
-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;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.AND;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.ANY_ATTRIBUTE;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.ANY_DELINEATED_VALUE;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.ANY_MATCHING_ATTRIBUTE;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.APPEND;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.ATTRIBUTE_REFERENCE;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.ATTR_NAME;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.CONTAINS;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.DECIMAL;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.IN;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.COUNT;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.DIVIDE;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.ENDS_WITH;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.EQUALS;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.EQUALS_IGNORE_CASE;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.EXPRESSION;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.FALSE;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.FIND;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.FORMAT;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.GET_DELIMITED_FIELD;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.GET_STATE_VALUE;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.GREATER_THAN;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.GREATER_THAN_OR_EQUAL;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.HOSTNAME;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.INDEX_OF;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.IP;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.IS_EMPTY;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.IS_NULL;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.JOIN;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.JSON_PATH;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.LAST_INDEX_OF;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.LENGTH;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.LESS_THAN;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.LESS_THAN_OR_EQUAL;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.MATCHES;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.MINUS;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.MOD;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.MULTIPLY;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.MULTI_ATTRIBUTE_REFERENCE;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.NEXT_INT;
-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.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;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.REPLACE;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.RANDOM;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.REPLACE_ALL;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.REPLACE_EMPTY;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.REPLACE_FIRST;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.REPLACE_NULL;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.STARTS_WITH;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.STRING_LITERAL;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.SUBSTRING;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.SUBSTRING_AFTER;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.SUBSTRING_AFTER_LAST;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.SUBSTRING_BEFORE;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.SUBSTRING_BEFORE_LAST;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.TO_DATE;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.TO_LITERAL;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.TO_LOWER;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.TO_NUMBER;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.TO_RADIX;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.TO_STRING;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.TO_UPPER;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.TRIM;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.TRUE;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.URL_DECODE;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.URL_ENCODE;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.BASE64_DECODE;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.BASE64_ENCODE;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.ESCAPE_JSON;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.ESCAPE_CSV;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.ESCAPE_HTML3;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.ESCAPE_HTML4;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.ESCAPE_XML;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.UNESCAPE_JSON;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.UNESCAPE_CSV;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.UNESCAPE_HTML3;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.UNESCAPE_HTML4;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.UNESCAPE_XML;
-import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.UUID;
-
-import 
org.apache.nifi.attribute.expression.language.evaluation.selection.MappingEvaluator;
 
 /**
  * Class used for creating and evaluating NiFi Expression Language. Once a 
Query
@@ -413,18 +225,6 @@ public class Query {
         return Query.prepare(rawValue).evaluateExpressions(valueLookup, 
decorator);
     }
 
-    private static Evaluator<?> getRootSubjectEvaluator(final Evaluator<?> 
evaluator) {
-        if (evaluator == null) {
-            return null;
-        }
-
-        final Evaluator<?> subject = evaluator.getSubjectEvaluator();
-        if (subject == null) {
-            return evaluator;
-        }
-
-        return getRootSubjectEvaluator(subject);
-    }
 
     /**
      * Un-escapes ${...} patterns that were escaped
@@ -436,28 +236,11 @@ public class Query {
         return value.replaceAll("\\$\\$(?=\\$*\\{.*?\\})", "\\$");
     }
 
-
     public static Query fromTree(final Tree tree, final String text) {
-        return new Query(text, tree, buildEvaluator(tree));
+        final ExpressionCompiler compiler = new ExpressionCompiler();
+        return new Query(text, tree, compiler.buildEvaluator(tree));
     }
 
-    public static Tree compileTree(final String query) throws 
AttributeExpressionLanguageParsingException {
-        try {
-            final CommonTokenStream lexerTokenStream = 
createTokenStream(query);
-            final AttributeExpressionParser parser = new 
AttributeExpressionParser(lexerTokenStream);
-            final Tree ast = (Tree) parser.query().getTree();
-            final Tree tree = ast.getChild(0);
-
-            // ensure that we are able to build the evaluators, so that we 
validate syntax
-            final Evaluator<?> evaluator = buildEvaluator(tree);
-            verifyMappingEvaluatorReduced(evaluator);
-            return tree;
-        } catch (final AttributeExpressionLanguageParsingException e) {
-            throw e;
-        } catch (final Exception e) {
-            throw new AttributeExpressionLanguageParsingException(e);
-        }
-    }
 
     public static PreparedQuery prepare(final String query) throws 
AttributeExpressionLanguageParsingException {
         if (query == null) {
@@ -470,9 +253,11 @@ public class Query {
             return new EmptyPreparedQuery(query.replace("$$", "$"));
         }
 
+        final ExpressionCompiler compiler = new ExpressionCompiler();
+
         try {
             final List<String> substrings = new ArrayList<>();
-            final Map<String, Tree> trees = new HashMap<>();
+            final Map<String, CompiledExpression> compiledExpressions = new 
HashMap<>();
 
             int lastIndex = 0;
             for (final Range range : ranges) {
@@ -483,7 +268,10 @@ public class Query {
 
                 final String treeText = query.substring(range.getStart(), 
range.getEnd() + 1).replace("$$", "$");
                 substrings.add(treeText);
-                trees.put(treeText, Query.compileTree(treeText));
+
+                final CompiledExpression compiledExpression = 
compiler.compile(treeText);
+
+                compiledExpressions.put(treeText, compiledExpression);
                 lastIndex = range.getEnd() + 1;
             }
 
@@ -493,7 +281,7 @@ public class Query {
                 substrings.add(treeText);
             }
 
-            return new StandardPreparedQuery(substrings, trees);
+            return new StandardPreparedQuery(substrings, compiledExpressions);
         } catch (final AttributeExpressionLanguageParsingException e) {
             return new InvalidPreparedQuery(query, e.getMessage());
         }
@@ -501,15 +289,10 @@ public class Query {
 
     public static Query compile(final String query) throws 
AttributeExpressionLanguageParsingException {
         try {
-            final CommonTokenStream lexerTokenStream = 
createTokenStream(query);
-            final AttributeExpressionParser parser = new 
AttributeExpressionParser(lexerTokenStream);
-            final Tree ast = (Tree) parser.query().getTree();
-            final Tree tree = ast.getChild(0);
+            final ExpressionCompiler compiler = new ExpressionCompiler();
+            final CompiledExpression compiledExpression = 
compiler.compile(query);
 
-            final Evaluator<?> evaluator = buildEvaluator(tree);
-            verifyMappingEvaluatorReduced(evaluator);
-
-            return new Query(query, tree, evaluator);
+            return new Query(compiledExpression.getExpression(), 
compiledExpression.getTree(), compiledExpression.getRootEvaluator());
         } catch (final AttributeExpressionLanguageParsingException e) {
             throw e;
         } catch (final Exception e) {
@@ -517,57 +300,6 @@ public class Query {
         }
     }
 
-    private static void verifyMappingEvaluatorReduced(final Evaluator<?> 
evaluator) {
-        final Evaluator<?> rightMostEvaluator;
-        if (evaluator instanceof IteratingEvaluator) {
-            rightMostEvaluator = ((IteratingEvaluator<?>) 
evaluator).getLogicEvaluator();
-        } else {
-            rightMostEvaluator = evaluator;
-        }
-
-        Evaluator<?> eval = rightMostEvaluator.getSubjectEvaluator();
-        Evaluator<?> lastEval = rightMostEvaluator;
-        while (eval != null) {
-            if (eval instanceof ReduceEvaluator) {
-                throw new 
AttributeExpressionLanguageParsingException("Expression attempts to call 
function '" + lastEval.getToken() + "' on the result of '" + eval.getToken() +
-                    "'. This is not allowed. Instead, use \"${literal( 
${<embedded expression>} ):" + lastEval.getToken() + "(...)}\"");
-            }
-
-            lastEval = eval;
-            eval = eval.getSubjectEvaluator();
-        }
-
-        // if the result type of the evaluator is BOOLEAN, then it will always
-        // be reduced when evaluator.
-        final ResultType resultType = evaluator.getResultType();
-        if (resultType == ResultType.BOOLEAN) {
-            return;
-        }
-
-        final Evaluator<?> rootEvaluator = getRootSubjectEvaluator(evaluator);
-        if (rootEvaluator != null && rootEvaluator instanceof 
MultiAttributeEvaluator) {
-            final MultiAttributeEvaluator multiAttrEval = 
(MultiAttributeEvaluator) rootEvaluator;
-            switch (multiAttrEval.getEvaluationType()) {
-                case ALL_ATTRIBUTES:
-                case ALL_MATCHING_ATTRIBUTES:
-                case ALL_DELINEATED_VALUES: {
-                    if (!(evaluator instanceof ReduceEvaluator)) {
-                        throw new 
AttributeExpressionLanguageParsingException("Cannot evaluate expression because 
it attempts to reference multiple attributes but does not use a reducing 
function");
-                    }
-                    break;
-                }
-                default:
-                    throw new 
AttributeExpressionLanguageParsingException("Cannot evaluate expression because 
it attempts to reference multiple attributes but does not use a reducing 
function");
-            }
-        }
-    }
-
-    private static CommonTokenStream createTokenStream(final String 
expression) throws AttributeExpressionLanguageParsingException {
-        final CharStream input = new ANTLRStringStream(expression);
-        final AttributeExpressionLexer lexer = new 
AttributeExpressionLexer(input);
-        return new CommonTokenStream(lexer);
-    }
-
     public ResultType getResultType() {
         return evaluator.getResultType();
     }
@@ -598,789 +330,7 @@ public class Query {
         return "Query [" + query + "]";
     }
 
-    private static Evaluator<String> newStringLiteralEvaluator(final String 
literalValue) {
-        if (literalValue == null || literalValue.length() < 2) {
-            return new StringLiteralEvaluator(literalValue);
-        }
-
-        final List<Range> ranges = extractExpressionRanges(literalValue);
-        if (ranges.isEmpty()) {
-            return new StringLiteralEvaluator(literalValue);
-        }
-
-        final List<Evaluator<?>> evaluators = new ArrayList<>();
-
-        int lastIndex = 0;
-        for (final Range range : ranges) {
-            if (range.getStart() > lastIndex) {
-                
evaluators.add(newStringLiteralEvaluator(literalValue.substring(lastIndex, 
range.getStart())));
-            }
-
-            final String treeText = literalValue.substring(range.getStart(), 
range.getEnd() + 1);
-            evaluators.add(buildEvaluator(compileTree(treeText)));
-            lastIndex = range.getEnd() + 1;
-        }
-
-        final Range lastRange = ranges.get(ranges.size() - 1);
-        if (lastRange.getEnd() + 1 < literalValue.length()) {
-            final String treeText = literalValue.substring(lastRange.getEnd() 
+ 1);
-            evaluators.add(newStringLiteralEvaluator(treeText));
-        }
-
-        if (evaluators.size() == 1) {
-            return toStringEvaluator(evaluators.get(0));
-        }
-
-        Evaluator<String> lastEvaluator = toStringEvaluator(evaluators.get(0));
-        for (int i = 1; i < evaluators.size(); i++) {
-            lastEvaluator = new AppendEvaluator(lastEvaluator, 
toStringEvaluator(evaluators.get(i)));
-        }
-
-        return lastEvaluator;
-    }
-
-    private static Evaluator<?> buildEvaluator(final Tree tree) {
-        switch (tree.getType()) {
-            case EXPRESSION: {
-                return buildExpressionEvaluator(tree);
-            }
-            case ATTRIBUTE_REFERENCE: {
-                final Evaluator<?> childEvaluator = 
buildEvaluator(tree.getChild(0));
-                if (childEvaluator instanceof MultiAttributeEvaluator) {
-                    return childEvaluator;
-                }
-                return new 
AttributeEvaluator(toStringEvaluator(childEvaluator));
-            }
-            case MULTI_ATTRIBUTE_REFERENCE: {
-
-                final Tree functionTypeTree = tree.getChild(0);
-                final int multiAttrType = functionTypeTree.getType();
-                if (multiAttrType == ANY_DELINEATED_VALUE || multiAttrType == 
ALL_DELINEATED_VALUES) {
-                    final Evaluator<String> delineatedValueEvaluator = 
toStringEvaluator(buildEvaluator(tree.getChild(1)));
-                    final Evaluator<String> delimiterEvaluator = 
toStringEvaluator(buildEvaluator(tree.getChild(2)));
-
-                    return new 
DelineatedAttributeEvaluator(delineatedValueEvaluator, delimiterEvaluator, 
multiAttrType);
-                }
-
-                final List<String> attributeNames = new ArrayList<>();
-                for (int i = 1; i < tree.getChildCount(); i++) {  // skip the 
first child because that's the name of the multi-attribute function
-                    
attributeNames.add(newStringLiteralEvaluator(tree.getChild(i).getText()).evaluate(null).getValue());
-                }
-
-                switch (multiAttrType) {
-                    case ALL_ATTRIBUTES:
-                        for (final String attributeName : attributeNames) {
-                            try {
-                                
FlowFile.KeyValidator.validateKey(attributeName);
-                            } catch (final IllegalArgumentException iae) {
-                                throw new 
AttributeExpressionLanguageParsingException("Invalid Attribute Name: " + 
attributeName + ". " + iae.getMessage());
-                            }
-                        }
-
-                        return new 
MultiNamedAttributeEvaluator(attributeNames, ALL_ATTRIBUTES);
-                    case ALL_MATCHING_ATTRIBUTES:
-                        return new 
MultiMatchAttributeEvaluator(attributeNames, ALL_MATCHING_ATTRIBUTES);
-                    case ANY_ATTRIBUTE:
-                        for (final String attributeName : attributeNames) {
-                            try {
-                                
FlowFile.KeyValidator.validateKey(attributeName);
-                            } catch (final IllegalArgumentException iae) {
-                                throw new 
AttributeExpressionLanguageParsingException("Invalid Attribute Name: " + 
attributeName + ". " + iae.getMessage());
-                            }
-                        }
-
-                        return new 
MultiNamedAttributeEvaluator(attributeNames, ANY_ATTRIBUTE);
-                    case ANY_MATCHING_ATTRIBUTE:
-                        return new 
MultiMatchAttributeEvaluator(attributeNames, ANY_MATCHING_ATTRIBUTE);
-                    default:
-                        throw new AssertionError("Illegal Multi-Attribute 
Reference: " + functionTypeTree.toString());
-                }
-            }
-            case ATTR_NAME: {
-                return newStringLiteralEvaluator(tree.getChild(0).getText());
-            }
-            case WHOLE_NUMBER: {
-                return new WholeNumberLiteralEvaluator(tree.getText());
-            }
-            case STRING_LITERAL: {
-                return newStringLiteralEvaluator(tree.getText());
-            }
-            case DECIMAL: {
-                return new DecimalLiteralEvaluator(tree.getText());
-            }
-            case TRUE:
-            case FALSE:
-                return buildBooleanEvaluator(tree);
-            case UUID: {
-                return new UuidEvaluator();
-            }
-            case NOW: {
-                return new NowEvaluator();
-            }
-            case TO_LITERAL: {
-                final Evaluator<?> argEvaluator = 
buildEvaluator(tree.getChild(0));
-                return new ToLiteralEvaluator(argEvaluator);
-            }
-            case IP: {
-                try {
-                    return new IPEvaluator();
-                } catch (final UnknownHostException e) {
-                    throw new AttributeExpressionLanguageException(e);
-                }
-            }
-            case HOSTNAME: {
-                if (tree.getChildCount() == 0) {
-                    try {
-                        return new HostnameEvaluator(false);
-                    } catch (final UnknownHostException e) {
-                        throw new AttributeExpressionLanguageException(e);
-                    }
-                } else if (tree.getChildCount() == 1) {
-                    final Tree childTree = tree.getChild(0);
-                    try {
-                        switch (childTree.getType()) {
-                            case TRUE:
-                                return new HostnameEvaluator(true);
-                            case FALSE:
-                                return new HostnameEvaluator(false);
-                            default:
-                                throw new 
AttributeExpressionLanguageParsingException("Call to hostname() must take 0 or 
1 (boolean) parameter");
-                        }
-                    } catch (final UnknownHostException e) {
-                        throw new AttributeExpressionLanguageException(e);
-                    }
-                } else {
-                    throw new 
AttributeExpressionLanguageParsingException("Call to hostname() must take 0 or 
1 (boolean) parameter");
-                }
-            }
-            case NEXT_INT: {
-                return new OneUpSequenceEvaluator();
-            }
-            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");
-                }
-            }
-            case GET_STATE_VALUE: {
-                final Tree childTree = tree.getChild(0);
-                final Evaluator<?> argEvaluator = buildEvaluator(childTree);
-                final Evaluator<String> stringEvaluator = 
toStringEvaluator(argEvaluator);
-                return new GetStateVariableEvaluator(stringEvaluator);
-            }
-            default:
-                throw new 
AttributeExpressionLanguageParsingException("Unexpected token: " + 
tree.toString());
-        }
-    }
-
-    private static <T> Evaluator<T> addToken(final Evaluator<T> evaluator, 
final String token) {
-        evaluator.setToken(token);
-        return evaluator;
-    }
-
-    private static Evaluator<Boolean> buildBooleanEvaluator(final Tree tree) {
-        switch (tree.getType()) {
-            case TRUE:
-                return addToken(new BooleanLiteralEvaluator(true), "true");
-            case FALSE:
-                return addToken(new BooleanLiteralEvaluator(false), "true");
-        }
-        throw new AttributeExpressionLanguageParsingException("Cannot build 
Boolean evaluator from tree " + tree.toString());
-    }
-
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    private static Evaluator<?> buildExpressionEvaluator(final Tree tree) {
-        if (tree.getChildCount() == 0) {
-            throw new AttributeExpressionLanguageParsingException("EXPRESSION 
tree node has no children");
-        }
-
-        final Evaluator<?> evaluator;
-        if (tree.getChildCount() == 1) {
-            evaluator = buildEvaluator(tree.getChild(0));
-        } else {
-            // we can chain together functions in the form of:
-            // ${x:trim():substring(1,2):trim()}
-            // in this case, the subject of the right-most function is the 
function to its left; its
-            // subject is the function to its left (the first trim()), and its 
subject is the value of
-            // the 'x' attribute. We accomplish this logic by iterating over 
all of the children of the
-            // tree from the right-most child going left-ward.
-            evaluator = buildFunctionExpressionEvaluator(tree, 0);
-        }
-
-        Evaluator<?> chosenEvaluator = evaluator;
-        final Evaluator<?> rootEvaluator = getRootSubjectEvaluator(evaluator);
-        if (rootEvaluator != null) {
-            if (rootEvaluator instanceof MultiAttributeEvaluator) {
-                final MultiAttributeEvaluator multiAttrEval = 
(MultiAttributeEvaluator) rootEvaluator;
-
-                switch (multiAttrEval.getEvaluationType()) {
-                    case ANY_ATTRIBUTE:
-                    case ANY_MATCHING_ATTRIBUTE:
-                    case ANY_DELINEATED_VALUE:
-                        chosenEvaluator = new 
AnyAttributeEvaluator((BooleanEvaluator) evaluator, multiAttrEval);
-                        break;
-                    case ALL_ATTRIBUTES:
-                    case ALL_MATCHING_ATTRIBUTES:
-                    case ALL_DELINEATED_VALUES: {
-                        final ResultType resultType = 
evaluator.getResultType();
-                        if (resultType == ResultType.BOOLEAN) {
-                            chosenEvaluator = new 
AllAttributesEvaluator((BooleanEvaluator) evaluator, multiAttrEval);
-                        } else if (evaluator instanceof ReduceEvaluator) {
-                            chosenEvaluator = new 
MappingEvaluator((ReduceEvaluator) evaluator, multiAttrEval);
-                        } else {
-                            throw new 
AttributeExpressionLanguageException("Cannot evaluate Expression because it 
attempts to reference multiple attributes but does not use a reducing 
function");
-                        }
-                        break;
-                    }
-                }
-
-                switch (multiAttrEval.getEvaluationType()) {
-                    case ANY_ATTRIBUTE:
-                        chosenEvaluator.setToken("anyAttribute");
-                        break;
-                    case ANY_MATCHING_ATTRIBUTE:
-                        chosenEvaluator.setToken("anyMatchingAttribute");
-                        break;
-                    case ANY_DELINEATED_VALUE:
-                        chosenEvaluator.setToken("anyDelineatedValue");
-                        break;
-                    case ALL_ATTRIBUTES:
-                        chosenEvaluator.setToken("allAttributes");
-                        break;
-                    case ALL_MATCHING_ATTRIBUTES:
-                        chosenEvaluator.setToken("allMatchingAttributes");
-                        break;
-                    case ALL_DELINEATED_VALUES:
-                        chosenEvaluator.setToken("allDelineatedValues");
-                        break;
-                }
-            }
-        }
-
-        return chosenEvaluator;
-    }
-
-    private static Evaluator<?> buildFunctionExpressionEvaluator(final Tree 
tree, final int offset) {
-        if (tree.getChildCount() == 0) {
-            throw new AttributeExpressionLanguageParsingException("EXPRESSION 
tree node has no children");
-        }
-        final int firstChildIndex = tree.getChildCount() - offset - 1;
-        if (firstChildIndex == 0) {
-            return buildEvaluator(tree.getChild(0));
-        }
 
-        final Tree functionTree = tree.getChild(firstChildIndex);
-        final Evaluator<?> subjectEvaluator = 
buildFunctionExpressionEvaluator(tree, offset + 1);
-
-        final Tree functionNameTree = functionTree.getChild(0);
-        final List<Evaluator<?>> argEvaluators = new ArrayList<>();
-        for (int i = 1; i < functionTree.getChildCount(); i++) {
-            argEvaluators.add(buildEvaluator(functionTree.getChild(i)));
-        }
-        return buildFunctionEvaluator(functionNameTree, subjectEvaluator, 
argEvaluators);
-    }
-
-    private static List<Evaluator<?>> verifyArgCount(final List<Evaluator<?>> 
args, final int count, final String functionName) {
-        if (args.size() != count) {
-            throw new AttributeExpressionLanguageParsingException(functionName 
+ "() function takes " + count + " arguments");
-        }
-        return args;
-    }
-
-    private static Evaluator<String> toStringEvaluator(final Evaluator<?> 
evaluator) {
-        return toStringEvaluator(evaluator, null);
-    }
-
-    private static Evaluator<String> toStringEvaluator(final Evaluator<?> 
evaluator, final String location) {
-        if (evaluator.getResultType() == ResultType.STRING) {
-            return (StringEvaluator) evaluator;
-        }
-
-        return addToken(new StringCastEvaluator(evaluator), 
evaluator.getToken());
-    }
-
-    @SuppressWarnings("unchecked")
-    private static Evaluator<Boolean> toBooleanEvaluator(final Evaluator<?> 
evaluator, final String location) {
-        switch (evaluator.getResultType()) {
-            case BOOLEAN:
-                return (Evaluator<Boolean>) evaluator;
-            case STRING:
-                return addToken(new BooleanCastEvaluator((StringEvaluator) 
evaluator), evaluator.getToken());
-            default:
-                throw new AttributeExpressionLanguageParsingException("Cannot 
implicitly convert Data Type " + evaluator.getResultType() + " to " + 
ResultType.BOOLEAN
-                    + (location == null ? "" : " at location [" + location + 
"]"));
-        }
-
-    }
-
-    private static Evaluator<Boolean> toBooleanEvaluator(final Evaluator<?> 
evaluator) {
-        return toBooleanEvaluator(evaluator, null);
-    }
-
-    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<Number> toNumberEvaluator(final Evaluator<?> 
evaluator, final String location) {
-        switch (evaluator.getResultType()) {
-            case NUMBER:
-                return (Evaluator<Number>) evaluator;
-            case STRING:
-            case DATE:
-            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.WHOLE_NUMBER
-                        + (location == null ? "" : " at location [" + location 
+ "]"));
-        }
-    }
-
-    private static DateEvaluator toDateEvaluator(final Evaluator<?> evaluator) 
{
-        return toDateEvaluator(evaluator, null);
-    }
-
-    private static DateEvaluator toDateEvaluator(final Evaluator<?> evaluator, 
final String location) {
-        if (evaluator.getResultType() == ResultType.DATE) {
-            return (DateEvaluator) evaluator;
-        }
-
-        return new DateCastEvaluator(evaluator);
-    }
-
-    private static Evaluator<?> buildFunctionEvaluator(final Tree tree, final 
Evaluator<?> subjectEvaluator, final List<Evaluator<?>> argEvaluators) {
-        switch (tree.getType()) {
-            case TRIM: {
-                verifyArgCount(argEvaluators, 0, "trim");
-                return addToken(new 
TrimEvaluator(toStringEvaluator(subjectEvaluator)), "trim");
-            }
-            case TO_STRING: {
-                verifyArgCount(argEvaluators, 0, "toString");
-                return addToken(new ToStringEvaluator(subjectEvaluator), 
"toString");
-            }
-            case TO_LOWER: {
-                verifyArgCount(argEvaluators, 0, "toLower");
-                return addToken(new 
ToLowerEvaluator(toStringEvaluator(subjectEvaluator)), "toLower");
-            }
-            case TO_UPPER: {
-                verifyArgCount(argEvaluators, 0, "toUpper");
-                return addToken(new 
ToUpperEvaluator(toStringEvaluator(subjectEvaluator)), "toUpper");
-            }
-            case URL_ENCODE: {
-                verifyArgCount(argEvaluators, 0, "urlEncode");
-                return addToken(new 
UrlEncodeEvaluator(toStringEvaluator(subjectEvaluator)), "urlEncode");
-            }
-            case URL_DECODE: {
-                verifyArgCount(argEvaluators, 0, "urlDecode");
-                return addToken(new 
UrlDecodeEvaluator(toStringEvaluator(subjectEvaluator)), "urlDecode");
-            }
-            case BASE64_ENCODE: {
-                verifyArgCount(argEvaluators, 0, "base64Encode");
-                return addToken(new 
Base64EncodeEvaluator(toStringEvaluator(subjectEvaluator)), "base64Encode");
-            }
-            case BASE64_DECODE: {
-                verifyArgCount(argEvaluators, 0, "base64Decode");
-                return addToken(new 
Base64DecodeEvaluator(toStringEvaluator(subjectEvaluator)), "base64Decode");
-            }
-            case ESCAPE_CSV: {
-                verifyArgCount(argEvaluators, 0, "escapeCsv");
-                return 
addToken(CharSequenceTranslatorEvaluator.csvEscapeEvaluator(toStringEvaluator(subjectEvaluator)),
 "escapeJson");
-            }
-            case ESCAPE_HTML3: {
-                verifyArgCount(argEvaluators, 0, "escapeHtml3");
-                return 
addToken(CharSequenceTranslatorEvaluator.html3EscapeEvaluator(toStringEvaluator(subjectEvaluator)),
 "escapeJson");
-            }
-            case ESCAPE_HTML4: {
-                verifyArgCount(argEvaluators, 0, "escapeHtml4");
-                return 
addToken(CharSequenceTranslatorEvaluator.html4EscapeEvaluator(toStringEvaluator(subjectEvaluator)),
 "escapeJson");
-            }
-            case ESCAPE_JSON: {
-                verifyArgCount(argEvaluators, 0, "escapeJson");
-                return 
addToken(CharSequenceTranslatorEvaluator.jsonEscapeEvaluator(toStringEvaluator(subjectEvaluator)),
 "escapeJson");
-            }
-            case ESCAPE_XML: {
-                verifyArgCount(argEvaluators, 0, "escapeXml");
-                return 
addToken(CharSequenceTranslatorEvaluator.xmlEscapeEvaluator(toStringEvaluator(subjectEvaluator)),
 "escapeJson");
-            }
-            case UNESCAPE_CSV: {
-                verifyArgCount(argEvaluators, 0, "unescapeCsv");
-                return 
addToken(CharSequenceTranslatorEvaluator.csvUnescapeEvaluator(toStringEvaluator(subjectEvaluator)),
 "escapeJson");
-            }
-            case UNESCAPE_HTML3: {
-                verifyArgCount(argEvaluators, 0, "unescapeHtml3");
-                return 
addToken(CharSequenceTranslatorEvaluator.html3UnescapeEvaluator(toStringEvaluator(subjectEvaluator)),
 "escapeJson");
-            }
-            case UNESCAPE_HTML4: {
-                verifyArgCount(argEvaluators, 0, "unescapeHtml4");
-                return 
addToken(CharSequenceTranslatorEvaluator.html4UnescapeEvaluator(toStringEvaluator(subjectEvaluator)),
 "escapeJson");
-            }
-            case UNESCAPE_JSON: {
-                verifyArgCount(argEvaluators, 0, "unescapeJson");
-                return 
addToken(CharSequenceTranslatorEvaluator.jsonUnescapeEvaluator(toStringEvaluator(subjectEvaluator)),
 "escapeJson");
-            }
-            case UNESCAPE_XML: {
-                verifyArgCount(argEvaluators, 0, "unescapeXml");
-                return 
addToken(CharSequenceTranslatorEvaluator.xmlUnescapeEvaluator(toStringEvaluator(subjectEvaluator)),
 "escapeJson");
-            }
-            case SUBSTRING_BEFORE: {
-                verifyArgCount(argEvaluators, 1, "substringBefore");
-                return addToken(new 
SubstringBeforeEvaluator(toStringEvaluator(subjectEvaluator),
-                    toStringEvaluator(argEvaluators.get(0), "first argument to 
substringBefore")), "substringBefore");
-            }
-            case SUBSTRING_BEFORE_LAST: {
-                verifyArgCount(argEvaluators, 1, "substringBeforeLast");
-                return addToken(new 
SubstringBeforeLastEvaluator(toStringEvaluator(subjectEvaluator),
-                    toStringEvaluator(argEvaluators.get(0), "first argument to 
substringBeforeLast")), "substringBeforeLast");
-            }
-            case SUBSTRING_AFTER: {
-                verifyArgCount(argEvaluators, 1, "substringAfter");
-                return addToken(new 
SubstringAfterEvaluator(toStringEvaluator(subjectEvaluator),
-                    toStringEvaluator(argEvaluators.get(0), "first argument to 
substringAfter")), "substringAfter");
-            }
-            case SUBSTRING_AFTER_LAST: {
-                verifyArgCount(argEvaluators, 1, "substringAfterLast");
-                return addToken(new 
SubstringAfterLastEvaluator(toStringEvaluator(subjectEvaluator),
-                    toStringEvaluator(argEvaluators.get(0), "first argument to 
substringAfterLast")), "substringAfterLast");
-            }
-            case REPLACE_NULL: {
-                verifyArgCount(argEvaluators, 1, "replaceNull");
-                return addToken(new 
ReplaceNullEvaluator(toStringEvaluator(subjectEvaluator),
-                    toStringEvaluator(argEvaluators.get(0), "first argument to 
replaceNull")), "replaceNull");
-            }
-            case REPLACE_EMPTY: {
-                verifyArgCount(argEvaluators, 1, "replaceEmtpy");
-                return addToken(new 
ReplaceEmptyEvaluator(toStringEvaluator(subjectEvaluator), 
toStringEvaluator(argEvaluators.get(0), "first argument to replaceEmpty")), 
"replaceEmpty");
-            }
-            case REPLACE: {
-                verifyArgCount(argEvaluators, 2, "replace");
-                return addToken(new 
ReplaceEvaluator(toStringEvaluator(subjectEvaluator),
-                    toStringEvaluator(argEvaluators.get(0), "first argument to 
replace"),
-                    toStringEvaluator(argEvaluators.get(1), "second argument 
to replace")), "replace");
-            }
-            case REPLACE_FIRST: {
-                verifyArgCount(argEvaluators, 2, "replaceFirst");
-                return addToken(new 
ReplaceFirstEvaluator(toStringEvaluator(subjectEvaluator),
-                        toStringEvaluator(argEvaluators.get(0), "first 
argument to replaceFirst"),
-                        toStringEvaluator(argEvaluators.get(1), "second 
argument to replaceFirst")), "replaceFirst");
-            }
-            case REPLACE_ALL: {
-                verifyArgCount(argEvaluators, 2, "replaceAll");
-                return addToken(new 
ReplaceAllEvaluator(toStringEvaluator(subjectEvaluator),
-                    toStringEvaluator(argEvaluators.get(0), "first argument to 
replaceAll"),
-                    toStringEvaluator(argEvaluators.get(1), "second argument 
to replaceAll")), "replaceAll");
-            }
-            case APPEND: {
-                verifyArgCount(argEvaluators, 1, "append");
-                return addToken(new 
AppendEvaluator(toStringEvaluator(subjectEvaluator),
-                    toStringEvaluator(argEvaluators.get(0), "first argument to 
append")), "append");
-            }
-            case PREPEND: {
-                verifyArgCount(argEvaluators, 1, "prepend");
-                return addToken(new 
PrependEvaluator(toStringEvaluator(subjectEvaluator),
-                    toStringEvaluator(argEvaluators.get(0), "first argument to 
prepend")), "prepend");
-            }
-            case SUBSTRING: {
-                final int numArgs = argEvaluators.size();
-                if (numArgs == 1) {
-                    return addToken(new 
SubstringEvaluator(toStringEvaluator(subjectEvaluator),
-                        toWholeNumberEvaluator(argEvaluators.get(0), "first 
argument to substring")), "substring");
-                } else if (numArgs == 2) {
-                    return addToken(new 
SubstringEvaluator(toStringEvaluator(subjectEvaluator),
-                        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");
-                }
-            }
-            case JOIN: {
-                verifyArgCount(argEvaluators, 1, "join");
-                return addToken(new 
JoinEvaluator(toStringEvaluator(subjectEvaluator), 
toStringEvaluator(argEvaluators.get(0))), "join");
-            }
-            case COUNT: {
-                verifyArgCount(argEvaluators, 0, "count");
-                return addToken(new CountEvaluator(subjectEvaluator), "count");
-            }
-            case IS_NULL: {
-                verifyArgCount(argEvaluators, 0, "isNull");
-                return addToken(new 
IsNullEvaluator(toStringEvaluator(subjectEvaluator)), "isNull");
-            }
-            case IS_EMPTY: {
-                verifyArgCount(argEvaluators, 0, "isEmpty");
-                return addToken(new 
IsEmptyEvaluator(toStringEvaluator(subjectEvaluator)), "isEmpty");
-            }
-            case NOT_NULL: {
-                verifyArgCount(argEvaluators, 0, "notNull");
-                return addToken(new 
NotNullEvaluator(toStringEvaluator(subjectEvaluator)), "notNull");
-            }
-            case STARTS_WITH: {
-                verifyArgCount(argEvaluators, 1, "startsWith");
-                return addToken(new 
StartsWithEvaluator(toStringEvaluator(subjectEvaluator),
-                    toStringEvaluator(argEvaluators.get(0), "first argument to 
startsWith")), "startsWith");
-            }
-            case ENDS_WITH: {
-                verifyArgCount(argEvaluators, 1, "endsWith");
-                return addToken(new 
EndsWithEvaluator(toStringEvaluator(subjectEvaluator),
-                    toStringEvaluator(argEvaluators.get(0), "first argument to 
endsWith")), "endsWith");
-            }
-            case CONTAINS: {
-                verifyArgCount(argEvaluators, 1, "contains");
-                return addToken(new 
ContainsEvaluator(toStringEvaluator(subjectEvaluator),
-                    toStringEvaluator(argEvaluators.get(0), "first argument to 
contains")), "contains");
-            }
-            case IN: {
-                List<Evaluator<String>> list = new 
ArrayList<Evaluator<String>>();
-                for(int i = 0; i < argEvaluators.size(); i++) {
-                    list.add(toStringEvaluator(argEvaluators.get(i), i + "th 
argument to in"));
-                }
-                return addToken(new 
InEvaluator(toStringEvaluator(subjectEvaluator), list), "in");
-            }
-            case FIND: {
-                verifyArgCount(argEvaluators, 1, "find");
-                return addToken(new 
FindEvaluator(toStringEvaluator(subjectEvaluator),
-                    toStringEvaluator(argEvaluators.get(0), "first argument to 
find")), "find");
-            }
-            case MATCHES: {
-                verifyArgCount(argEvaluators, 1, "matches");
-                return addToken(new 
MatchesEvaluator(toStringEvaluator(subjectEvaluator),
-                    toStringEvaluator(argEvaluators.get(0), "first argument to 
matches")), "matches");
-            }
-            case EQUALS: {
-                verifyArgCount(argEvaluators, 1, "equals");
-                return addToken(new EqualsEvaluator(subjectEvaluator, 
argEvaluators.get(0)), "equals");
-            }
-            case EQUALS_IGNORE_CASE: {
-                verifyArgCount(argEvaluators, 1, "equalsIgnoreCase");
-                return addToken(new 
EqualsIgnoreCaseEvaluator(toStringEvaluator(subjectEvaluator),
-                    toStringEvaluator(argEvaluators.get(0), "first argument to 
equalsIgnoreCase")), "equalsIgnoreCase");
-            }
-            case GREATER_THAN: {
-                verifyArgCount(argEvaluators, 1, "gt");
-                return addToken(new 
GreaterThanEvaluator(toNumberEvaluator(subjectEvaluator),
-                    toNumberEvaluator(argEvaluators.get(0), "first argument to 
gt")), "gt");
-            }
-            case GREATER_THAN_OR_EQUAL: {
-                verifyArgCount(argEvaluators, 1, "ge");
-                return addToken(new 
GreaterThanOrEqualEvaluator(toNumberEvaluator(subjectEvaluator),
-                    toNumberEvaluator(argEvaluators.get(0), "first argument to 
ge")), "ge");
-            }
-            case LESS_THAN: {
-                verifyArgCount(argEvaluators, 1, "lt");
-                return addToken(new 
LessThanEvaluator(toNumberEvaluator(subjectEvaluator),
-                    toNumberEvaluator(argEvaluators.get(0), "first argument to 
lt")), "lt");
-            }
-            case LESS_THAN_OR_EQUAL: {
-                verifyArgCount(argEvaluators, 1, "le");
-                return addToken(new 
LessThanOrEqualEvaluator(toNumberEvaluator(subjectEvaluator),
-                    toNumberEvaluator(argEvaluators.get(0), "first argument to 
le")), "le");
-            }
-            case LENGTH: {
-                verifyArgCount(argEvaluators, 0, "length");
-                return addToken(new 
LengthEvaluator(toStringEvaluator(subjectEvaluator)), "length");
-            }
-            case TO_DATE: {
-                if (argEvaluators.isEmpty()) {
-                    return addToken(new 
NumberToDateEvaluator(toWholeNumberEvaluator(subjectEvaluator)), "toDate");
-                } else if (subjectEvaluator.getResultType() == 
ResultType.STRING && argEvaluators.size() == 1) {
-                    return addToken(new 
StringToDateEvaluator(toStringEvaluator(subjectEvaluator), 
toStringEvaluator(argEvaluators.get(0)), null), "toDate");
-                } else if (subjectEvaluator.getResultType() == 
ResultType.STRING && argEvaluators.size() == 2) {
-                    return addToken(new 
StringToDateEvaluator(toStringEvaluator(subjectEvaluator), 
toStringEvaluator(argEvaluators.get(0)), 
toStringEvaluator(argEvaluators.get(1))), "toDate");
-                } else {
-                    return addToken(new 
NumberToDateEvaluator(toWholeNumberEvaluator(subjectEvaluator)), "toDate");
-                }
-            }
-            case TO_NUMBER: {
-                verifyArgCount(argEvaluators, 0, "toNumber");
-                switch (subjectEvaluator.getResultType()) {
-                    case STRING:
-                    case WHOLE_NUMBER:
-                    case DECIMAL:
-                    case NUMBER:
-                    case DATE:
-                        return 
addToken(toWholeNumberEvaluator(subjectEvaluator), "toNumber");
-                    default:
-                        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(toWholeNumberEvaluator(subjectEvaluator),
-                            toWholeNumberEvaluator(argEvaluators.get(0))), 
"toRadix");
-                } else {
-                    return addToken(new 
ToRadixEvaluator(toWholeNumberEvaluator(subjectEvaluator),
-                            toWholeNumberEvaluator(argEvaluators.get(0)), 
toWholeNumberEvaluator(argEvaluators.get(1))), "toRadix");
-                }
-            }
-            case FROM_RADIX: {
-                return addToken(new 
FromRadixEvaluator(toStringEvaluator(subjectEvaluator),
-                        toWholeNumberEvaluator(argEvaluators.get(0))), 
"fromRadix");
-            }
-            case MOD: {
-                return addToken(new 
ModEvaluator(toNumberEvaluator(subjectEvaluator), 
toNumberEvaluator(argEvaluators.get(0))), "mod");
-            }
-            case PLUS: {
-                return addToken(new 
PlusEvaluator(toNumberEvaluator(subjectEvaluator), 
toNumberEvaluator(argEvaluators.get(0))), "plus");
-            }
-            case MINUS: {
-                return addToken(new 
MinusEvaluator(toNumberEvaluator(subjectEvaluator), 
toNumberEvaluator(argEvaluators.get(0))), "minus");
-            }
-            case MULTIPLY: {
-                return addToken(new 
MultiplyEvaluator(toNumberEvaluator(subjectEvaluator), 
toNumberEvaluator(argEvaluators.get(0))), "multiply");
-            }
-            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");
-            }
-            case INDEX_OF: {
-                verifyArgCount(argEvaluators, 1, "indexOf");
-                return addToken(new 
IndexOfEvaluator(toStringEvaluator(subjectEvaluator),
-                    toStringEvaluator(argEvaluators.get(0), "first argument to 
indexOf")), "indexOf");
-            }
-            case LAST_INDEX_OF: {
-                verifyArgCount(argEvaluators, 1, "lastIndexOf");
-                return addToken(new 
LastIndexOfEvaluator(toStringEvaluator(subjectEvaluator),
-                    toStringEvaluator(argEvaluators.get(0), "first argument to 
lastIndexOf")), "lastIndexOf");
-            }
-            case FORMAT: {
-                if(argEvaluators.size() == 1) {
-                    return addToken(new 
FormatEvaluator(toDateEvaluator(subjectEvaluator), 
toStringEvaluator(argEvaluators.get(0), "first argument of format"), null), 
"format");
-                } else if (argEvaluators.size() == 2) {
-                    return addToken(new 
FormatEvaluator(toDateEvaluator(subjectEvaluator), 
toStringEvaluator(argEvaluators.get(0)), 
toStringEvaluator(argEvaluators.get(1))), "format");
-                } else {
-                    throw new 
AttributeExpressionLanguageParsingException("format() function takes 1 or 2 
arguments");
-                }
-            }
-            case OR: {
-                return addToken(new 
OrEvaluator(toBooleanEvaluator(subjectEvaluator), 
toBooleanEvaluator(argEvaluators.get(0))), "or");
-            }
-            case AND: {
-                return addToken(new 
AndEvaluator(toBooleanEvaluator(subjectEvaluator), 
toBooleanEvaluator(argEvaluators.get(0))), "and");
-            }
-            case NOT: {
-                return addToken(new 
NotEvaluator(toBooleanEvaluator(subjectEvaluator)), "not");
-            }
-            case GET_DELIMITED_FIELD: {
-                if (argEvaluators.size() == 1) {
-                    // Only a single argument - the index to return.
-                    return addToken(new 
GetDelimitedFieldEvaluator(toStringEvaluator(subjectEvaluator),
-                        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),
-                        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),
-                        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),
-                        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")),
-                        "getDelimitedField");
-                } else {
-                    // 5 arguments - index, delimiter, quote char, escape 
char, strip escape/quote chars flag
-                    return addToken(new 
GetDelimitedFieldEvaluator(toStringEvaluator(subjectEvaluator),
-                        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"),
-                        toBooleanEvaluator(argEvaluators.get(4), "fifth 
argument of getDelimitedField")),
-                        "getDelimitedField");
-                }
-            }
-            case JSON_PATH: {
-                verifyArgCount(argEvaluators, 1, "jsonPath");
-                return addToken(new 
JsonPathEvaluator(toStringEvaluator(subjectEvaluator),
-                        toStringEvaluator(argEvaluators.get(0), "first 
argument to jsonPath")), "jsonPath");
-            }
-            case IF_ELSE: {
-                verifyArgCount(argEvaluators, 2, "ifElse");
-                return addToken(new 
IfElseEvaluator(toBooleanEvaluator(subjectEvaluator),
-                        toStringEvaluator(argEvaluators.get(0), "argument to 
return if true"),
-                        toStringEvaluator(argEvaluators.get(1), "argument to 
return if false")), "ifElse");
-            }
-            default:
-                throw new 
AttributeExpressionLanguageParsingException("Expected a Function-type 
expression but got " + tree.toString());
-            }
-    }
 
     public static class Range {
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/5cd8e93b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPreparedQuery.java
----------------------------------------------------------------------
diff --git 
a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPreparedQuery.java
 
b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPreparedQuery.java
index 9f12c92..cdf5a2d 100644
--- 
a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPreparedQuery.java
+++ 
b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPreparedQuery.java
@@ -16,36 +16,45 @@
  */
 package org.apache.nifi.attribute.expression.language;
 
-import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
+import 
org.apache.nifi.attribute.expression.language.compile.CompiledExpression;
+import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.literals.StringLiteralEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.selection.AllAttributesEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.selection.AnyAttributeEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.selection.AttributeEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.selection.MappingEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.selection.MultiAttributeEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.selection.MultiMatchAttributeEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.selection.MultiNamedAttributeEvaluator;
 import org.apache.nifi.expression.AttributeValueDecorator;
 import org.apache.nifi.processor.exception.ProcessException;
 
-import org.antlr.runtime.tree.Tree;
-
 public class StandardPreparedQuery implements PreparedQuery {
 
     private final List<String> queryStrings;
-    private final Map<String, Tree> trees;
+    private final Map<String, CompiledExpression> expressions;
+    private volatile VariableImpact variableImpact;
 
-    public StandardPreparedQuery(final List<String> queryStrings, final 
Map<String, Tree> trees) {
-        this.queryStrings = new ArrayList<>(queryStrings);
-        this.trees = new HashMap<>(trees);
+    public StandardPreparedQuery(final List<String> queryStrings, final 
Map<String, CompiledExpression> expressions) {
+        this.queryStrings = queryStrings;
+        this.expressions = expressions;
     }
 
-
     @Override
     public String evaluateExpressions(final Map<String, String> valMap, final 
AttributeValueDecorator decorator, final Map<String, String> stateVariables) 
throws ProcessException {
         final StringBuilder sb = new StringBuilder();
         for (final String val : queryStrings) {
-            final Tree tree = trees.get(val);
-            if (tree == null) {
+            final CompiledExpression expression = expressions.get(val);
+            if (expression == null) {
                 sb.append(val);
             } else {
-                final String evaluated = Query.evaluateExpression(tree, val, 
valMap, decorator, stateVariables);
+                final String evaluated = 
Query.evaluateExpression(expression.getTree(), val, valMap, decorator, 
stateVariables);
                 if (evaluated != null) {
                     sb.append(evaluated);
                 }
@@ -62,6 +71,56 @@ public class StandardPreparedQuery implements PreparedQuery {
 
     @Override
     public boolean isExpressionLanguagePresent() {
-        return !trees.isEmpty();
+        return !expressions.isEmpty();
+    }
+
+    @Override
+    public VariableImpact getVariableImpact() {
+        final VariableImpact existing = this.variableImpact;
+        if (existing != null) {
+            return existing;
+        }
+
+        final Set<String> variables = new HashSet<>();
+
+        for (final CompiledExpression expression : expressions.values()) {
+            for (final Evaluator<?> evaluator : expression.getAllEvaluators()) 
{
+                if (evaluator instanceof AttributeEvaluator) {
+                    final AttributeEvaluator attributeEval = 
(AttributeEvaluator) evaluator;
+                    final Evaluator<String> nameEval = 
attributeEval.getNameEvaluator();
+
+                    if (nameEval instanceof StringLiteralEvaluator) {
+                        final String referencedVar = 
nameEval.evaluate(Collections.emptyMap()).getValue();
+                        variables.add(referencedVar);
+                    }
+                } else if (evaluator instanceof AllAttributesEvaluator) {
+                    final AllAttributesEvaluator allAttrsEval = 
(AllAttributesEvaluator) evaluator;
+                    final MultiAttributeEvaluator iteratingEval = 
allAttrsEval.getVariableIteratingEvaluator();
+                    if (iteratingEval instanceof MultiNamedAttributeEvaluator) 
{
+                        variables.addAll(((MultiNamedAttributeEvaluator) 
iteratingEval).getAttributeNames());
+                    } else if (iteratingEval instanceof 
MultiMatchAttributeEvaluator) {
+                        return VariableImpact.ALWAYS_IMPACTED;
+                    }
+                } else if (evaluator instanceof AnyAttributeEvaluator) {
+                    final AnyAttributeEvaluator allAttrsEval = 
(AnyAttributeEvaluator) evaluator;
+                    final MultiAttributeEvaluator iteratingEval = 
allAttrsEval.getVariableIteratingEvaluator();
+                    if (iteratingEval instanceof MultiNamedAttributeEvaluator) 
{
+                        variables.addAll(((MultiNamedAttributeEvaluator) 
iteratingEval).getAttributeNames());
+                    } else if (iteratingEval instanceof 
MultiMatchAttributeEvaluator) {
+                        return VariableImpact.ALWAYS_IMPACTED;
+                    }
+                } else if (evaluator instanceof MappingEvaluator) {
+                    final MappingEvaluator<?> allAttrsEval = 
(MappingEvaluator<?>) evaluator;
+                    final MultiAttributeEvaluator iteratingEval = 
allAttrsEval.getVariableIteratingEvaluator();
+                    if (iteratingEval instanceof MultiNamedAttributeEvaluator) 
{
+                        variables.addAll(((MultiNamedAttributeEvaluator) 
iteratingEval).getAttributeNames());
+                    }
+                }
+            }
+        }
+
+        final VariableImpact impact = new NamedVariableImpact(variables);
+        this.variableImpact = impact;
+        return impact;
     }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/5cd8e93b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/ValueLookup.java
----------------------------------------------------------------------
diff --git 
a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/ValueLookup.java
 
b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/ValueLookup.java
index 5b0cdda..06c1877 100644
--- 
a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/ValueLookup.java
+++ 
b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/ValueLookup.java
@@ -49,6 +49,7 @@ final class ValueLookup implements Map<String, String> {
      * @param flowFile the flowFile to pull attributes from; may be null
      * @param additionalMaps the maps to pull values from; may be null or empty
      */
+    @SuppressWarnings("unchecked")
     ValueLookup(final VariableRegistry registry, final FlowFile flowFile, 
final Map<String, String>... additionalMaps) {
         for (final Map<String, String> map : additionalMaps) {
             if (map != null && !map.isEmpty()) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/5cd8e93b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/VariableImpact.java
----------------------------------------------------------------------
diff --git 
a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/VariableImpact.java
 
b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/VariableImpact.java
new file mode 100644
index 0000000..4a66c87
--- /dev/null
+++ 
b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/VariableImpact.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+public interface VariableImpact {
+    boolean isImpacted(String variableName);
+
+    public static final VariableImpact NEVER_IMPACTED = var -> false;
+
+    public static final VariableImpact ALWAYS_IMPACTED = var -> true;
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/5cd8e93b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/CompiledExpression.java
----------------------------------------------------------------------
diff --git 
a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/CompiledExpression.java
 
b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/CompiledExpression.java
new file mode 100644
index 0000000..d234445
--- /dev/null
+++ 
b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/CompiledExpression.java
@@ -0,0 +1,53 @@
+/*
+ * 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.compile;
+
+import java.util.Set;
+
+import org.antlr.runtime.tree.Tree;
+import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
+
+public class CompiledExpression {
+    private final Evaluator<?> rootEvaluator;
+    private final Tree tree;
+    private final String expression;
+    private final Set<Evaluator<?>> allEvaluators;
+
+    public CompiledExpression(final String expression, final Evaluator<?> 
rootEvaluator, final Tree tree, final Set<Evaluator<?>> allEvaluators) {
+        this.rootEvaluator = rootEvaluator;
+        this.tree = tree;
+        this.expression = expression;
+        this.allEvaluators = allEvaluators;
+    }
+
+    public Evaluator<?> getRootEvaluator() {
+        return rootEvaluator;
+    }
+
+    public Tree getTree() {
+        return tree;
+    }
+
+    public String getExpression() {
+        return expression;
+    }
+
+    public Set<Evaluator<?>> getAllEvaluators() {
+        return allEvaluators;
+    }
+}

Reply via email to