From 5cd8e93beb30075ebe8623c2a278a5c9cf1afc75 Mon Sep 17 00:00:00 2001 From: Mark Payne Date: Tue, 25 Jul 2017 13:46:10 -0400 Subject: [PATCH] NIFI-4224: - Initial implementation of Process Group level Variable Registry - Updated to incorporate PR Feedback - Changed log message because slf4j-simple apparently has a memory leak; passing a String instead of passing in the Controller Service object as an argument addresses this. - This closes #2051 --- .../registry/ComponentVariableRegistry.java | 24 + .../nifi/registry/VariableDescriptor.java | 77 +- .../language/EmptyPreparedQuery.java | 6 + .../language/InvalidPreparedQuery.java | 6 + .../language/NamedVariableImpact.java | 34 + .../language/NoVariablesImpacted.java | 25 + .../expression/language/PreparedQuery.java | 10 + .../attribute/expression/language/Query.java | 1082 +--------------- .../language/StandardPreparedQuery.java | 85 +- .../expression/language/ValueLookup.java | 1 + .../expression/language/VariableImpact.java | 26 + .../language/compile/CompiledExpression.java | 53 + .../language/compile/ExpressionCompiler.java | 1122 +++++++++++++++++ .../selection/AllAttributesEvaluator.java | 4 + .../selection/AnyAttributeEvaluator.java | 4 + .../AttributeEvaluator.java | 6 +- .../selection/MappingEvaluator.java | 4 + .../MultiNamedAttributeEvaluator.java | 4 + .../expression/language/TestQuery.java | 44 +- .../language/TestStandardPreparedQuery.java | 41 + .../expression/language/TestValueLookup.java | 4 +- .../web/api/dto/AffectedComponentDTO.java | 59 + .../nifi/web/api/dto/ProcessGroupDTO.java | 16 + .../apache/nifi/web/api/dto/VariableDTO.java | 69 + .../nifi/web/api/dto/VariableRegistryDTO.java | 50 + .../dto/VariableRegistryUpdateRequestDTO.java | 115 ++ .../dto/VariableRegistryUpdateStepDTO.java | 59 + .../ActivateControllerServicesEntity.java | 66 + .../api/entity/ScheduleComponentsEntity.java | 12 +- .../nifi/web/api/entity/VariableEntity.java | 51 + .../api/entity/VariableRegistryEntity.java | 50 + .../VariableRegistryUpdateRequestEntity.java | 49 + .../http/StandardHttpResponseMapper.java | 19 +- .../AbstractConfiguredComponent.java | 42 +- .../nifi/controller/ConfiguredComponent.java | 7 + .../nifi/controller/ProcessScheduler.java | 9 +- .../apache/nifi/controller/ProcessorNode.java | 19 +- .../service/ControllerServiceNode.java | 2 +- .../service/ControllerServiceProvider.java | 6 +- .../service/ControllerServiceReference.java | 10 + .../org/apache/nifi/groups/ProcessGroup.java | 51 +- .../nifi/controller/FlowController.java | 46 +- .../controller/StandardFlowSynchronizer.java | 70 +- .../controller/StandardProcessorNode.java | 77 +- .../reporting/AbstractReportingTaskNode.java | 22 +- .../reporting/StandardReportingTaskNode.java | 8 +- .../EventDrivenSchedulingAgent.java | 9 +- .../scheduling/QuartzSchedulingAgent.java | 8 +- .../scheduling/StandardProcessScheduler.java | 42 +- .../TimerDrivenSchedulingAgent.java | 6 +- .../serialization/FlowFromDOMFactory.java | 10 + .../serialization/StandardFlowSerializer.java | 16 +- .../service/ServiceStateTransition.java | 68 + .../StandardControllerServiceNode.java | 93 +- .../StandardControllerServiceProvider.java | 76 +- .../StandardControllerServiceReference.java | 33 + .../nifi/fingerprint/FingerprintFactory.java | 69 +- .../nifi/groups/StandardProcessGroup.java | 189 ++- .../processor/StandardProcessContext.java | 12 +- .../processor/StandardValidationContext.java | 7 +- .../variable}/FileBasedVariableRegistry.java | 7 +- .../variable/MutableVariableRegistry.java | 52 + .../StandardComponentVariableRegistry.java | 94 ++ .../src/main/resources/FlowConfiguration.xsd | 6 + .../src/main/resources/nifi-context.xml | 2 +- .../controller/StandardFlowServiceTest.java | 2 +- .../nifi/controller/TestFlowController.java | 2 +- .../controller/TestStandardProcessorNode.java | 48 +- .../TestStandardReportingContext.java | 20 +- .../scheduling/TestProcessorLifecycle.java | 2 +- .../TestStandardProcessScheduler.java | 10 +- .../StandardFlowSerializerTest.java | 2 +- ...StandardControllerServiceProviderTest.java | 2 +- ...TestStandardControllerServiceProvider.java | 41 +- .../service/mock/MockProcessGroup.java | 32 +- .../util/TestFileBasedVariableRegistry.java | 1 + .../nifi/audit/ProcessGroupAuditor.java | 63 +- .../VariableRegistryUpdateRequest.java | 109 ++ .../variable/VariableRegistryUpdateStep.java | 48 + .../apache/nifi/web/NiFiServiceFacade.java | 126 +- .../nifi/web/NiFiServiceFacadeLock.java | 12 + .../nifi/web/StandardNiFiServiceFacade.java | 257 +++- .../nifi/web/api/ApplicationResource.java | 52 +- .../org/apache/nifi/web/api/FlowResource.java | 149 ++- .../nifi/web/api/ProcessGroupResource.java | 1102 ++++++++++++++-- .../apache/nifi/web/api/dto/DtoFactory.java | 217 +++- .../nifi/web/api/dto/EntityFactory.java | 13 + .../nifi/web/dao/ControllerServiceDAO.java | 2 +- .../apache/nifi/web/dao/ProcessGroupDAO.java | 44 +- .../impl/StandardControllerServiceDAO.java | 9 +- .../web/dao/impl/StandardProcessGroupDAO.java | 80 +- .../java/org/apache/nifi/web/util/Pause.java | 30 + .../main/resources/nifi-web-api-context.xml | 1 + 93 files changed, 5174 insertions(+), 1747 deletions(-) create mode 100644 nifi-api/src/main/java/org/apache/nifi/registry/ComponentVariableRegistry.java create mode 100644 nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/NamedVariableImpact.java create mode 100644 nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/NoVariablesImpacted.java create mode 100644 nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/VariableImpact.java create mode 100644 nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/CompiledExpression.java create mode 100644 nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/ExpressionCompiler.java rename nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/{functions => selection}/AttributeEvaluator.java (94%) create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AffectedComponentDTO.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableDTO.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryDTO.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryUpdateRequestDTO.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryUpdateStepDTO.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ActivateControllerServicesEntity.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableEntity.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableRegistryEntity.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableRegistryUpdateRequestEntity.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/ServiceStateTransition.java rename nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/{util => registry/variable}/FileBasedVariableRegistry.java (97%) create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/registry/variable/MutableVariableRegistry.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/registry/variable/StandardComponentVariableRegistry.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/registry/variable/VariableRegistryUpdateRequest.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/registry/variable/VariableRegistryUpdateStep.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/Pause.java diff --git a/nifi-api/src/main/java/org/apache/nifi/registry/ComponentVariableRegistry.java b/nifi-api/src/main/java/org/apache/nifi/registry/ComponentVariableRegistry.java new file mode 100644 index 0000000000..7284b77e34 --- /dev/null +++ b/nifi-api/src/main/java/org/apache/nifi/registry/ComponentVariableRegistry.java @@ -0,0 +1,24 @@ +/* + * 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.registry; + +public interface ComponentVariableRegistry extends VariableRegistry { + VariableRegistry getParent(); + + void setParent(VariableRegistry parentRegistry); +} diff --git a/nifi-api/src/main/java/org/apache/nifi/registry/VariableDescriptor.java b/nifi-api/src/main/java/org/apache/nifi/registry/VariableDescriptor.java index 5b1e88faa9..f9e9ce276e 100644 --- a/nifi-api/src/main/java/org/apache/nifi/registry/VariableDescriptor.java +++ b/nifi-api/src/main/java/org/apache/nifi/registry/VariableDescriptor.java @@ -66,6 +66,44 @@ public final class VariableDescriptor implements Comparable return getName().compareTo(o.getName()); } + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public boolean isSensitive() { + return sensitive; + } + + @Override + public boolean equals(final Object other) { + if (other == null) { + return false; + } + if (this == other) { + return true; + } + if (!(other instanceof VariableDescriptor)) { + return false; + } + + final VariableDescriptor desc = (VariableDescriptor) other; + return this.name.equals(desc.name); + } + + @Override + public int hashCode() { + return 797 + this.name.hashCode() * 97; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + name + "]"; + } + public static final class Builder { private String name = null; @@ -112,43 +150,4 @@ public final class VariableDescriptor implements Comparable return new VariableDescriptor(this); } } - - public String getName() { - return name; - } - - public String getDescription() { - return description; - } - - public boolean isSensitive() { - return sensitive; - } - - @Override - public boolean equals(final Object other) { - if (other == null) { - return false; - } - if (!(other instanceof VariableDescriptor)) { - return false; - } - if (this == other) { - return true; - } - - final VariableDescriptor desc = (VariableDescriptor) other; - return this.name.equals(desc.name); - } - - @Override - public int hashCode() { - return 797 + this.name.hashCode() * 97; - } - - @Override - public String toString() { - return getClass().getSimpleName() + "[" + name + "]"; - } - } diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/EmptyPreparedQuery.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/EmptyPreparedQuery.java index a435b08167..e9ac03b570 100644 --- a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/EmptyPreparedQuery.java +++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/EmptyPreparedQuery.java @@ -18,6 +18,7 @@ package org.apache.nifi.attribute.expression.language; import java.util.Map; + import org.apache.nifi.expression.AttributeValueDecorator; import org.apache.nifi.processor.exception.ProcessException; @@ -43,4 +44,9 @@ public class EmptyPreparedQuery implements PreparedQuery { public boolean isExpressionLanguagePresent() { return false; } + + @Override + public VariableImpact getVariableImpact() { + return VariableImpact.NEVER_IMPACTED; + } } diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/InvalidPreparedQuery.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/InvalidPreparedQuery.java index ce0dec38d4..8ef996b96c 100644 --- a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/InvalidPreparedQuery.java +++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/InvalidPreparedQuery.java @@ -18,6 +18,7 @@ package org.apache.nifi.attribute.expression.language; import java.util.Map; + import org.apache.nifi.attribute.expression.language.exception.AttributeExpressionLanguageException; import org.apache.nifi.expression.AttributeValueDecorator; import org.apache.nifi.processor.exception.ProcessException; @@ -52,4 +53,9 @@ public class InvalidPreparedQuery implements PreparedQuery { public boolean isExpressionLanguagePresent() { return false; } + + @Override + public VariableImpact getVariableImpact() { + return VariableImpact.NEVER_IMPACTED; + } } diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/NamedVariableImpact.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/NamedVariableImpact.java new file mode 100644 index 0000000000..730c6ec589 --- /dev/null +++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/NamedVariableImpact.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; + +import java.util.Set; + +public class NamedVariableImpact implements VariableImpact { + private final Set impactedVariableNames; + + public NamedVariableImpact(final Set impactedVariableNames) { + this.impactedVariableNames = impactedVariableNames; + } + + @Override + public boolean isImpacted(final String variableName) { + return impactedVariableNames.contains(variableName); + } + +} diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/NoVariablesImpacted.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/NoVariablesImpacted.java new file mode 100644 index 0000000000..669a532241 --- /dev/null +++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/NoVariablesImpacted.java @@ -0,0 +1,25 @@ +/* + * 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 class NoVariablesImpacted implements VariableImpact { + @Override + public boolean isImpacted(final String variableName) { + return false; + } +} diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/PreparedQuery.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/PreparedQuery.java index 5552cace69..c51656d028 100644 --- a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/PreparedQuery.java +++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/PreparedQuery.java @@ -18,6 +18,7 @@ package org.apache.nifi.attribute.expression.language; import java.util.Map; + import org.apache.nifi.expression.AttributeValueDecorator; import org.apache.nifi.processor.exception.ProcessException; @@ -28,4 +29,13 @@ public interface PreparedQuery { String evaluateExpressions(final Map valueLookup, final AttributeValueDecorator decorator, final Map stateVariables) throws ProcessException; boolean isExpressionLanguagePresent(); + + /** + * Returns a {@link VariableImpact} that can be used to determine whether or not a given + * variable impacts this Expression. + * + * @return a {@link VariableImpact} that can be used to determine whether or not a given + * variable impacts this Expression. + */ + VariableImpact getVariableImpact(); } 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 4b1ce59f52..fe2c0608d2 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 substrings = new ArrayList<>(); - final Map trees = new HashMap<>(); + final Map 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( ${} ):" + 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 newStringLiteralEvaluator(final String literalValue) { - if (literalValue == null || literalValue.length() < 2) { - return new StringLiteralEvaluator(literalValue); - } - final List ranges = extractExpressionRanges(literalValue); - if (ranges.isEmpty()) { - return new StringLiteralEvaluator(literalValue); - } - - final List> 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 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 delineatedValueEvaluator = toStringEvaluator(buildEvaluator(tree.getChild(1))); - final Evaluator delimiterEvaluator = toStringEvaluator(buildEvaluator(tree.getChild(2))); - - return new DelineatedAttributeEvaluator(delineatedValueEvaluator, delimiterEvaluator, multiAttrType); - } - - final List 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 stringEvaluator = toStringEvaluator(argEvaluator); - return new GetStateVariableEvaluator(stringEvaluator); - } - default: - throw new AttributeExpressionLanguageParsingException("Unexpected token: " + tree.toString()); - } - } - - private static Evaluator addToken(final Evaluator evaluator, final String token) { - evaluator.setToken(token); - return evaluator; - } - - private static Evaluator 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> 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> verifyArgCount(final List> args, final int count, final String functionName) { - if (args.size() != count) { - throw new AttributeExpressionLanguageParsingException(functionName + "() function takes " + count + " arguments"); - } - return args; - } - - private static Evaluator toStringEvaluator(final Evaluator evaluator) { - return toStringEvaluator(evaluator, null); - } - - private static Evaluator 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 toBooleanEvaluator(final Evaluator evaluator, final String location) { - switch (evaluator.getResultType()) { - case BOOLEAN: - return (Evaluator) 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 toBooleanEvaluator(final Evaluator evaluator) { - return toBooleanEvaluator(evaluator, null); - } - - private static Evaluator toWholeNumberEvaluator(final Evaluator evaluator) { - return toWholeNumberEvaluator(evaluator, null); - } - - @SuppressWarnings("unchecked") - private static Evaluator toWholeNumberEvaluator(final Evaluator evaluator, final String location) { - switch (evaluator.getResultType()) { - case WHOLE_NUMBER: - return (Evaluator) 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 toDecimalEvaluator(final Evaluator evaluator) { - return toDecimalEvaluator(evaluator, null); - } - - @SuppressWarnings("unchecked") - private static Evaluator toDecimalEvaluator(final Evaluator evaluator, final String location) { - switch (evaluator.getResultType()) { - case DECIMAL: - return (Evaluator) 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 toNumberEvaluator(final Evaluator evaluator) { - return toNumberEvaluator(evaluator, null); - } - - @SuppressWarnings("unchecked") - private static Evaluator toNumberEvaluator(final Evaluator evaluator, final String location) { - switch (evaluator.getResultType()) { - case NUMBER: - return (Evaluator) 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> 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> list = new ArrayList>(); - 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 { 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 9f12c92447..cdf5a2de60 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 queryStrings; - private final Map trees; + private final Map expressions; + private volatile VariableImpact variableImpact; - public StandardPreparedQuery(final List queryStrings, final Map trees) { - this.queryStrings = new ArrayList<>(queryStrings); - this.trees = new HashMap<>(trees); + public StandardPreparedQuery(final List queryStrings, final Map expressions) { + this.queryStrings = queryStrings; + this.expressions = expressions; } - @Override public String evaluateExpressions(final Map valMap, final AttributeValueDecorator decorator, final Map 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 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 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; } } 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 5b0cdda852..06c187711c 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 { * @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... additionalMaps) { for (final Map map : additionalMaps) { if (map != null && !map.isEmpty()) { 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 0000000000..4a66c877c4 --- /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; +} 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 0000000000..d234445137 --- /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> allEvaluators; + + public CompiledExpression(final String expression, final Evaluator rootEvaluator, final Tree tree, final Set> 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> getAllEvaluators() { + return allEvaluators; + } +} diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/ExpressionCompiler.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/ExpressionCompiler.java new file mode 100644 index 0000000000..53a27fecb4 --- /dev/null +++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/ExpressionCompiler.java @@ -0,0 +1,1122 @@ +/* + * 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 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.BASE64_DECODE; +import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.BASE64_ENCODE; +import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.CONTAINS; +import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.COUNT; +import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.DECIMAL; +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.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_JSON; +import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.ESCAPE_XML; +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.FROM_RADIX; +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.IF_ELSE; +import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.IN; +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.MATH; +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.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.RANDOM; +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_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_DECIMAL; +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.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_JSON; +import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.UNESCAPE_XML; +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 static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.WHOLE_NUMBER; + +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.antlr.runtime.ANTLRStringStream; +import org.antlr.runtime.CharStream; +import org.antlr.runtime.CommonTokenStream; +import org.antlr.runtime.tree.Tree; +import org.apache.nifi.attribute.expression.language.Query; +import org.apache.nifi.attribute.expression.language.Query.Range; +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.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.Base64DecodeEvaluator; +import org.apache.nifi.attribute.expression.language.evaluation.functions.Base64EncodeEvaluator; +import org.apache.nifi.attribute.expression.language.evaluation.functions.CharSequenceTranslatorEvaluator; +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.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.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.AttributeEvaluator; +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.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.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.flowfile.FlowFile; + +public class ExpressionCompiler { + private final Set> evaluators = new HashSet<>(); + + public CompiledExpression compile(final String expression) { + try { + final CharStream input = new ANTLRStringStream(expression); + final AttributeExpressionLexer lexer = new AttributeExpressionLexer(input); + final CommonTokenStream lexerTokenStream = new CommonTokenStream(lexer); + + final AttributeExpressionParser parser = new AttributeExpressionParser(lexerTokenStream); + final Tree ast = (Tree) parser.query().getTree(); + final Tree tree = ast.getChild(0); + + final Evaluator evaluator = buildEvaluator(tree); + verifyMappingEvaluatorReduced(evaluator); + + final Set> allEvaluators = new HashSet<>(evaluators); + this.evaluators.clear(); + + return new CompiledExpression(expression, evaluator, tree, allEvaluators); + } catch (final AttributeExpressionLanguageParsingException e) { + throw e; + } catch (final Exception e) { + throw new AttributeExpressionLanguageParsingException(e); + } + } + + private Tree compileTree(final String expression) throws AttributeExpressionLanguageParsingException { + try { + final CharStream input = new ANTLRStringStream(expression); + final AttributeExpressionLexer lexer = new AttributeExpressionLexer(input); + final CommonTokenStream lexerTokenStream = new CommonTokenStream(lexer); + + 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); + } + } + + private 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( ${} ):" + 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 Evaluator getRootSubjectEvaluator(final Evaluator evaluator) { + if (evaluator == null) { + return null; + } + + final Evaluator subject = evaluator.getSubjectEvaluator(); + if (subject == null) { + return evaluator; + } + + return getRootSubjectEvaluator(subject); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private 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; + } + } + + evaluators.add(chosenEvaluator); + 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 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> argEvaluators = new ArrayList<>(); + for (int i = 1; i < functionTree.getChildCount(); i++) { + argEvaluators.add(buildEvaluator(functionTree.getChild(i))); + } + return buildFunctionEvaluator(functionNameTree, subjectEvaluator, argEvaluators); + } + + private List> verifyArgCount(final List> args, final int count, final String functionName) { + if (args.size() != count) { + throw new AttributeExpressionLanguageParsingException(functionName + "() function takes " + count + " arguments"); + } + return args; + } + + private Evaluator toStringEvaluator(final Evaluator evaluator) { + return toStringEvaluator(evaluator, null); + } + + private Evaluator 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 Evaluator toBooleanEvaluator(final Evaluator evaluator, final String location) { + switch (evaluator.getResultType()) { + case BOOLEAN: + return (Evaluator) 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 Evaluator toBooleanEvaluator(final Evaluator evaluator) { + return toBooleanEvaluator(evaluator, null); + } + + private Evaluator toWholeNumberEvaluator(final Evaluator evaluator) { + return toWholeNumberEvaluator(evaluator, null); + } + + @SuppressWarnings("unchecked") + private Evaluator toWholeNumberEvaluator(final Evaluator evaluator, final String location) { + switch (evaluator.getResultType()) { + case WHOLE_NUMBER: + return (Evaluator) 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 Evaluator toDecimalEvaluator(final Evaluator evaluator) { + return toDecimalEvaluator(evaluator, null); + } + + @SuppressWarnings("unchecked") + private Evaluator toDecimalEvaluator(final Evaluator evaluator, final String location) { + switch (evaluator.getResultType()) { + case DECIMAL: + return (Evaluator) 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 Evaluator toNumberEvaluator(final Evaluator evaluator) { + return toNumberEvaluator(evaluator, null); + } + + @SuppressWarnings("unchecked") + private Evaluator toNumberEvaluator(final Evaluator evaluator, final String location) { + switch (evaluator.getResultType()) { + case NUMBER: + return (Evaluator) 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 DateEvaluator toDateEvaluator(final Evaluator evaluator) { + return toDateEvaluator(evaluator, null); + } + + private DateEvaluator toDateEvaluator(final Evaluator evaluator, final String location) { + if (evaluator.getResultType() == ResultType.DATE) { + return (DateEvaluator) evaluator; + } + + return new DateCastEvaluator(evaluator); + } + + private Evaluator buildFunctionEvaluator(final Tree tree, final Evaluator subjectEvaluator, final List> 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> list = new ArrayList>(); + 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 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; + } + final AttributeEvaluator eval = new AttributeEvaluator(toStringEvaluator(childEvaluator)); + evaluators.add(eval); + return eval; + } + 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 delineatedValueEvaluator = toStringEvaluator(buildEvaluator(tree.getChild(1))); + final Evaluator delimiterEvaluator = toStringEvaluator(buildEvaluator(tree.getChild(2))); + + final String token = (multiAttrType == ANY_DELINEATED_VALUE) ? "anyDelineatedValue" : "allDelineatedValues"; + return addToken(new DelineatedAttributeEvaluator(delineatedValueEvaluator, delimiterEvaluator, multiAttrType), token); + } + + final List 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 addToken(new MultiNamedAttributeEvaluator(attributeNames, ALL_ATTRIBUTES), "allAttributes"); + case ALL_MATCHING_ATTRIBUTES: + return addToken(new MultiMatchAttributeEvaluator(attributeNames, ALL_MATCHING_ATTRIBUTES), "allMatchingAttributes"); + 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 addToken(new MultiNamedAttributeEvaluator(attributeNames, ANY_ATTRIBUTE), "anyAttribute"); + case ANY_MATCHING_ATTRIBUTE: + return addToken(new MultiMatchAttributeEvaluator(attributeNames, ANY_MATCHING_ATTRIBUTE), "anyMatchingAttribute"); + default: + throw new AssertionError("Illegal Multi-Attribute Reference: " + functionTypeTree.toString()); + } + } + case ATTR_NAME: { + return newStringLiteralEvaluator(tree.getChild(0).getText()); + } + case WHOLE_NUMBER: { + return addToken(new WholeNumberLiteralEvaluator(tree.getText()), "wholeNumber"); + } + case STRING_LITERAL: { + return newStringLiteralEvaluator(tree.getText()); + } + case DECIMAL: { + return addToken(new DecimalLiteralEvaluator(tree.getText()), "decimal"); + } + case TRUE: + case FALSE: + return buildBooleanEvaluator(tree); + case UUID: { + return addToken(new UuidEvaluator(), "uuid"); + } + case NOW: { + return addToken(new NowEvaluator(), "now"); + } + case TO_LITERAL: { + final Evaluator argEvaluator = buildEvaluator(tree.getChild(0)); + return addToken(new ToLiteralEvaluator(argEvaluator), "toLiteral"); + } + case IP: { + try { + return addToken(new IPEvaluator(), "ip"); + } catch (final UnknownHostException e) { + throw new AttributeExpressionLanguageException(e); + } + } + case HOSTNAME: { + if (tree.getChildCount() == 0) { + try { + return addToken(new HostnameEvaluator(false), "hostname"); + } 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 addToken(new HostnameEvaluator(true), "hostname"); + case FALSE: + return addToken(new HostnameEvaluator(false), "hostname"); + 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 addToken(new OneUpSequenceEvaluator(), "nextInt"); + } + case RANDOM: { + return addToken(new RandomNumberGeneratorEvaluator(), "random"); + } + 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 stringEvaluator = toStringEvaluator(argEvaluator); + final GetStateVariableEvaluator eval = new GetStateVariableEvaluator(stringEvaluator); + evaluators.add(eval); + return eval; + } + default: + throw new AttributeExpressionLanguageParsingException("Unexpected token: " + tree.toString()); + } + } + + private Evaluator addToken(final Evaluator evaluator, final String token) { + evaluator.setToken(token); + evaluators.add(evaluator); + return evaluator; + } + + + private Evaluator newStringLiteralEvaluator(final String literalValue) { + if (literalValue == null || literalValue.length() < 2) { + return addToken(new StringLiteralEvaluator(literalValue), literalValue); + } + + final List ranges = Query.extractExpressionRanges(literalValue); + if (ranges.isEmpty()) { + return addToken(new StringLiteralEvaluator(literalValue), literalValue); + } + + final List> 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 lastEvaluator = toStringEvaluator(evaluators.get(0)); + for (int i = 1; i < evaluators.size(); i++) { + lastEvaluator = new AppendEvaluator(lastEvaluator, toStringEvaluator(evaluators.get(i))); + } + + this.evaluators.addAll(evaluators); + return lastEvaluator; + } + + + private Evaluator 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()); + } + +} diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AllAttributesEvaluator.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AllAttributesEvaluator.java index ccf9d1908a..8c4213b8db 100644 --- a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AllAttributesEvaluator.java +++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AllAttributesEvaluator.java @@ -70,4 +70,8 @@ public class AllAttributesEvaluator extends BooleanEvaluator implements Iteratin public Evaluator getLogicEvaluator() { return booleanEvaluator; } + + public MultiAttributeEvaluator getVariableIteratingEvaluator() { + return multiAttributeEvaluator; + } } diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AnyAttributeEvaluator.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AnyAttributeEvaluator.java index 6e8d485310..eac571b712 100644 --- a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AnyAttributeEvaluator.java +++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AnyAttributeEvaluator.java @@ -70,4 +70,8 @@ public class AnyAttributeEvaluator extends BooleanEvaluator implements Iterating public Evaluator getLogicEvaluator() { return booleanEvaluator; } + + public MultiAttributeEvaluator getVariableIteratingEvaluator() { + return multiAttributeEvaluator; + } } diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/AttributeEvaluator.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AttributeEvaluator.java similarity index 94% rename from nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/AttributeEvaluator.java rename to nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AttributeEvaluator.java index a0695a9457..0da063e3ed 100644 --- a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/AttributeEvaluator.java +++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AttributeEvaluator.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.attribute.expression.language.evaluation.functions; +package org.apache.nifi.attribute.expression.language.evaluation.selection; import java.util.Map; @@ -42,4 +42,8 @@ public class AttributeEvaluator extends StringEvaluator { public Evaluator getSubjectEvaluator() { return null; } + + public Evaluator getNameEvaluator() { + return nameEvaluator; + } } diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/MappingEvaluator.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/MappingEvaluator.java index e007a56392..5f182c786f 100644 --- a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/MappingEvaluator.java +++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/MappingEvaluator.java @@ -68,4 +68,8 @@ public class MappingEvaluator implements Evaluator { public void setToken(final String token) { this.token = token; } + + public MultiAttributeEvaluator getVariableIteratingEvaluator() { + return multiAttributeEvaluator; + } } diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/MultiNamedAttributeEvaluator.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/MultiNamedAttributeEvaluator.java index 509d7dd2c7..cff91852df 100644 --- a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/MultiNamedAttributeEvaluator.java +++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/MultiNamedAttributeEvaluator.java @@ -66,4 +66,8 @@ public class MultiNamedAttributeEvaluator extends MultiAttributeEvaluator { public Evaluator getLogicEvaluator() { return this; } + + public List getAttributeNames() { + return attributeNames; + } } diff --git a/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java b/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java index 5946aa5b31..c4113c0d06 100644 --- a/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java +++ b/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java @@ -19,11 +19,11 @@ package org.apache.nifi.attribute.expression.language; import static java.lang.Double.NEGATIVE_INFINITY; import static java.lang.Double.NaN; import static java.lang.Double.POSITIVE_INFINITY; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; import java.io.BufferedInputStream; @@ -40,6 +40,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import org.antlr.runtime.tree.Tree; import org.apache.nifi.attribute.expression.language.Query.Range; import org.apache.nifi.attribute.expression.language.evaluation.NumberQueryResult; import org.apache.nifi.attribute.expression.language.evaluation.QueryResult; @@ -47,14 +48,10 @@ import org.apache.nifi.attribute.expression.language.exception.AttributeExpressi import org.apache.nifi.attribute.expression.language.exception.AttributeExpressionLanguageParsingException; import org.apache.nifi.expression.AttributeExpression.ResultType; import org.apache.nifi.flowfile.FlowFile; -import org.antlr.runtime.tree.Tree; - import org.apache.nifi.registry.VariableRegistry; import org.junit.Assert; - import org.junit.Ignore; import org.junit.Test; - import org.mockito.Mockito; public class TestQuery { @@ -304,7 +301,7 @@ public class TestQuery { @Test(expected = AttributeExpressionLanguageException.class) public void testCannotCombineWithNonReducingFunction() { - Query.compileTree("${allAttributes( 'a.1' ):plus(1)}"); + Query.compile("${allAttributes( 'a.1' ):plus(1)}"); } @Test @@ -357,6 +354,7 @@ public class TestQuery { assertEquals("Val", evaluateQueryForEscape("${attr:replaceAll(\"My (Val)ue{1,2}\", '$1')}", attributes)); } + @SuppressWarnings("unchecked") private String evaluateQueryForEscape(final String queryString, final Map attributes) { final FlowFile mockFlowFile = Mockito.mock(FlowFile.class); Mockito.when(mockFlowFile.getAttributes()).thenReturn(attributes); @@ -639,7 +637,7 @@ public class TestQuery { final String query = "${ abc:equals('abc'):or( \n\t${xx:isNull()}\n) }"; assertEquals(ResultType.BOOLEAN, Query.getResultType(query)); Query.validateExpression(query, false); - assertEquals("true", Query.evaluateExpressions(query, Collections.EMPTY_MAP)); + assertEquals("true", Query.evaluateExpressions(query, Collections.emptyMap())); } @Test @@ -1675,25 +1673,25 @@ public class TestQuery { verifyEquals("${string:escapeHtml4()}", attributes, "special ♣"); } - @Test - public void testUnescapeFunctions() { - final Map attributes = new HashMap<>(); + @Test + public void testUnescapeFunctions() { + final Map attributes = new HashMap<>(); - attributes.put("string", "making air \\\"QUOTES\\\"."); - verifyEquals("${string:unescapeJson()}", attributes, "making air \"QUOTES\"."); + attributes.put("string", "making air \\\"QUOTES\\\"."); + verifyEquals("${string:unescapeJson()}", attributes, "making air \"QUOTES\"."); - attributes.put("string", "M & M"); - verifyEquals("${string:unescapeXml()}", attributes, "M & M"); + attributes.put("string", "M & M"); + verifyEquals("${string:unescapeXml()}", attributes, "M & M"); - attributes.put("string", "\"making air \"\"QUOTES\"\".\""); - verifyEquals("${string:unescapeCsv()}", attributes, "making air \"QUOTES\"."); + attributes.put("string", "\"making air \"\"QUOTES\"\".\""); + verifyEquals("${string:unescapeCsv()}", attributes, "making air \"QUOTES\"."); - attributes.put("string", "special ¡"); - verifyEquals("${string:unescapeHtml3()}", attributes, "special ¡"); + attributes.put("string", "special ¡"); + verifyEquals("${string:unescapeHtml3()}", attributes, "special ¡"); - attributes.put("string", "special ♣"); - verifyEquals("${string:unescapeHtml4()}", attributes, "special ♣"); - } + attributes.put("string", "special ♣"); + verifyEquals("${string:unescapeHtml4()}", attributes, "special ♣"); + } @Test public void testIfElse() { @@ -1709,9 +1707,9 @@ public class TestQuery { verifyEquals("${attr2:isNull():ifElse('a', 'b')}", attributes, "a"); verifyEquals("${literal(true):ifElse('a', 'b')}", attributes, "a"); verifyEquals("${literal(true):ifElse(false, 'b')}", attributes, "false"); - } + private void verifyEquals(final String expression, final Map attributes, final Object expectedResult) { verifyEquals(expression,attributes, null, expectedResult); } diff --git a/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestStandardPreparedQuery.java b/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestStandardPreparedQuery.java index 5acba8deda..ea7cb96f23 100644 --- a/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestStandardPreparedQuery.java +++ b/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestStandardPreparedQuery.java @@ -17,9 +17,13 @@ package org.apache.nifi.attribute.expression.language; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; import org.junit.Ignore; @@ -46,6 +50,7 @@ public class TestStandardPreparedQuery { } @Test + @Ignore("Intended for manual performance testing; should not be run in an automated environment") public void test10MIterations() { final Map attrs = new HashMap<>(); attrs.put("xx", "world"); @@ -84,6 +89,42 @@ public class TestStandardPreparedQuery { } + @Test + public void testVariableImpacted() { + final Set attr = new HashSet<>(); + attr.add("attr"); + + final Set attr2 = new HashSet<>(); + attr2.add("attr"); + attr2.add("attr2"); + + final Set abc = new HashSet<>(); + abc.add("a"); + abc.add("b"); + abc.add("c"); + + assertTrue(Query.prepare("${attr}").getVariableImpact().isImpacted("attr")); + assertFalse(Query.prepare("${attr}").getVariableImpact().isImpacted("attr2")); + assertTrue(Query.prepare("${attr:trim():toUpper():equals('abc')}").getVariableImpact().isImpacted("attr")); + + assertFalse(Query.prepare("${anyAttribute('a', 'b', 'c'):equals('hello')}").getVariableImpact().isImpacted("attr")); + assertTrue(Query.prepare("${anyAttribute('a', 'b', 'c'):equals('hello')}").getVariableImpact().isImpacted("a")); + assertTrue(Query.prepare("${anyAttribute('a', 'b', 'c'):equals('hello')}").getVariableImpact().isImpacted("b")); + assertTrue(Query.prepare("${anyAttribute('a', 'b', 'c'):equals('hello')}").getVariableImpact().isImpacted("c")); + + assertFalse(Query.prepare("${allAttributes('a', 'b', 'c'):equals('hello')}").getVariableImpact().isImpacted("attr")); + assertTrue(Query.prepare("${allAttributes('a', 'b', 'c'):equals('hello')}").getVariableImpact().isImpacted("a")); + assertTrue(Query.prepare("${allAttributes('a', 'b', 'c'):equals('hello')}").getVariableImpact().isImpacted("b")); + assertTrue(Query.prepare("${allAttributes('a', 'b', 'c'):equals('hello')}").getVariableImpact().isImpacted("c")); + + assertTrue(Query.prepare("${attr:equals('${attr2}')}").getVariableImpact().isImpacted("attr")); + assertTrue(Query.prepare("${attr:equals('${attr2}')}").getVariableImpact().isImpacted("attr2")); + assertFalse(Query.prepare("${attr:equals('${attr2}')}").getVariableImpact().isImpacted("attr3")); + + assertTrue(Query.prepare("${allMatchingAttributes('a.*'):equals('hello')}").getVariableImpact().isImpacted("attr")); + assertTrue(Query.prepare("${anyMatchingAttribute('a.*'):equals('hello')}").getVariableImpact().isImpacted("attr")); + } + private String evaluate(final String query, final Map attrs) { final String evaluated = ((StandardPreparedQuery) Query.prepare(query)).evaluateExpressions(attrs, null); return evaluated; diff --git a/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestValueLookup.java b/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestValueLookup.java index ec8ba66640..1010ac776f 100644 --- a/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestValueLookup.java +++ b/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestValueLookup.java @@ -25,12 +25,10 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertEquals; -/** - * - */ public class TestValueLookup { @Test + @SuppressWarnings("unchecked") public void testCreateCustomVariableRegistry() { final VariableRegistry variableRegistry = VariableRegistry.ENVIRONMENT_SYSTEM_REGISTRY; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AffectedComponentDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AffectedComponentDTO.java new file mode 100644 index 0000000000..5d631ed331 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AffectedComponentDTO.java @@ -0,0 +1,59 @@ +/* + * 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.web.api.dto; + +import javax.xml.bind.annotation.XmlType; + +import com.wordnik.swagger.annotations.ApiModelProperty; + +@XmlType(name = "affectedComponent") +public class AffectedComponentDTO { + public static final String COMPONENT_TYPE_PROCESSOR = "PROCESSOR"; + public static final String COMPONENT_TYPE_CONTROLLER_SERVICE = "CONTROLLER_SERVICE"; + + private String parentGroupId; + private String componentId; + private String componentType; + + @ApiModelProperty("The UUID of the Process Group that this component is in") + public String getParentGroupId() { + return parentGroupId; + } + + public void setParentGroupId(final String parentGroupId) { + this.parentGroupId = parentGroupId; + } + + @ApiModelProperty("The UUID of this component") + public String getComponentId() { + return componentId; + } + + public void setComponentId(final String componentId) { + this.componentId = componentId; + } + + @ApiModelProperty(value = "The type of this component", allowableValues = COMPONENT_TYPE_PROCESSOR + "," + COMPONENT_TYPE_CONTROLLER_SERVICE) + public String getComponentType() { + return componentType; + } + + public void setComponentType(final String componentType) { + this.componentType = componentType; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessGroupDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessGroupDTO.java index 42d605d16b..f4b6f31f96 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessGroupDTO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessGroupDTO.java @@ -17,6 +17,9 @@ package org.apache.nifi.web.api.dto; import com.wordnik.swagger.annotations.ApiModelProperty; + +import java.util.Map; + import javax.xml.bind.annotation.XmlType; /** @@ -27,6 +30,7 @@ public class ProcessGroupDTO extends ComponentDTO { private String name; private String comments; + private Map variables; private Integer runningCount; private Integer stoppedCount; @@ -200,4 +204,16 @@ public class ProcessGroupDTO extends ComponentDTO { this.inactiveRemotePortCount = inactiveRemotePortCount; } + + @ApiModelProperty(value = "The variables that are configured for the Process Group. Note that this map contains only " + + "those variables that are defined on this Process Group and not any variables that are defined in the parent " + + "Process Group, etc. I.e., this Map will not contain all variables that are accessible by components in this " + + "Process Group by rather only the variables that are defined for this Process Group itself.", readOnly = true) + public Map getVariables() { + return variables; + } + + public void setVariables(final Map variables) { + this.variables = variables; + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableDTO.java new file mode 100644 index 0000000000..c686316451 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableDTO.java @@ -0,0 +1,69 @@ +/* + * 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.web.api.dto; + +import java.util.HashSet; +import java.util.Set; + +import javax.xml.bind.annotation.XmlType; + +import com.wordnik.swagger.annotations.ApiModelProperty; + +@XmlType(name = "variable") +public class VariableDTO { + private String name; + private String value; + private String processGroupId; + private Set affectedComponents = new HashSet<>(); + + @ApiModelProperty("The name of the variable") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @ApiModelProperty("The value of the variable") + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @ApiModelProperty(value = "The ID of the Process Group where this Variable is defined", readOnly = true) + public String getProcessGroupId() { + return processGroupId; + } + + public void setProcessGroupId(String groupId) { + this.processGroupId = groupId; + } + + @ApiModelProperty(value = "A set of all components that will be affected if the value of this variable is changed", readOnly = true) + public Set getAffectedComponents() { + return affectedComponents; + } + + public void setAffectedComponents(Set affectedComponents) { + this.affectedComponents = affectedComponents; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryDTO.java new file mode 100644 index 0000000000..c106a9a281 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryDTO.java @@ -0,0 +1,50 @@ +/* + * 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.web.api.dto; + +import java.util.Set; + +import javax.xml.bind.annotation.XmlType; + +import org.apache.nifi.web.api.entity.VariableEntity; + +import com.wordnik.swagger.annotations.ApiModelProperty; + +@XmlType(name = "variableRegistry") +public class VariableRegistryDTO { + private Set variables; + private String groupId; + + public void setVariables(final Set variables) { + this.variables = variables; + } + + @ApiModelProperty("The variables that are available in this Variable Registry") + public Set getVariables() { + return variables; + } + + public void setProcessGroupId(final String groupId) { + this.groupId = groupId; + } + + @ApiModelProperty("The UUID of the Process Group that this Variable Registry belongs to") + public String getProcessGroupId() { + return groupId; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryUpdateRequestDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryUpdateRequestDTO.java new file mode 100644 index 0000000000..06a0dc2d75 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryUpdateRequestDTO.java @@ -0,0 +1,115 @@ +/* + * 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.web.api.dto; + +import java.util.Date; +import java.util.List; + +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import org.apache.nifi.web.api.dto.util.TimestampAdapter; + +import com.wordnik.swagger.annotations.ApiModelProperty; + +@XmlType(name = "variableRegistryUpdateRequest") +public class VariableRegistryUpdateRequestDTO { + private String requestId; + private String processGroupId; + private String uri; + private Date submissionTime = new Date(); + private Date lastUpdated = new Date(); + private boolean complete = false; + private String failureReason; + private List updateSteps; + + + @ApiModelProperty("The unique ID of the Process Group that the variable registry belongs to") + public String getProcessGroupId() { + return processGroupId; + } + + public void setProcessGroupId(String processGroupId) { + this.processGroupId = processGroupId; + } + + @ApiModelProperty(value = "The unique ID of this request.", readOnly = true) + public String getRequestId() { + return requestId; + } + + public void setRequestId(String requestId) { + this.requestId = requestId; + } + + @ApiModelProperty(value = "The URI for future requests to this drop request.", readOnly = true) + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + @XmlJavaTypeAdapter(TimestampAdapter.class) + @ApiModelProperty(value = "The time at which this request was submitted.", dataType = "string", readOnly = true) + public Date getSubmissionTime() { + return submissionTime; + } + + public void setSubmissionTime(Date submissionTime) { + this.submissionTime = submissionTime; + } + + @XmlJavaTypeAdapter(TimestampAdapter.class) + @ApiModelProperty(value = "The last time this request was updated.", dataType = "string", readOnly = true) + public Date getLastUpdated() { + return lastUpdated; + } + + public void setLastUpdated(Date lastUpdated) { + this.lastUpdated = lastUpdated; + } + + @ApiModelProperty(value = "The steps that are required in order to complete the request, along with the status of each", readOnly = true) + public List getUpdateSteps() { + return updateSteps; + } + + public void setUpdateSteps(List updateSteps) { + this.updateSteps = updateSteps; + } + + @ApiModelProperty(value = "Whether or not this request has completed", readOnly = true) + public boolean isComplete() { + return complete; + } + + public void setComplete(boolean complete) { + this.complete = complete; + } + + @ApiModelProperty(value = "An explanation of why this request failed, or null if this request has not failed", readOnly = true) + public String getFailureReason() { + return failureReason; + } + + public void setFailureReason(String reason) { + this.failureReason = reason; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryUpdateStepDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryUpdateStepDTO.java new file mode 100644 index 0000000000..e1c8cee976 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VariableRegistryUpdateStepDTO.java @@ -0,0 +1,59 @@ +/* + * 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.web.api.dto; + +import javax.xml.bind.annotation.XmlType; + +import com.wordnik.swagger.annotations.ApiModelProperty; + +@XmlType(name = "varaibleRegistryUpdateStep") +public class VariableRegistryUpdateStepDTO { + private String description; + private boolean complete; + private String failureReason; + + public VariableRegistryUpdateStepDTO() { + } + + @ApiModelProperty(value = "Explanation of what happens in this step", readOnly = true) + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @ApiModelProperty(value = "Whether or not this step has completed", readOnly = true) + public boolean isComplete() { + return complete; + } + + public void setComplete(boolean complete) { + this.complete = complete; + } + + @ApiModelProperty(value = "An explanation of why this step failed, or null if this step did not fail", readOnly = true) + public String getFailureReason() { + return failureReason; + } + + public void setFailureReason(String reason) { + this.failureReason = reason; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ActivateControllerServicesEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ActivateControllerServicesEntity.java new file mode 100644 index 0000000000..a58f821681 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ActivateControllerServicesEntity.java @@ -0,0 +1,66 @@ +/* + * 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.web.api.entity; + +import java.util.Map; + +import javax.xml.bind.annotation.XmlRootElement; + +import org.apache.nifi.web.api.dto.RevisionDTO; + +import com.wordnik.swagger.annotations.ApiModelProperty; + +@XmlRootElement(name = "activateControllerServicesEntity") +public class ActivateControllerServicesEntity extends Entity { + public static final String STATE_ENABLED = "ENABLED"; + public static final String STATE_DISABLED = "DISABLED"; + + private String id; + private String state; + private Map components; + + @ApiModelProperty("The id of the ProcessGroup") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + /** + * @return The desired state of the descendant components. Possible states are 'RUNNING' and 'STOPPED' + */ + @ApiModelProperty(value = "The desired state of the descendant components", + allowableValues = STATE_ENABLED + ", " + STATE_DISABLED) + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + @ApiModelProperty("Optional services to schedule. If not specified, all authorized descendant controller services will be used.") + public Map getComponents() { + return components; + } + + public void setComponents(Map components) { + this.components = components; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ScheduleComponentsEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ScheduleComponentsEntity.java index 9aeef405e2..3e4c9a42a3 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ScheduleComponentsEntity.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ScheduleComponentsEntity.java @@ -16,17 +16,21 @@ */ package org.apache.nifi.web.api.entity; -import com.wordnik.swagger.annotations.ApiModelProperty; -import org.apache.nifi.web.api.dto.RevisionDTO; +import java.util.Map; import javax.xml.bind.annotation.XmlRootElement; -import java.util.Map; + +import org.apache.nifi.web.api.dto.RevisionDTO; + +import com.wordnik.swagger.annotations.ApiModelProperty; /** * A serialized representation of this class can be placed in the entity body of a request to the API. */ @XmlRootElement(name = "scheduleComponentEntity") public class ScheduleComponentsEntity extends Entity { + public static final String STATE_RUNNING = "RUNNING"; + public static final String STATE_STOPPED = "STOPPED"; private String id; private String state; @@ -51,7 +55,7 @@ public class ScheduleComponentsEntity extends Entity { */ @ApiModelProperty( value = "The desired state of the descendant components", - allowableValues = "RUNNING, STOPPED" + allowableValues = STATE_RUNNING + ", " + STATE_STOPPED ) public String getState() { return state; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableEntity.java new file mode 100644 index 0000000000..06f6fcfc29 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableEntity.java @@ -0,0 +1,51 @@ +/* + * 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.web.api.entity; + +import javax.xml.bind.annotation.XmlRootElement; + +import org.apache.nifi.web.api.dto.VariableDTO; +import org.apache.nifi.web.api.dto.WritablePermission; + +import com.wordnik.swagger.annotations.ApiModelProperty; + +@XmlRootElement(name = "variableEntity") +public class VariableEntity extends Entity implements WritablePermission { + private VariableDTO variable; + private Boolean canWrite; + + @Override + @ApiModelProperty(value = "Indicates whether the user can write a given resource.", readOnly = true) + public Boolean getCanWrite() { + return canWrite; + } + + @Override + public void setCanWrite(Boolean canWrite) { + this.canWrite = canWrite; + } + + @ApiModelProperty("The variable information") + public VariableDTO getVariable() { + return variable; + } + + public void setVariable(VariableDTO variable) { + this.variable = variable; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableRegistryEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableRegistryEntity.java new file mode 100644 index 0000000000..d8764530bf --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableRegistryEntity.java @@ -0,0 +1,50 @@ +/* + * 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.web.api.entity; + +import javax.xml.bind.annotation.XmlRootElement; + +import org.apache.nifi.web.api.dto.RevisionDTO; +import org.apache.nifi.web.api.dto.VariableRegistryDTO; + +import com.wordnik.swagger.annotations.ApiModelProperty; + +@XmlRootElement(name = "variableRegistryEntity") +public class VariableRegistryEntity extends Entity { + private RevisionDTO groupRevision; + private VariableRegistryDTO variableRegistry; + + + @ApiModelProperty("The Variable Registry.") + public VariableRegistryDTO getVariableRegistry() { + return variableRegistry; + } + + public void setVariableRegistry(VariableRegistryDTO variableRegistry) { + this.variableRegistry = variableRegistry; + } + + @ApiModelProperty("The revision of the Process Group that the Variable Registry belongs to") + public RevisionDTO getProcessGroupRevision() { + return groupRevision; + } + + public void setProcessGroupRevision(RevisionDTO revision) { + this.groupRevision = revision; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableRegistryUpdateRequestEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableRegistryUpdateRequestEntity.java new file mode 100644 index 0000000000..77257afec3 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VariableRegistryUpdateRequestEntity.java @@ -0,0 +1,49 @@ +/* + * 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.web.api.entity; + +import javax.xml.bind.annotation.XmlRootElement; + +import org.apache.nifi.web.api.dto.RevisionDTO; +import org.apache.nifi.web.api.dto.VariableRegistryUpdateRequestDTO; + +import com.wordnik.swagger.annotations.ApiModelProperty; + +@XmlRootElement(name = "variableRegistryUpdateRequestEntity") +public class VariableRegistryUpdateRequestEntity extends Entity { + private VariableRegistryUpdateRequestDTO requestDto; + private RevisionDTO processGroupRevision; + + @ApiModelProperty("The revision for the Process Group that owns this variable registry.") + public RevisionDTO getProcessGroupRevision() { + return processGroupRevision; + } + + public void setProcessGroupRevision(RevisionDTO revision) { + this.processGroupRevision = revision; + } + + @ApiModelProperty("The Variable Registry Update Request") + public VariableRegistryUpdateRequestDTO getRequestDto() { + return requestDto; + } + + public void setRequestDto(VariableRegistryUpdateRequestDTO requestDto) { + this.requestDto = requestDto; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/StandardHttpResponseMapper.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/StandardHttpResponseMapper.java index 2fc55a4c6b..c102746013 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/StandardHttpResponseMapper.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/StandardHttpResponseMapper.java @@ -16,6 +16,16 @@ */ package org.apache.nifi.cluster.coordination.http; +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import javax.ws.rs.core.StreamingOutput; + import org.apache.nifi.cluster.coordination.http.endpoints.AccessPolicyEndpointMerger; import org.apache.nifi.cluster.coordination.http.endpoints.BulletinBoardEndpointMerger; import org.apache.nifi.cluster.coordination.http.endpoints.ComponentStateEndpointMerger; @@ -77,15 +87,6 @@ import org.apache.nifi.util.NiFiProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.ws.rs.core.StreamingOutput; -import java.io.IOException; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - public class StandardHttpResponseMapper implements HttpResponseMapper { private Logger logger = LoggerFactory.getLogger(StandardHttpResponseMapper.class); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/AbstractConfiguredComponent.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/AbstractConfiguredComponent.java index 45c6eadaac..ff5a8aff0a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/AbstractConfiguredComponent.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/AbstractConfiguredComponent.java @@ -16,24 +16,6 @@ */ package org.apache.nifi.controller; -import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.attribute.expression.language.StandardPropertyValue; -import org.apache.nifi.bundle.Bundle; -import org.apache.nifi.bundle.BundleCoordinate; -import org.apache.nifi.components.ConfigurableComponent; -import org.apache.nifi.components.PropertyDescriptor; -import org.apache.nifi.components.ValidationContext; -import org.apache.nifi.components.ValidationResult; -import org.apache.nifi.controller.service.ControllerServiceNode; -import org.apache.nifi.controller.service.ControllerServiceProvider; -import org.apache.nifi.nar.ExtensionManager; -import org.apache.nifi.nar.NarCloseable; -import org.apache.nifi.registry.VariableRegistry; -import org.apache.nifi.util.CharacterFilterUtils; -import org.apache.nifi.util.file.classloader.ClassLoaderUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; @@ -53,6 +35,24 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.attribute.expression.language.StandardPropertyValue; +import org.apache.nifi.bundle.Bundle; +import org.apache.nifi.bundle.BundleCoordinate; +import org.apache.nifi.components.ConfigurableComponent; +import org.apache.nifi.components.PropertyDescriptor; +import org.apache.nifi.components.ValidationContext; +import org.apache.nifi.components.ValidationResult; +import org.apache.nifi.controller.service.ControllerServiceNode; +import org.apache.nifi.controller.service.ControllerServiceProvider; +import org.apache.nifi.nar.ExtensionManager; +import org.apache.nifi.nar.NarCloseable; +import org.apache.nifi.registry.ComponentVariableRegistry; +import org.apache.nifi.util.CharacterFilterUtils; +import org.apache.nifi.util.file.classloader.ClassLoaderUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public abstract class AbstractConfiguredComponent implements ConfigurableComponent, ConfiguredComponent { private static final Logger logger = LoggerFactory.getLogger(AbstractConfiguredComponent.class); @@ -64,7 +64,7 @@ public abstract class AbstractConfiguredComponent implements ConfigurableCompone private final AtomicReference validationContext = new AtomicReference<>(); private final String componentType; private final String componentCanonicalClass; - private final VariableRegistry variableRegistry; + private final ComponentVariableRegistry variableRegistry; private final ReloadComponent reloadComponent; private final AtomicBoolean isExtensionMissing; @@ -74,7 +74,7 @@ public abstract class AbstractConfiguredComponent implements ConfigurableCompone public AbstractConfiguredComponent(final String id, final ValidationContextFactory validationContextFactory, final ControllerServiceProvider serviceProvider, - final String componentType, final String componentCanonicalClass, final VariableRegistry variableRegistry, + final String componentType, final String componentCanonicalClass, final ComponentVariableRegistry variableRegistry, final ReloadComponent reloadComponent, final boolean isExtensionMissing) { this.id = id; this.validationContextFactory = validationContextFactory; @@ -541,7 +541,7 @@ public abstract class AbstractConfiguredComponent implements ConfigurableCompone } } - protected VariableRegistry getVariableRegistry() { + public ComponentVariableRegistry getVariableRegistry() { return this.variableRegistry; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ConfiguredComponent.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ConfiguredComponent.java index 069e6ce4fc..940ac21adb 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ConfiguredComponent.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ConfiguredComponent.java @@ -29,6 +29,7 @@ import org.apache.nifi.components.ConfigurableComponent; import org.apache.nifi.components.PropertyDescriptor; import org.apache.nifi.components.ValidationResult; import org.apache.nifi.logging.ComponentLog; +import org.apache.nifi.registry.ComponentVariableRegistry; import java.net.URL; import java.util.Collection; @@ -38,6 +39,7 @@ import java.util.Set; public interface ConfiguredComponent extends ComponentAuthorizable { + @Override public String getIdentifier(); public String getName(); @@ -99,6 +101,11 @@ public interface ConfiguredComponent extends ComponentAuthorizable { */ boolean isDeprecated(); + /** + * @return the variable registry for this component + */ + ComponentVariableRegistry getVariableRegistry(); + @Override default AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user, Map resourceContext) { // if this is a modification request and the reporting task is restricted ensure the user has elevated privileges. if this diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessScheduler.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessScheduler.java index 5bb8981c32..c6f30b5bdc 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessScheduler.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessScheduler.java @@ -18,6 +18,7 @@ package org.apache.nifi.controller; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; import org.apache.nifi.connectable.Connectable; import org.apache.nifi.connectable.Funnel; @@ -41,7 +42,7 @@ public interface ProcessScheduler { * @param procNode to start * @throws IllegalStateException if the Processor is disabled */ - void startProcessor(ProcessorNode procNode); + Future startProcessor(ProcessorNode procNode); /** * Stops scheduling the given processor to run and invokes all methods on @@ -52,7 +53,7 @@ public interface ProcessScheduler { * * @param procNode to stop */ - void stopProcessor(ProcessorNode procNode); + Future stopProcessor(ProcessorNode procNode); /** * Starts scheduling the given Port to run. If the Port is already scheduled @@ -169,12 +170,12 @@ public interface ProcessScheduler { * Disables all of the given Controller Services in the order provided by the List * @param services the controller services to disable */ - void disableControllerServices(List services); + CompletableFuture disableControllerServices(List services); /** * Disables the Controller Service so that it can be updated * * @param service to disable */ - void disableControllerService(ControllerServiceNode service); + CompletableFuture disableControllerService(ControllerServiceNode service); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessorNode.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessorNode.java index aac5e524fe..ba2e59b7f1 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessorNode.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessorNode.java @@ -16,6 +16,13 @@ */ package org.apache.nifi.controller; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + import org.apache.nifi.annotation.behavior.InputRequirement.Requirement; import org.apache.nifi.connectable.Connectable; import org.apache.nifi.controller.scheduling.ScheduleState; @@ -26,18 +33,12 @@ import org.apache.nifi.logging.LogLevel; import org.apache.nifi.processor.ProcessContext; import org.apache.nifi.processor.Processor; import org.apache.nifi.processor.Relationship; -import org.apache.nifi.registry.VariableRegistry; +import org.apache.nifi.registry.ComponentVariableRegistry; import org.apache.nifi.scheduling.ExecutionNode; import org.apache.nifi.scheduling.SchedulingStrategy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - public abstract class ProcessorNode extends AbstractConfiguredComponent implements Connectable { private static final Logger logger = LoggerFactory.getLogger(ProcessorNode.class); @@ -46,7 +47,7 @@ public abstract class ProcessorNode extends AbstractConfiguredComponent implemen public ProcessorNode(final String id, final ValidationContextFactory validationContextFactory, final ControllerServiceProvider serviceProvider, - final String componentType, final String componentCanonicalClass, final VariableRegistry variableRegistry, + final String componentType, final String componentCanonicalClass, final ComponentVariableRegistry variableRegistry, final ReloadComponent reloadComponent, final boolean isExtensionMissing) { super(id, validationContextFactory, serviceProvider, componentType, componentCanonicalClass, variableRegistry, reloadComponent, isExtensionMissing); this.scheduledState = new AtomicReference<>(ScheduledState.STOPPED); @@ -185,7 +186,7 @@ public abstract class ProcessorNode extends AbstractConfiguredComponent implemen * the ScheduleState that can be used to ensure that the running state (STOPPED, RUNNING, etc.) * as well as the active thread counts are kept in sync */ - public abstract void stop(ScheduledExecutorService scheduler, + public abstract CompletableFuture stop(ScheduledExecutorService scheduler, T processContext, SchedulingAgent schedulingAgent, ScheduleState scheduleState); /** diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceNode.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceNode.java index faf530ff96..3dd1076733 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceNode.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceNode.java @@ -108,7 +108,7 @@ public interface ControllerServiceNode extends ConfiguredComponent { * implementation of {@link ScheduledExecutorService} used to * initiate service disabling task */ - void disable(ScheduledExecutorService scheduler); + CompletableFuture disable(ScheduledExecutorService scheduler); /** * @return the ControllerServiceReference that describes which components are referencing this Controller Service diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceProvider.java index f7ba5e5d35..010ecdf712 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceProvider.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceProvider.java @@ -19,7 +19,7 @@ package org.apache.nifi.controller.service; import java.net.URL; import java.util.Collection; import java.util.Set; -import java.util.concurrent.Future; +import java.util.concurrent.CompletableFuture; import org.apache.nifi.annotation.lifecycle.OnAdded; import org.apache.nifi.bundle.BundleCoordinate; @@ -72,7 +72,7 @@ public interface ControllerServiceProvider extends ControllerServiceLookup { * @param serviceNode the service node * @return a Future that can be used to wait for the service to finish being enabled. */ - Future enableControllerService(ControllerServiceNode serviceNode); + CompletableFuture enableControllerService(ControllerServiceNode serviceNode); /** * Enables the collection of services. If a service in this collection @@ -90,7 +90,7 @@ public interface ControllerServiceProvider extends ControllerServiceLookup { * * @param serviceNode the node */ - void disableControllerService(ControllerServiceNode serviceNode); + CompletableFuture disableControllerService(ControllerServiceNode serviceNode); /** * @return a Set of all Controller Services that exist for this service diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceReference.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceReference.java index df18c62e56..13c58448d7 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceReference.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceReference.java @@ -16,6 +16,7 @@ */ package org.apache.nifi.controller.service; +import java.util.List; import java.util.Set; import org.apache.nifi.controller.ConfiguredComponent; @@ -43,4 +44,13 @@ public interface ControllerServiceReference { * Controller Services) */ Set getActiveReferences(); + + /** + * Returns a List of all components that reference this Controller Service (recursively) that + * are of the given type + * + * @param componentType the type of component that is desirable + * @return a List of all components that reference this Controller Service that are of the given type + */ + List findRecursiveReferences(Class componentType); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/ProcessGroup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/ProcessGroup.java index 122e454223..bf789f7b1b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/ProcessGroup.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/ProcessGroup.java @@ -16,12 +16,20 @@ */ package org.apache.nifi.groups; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; + import org.apache.nifi.authorization.resource.ComponentAuthorizable; import org.apache.nifi.connectable.Connectable; import org.apache.nifi.connectable.Connection; import org.apache.nifi.connectable.Funnel; import org.apache.nifi.connectable.Port; import org.apache.nifi.connectable.Positionable; +import org.apache.nifi.controller.ConfiguredComponent; import org.apache.nifi.controller.ProcessorNode; import org.apache.nifi.controller.ScheduledState; import org.apache.nifi.controller.Snippet; @@ -30,13 +38,9 @@ import org.apache.nifi.controller.label.Label; import org.apache.nifi.controller.service.ControllerServiceNode; import org.apache.nifi.flowfile.FlowFile; import org.apache.nifi.processor.Processor; +import org.apache.nifi.registry.ComponentVariableRegistry; import org.apache.nifi.remote.RemoteGroupPort; -import java.util.Collection; -import java.util.List; -import java.util.Set; -import java.util.function.Predicate; - /** *

