http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/Query.java
----------------------------------------------------------------------
diff --git 
a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/Query.java
 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/Query.java
new file mode 100644
index 0000000..6d9cc91
--- /dev/null
+++ 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/Query.java
@@ -0,0 +1,1183 @@
+/*
+ * 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;
+
+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.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.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_NULL;
+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.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.REPLACE_ALL;
+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_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.UUID;
+
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+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.apache.nifi.attribute.expression.language.evaluation.Evaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.NumberEvaluator;
+import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
+import 
org.apache.nifi.attribute.expression.language.evaluation.StringEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.cast.BooleanCastEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.cast.DateCastEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.cast.NumberCastEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.cast.StringCastEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.functions.AndEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.functions.AppendEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.functions.AttributeEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.functions.ContainsEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.functions.DateToNumberEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.functions.DivideEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.functions.EndsWithEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.functions.EqualsEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.functions.EqualsIgnoreCaseEvaluator;
+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.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.IndexOfEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.functions.IsNullEvaluator;
+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.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.ReplaceAllEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.functions.ReplaceEvaluator;
+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.ToNumberEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.functions.ToRadixEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.functions.ToStringEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.functions.ToUpperEvaluator;
+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.UuidEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.literals.BooleanLiteralEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.literals.NumberLiteralEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.literals.StringLiteralEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.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.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;
+
+/**
+ * Class used for creating and evaluating NiFi Expression Language. Once a 
Query
+ * has been created, it may be evaluated using the evaluate methods exactly
+ * once.
+ */
+public class Query {
+
+    private final String query;
+    private final Tree tree;
+    private final Evaluator<?> evaluator;
+    private final AtomicBoolean evaluated = new AtomicBoolean(false);
+
+    private Query(final String query, final Tree tree, final Evaluator<?> 
evaluator) {
+        this.query = query;
+        this.tree = tree;
+        this.evaluator = evaluator;
+    }
+
+    public static boolean isValidExpression(final String value) {
+        try {
+            validateExpression(value, false);
+            return true;
+        } catch (final ProcessException e) {
+            return false;
+        }
+    }
+
+    public static ResultType getResultType(final String value) throws 
AttributeExpressionLanguageParsingException {
+        return Query.compile(value).getResultType();
+    }
+
+    public static List<ResultType> extractResultTypes(final String value) 
throws AttributeExpressionLanguageParsingException {
+        final List<ResultType> types = new ArrayList<>();
+
+        for (final Range range : extractExpressionRanges(value)) {
+            final String text = value.substring(range.getStart(), 
range.getEnd() + 1);
+            types.add(getResultType(text));
+        }
+
+        return types;
+    }
+
+    public static List<String> extractExpressions(final String value) throws 
AttributeExpressionLanguageParsingException {
+        final List<String> expressions = new ArrayList<>();
+
+        for (final Range range : extractExpressionRanges(value)) {
+            expressions.add(value.substring(range.getStart(), range.getEnd() + 
1));
+        }
+
+        return expressions;
+    }
+
+    public static List<Range> extractExpressionRanges(final String value) 
throws AttributeExpressionLanguageParsingException {
+        final List<Range> ranges = new ArrayList<>();
+        char lastChar = 0;
+        int embeddedCount = 0;
+        int expressionStart = -1;
+        boolean oddDollarCount = false;
+        int backslashCount = 0;
+
+        charLoop:
+        for (int i = 0; i < value.length(); i++) {
+            final char c = value.charAt(i);
+
+            if (expressionStart > -1 && (c == '\'' || c == '"') && (lastChar 
!= '\\' || backslashCount % 2 == 0)) {
+                final int endQuoteIndex = findEndQuoteChar(value, i);
+                if (endQuoteIndex < 0) {
+                    break charLoop;
+                }
+
+                i = endQuoteIndex;
+                continue;
+            }
+
+            if (c == '{') {
+                if (oddDollarCount && lastChar == '$') {
+                    if (embeddedCount == 0) {
+                        expressionStart = i - 1;
+                    }
+                }
+
+                embeddedCount++;
+            } else if (c == '}') {
+                if (embeddedCount <= 0) {
+                    continue;
+                }
+
+                if (--embeddedCount == 0) {
+                    if (expressionStart > -1) {
+                        // ended expression. Add a new range.
+                        final Range range = new Range(expressionStart, i);
+                        ranges.add(range);
+                    }
+
+                    expressionStart = -1;
+                }
+            } else if (c == '$') {
+                oddDollarCount = !oddDollarCount;
+            } else if (c == '\\') {
+                backslashCount++;
+            } else {
+                oddDollarCount = false;
+            }
+
+            lastChar = c;
+        }
+
+        return ranges;
+    }
+
+    /**
+     *
+     *
+     * @param value
+     * @param allowSurroundingCharacters
+     * @throws AttributeExpressionLanguageParsingException
+     */
+    public static void validateExpression(final String value, final boolean 
allowSurroundingCharacters) throws AttributeExpressionLanguageParsingException {
+        if (!allowSurroundingCharacters) {
+            final List<Range> ranges = extractExpressionRanges(value);
+            if (ranges.size() > 1) {
+                throw new AttributeExpressionLanguageParsingException("Found 
multiple Expressions but expected only 1");
+            }
+
+            if (ranges.isEmpty()) {
+                throw new AttributeExpressionLanguageParsingException("No 
Expressions found");
+            }
+
+            final Range range = ranges.get(0);
+            final String expression = value.substring(range.getStart(), 
range.getEnd() + 1);
+            Query.compile(expression);
+
+            if (range.getStart() > 0 || range.getEnd() < value.length() - 1) {
+                throw new AttributeExpressionLanguageParsingException("Found 
characters outside of Expression");
+            }
+        } else {
+            for (final Range range : extractExpressionRanges(value)) {
+                final String expression = value.substring(range.getStart(), 
range.getEnd() + 1);
+                Query.compile(expression);
+            }
+        }
+    }
+
+    static int findEndQuoteChar(final String value, final int quoteStart) {
+        final char quoteChar = value.charAt(quoteStart);
+
+        int backslashCount = 0;
+        char lastChar = 0;
+        for (int i = quoteStart + 1; i < value.length(); i++) {
+            final char c = value.charAt(i);
+
+            if (c == '\\') {
+                backslashCount++;
+            } else if (c == quoteChar && ((backslashCount % 2 == 0) || 
lastChar != '\\')) {
+                return i;
+            }
+
+            lastChar = c;
+        }
+
+        return -1;
+    }
+
+    static String evaluateExpression(final Tree tree, final String queryText, 
final Map<String, String> expressionMap, final AttributeValueDecorator 
decorator) throws ProcessException {
+        final Object evaluated = Query.fromTree(tree, 
queryText).evaluate(expressionMap).getValue();
+        if (evaluated == null) {
+            return null;
+        }
+
+        final String value = evaluated.toString();
+        final String escaped = value.replace("$$", "$");
+        return (decorator == null) ? escaped : decorator.decorate(escaped);
+    }
+
+    static String evaluateExpressions(final String rawValue, Map<String, 
String> expressionMap) throws ProcessException {
+        return evaluateExpressions(rawValue, expressionMap, null);
+    }
+
+    static String evaluateExpressions(final String rawValue) throws 
ProcessException {
+        return evaluateExpressions(rawValue, createExpressionMap(null), null);
+    }
+
+    static String evaluateExpressions(final String rawValue, final FlowFile 
flowFile) throws ProcessException {
+        return evaluateExpressions(rawValue, createExpressionMap(flowFile), 
null);
+    }
+
+    static String evaluateExpressions(final String rawValue, Map<String, 
String> expressionMap, final AttributeValueDecorator decorator) throws 
ProcessException {
+        return Query.prepare(rawValue).evaluateExpressions(expressionMap, 
decorator);
+    }
+
+    public static String evaluateExpressions(final String rawValue, final 
FlowFile flowFile, final AttributeValueDecorator decorator) throws 
ProcessException {
+        if (rawValue == null) {
+            return null;
+        }
+
+        final Map<String, String> expressionMap = 
createExpressionMap(flowFile);
+        return evaluateExpressions(rawValue, expressionMap, 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
+     *
+     * @param value
+     * @return
+     */
+    public static String unescape(final String value) {
+        return value.replaceAll("\\$\\$(?=\\$*\\{.*?\\})", "\\$");
+    }
+
+    static Map<String, String> createExpressionMap(final FlowFile flowFile) {
+        final Map<String, String> attributeMap = flowFile == null ? new 
HashMap<String, String>() : flowFile.getAttributes();
+        final Map<String, String> envMap = System.getenv();
+        final Map<?, ?> sysProps = System.getProperties();
+
+        final Map<String, String> flowFileProps = new HashMap<>();
+        if (flowFile != null) {
+            flowFileProps.put("flowFileId", String.valueOf(flowFile.getId()));
+            flowFileProps.put("fileSize", String.valueOf(flowFile.getSize()));
+            flowFileProps.put("entryDate", 
String.valueOf(flowFile.getEntryDate()));
+            flowFileProps.put("lineageStartDate", 
String.valueOf(flowFile.getLineageStartDate()));
+        }
+
+        return wrap(attributeMap, flowFileProps, envMap, sysProps);
+    }
+
+    private static Map<String, String> wrap(final Map<String, String> 
attributes, final Map<String, String> flowFileProps,
+            final Map<String, String> env, final Map<?, ?> sysProps) {
+        @SuppressWarnings("rawtypes")
+        final Map[] maps = new Map[]{attributes, flowFileProps, env, sysProps};
+
+        return new Map<String, String>() {
+            @Override
+            public int size() {
+                int size = 0;
+                for (final Map<?, ?> map : maps) {
+                    size += map.size();
+                }
+                return size;
+            }
+
+            @Override
+            public boolean isEmpty() {
+                for (final Map<?, ?> map : maps) {
+                    if (!map.isEmpty()) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            @Override
+            public boolean containsKey(final Object key) {
+                if (key == null) {
+                    return false;
+                }
+                if (!(key instanceof String)) {
+                    return false;
+                }
+
+                for (final Map<?, ?> map : maps) {
+                    if (map.containsKey(key)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            @Override
+            public boolean containsValue(final Object value) {
+                for (final Map<?, ?> map : maps) {
+                    if (map.containsValue(value)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            @Override
+            @SuppressWarnings("rawtypes")
+            public String get(final Object key) {
+                if (key == null) {
+                    throw new IllegalArgumentException("Null Keys are not 
allowed");
+                }
+                if (!(key instanceof String)) {
+                    return null;
+                }
+
+                for (final Map map : maps) {
+                    final Object val = map.get(key);
+                    if (val != null) {
+                        return String.valueOf(val);
+                    }
+                }
+                return null;
+            }
+
+            @Override
+            public String put(String key, String value) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public String remove(final Object key) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public void putAll(final Map<? extends String, ? extends String> 
m) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public void clear() {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            @SuppressWarnings({"unchecked", "rawtypes"})
+            public Set<String> keySet() {
+                final Set<String> keySet = new HashSet<>();
+                for (final Map map : maps) {
+                    keySet.addAll(map.keySet());
+                }
+                return keySet;
+            }
+
+            @Override
+            @SuppressWarnings({"unchecked", "rawtypes"})
+            public Collection<String> values() {
+                final Set<String> values = new HashSet<>();
+                for (final Map map : maps) {
+                    values.addAll(map.values());
+                }
+                return values;
+            }
+
+            @Override
+            @SuppressWarnings({"unchecked", "rawtypes"})
+            public Set<java.util.Map.Entry<String, String>> entrySet() {
+                final Set<java.util.Map.Entry<String, String>> entrySet = new 
HashSet<>();
+                for (final Map map : maps) {
+                    entrySet.addAll(map.entrySet());
+                }
+                return entrySet;
+            }
+
+        };
+    }
+
+    public static Query fromTree(final Tree tree, final String text) {
+        return new Query(text, tree, 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
+            buildEvaluator(tree);
+            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) {
+            return new EmptyPreparedQuery(null);
+        }
+
+        final List<Range> ranges = extractExpressionRanges(query);
+
+        if (ranges.isEmpty()) {
+            return new EmptyPreparedQuery(query.replace("$$", "$"));
+        }
+
+        final List<String> substrings = new ArrayList<>();
+        final Map<String, Tree> trees = new HashMap<>();
+
+        int lastIndex = 0;
+        for (final Range range : ranges) {
+            if (range.getStart() > lastIndex) {
+                substrings.add(query.substring(lastIndex, 
range.getStart()).replace("$$", "$"));
+                lastIndex = range.getEnd() + 1;
+            }
+
+            final String treeText = query.substring(range.getStart(), 
range.getEnd() + 1).replace("$$", "$");
+            substrings.add(treeText);
+            trees.put(treeText, Query.compileTree(treeText));
+            lastIndex = range.getEnd() + 1;
+        }
+
+        final Range lastRange = ranges.get(ranges.size() - 1);
+        if (lastRange.getEnd() + 1 < query.length()) {
+            final String treeText = query.substring(lastRange.getEnd() + 
1).replace("$$", "$");
+            substrings.add(treeText);
+        }
+
+        return new StandardPreparedQuery(substrings, trees);
+    }
+
+    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);
+
+            return new Query(query, tree, buildEvaluator(tree));
+        } catch (final AttributeExpressionLanguageParsingException e) {
+            throw e;
+        } catch (final Exception e) {
+            throw new AttributeExpressionLanguageParsingException(e);
+        }
+    }
+
+    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();
+    }
+
+    QueryResult<?> evaluate() {
+        return evaluate(createExpressionMap(null));
+    }
+
+    QueryResult<?> evaluate(final FlowFile flowFile) {
+        return evaluate(createExpressionMap(flowFile));
+    }
+
+    QueryResult<?> evaluate(final Map<String, String> attributes) {
+        if (evaluated.getAndSet(true)) {
+            throw new IllegalStateException("A Query cannot be evaluated more 
than once");
+        }
+
+        Evaluator<?> chosenEvaluator = evaluator;
+        final Evaluator<?> rootEvaluator = getRootSubjectEvaluator(evaluator);
+        if (rootEvaluator != null) {
+            if (rootEvaluator instanceof MultiAttributeEvaluator) {
+                if (evaluator.getResultType() != ResultType.BOOLEAN) {
+                    throw new 
AttributeExpressionLanguageParsingException("Found Multi-Attribute function but 
return type is " + evaluator.getResultType() + ", not " + ResultType.BOOLEAN + 
", for query: " + query);
+                }
+
+                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:
+                        chosenEvaluator = new 
AllAttributesEvaluator((BooleanEvaluator) evaluator, multiAttrEval);
+                        break;
+                }
+            }
+        }
+
+        return chosenEvaluator.evaluate(attributes);
+    }
+
+    Tree getTree() {
+        return this.tree;
+    }
+
+    @Override
+    public String toString() {
+        return "Query [" + query + "]";
+    }
+
+    private static StringEvaluator 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));
+        }
+
+        StringEvaluator 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 StringEvaluator delineatedValueEvaluator = 
toStringEvaluator(buildEvaluator(tree.getChild(1)));
+                    final StringEvaluator 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 NUMBER: {
+                return new NumberLiteralEvaluator(tree.getText());
+            }
+            case STRING_LITERAL: {
+                return newStringLiteralEvaluator(tree.getText());
+            }
+            case TRUE:
+            case FALSE:
+                return buildBooleanEvaluator(tree);
+            case UUID: {
+                return new UuidEvaluator();
+            }
+            case NOW: {
+                return new NowEvaluator();
+            }
+            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 (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 (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();
+            }
+            default:
+                throw new 
AttributeExpressionLanguageParsingException("Unexpected token: " + 
tree.toString());
+        }
+    }
+
+    private static Evaluator<Boolean> buildBooleanEvaluator(final Tree tree) {
+        switch (tree.getType()) {
+            case TRUE:
+                return new BooleanLiteralEvaluator(true);
+            case FALSE:
+                return new BooleanLiteralEvaluator(false);
+        }
+        throw new AttributeExpressionLanguageParsingException("Cannot build 
Boolean evaluator from tree " + tree.toString());
+    }
+
+    private static Evaluator<?> buildExpressionEvaluator(final Tree tree) {
+        if (tree.getChildCount() == 0) {
+            throw new AttributeExpressionLanguageParsingException("EXPRESSION 
tree node has no children");
+        }
+        if (tree.getChildCount() == 1) {
+            return 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.
+            return buildFunctionExpressionEvaluator(tree, 0);
+        }
+    }
+
+    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 StringEvaluator toStringEvaluator(final Evaluator<?> 
evaluator) {
+        return toStringEvaluator(evaluator, null);
+    }
+
+    private static StringEvaluator toStringEvaluator(final Evaluator<?> 
evaluator, final String location) {
+        if (evaluator.getResultType() == ResultType.STRING) {
+            return (StringEvaluator) evaluator;
+        }
+
+        return new StringCastEvaluator(evaluator);
+    }
+
+    private static BooleanEvaluator toBooleanEvaluator(final Evaluator<?> 
evaluator, final String location) {
+        switch (evaluator.getResultType()) {
+            case BOOLEAN:
+                return (BooleanEvaluator) evaluator;
+            case STRING:
+                return new BooleanCastEvaluator((StringEvaluator) evaluator);
+            default:
+                throw new AttributeExpressionLanguageParsingException("Cannot 
implicitly convert Data Type " + evaluator.getResultType() + " to " + 
ResultType.BOOLEAN
+                        + (location == null ? "" : " at location [" + location 
+ "]"));
+        }
+
+    }
+
+    private static BooleanEvaluator toBooleanEvaluator(final Evaluator<?> 
evaluator) {
+        return toBooleanEvaluator(evaluator, null);
+    }
+
+    private static NumberEvaluator toNumberEvaluator(final Evaluator<?> 
evaluator) {
+        return toNumberEvaluator(evaluator, null);
+    }
+
+    private static NumberEvaluator toNumberEvaluator(final Evaluator<?> 
evaluator, final String location) {
+        switch (evaluator.getResultType()) {
+            case NUMBER:
+                return (NumberEvaluator) evaluator;
+            case STRING:
+                return new NumberCastEvaluator((StringEvaluator) evaluator);
+            default:
+                throw new AttributeExpressionLanguageParsingException("Cannot 
implicitly convert Data Type " + evaluator.getResultType() + " to " + 
ResultType.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 new TrimEvaluator(toStringEvaluator(subjectEvaluator));
+            }
+            case TO_STRING: {
+                verifyArgCount(argEvaluators, 0, "toString");
+                return new ToStringEvaluator(subjectEvaluator);
+            }
+            case TO_LOWER: {
+                verifyArgCount(argEvaluators, 0, "toLower");
+                return new 
ToLowerEvaluator(toStringEvaluator(subjectEvaluator));
+            }
+            case TO_UPPER: {
+                verifyArgCount(argEvaluators, 0, "toUpper");
+                return new 
ToUpperEvaluator(toStringEvaluator(subjectEvaluator));
+            }
+            case URL_ENCODE: {
+                verifyArgCount(argEvaluators, 0, "urlEncode");
+                return new 
UrlEncodeEvaluator(toStringEvaluator(subjectEvaluator));
+            }
+            case URL_DECODE: {
+                verifyArgCount(argEvaluators, 0, "urlDecode");
+                return new 
UrlDecodeEvaluator(toStringEvaluator(subjectEvaluator));
+            }
+            case SUBSTRING_BEFORE: {
+                verifyArgCount(argEvaluators, 1, "substringBefore");
+                return new 
SubstringBeforeEvaluator(toStringEvaluator(subjectEvaluator),
+                        toStringEvaluator(argEvaluators.get(0), "first 
argument to substringBefore"));
+            }
+            case SUBSTRING_BEFORE_LAST: {
+                verifyArgCount(argEvaluators, 1, "substringBeforeLast");
+                return new 
SubstringBeforeLastEvaluator(toStringEvaluator(subjectEvaluator),
+                        toStringEvaluator(argEvaluators.get(0), "first 
argument to substringBeforeLast"));
+            }
+            case SUBSTRING_AFTER: {
+                verifyArgCount(argEvaluators, 1, "substringAfter");
+                return new 
SubstringAfterEvaluator(toStringEvaluator(subjectEvaluator),
+                        toStringEvaluator(argEvaluators.get(0), "first 
argument to substringAfter"));
+            }
+            case SUBSTRING_AFTER_LAST: {
+                verifyArgCount(argEvaluators, 1, "substringAfterLast");
+                return new 
SubstringAfterLastEvaluator(toStringEvaluator(subjectEvaluator),
+                        toStringEvaluator(argEvaluators.get(0), "first 
argument to substringAfterLast"));
+            }
+            case REPLACE_NULL: {
+                verifyArgCount(argEvaluators, 1, "replaceNull");
+                return new 
ReplaceNullEvaluator(toStringEvaluator(subjectEvaluator),
+                        toStringEvaluator(argEvaluators.get(0), "first 
argument to replaceNull"));
+            }
+            case REPLACE: {
+                verifyArgCount(argEvaluators, 2, "replace");
+                return new 
ReplaceEvaluator(toStringEvaluator(subjectEvaluator),
+                        toStringEvaluator(argEvaluators.get(0), "first 
argument to replace"),
+                        toStringEvaluator(argEvaluators.get(1), "second 
argument to replace"));
+            }
+            case REPLACE_ALL: {
+                verifyArgCount(argEvaluators, 2, "replaceAll");
+                return new 
ReplaceAllEvaluator(toStringEvaluator(subjectEvaluator),
+                        toStringEvaluator(argEvaluators.get(0), "first 
argument to replaceAll"),
+                        toStringEvaluator(argEvaluators.get(1), "second 
argument to replaceAll"));
+            }
+            case APPEND: {
+                verifyArgCount(argEvaluators, 1, "append");
+                return new AppendEvaluator(toStringEvaluator(subjectEvaluator),
+                        toStringEvaluator(argEvaluators.get(0), "first 
argument to append"));
+            }
+            case PREPEND: {
+                verifyArgCount(argEvaluators, 1, "prepend");
+                return new 
PrependEvaluator(toStringEvaluator(subjectEvaluator),
+                        toStringEvaluator(argEvaluators.get(0), "first 
argument to prepend"));
+            }
+            case SUBSTRING: {
+                final int numArgs = argEvaluators.size();
+                if (numArgs == 1) {
+                    return new 
SubstringEvaluator(toStringEvaluator(subjectEvaluator),
+                            toNumberEvaluator(argEvaluators.get(0), "first 
argument to substring"));
+                } else if (numArgs == 2) {
+                    return new 
SubstringEvaluator(toStringEvaluator(subjectEvaluator),
+                            toNumberEvaluator(argEvaluators.get(0), "first 
argument to substring"),
+                            toNumberEvaluator(argEvaluators.get(1), "second 
argument to substring"));
+                } else {
+                    throw new 
AttributeExpressionLanguageParsingException("substring() function can take 
either 1 or 2 arguments but cannot take " + numArgs + " arguments");
+                }
+            }
+            case IS_NULL: {
+                verifyArgCount(argEvaluators, 0, "isNull");
+                return new 
IsNullEvaluator(toStringEvaluator(subjectEvaluator));
+            }
+            case NOT_NULL: {
+                verifyArgCount(argEvaluators, 0, "notNull");
+                return new 
NotNullEvaluator(toStringEvaluator(subjectEvaluator));
+            }
+            case STARTS_WITH: {
+                verifyArgCount(argEvaluators, 1, "startsWith");
+                return new 
StartsWithEvaluator(toStringEvaluator(subjectEvaluator),
+                        toStringEvaluator(argEvaluators.get(0), "first 
argument to startsWith"));
+            }
+            case ENDS_WITH: {
+                verifyArgCount(argEvaluators, 1, "endsWith");
+                return new 
EndsWithEvaluator(toStringEvaluator(subjectEvaluator),
+                        toStringEvaluator(argEvaluators.get(0), "first 
argument to endsWith"));
+            }
+            case CONTAINS: {
+                verifyArgCount(argEvaluators, 1, "contains");
+                return new 
ContainsEvaluator(toStringEvaluator(subjectEvaluator),
+                        toStringEvaluator(argEvaluators.get(0), "first 
argument to contains"));
+            }
+            case FIND: {
+                verifyArgCount(argEvaluators, 1, "find");
+                return new FindEvaluator(toStringEvaluator(subjectEvaluator),
+                        toStringEvaluator(argEvaluators.get(0), "first 
argument to find"));
+            }
+            case MATCHES: {
+                verifyArgCount(argEvaluators, 1, "matches");
+                return new 
MatchesEvaluator(toStringEvaluator(subjectEvaluator),
+                        toStringEvaluator(argEvaluators.get(0), "first 
argument to matches"));
+            }
+            case EQUALS: {
+                verifyArgCount(argEvaluators, 1, "equals");
+                return new EqualsEvaluator(subjectEvaluator, 
argEvaluators.get(0));
+            }
+            case EQUALS_IGNORE_CASE: {
+                verifyArgCount(argEvaluators, 1, "equalsIgnoreCase");
+                return new 
EqualsIgnoreCaseEvaluator(toStringEvaluator(subjectEvaluator),
+                        toStringEvaluator(argEvaluators.get(0), "first 
argument to equalsIgnoreCase"));
+            }
+            case GREATER_THAN: {
+                verifyArgCount(argEvaluators, 1, "gt");
+                return new 
GreaterThanEvaluator(toNumberEvaluator(subjectEvaluator),
+                        toNumberEvaluator(argEvaluators.get(0), "first 
argument to gt"));
+            }
+            case GREATER_THAN_OR_EQUAL: {
+                verifyArgCount(argEvaluators, 1, "ge");
+                return new 
GreaterThanOrEqualEvaluator(toNumberEvaluator(subjectEvaluator),
+                        toNumberEvaluator(argEvaluators.get(0), "first 
argument to ge"));
+            }
+            case LESS_THAN: {
+                verifyArgCount(argEvaluators, 1, "lt");
+                return new 
LessThanEvaluator(toNumberEvaluator(subjectEvaluator),
+                        toNumberEvaluator(argEvaluators.get(0), "first 
argument to lt"));
+            }
+            case LESS_THAN_OR_EQUAL: {
+                verifyArgCount(argEvaluators, 1, "le");
+                return new 
LessThanOrEqualEvaluator(toNumberEvaluator(subjectEvaluator),
+                        toNumberEvaluator(argEvaluators.get(0), "first 
argument to le"));
+            }
+            case LENGTH: {
+                verifyArgCount(argEvaluators, 0, "length");
+                return new 
LengthEvaluator(toStringEvaluator(subjectEvaluator));
+            }
+            case TO_DATE: {
+                if (argEvaluators.isEmpty()) {
+                    return new 
NumberToDateEvaluator(toNumberEvaluator(subjectEvaluator));
+                } else if (subjectEvaluator.getResultType() == 
ResultType.STRING) {
+                    return new 
StringToDateEvaluator(toStringEvaluator(subjectEvaluator), 
toStringEvaluator(argEvaluators.get(0)));
+                } else {
+                    return new 
NumberToDateEvaluator(toNumberEvaluator(subjectEvaluator));
+                }
+            }
+            case TO_NUMBER: {
+                verifyArgCount(argEvaluators, 0, "toNumber");
+                switch (subjectEvaluator.getResultType()) {
+                    case STRING:
+                        return new ToNumberEvaluator((StringEvaluator) 
subjectEvaluator);
+                    case DATE:
+                        return new DateToNumberEvaluator((DateEvaluator) 
subjectEvaluator);
+                    default:
+                        throw new 
AttributeExpressionLanguageParsingException(subjectEvaluator + " returns type " 
+ subjectEvaluator.getResultType() + " but expected to get " + 
ResultType.STRING);
+                }
+            }
+            case TO_RADIX: {
+                if (argEvaluators.size() == 1) {
+                    return new ToRadixEvaluator((NumberEvaluator) 
subjectEvaluator, toNumberEvaluator(argEvaluators.get(0)));
+                } else {
+                    return new ToRadixEvaluator((NumberEvaluator) 
subjectEvaluator, toNumberEvaluator(argEvaluators.get(0)), 
toNumberEvaluator(argEvaluators.get(1)));
+                }
+            }
+            case MOD: {
+                return new ModEvaluator(toNumberEvaluator(subjectEvaluator), 
toNumberEvaluator(argEvaluators.get(0)));
+            }
+            case PLUS: {
+                return new PlusEvaluator(toNumberEvaluator(subjectEvaluator), 
toNumberEvaluator(argEvaluators.get(0)));
+            }
+            case MINUS: {
+                return new MinusEvaluator(toNumberEvaluator(subjectEvaluator), 
toNumberEvaluator(argEvaluators.get(0)));
+            }
+            case MULTIPLY: {
+                return new 
MultiplyEvaluator(toNumberEvaluator(subjectEvaluator), 
toNumberEvaluator(argEvaluators.get(0)));
+            }
+            case DIVIDE: {
+                return new 
DivideEvaluator(toNumberEvaluator(subjectEvaluator), 
toNumberEvaluator(argEvaluators.get(0)));
+            }
+            case INDEX_OF: {
+                verifyArgCount(argEvaluators, 1, "indexOf");
+                return new 
IndexOfEvaluator(toStringEvaluator(subjectEvaluator),
+                        toStringEvaluator(argEvaluators.get(0), "first 
argument to indexOf"));
+            }
+            case LAST_INDEX_OF: {
+                verifyArgCount(argEvaluators, 1, "lastIndexOf");
+                return new 
LastIndexOfEvaluator(toStringEvaluator(subjectEvaluator),
+                        toStringEvaluator(argEvaluators.get(0), "first 
argument to lastIndexOf"));
+            }
+            case FORMAT: {
+                return new FormatEvaluator(toDateEvaluator(subjectEvaluator), 
toStringEvaluator(argEvaluators.get(0), "first argument of format"));
+            }
+            case OR: {
+                return new OrEvaluator(toBooleanEvaluator(subjectEvaluator), 
toBooleanEvaluator(argEvaluators.get(0)));
+            }
+            case AND: {
+                return new AndEvaluator(toBooleanEvaluator(subjectEvaluator), 
toBooleanEvaluator(argEvaluators.get(0)));
+            }
+            case NOT: {
+                return new NotEvaluator(toBooleanEvaluator(subjectEvaluator));
+            }
+            default:
+                throw new 
AttributeExpressionLanguageParsingException("Expected a Function-type 
expression but got " + tree.toString());
+        }
+    }
+
+    public static class Range {
+
+        private final int start;
+        private final int end;
+
+        public Range(final int start, final int end) {
+            this.start = start;
+            this.end = end;
+        }
+
+        public int getStart() {
+            return start;
+        }
+
+        public int getEnd() {
+            return end;
+        }
+
+        @Override
+        public String toString() {
+            return start + " - " + end;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardAttributeExpression.java
----------------------------------------------------------------------
diff --git 
a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardAttributeExpression.java
 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardAttributeExpression.java
new file mode 100644
index 0000000..49ef6ef
--- /dev/null
+++ 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardAttributeExpression.java
@@ -0,0 +1,65 @@
+/*
+ * 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;
+
+import org.apache.nifi.expression.AttributeExpression;
+import org.apache.nifi.expression.AttributeValueDecorator;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.exception.ProcessException;
+
+public class StandardAttributeExpression implements AttributeExpression {
+
+    private final Query query;
+
+    public StandardAttributeExpression(final Query query) {
+        this.query = query;
+    }
+
+    @Override
+    public ResultType getResultType() {
+        return query.getResultType();
+    }
+
+    @Override
+    public String evaluate() throws ProcessException {
+        return evaluate((AttributeValueDecorator) null);
+    }
+
+    @Override
+    public String evaluate(final AttributeValueDecorator decorator) throws 
ProcessException {
+        return evaluate(null, decorator);
+    }
+
+    @Override
+    public String evaluate(final FlowFile flowFile) throws ProcessException {
+        return evaluate(flowFile, null);
+    }
+
+    @Override
+    public String evaluate(final FlowFile flowFile, final 
AttributeValueDecorator decorator) throws ProcessException {
+        final Object evaluationResult = query.evaluate(flowFile).getValue();
+        if (evaluationResult == null) {
+            return "";
+        }
+
+        String result = evaluationResult.toString();
+        if (decorator != null) {
+            result = decorator.decorate(result);
+        }
+        return Query.unescape(result);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardExpressionLanguageCompiler.java
----------------------------------------------------------------------
diff --git 
a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardExpressionLanguageCompiler.java
 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardExpressionLanguageCompiler.java
new file mode 100644
index 0000000..cec73d1
--- /dev/null
+++ 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardExpressionLanguageCompiler.java
@@ -0,0 +1,58 @@
+/*
+ * 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;
+
+import 
org.apache.nifi.attribute.expression.language.exception.AttributeExpressionLanguageParsingException;
+import org.apache.nifi.expression.AttributeExpression;
+import org.apache.nifi.expression.ExpressionLanguageCompiler;
+import org.apache.nifi.expression.AttributeExpression.ResultType;
+
+public class StandardExpressionLanguageCompiler implements 
ExpressionLanguageCompiler {
+
+    @Override
+    public AttributeExpression compile(final String expression) throws 
IllegalArgumentException {
+        try {
+            return new StandardAttributeExpression(Query.compile(expression));
+        } catch (final AttributeExpressionLanguageParsingException e) {
+            throw new IllegalArgumentException(e.getMessage());
+        }
+    }
+
+    @Override
+    public boolean isValidExpression(final String expression) {
+        return Query.isValidExpression(expression);
+    }
+
+    @Override
+    public String validateExpression(final String expression, final boolean 
allowSurroundingCharacters) {
+        try {
+            Query.validateExpression(expression, allowSurroundingCharacters);
+            return null;
+        } catch (final AttributeExpressionLanguageParsingException aelpe) {
+            return aelpe.getMessage();
+        }
+    }
+
+    @Override
+    public ResultType getResultType(final String expression) throws 
IllegalArgumentException {
+        try {
+            return Query.getResultType(expression);
+        } catch (final AttributeExpressionLanguageParsingException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPreparedQuery.java
----------------------------------------------------------------------
diff --git 
a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPreparedQuery.java
 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPreparedQuery.java
new file mode 100644
index 0000000..0affb7f
--- /dev/null
+++ 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPreparedQuery.java
@@ -0,0 +1,83 @@
+/*
+ * 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;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.nifi.expression.AttributeValueDecorator;
+import org.apache.nifi.flowfile.FlowFile;
+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;
+
+    public StandardPreparedQuery(final List<String> queryStrings, final 
Map<String, Tree> trees) {
+        this.queryStrings = new ArrayList<>(queryStrings);
+        this.trees = new HashMap<>(trees);
+    }
+
+    @Override
+    public String evaluateExpressions(Map<String, String> attributes) throws 
ProcessException {
+        return evaluateExpressions(attributes, null);
+    }
+
+    @Override
+    public String evaluateExpressions(final Map<String, String> attributes, 
final AttributeValueDecorator decorator) throws ProcessException {
+        final StringBuilder sb = new StringBuilder();
+        for (final String val : queryStrings) {
+            final Tree tree = trees.get(val);
+            if (tree == null) {
+                sb.append(val);
+            } else {
+                final String evaluated = Query.evaluateExpression(tree, val, 
attributes, decorator);
+                if (evaluated != null) {
+                    sb.append(evaluated);
+                }
+            }
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public String evaluateExpressions(final FlowFile flowFile, final 
AttributeValueDecorator decorator) throws ProcessException {
+        final Map<String, String> expressionMap = 
Query.createExpressionMap(flowFile);
+        return evaluateExpressions(expressionMap, decorator);
+    }
+
+    @Override
+    public String evaluateExpressions() throws ProcessException {
+        return evaluateExpressions((FlowFile) null, null);
+    }
+
+    @Override
+    public String evaluateExpressions(final AttributeValueDecorator decorator) 
throws ProcessException {
+        return evaluateExpressions((FlowFile) null, decorator);
+    }
+
+    @Override
+    public String evaluateExpressions(final FlowFile flowFile) throws 
ProcessException {
+        return evaluateExpressions(flowFile, null);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/BooleanEvaluator.java
----------------------------------------------------------------------
diff --git 
a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/BooleanEvaluator.java
 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/BooleanEvaluator.java
new file mode 100644
index 0000000..376ddfe
--- /dev/null
+++ 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/BooleanEvaluator.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.attribute.expression.language.evaluation;
+
+import org.apache.nifi.expression.AttributeExpression.ResultType;
+
+public abstract class BooleanEvaluator implements Evaluator<Boolean> {
+
+    @Override
+    public ResultType getResultType() {
+        return ResultType.BOOLEAN;
+    }
+
+    @Override
+    public int getEvaluationsRemaining() {
+        return 0;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/BooleanQueryResult.java
----------------------------------------------------------------------
diff --git 
a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/BooleanQueryResult.java
 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/BooleanQueryResult.java
new file mode 100644
index 0000000..e5ef113
--- /dev/null
+++ 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/BooleanQueryResult.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.attribute.expression.language.evaluation;
+
+import org.apache.nifi.expression.AttributeExpression.ResultType;
+
+public class BooleanQueryResult implements QueryResult<Boolean> {
+
+    private final Boolean value;
+
+    public BooleanQueryResult(final Boolean value) {
+        this.value = value;
+    }
+
+    @Override
+    public Boolean getValue() {
+        return value;
+    }
+
+    @Override
+    public ResultType getResultType() {
+        return ResultType.BOOLEAN;
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(getValue());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/DateEvaluator.java
----------------------------------------------------------------------
diff --git 
a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/DateEvaluator.java
 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/DateEvaluator.java
new file mode 100644
index 0000000..7474b60
--- /dev/null
+++ 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/DateEvaluator.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.attribute.expression.language.evaluation;
+
+import java.util.Date;
+
+import org.apache.nifi.expression.AttributeExpression.ResultType;
+
+public abstract class DateEvaluator implements Evaluator<Date> {
+
+    @Override
+    public ResultType getResultType() {
+        return ResultType.DATE;
+    }
+
+    @Override
+    public int getEvaluationsRemaining() {
+        return 0;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/DateQueryResult.java
----------------------------------------------------------------------
diff --git 
a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/DateQueryResult.java
 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/DateQueryResult.java
new file mode 100644
index 0000000..a77bbe9
--- /dev/null
+++ 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/DateQueryResult.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.attribute.expression.language.evaluation;
+
+import java.util.Date;
+
+import org.apache.nifi.expression.AttributeExpression.ResultType;
+
+public class DateQueryResult implements QueryResult<Date> {
+
+    private final Date date;
+
+    public DateQueryResult(final Date date) {
+        this.date = date;
+    }
+
+    @Override
+    public Date getValue() {
+        return date;
+    }
+
+    @Override
+    public ResultType getResultType() {
+        return ResultType.DATE;
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(getValue());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/Evaluator.java
----------------------------------------------------------------------
diff --git 
a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/Evaluator.java
 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/Evaluator.java
new file mode 100644
index 0000000..6d164df
--- /dev/null
+++ 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/Evaluator.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.attribute.expression.language.evaluation;
+
+import java.util.Map;
+
+import org.apache.nifi.expression.AttributeExpression.ResultType;
+
+public interface Evaluator<T> {
+
+    QueryResult<T> evaluate(Map<String, String> attributes);
+
+    ResultType getResultType();
+
+    int getEvaluationsRemaining();
+
+    Evaluator<?> getSubjectEvaluator();
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/NumberEvaluator.java
----------------------------------------------------------------------
diff --git 
a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/NumberEvaluator.java
 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/NumberEvaluator.java
new file mode 100644
index 0000000..403bae3
--- /dev/null
+++ 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/NumberEvaluator.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.attribute.expression.language.evaluation;
+
+import org.apache.nifi.expression.AttributeExpression.ResultType;
+
+public abstract class NumberEvaluator implements Evaluator<Long> {
+
+    @Override
+    public ResultType getResultType() {
+        return ResultType.NUMBER;
+    }
+
+    @Override
+    public int getEvaluationsRemaining() {
+        return 0;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/NumberQueryResult.java
----------------------------------------------------------------------
diff --git 
a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/NumberQueryResult.java
 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/NumberQueryResult.java
new file mode 100644
index 0000000..fc3c961
--- /dev/null
+++ 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/NumberQueryResult.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.attribute.expression.language.evaluation;
+
+import org.apache.nifi.expression.AttributeExpression.ResultType;
+
+public class NumberQueryResult implements QueryResult<Long> {
+
+    private final Long value;
+
+    public NumberQueryResult(final Long value) {
+        this.value = value;
+    }
+
+    @Override
+    public Long getValue() {
+        return value;
+    }
+
+    @Override
+    public ResultType getResultType() {
+        return ResultType.NUMBER;
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(getValue());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/QueryResult.java
----------------------------------------------------------------------
diff --git 
a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/QueryResult.java
 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/QueryResult.java
new file mode 100644
index 0000000..56bd76a
--- /dev/null
+++ 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/QueryResult.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.evaluation;
+
+import org.apache.nifi.expression.AttributeExpression.ResultType;
+
+public interface QueryResult<T> {
+
+    T getValue();
+
+    ResultType getResultType();
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/StringEvaluator.java
----------------------------------------------------------------------
diff --git 
a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/StringEvaluator.java
 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/StringEvaluator.java
new file mode 100644
index 0000000..1f4ff21
--- /dev/null
+++ 
b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/StringEvaluator.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.attribute.expression.language.evaluation;
+
+import org.apache.nifi.expression.AttributeExpression.ResultType;
+
+public abstract class StringEvaluator implements Evaluator<String> {
+
+    @Override
+    public ResultType getResultType() {
+        return ResultType.STRING;
+    }
+
+    @Override
+    public int getEvaluationsRemaining() {
+        return 0;
+    }
+}

Reply via email to