* ProcessGroup objects are containers for processing entities, such as @@ -84,6 +88,7 @@ public interface ProcessGroup extends ComponentAuthorizable, Positionable { /** * @return the ID of the ProcessGroup */ + @Override String getIdentifier(); /** @@ -159,7 +164,7 @@ public interface ProcessGroup extends ComponentAuthorizable, Positionable { * @throws IllegalStateException if the processor is not valid, or is * already running */ - void startProcessor(ProcessorNode processor); + CompletableFuture startProcessor(ProcessorNode processor); /** * Starts the given Input Port @@ -187,7 +192,7 @@ public interface ProcessGroup extends ComponentAuthorizable, Positionable { * * @param processor to stop */ - void stopProcessor(ProcessorNode processor); + CompletableFuture stopProcessor(ProcessorNode processor); /** * Stops the given Port @@ -813,6 +818,15 @@ public interface ProcessGroup extends ComponentAuthorizable, Positionable { */ void verifyCanMove(Snippet snippet, ProcessGroup newProcessGroup); + /** + * Ensures that the given variables can be updated + * + * @param updatedVariables the new set of variable names and values + * + * @throws IllegalStateException if one or more variables that are listed cannot be updated at this time + */ + void verifyCanUpdateVariables(Map updatedVariables); + /** * Adds the given template to this Process Group * @@ -853,4 +867,27 @@ public interface ProcessGroup extends ComponentAuthorizable, Positionable { * @return a Set of all Templates that belong to this Process Group and any descendant Process Groups */ Set