http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/literals/NumberLiteralEvaluator.java ---------------------------------------------------------------------- diff --git a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/literals/NumberLiteralEvaluator.java b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/literals/NumberLiteralEvaluator.java new file mode 100644 index 0000000..d7569e0 --- /dev/null +++ b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/literals/NumberLiteralEvaluator.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.attribute.expression.language.evaluation.literals; + +import java.util.Map; + +import org.apache.nifi.attribute.expression.language.evaluation.Evaluator; +import org.apache.nifi.attribute.expression.language.evaluation.NumberEvaluator; +import org.apache.nifi.attribute.expression.language.evaluation.NumberQueryResult; +import org.apache.nifi.attribute.expression.language.evaluation.QueryResult; + +public class NumberLiteralEvaluator extends NumberEvaluator { + + private final long literal; + + public NumberLiteralEvaluator(final String value) { + this.literal = Long.parseLong(value); + } + + @Override + public QueryResult<Long> evaluate(final Map<String, String> attributes) { + return new NumberQueryResult(literal); + } + + @Override + public Evaluator<?> getSubjectEvaluator() { + return null; + } + +}
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/literals/StringLiteralEvaluator.java ---------------------------------------------------------------------- diff --git a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/literals/StringLiteralEvaluator.java b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/literals/StringLiteralEvaluator.java new file mode 100644 index 0000000..d739ac7 --- /dev/null +++ b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/literals/StringLiteralEvaluator.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.attribute.expression.language.evaluation.literals; + +import java.util.Map; + +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.StringQueryResult; + +public class StringLiteralEvaluator extends StringEvaluator { + + private final String value; + + public StringLiteralEvaluator(final String value) { + // need to escape characters after backslashes + final StringBuilder sb = new StringBuilder(); + boolean lastCharIsBackslash = false; + for (int i = 0; i < value.length(); i++) { + final char c = value.charAt(i); + + if (lastCharIsBackslash) { + switch (c) { + case 'n': + sb.append("\n"); + break; + case 'r': + sb.append("\r"); + break; + case '\\': + sb.append("\\"); + break; + case 't': + sb.append("\\t"); + break; + default: + sb.append("\\").append(c); + break; + } + + lastCharIsBackslash = false; + } else if (c == '\\') { + lastCharIsBackslash = true; + } else { + sb.append(c); + } + } + + this.value = sb.toString(); + } + + @Override + public QueryResult<String> evaluate(final Map<String, String> attributes) { + return new StringQueryResult(value); + } + + @Override + public Evaluator<?> getSubjectEvaluator() { + return null; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AllAttributesEvaluator.java ---------------------------------------------------------------------- diff --git a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AllAttributesEvaluator.java b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AllAttributesEvaluator.java new file mode 100644 index 0000000..d9dd4d3 --- /dev/null +++ b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AllAttributesEvaluator.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.attribute.expression.language.evaluation.selection; + +import java.util.Map; + +import org.apache.nifi.attribute.expression.language.evaluation.BooleanEvaluator; +import org.apache.nifi.attribute.expression.language.evaluation.BooleanQueryResult; +import org.apache.nifi.attribute.expression.language.evaluation.Evaluator; +import org.apache.nifi.attribute.expression.language.evaluation.QueryResult; + +public class AllAttributesEvaluator extends BooleanEvaluator { + + private final BooleanEvaluator booleanEvaluator; + private final MultiAttributeEvaluator multiAttributeEvaluator; + + public AllAttributesEvaluator(final BooleanEvaluator booleanEvaluator, final MultiAttributeEvaluator multiAttributeEvaluator) { + this.booleanEvaluator = booleanEvaluator; + this.multiAttributeEvaluator = multiAttributeEvaluator; + } + + @Override + public QueryResult<Boolean> evaluate(final Map<String, String> attributes) { + QueryResult<Boolean> attributeValueQuery = booleanEvaluator.evaluate(attributes); + Boolean result = attributeValueQuery.getValue(); + if (result == null) { + return new BooleanQueryResult(false); + } + + if (!result) { + return new BooleanQueryResult(false); + } + + while (multiAttributeEvaluator.getEvaluationsRemaining() > 0) { + attributeValueQuery = booleanEvaluator.evaluate(attributes); + result = attributeValueQuery.getValue(); + if (result != null && !result) { + return attributeValueQuery; + } + } + + return new BooleanQueryResult(true); + } + + @Override + public int getEvaluationsRemaining() { + return 0; + } + + @Override + public Evaluator<?> getSubjectEvaluator() { + return null; + } +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AnyAttributeEvaluator.java ---------------------------------------------------------------------- diff --git a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AnyAttributeEvaluator.java b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AnyAttributeEvaluator.java new file mode 100644 index 0000000..9192958 --- /dev/null +++ b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AnyAttributeEvaluator.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.attribute.expression.language.evaluation.selection; + +import java.util.Map; + +import org.apache.nifi.attribute.expression.language.evaluation.BooleanEvaluator; +import org.apache.nifi.attribute.expression.language.evaluation.BooleanQueryResult; +import org.apache.nifi.attribute.expression.language.evaluation.Evaluator; +import org.apache.nifi.attribute.expression.language.evaluation.QueryResult; + +public class AnyAttributeEvaluator extends BooleanEvaluator { + + private final BooleanEvaluator booleanEvaluator; + private final MultiAttributeEvaluator multiAttributeEvaluator; + + public AnyAttributeEvaluator(final BooleanEvaluator booleanEvaluator, final MultiAttributeEvaluator multiAttributeEvaluator) { + this.booleanEvaluator = booleanEvaluator; + this.multiAttributeEvaluator = multiAttributeEvaluator; + } + + @Override + public QueryResult<Boolean> evaluate(final Map<String, String> attributes) { + QueryResult<Boolean> attributeValueQuery = booleanEvaluator.evaluate(attributes); + Boolean result = attributeValueQuery.getValue(); + if (result == null) { + return new BooleanQueryResult(false); + } + + if (result) { + return new BooleanQueryResult(true); + } + + while (multiAttributeEvaluator.getEvaluationsRemaining() > 0) { + attributeValueQuery = booleanEvaluator.evaluate(attributes); + result = attributeValueQuery.getValue(); + if (result != null && result) { + return attributeValueQuery; + } + } + + return new BooleanQueryResult(false); + } + + @Override + public int getEvaluationsRemaining() { + return 0; + } + + @Override + public Evaluator<?> getSubjectEvaluator() { + return null; + } +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AnyMatchingAttributeEvaluator.java ---------------------------------------------------------------------- diff --git a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AnyMatchingAttributeEvaluator.java b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AnyMatchingAttributeEvaluator.java new file mode 100644 index 0000000..8c07278 --- /dev/null +++ b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/AnyMatchingAttributeEvaluator.java @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.attribute.expression.language.evaluation.selection; + +public class AnyMatchingAttributeEvaluator { + +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/DelineatedAttributeEvaluator.java ---------------------------------------------------------------------- diff --git a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/DelineatedAttributeEvaluator.java b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/DelineatedAttributeEvaluator.java new file mode 100644 index 0000000..209c86f --- /dev/null +++ b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/DelineatedAttributeEvaluator.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.attribute.expression.language.evaluation.selection; + +import java.util.Map; + +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.StringQueryResult; + +public class DelineatedAttributeEvaluator extends MultiAttributeEvaluator { + + private final StringEvaluator subjectEvaluator; + private final StringEvaluator delimiterEvaluator; + private final int evaluationType; + private String[] delineatedValues; + private int evaluationCount = 0; + private int evaluationsLeft = 1; + + public DelineatedAttributeEvaluator(final StringEvaluator subjectEvaluator, final StringEvaluator delimiterEvaluator, final int evaluationType) { + this.subjectEvaluator = subjectEvaluator; + this.delimiterEvaluator = delimiterEvaluator; + this.evaluationType = evaluationType; + } + + @Override + public QueryResult<String> evaluate(final Map<String, String> attributes) { + if (delineatedValues == null) { + final QueryResult<String> subjectValue = subjectEvaluator.evaluate(attributes); + if (subjectValue.getValue() == null) { + evaluationsLeft = 0; + return new StringQueryResult(null); + } + + final QueryResult<String> delimiterValue = delimiterEvaluator.evaluate(attributes); + if (subjectValue.getValue() == null) { + evaluationsLeft = 0; + return new StringQueryResult(null); + } + + delineatedValues = subjectValue.getValue().split(delimiterValue.getValue()); + } + + if (evaluationCount > delineatedValues.length) { + evaluationsLeft = 0; + return new StringQueryResult(null); + } + + evaluationsLeft = delineatedValues.length - evaluationCount - 1; + + return new StringQueryResult(delineatedValues[evaluationCount++]); + } + + @Override + public int getEvaluationsRemaining() { + return evaluationsLeft; + } + + @Override + public Evaluator<?> getSubjectEvaluator() { + return null; + } + + @Override + public int getEvaluationType() { + return evaluationType; + } +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/MultiAttributeEvaluator.java ---------------------------------------------------------------------- diff --git a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/MultiAttributeEvaluator.java b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/MultiAttributeEvaluator.java new file mode 100644 index 0000000..f80ed97 --- /dev/null +++ b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/MultiAttributeEvaluator.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.attribute.expression.language.evaluation.selection; + +import org.apache.nifi.attribute.expression.language.evaluation.StringEvaluator; + +public abstract class MultiAttributeEvaluator extends StringEvaluator { + + public abstract int getEvaluationType(); +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/MultiMatchAttributeEvaluator.java ---------------------------------------------------------------------- diff --git a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/MultiMatchAttributeEvaluator.java b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/MultiMatchAttributeEvaluator.java new file mode 100644 index 0000000..9a441ce --- /dev/null +++ b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/MultiMatchAttributeEvaluator.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.attribute.expression.language.evaluation.selection; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +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.StringQueryResult; + +public class MultiMatchAttributeEvaluator extends MultiAttributeEvaluator { + + private final List<Pattern> attributePatterns; + private final int evaluationType; + private final List<String> attributeNames = new ArrayList<>(); + private int evaluationCount = 0; + + public MultiMatchAttributeEvaluator(final List<String> attributeRegexes, final int evaluationType) { + this.attributePatterns = new ArrayList<>(); + for (final String regex : attributeRegexes) { + attributePatterns.add(Pattern.compile(regex)); + } + + this.evaluationType = evaluationType; + } + + /** + * Can be called only after the first call to evaluate + * + * @return + */ + @Override + public int getEvaluationsRemaining() { + return attributeNames.size() - evaluationCount; + } + + @Override + public QueryResult<String> evaluate(final Map<String, String> attributes) { + if (evaluationCount == 0) { + for (final Pattern pattern : attributePatterns) { + for (final String attrName : attributes.keySet()) { + if (pattern.matcher(attrName).matches()) { + attributeNames.add(attrName); + } + } + } + } + + if (evaluationCount >= attributeNames.size()) { + return new StringQueryResult(null); + } + + return new StringQueryResult(attributes.get(attributeNames.get(evaluationCount++))); + } + + @Override + public Evaluator<?> getSubjectEvaluator() { + return null; + } + + @Override + public int getEvaluationType() { + return evaluationType; + } +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/MultiNamedAttributeEvaluator.java ---------------------------------------------------------------------- diff --git a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/MultiNamedAttributeEvaluator.java b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/MultiNamedAttributeEvaluator.java new file mode 100644 index 0000000..6dabc0a --- /dev/null +++ b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/selection/MultiNamedAttributeEvaluator.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.attribute.expression.language.evaluation.selection; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +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.StringQueryResult; + +public class MultiNamedAttributeEvaluator extends MultiAttributeEvaluator { + + private final List<String> attributeNames; + private final int evaluationType; + private int evaluationCount = 0; + private List<String> matchingAttributeNames = null; + + public MultiNamedAttributeEvaluator(final List<String> attributeNames, final int evaluationType) { + this.attributeNames = attributeNames; + this.evaluationType = evaluationType; + } + + @Override + public QueryResult<String> evaluate(final Map<String, String> attributes) { + matchingAttributeNames = new ArrayList<>(attributeNames); + + if (matchingAttributeNames.size() <= evaluationCount) { + return new StringQueryResult(null); + } + + return new StringQueryResult(attributes.get(matchingAttributeNames.get(evaluationCount++))); + } + + @Override + public int getEvaluationsRemaining() { + return matchingAttributeNames.size() - evaluationCount; + } + + @Override + public Evaluator<?> getSubjectEvaluator() { + return null; + } + + @Override + public int getEvaluationType() { + return evaluationType; + } +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/exception/AttributeExpressionLanguageException.java ---------------------------------------------------------------------- diff --git a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/exception/AttributeExpressionLanguageException.java b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/exception/AttributeExpressionLanguageException.java new file mode 100644 index 0000000..47d42cb --- /dev/null +++ b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/exception/AttributeExpressionLanguageException.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.exception; + +public class AttributeExpressionLanguageException extends RuntimeException { + + private static final long serialVersionUID = -5637284498692447901L; + + public AttributeExpressionLanguageException(final String explanation) { + super(explanation); + } + + public AttributeExpressionLanguageException(final String explanation, final Throwable t) { + super(explanation, t); + } + + public AttributeExpressionLanguageException(final Throwable t) { + super(t); + } +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/exception/AttributeExpressionLanguageParsingException.java ---------------------------------------------------------------------- diff --git a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/exception/AttributeExpressionLanguageParsingException.java b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/exception/AttributeExpressionLanguageParsingException.java new file mode 100644 index 0000000..f8531cb --- /dev/null +++ b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/exception/AttributeExpressionLanguageParsingException.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.exception; + +public class AttributeExpressionLanguageParsingException extends AttributeExpressionLanguageException { + + private static final long serialVersionUID = 7422163230677064726L; + + public AttributeExpressionLanguageParsingException(final String explanation) { + super(explanation); + } + + public AttributeExpressionLanguageParsingException(final String explanation, final Throwable t) { + super(explanation, t); + } + + public AttributeExpressionLanguageParsingException(final Throwable t) { + super(t); + } +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/exception/IllegalAttributeException.java ---------------------------------------------------------------------- diff --git a/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/exception/IllegalAttributeException.java b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/exception/IllegalAttributeException.java new file mode 100644 index 0000000..4a9a9c5 --- /dev/null +++ b/commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/exception/IllegalAttributeException.java @@ -0,0 +1,28 @@ +/* + * 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.exception; + +public class IllegalAttributeException extends RuntimeException { + + public IllegalAttributeException() { + super(); + } + + public IllegalAttributeException(final String explanation) { + super(explanation); + } +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java ---------------------------------------------------------------------- diff --git a/commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java b/commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java new file mode 100644 index 0000000..a2b7214 --- /dev/null +++ b/commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java @@ -0,0 +1,1068 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.attribute.expression.language; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import org.apache.nifi.attribute.expression.language.Query.Range; +import org.apache.nifi.attribute.expression.language.evaluation.QueryResult; +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.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; +import org.mockito.Mockito; + +public class TestQuery { + + @Test + public void testCompilation() { + assertInvalid("${attr:uuid()}"); + assertInvalid("${attr:indexOf(length())}"); + assertValid("${UUID()}"); + assertInvalid("${UUID():nextInt()}"); + assertValid("${nextInt()}"); + assertValid("${now():format('yyyy/MM/dd')}"); + assertInvalid("${attr:times(3)}"); + assertValid("${attr:toNumber():multiply(3)}"); + // left here because it's convenient for looking at the output + //System.out.println(Query.compile("").evaluate(null)); + } + + private void assertValid(final String query) { + try { + Query.compile(query); + } catch (final Exception e) { + e.printStackTrace(); + Assert.fail("Expected query to be valid, but it failed to compile due to " + e); + } + } + + private void assertInvalid(final String query) { + try { + Query.compile(query); + Assert.fail("Expected query to be invalid, but it did compile"); + } catch (final Exception e) { + } + } + + @Test + public void testIsValidExpression() { + Query.validateExpression("${abc:substring(${xyz:length()})}", false); + Query.isValidExpression("${now():format('yyyy-MM-dd')}"); + + + try { + Query.validateExpression("$${attr}", false); + Assert.fail("invalid query validated"); + } catch (final AttributeExpressionLanguageParsingException e) { + } + + Query.validateExpression("$${attr}", true); + + Query.validateExpression("${filename:startsWith('T8MTXBC')\n" + + ":or( ${filename:startsWith('C4QXABC')} )\n" + + ":or( ${filename:startsWith('U6CXEBC')} )" + + ":or( ${filename:startsWith('KYM3ABC')} )}", false); + } + + + @Test + public void testCompileEmbedded() { + final String expression = "${x:equals( ${y} )}"; + final Query query = Query.compile(expression); + final Tree tree = query.getTree(); + System.out.println( printTree(tree) ); + + final Map<String, String> attributes = new HashMap<>(); + attributes.put("x", "x"); + attributes.put("y", "x"); + final String result = Query.evaluateExpressions(expression, attributes, null); + assertEquals("true", result); + + Query.validateExpression(expression, false); + } + + private String printTree(final Tree tree) { + final StringBuilder sb = new StringBuilder(); + printTree(tree, 0, sb); + + return sb.toString(); + } + + private void printTree(final Tree tree, final int spaces, final StringBuilder sb) { + for (int i=0; i < spaces; i++) { + sb.append(" "); + } + + if ( tree.getText().trim().isEmpty() ) { + sb.append(tree.toString()).append("\n"); + } else { + sb.append(tree.getText()).append("\n"); + } + + for (int i=0; i < tree.getChildCount(); i++) { + printTree(tree.getChild(i), spaces + 2, sb); + } + } + + @Test + public void testEscape() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("attr", "My Value"); + attributes.put("${xx}", "hello"); + + assertEquals("My Value", evaluateQueryForEscape("${attr}", attributes)); + assertEquals("${attr}", evaluateQueryForEscape("$${attr}", attributes)); + assertEquals("$My Value", evaluateQueryForEscape("$$${attr}", attributes)); + assertEquals("$${attr}", evaluateQueryForEscape("$$$${attr}", attributes)); + assertEquals("$$My Value", evaluateQueryForEscape("$$$$${attr}", attributes)); + } + + @Test + public void testWithBackSlashes() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("x", "C:\\test\\1.txt"); + attributes.put("y", "y\ny"); + + final String query = "${x:substringAfterLast( '/' ):substringAfterLast( '\\\\' )}"; + verifyEquals(query, attributes, "1.txt"); + attributes.put("x", "C:/test/1.txt"); + verifyEquals(query, attributes, "1.txt"); + + verifyEquals("${y:equals('y\\ny')}", attributes, Boolean.TRUE); + } + + @Test + public void testWithTicksOutside() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("attr", "My Value"); + + assertEquals(1, Query.extractExpressionRanges("\"${attr}").size()); + assertEquals(1, Query.extractExpressionRanges("'${attr}").size()); + assertEquals(1, Query.extractExpressionRanges("'${attr}'").size()); + assertEquals(1, Query.extractExpressionRanges("${attr}").size()); + + assertEquals("'My Value'", Query.evaluateExpressions("'${attr}'", attributes, null)); + assertEquals("'My Value", Query.evaluateExpressions("'${attr}", attributes, null)); + } + + + @Test + @Ignore("Depends on TimeZone") + public void testDateToNumber() { + final Query query = Query.compile("${dateTime:toDate('yyyy/MM/dd HH:mm:ss.SSS'):toNumber()}"); + final Map<String, String> attributes = new HashMap<>(); + attributes.put("dateTime", "2013/11/18 10:22:27.678"); + + final QueryResult<?> result = query.evaluate(attributes); + assertEquals(ResultType.NUMBER, result.getResultType()); + assertEquals(1384788147678L, result.getValue()); + } + + @Test + public void testAddOneDayToDate() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("dateTime", "2013/11/18 10:22:27.678"); + + verifyEquals("${dateTime:toDate('yyyy/MM/dd HH:mm:ss.SSS'):toNumber():plus(86400000):toDate():format('yyyy/MM/dd HH:mm:ss.SSS')}", attributes, "2013/11/19 10:22:27.678"); + } + + @Test + public void implicitDateConversion() { + final Date date = new Date(); + final Query query = Query.compile("${dateTime:format('yyyy/MM/dd HH:mm:ss.SSS')}"); + final Map<String, String> attributes = new HashMap<>(); + attributes.put("dateTime", date.toString()); + + // the date.toString() above will end up truncating the milliseconds. So remove millis from the Date before + // formatting it + final SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS"); + final long millis = date.getTime() % 1000L; + final Date roundedToNearestSecond = new Date(date.getTime() - millis); + final String formatted = sdf.format(roundedToNearestSecond); + + final QueryResult<?> result = query.evaluate(attributes); + assertEquals(ResultType.STRING, result.getResultType()); + assertEquals(formatted, result.getValue()); + } + + + @Test + public void testEmbeddedExpressionsAndQuotes() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("x", "abc"); + attributes.put("a", "abc"); + + verifyEquals("${x:equals(${a})}", attributes, true); + + Query.validateExpression("${x:equals('${a}')}", false); + assertEquals("true", Query.evaluateExpressions("${x:equals('${a}')}", attributes, null)); + + Query.validateExpression("${x:equals(\"${a}\")}", false); + assertEquals("true", Query.evaluateExpressions("${x:equals(\"${a}\")}", attributes, null)); + } + + + @Test + public void testCurlyBracesInQuotes() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("attr", "My Valuee"); + + assertEquals("Val", evaluateQueryForEscape("${attr:replaceAll('My (Val)ue{1,2}', '$1')}", attributes)); + assertEquals("Val", evaluateQueryForEscape("${attr:replaceAll(\"My (Val)ue{1,2}\", '$1')}", attributes)); + } + + + private String evaluateQueryForEscape(final String queryString, final Map<String, String> attributes) { + FlowFile mockFlowFile = Mockito.mock(FlowFile.class); + Mockito.when(mockFlowFile.getAttributes()).thenReturn(attributes); + Mockito.when(mockFlowFile.getId()).thenReturn(1L); + Mockito.when(mockFlowFile.getEntryDate()).thenReturn(System.currentTimeMillis()); + Mockito.when(mockFlowFile.getSize()).thenReturn(1L); + Mockito.when(mockFlowFile.getLineageIdentifiers()).thenReturn(new HashSet<String>()); + Mockito.when(mockFlowFile.getLineageStartDate()).thenReturn(System.currentTimeMillis()); + return Query.evaluateExpressions(queryString, mockFlowFile); + } + + + @Test + public void testGetAttributeValue() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("attr", "My Value"); + verifyEquals("${attr}", attributes, "My Value"); + } + + @Test + public void testGetAttributeValueEmbedded() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("attr", "XX "); + attributes.put("XX", "My Value"); + verifyEquals("${${attr:trim()}}", attributes, "My Value"); + } + + @Test + public void testSimpleSubstring() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("attr", "My Value"); + verifyEquals("${attr:substring(2, 5)}", attributes, " Va"); + } + + @Test + public void testCallToFunctionWithSubjectResultOfAnotherFunctionCall() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("attr", " My Value "); + verifyEquals("${attr:trim():substring(2, 5)}", attributes, " Va"); + } + + @Test + public void testProblematic1() { + // There was a bug that prevented this expression from compiling. This test just verifies that it now compiles. + final String queryString = "${xx:append( \"120101\" ):toDate( 'yyMMddHHmmss' ):format( \"yy-MM-ddâTâHH:mm:ss\") }"; + Query.compile(queryString); + } + + @Test + public void testEquals() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("attr", " XX "); + verifyEquals("${attr:trim():equals('XX')}", attributes, true); + } + + @Test + public void testDeeplyEmbedded() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("x", "false"); + attributes.put("abc", "a"); + attributes.put("a", "a"); + + verifyEquals("${x:or( ${${abc}:length():equals(1)} )}", attributes, true); + } + + + @Test + public void testExtractExpressionRanges() { + List<Range> ranges = Query.extractExpressionRanges("hello"); + assertTrue(ranges.isEmpty()); + + ranges = Query.extractExpressionRanges("${hello"); + assertTrue(ranges.isEmpty()); + + ranges = Query.extractExpressionRanges("hello}"); + assertTrue(ranges.isEmpty()); + + ranges = Query.extractExpressionRanges("$${hello"); + assertTrue(ranges.isEmpty()); + + ranges = Query.extractExpressionRanges("$he{ll}o"); + assertTrue(ranges.isEmpty()); + + ranges = Query.extractExpressionRanges("${hello}"); + assertEquals(1, ranges.size()); + Range range = ranges.get(0); + assertEquals(0, range.getStart()); + assertEquals(7, range.getEnd()); + + ranges = Query.extractExpressionRanges("${hello:equals( ${goodbye} )}"); + assertEquals(1, ranges.size()); + range = ranges.get(0); + assertEquals(0, range.getStart()); + assertEquals(28, range.getEnd()); + + ranges = Query.extractExpressionRanges("${hello:equals( $${goodbye} )}"); + assertEquals(1, ranges.size()); + range = ranges.get(0); + assertEquals(0, range.getStart()); + assertEquals(29, range.getEnd()); + + ranges = Query.extractExpressionRanges("${hello:equals( $${goodbye} )} or just hi, ${bob:or(${jerry})}"); + assertEquals(2, ranges.size()); + range = ranges.get(0); + assertEquals(0, range.getStart()); + assertEquals(29, range.getEnd()); + + range = ranges.get(1); + assertEquals(43, range.getStart()); + assertEquals(61, range.getEnd()); + + + ranges = Query.extractExpressionRanges("${hello:equals( ${goodbye} )} or just hi, ${bob}, are you ${bob.age:toNumber()} yet? $$$${bob}"); + assertEquals(3, ranges.size()); + range = ranges.get(0); + assertEquals(0, range.getStart()); + assertEquals(28, range.getEnd()); + + range = ranges.get(1); + assertEquals(42, range.getStart()); + assertEquals(47, range.getEnd()); + + range = ranges.get(2); + assertEquals(58, range.getStart()); + assertEquals(78, range.getEnd()); + + ranges = Query.extractExpressionRanges("${x:matches( '.{4}' )}"); + assertEquals(1, ranges.size()); + range = ranges.get(0); + assertEquals(0, range.getStart()); + assertEquals(21, range.getEnd()); + } + + + @Test + public void testExtractExpressionTypes() { + List<ResultType> types = Query.extractResultTypes("${hello:equals( ${goodbye} )} or just hi, ${bob}, are you ${bob.age:toNumber()} yet? $$$${bob}"); + assertEquals(3, types.size()); + assertEquals(ResultType.BOOLEAN, types.get(0)); + assertEquals(ResultType.STRING, types.get(1)); + assertEquals(ResultType.NUMBER, types.get(2)); + } + + + @Test + public void testEqualsEmbedded() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("x", "hello"); + attributes.put("y", "good-bye"); + + verifyEquals("${x:equals( ${y} )}", attributes, false); + + attributes.put("y", "hello"); + verifyEquals("${x:equals( ${y} )}", attributes, true); + + attributes.put("x", "4"); + attributes.put("y", "3"); + attributes.put("z", "1"); + attributes.put("h", "100"); + verifyEquals("${x:toNumber():lt( ${y:toNumber():plus( ${h:toNumber()} )} )}", attributes, true); + verifyEquals("${h:toNumber():ge( ${y:toNumber():plus( ${z:toNumber()} )} )}", attributes, true); + verifyEquals("${x:toNumber():equals( ${y:toNumber():plus( ${z:toNumber()} )} )}", attributes, true); + + attributes.put("x", "88"); + verifyEquals("${x:toNumber():gt( ${y:toNumber():plus( ${z:toNumber()} )} )}", attributes, true); + + attributes.put("y", "88"); + assertEquals("true", Query.evaluateExpressions("${x:equals( '${y}' )}", attributes, null)); + } + + + @Test + public void testComplicatedEmbeddedExpressions() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("fox", "quick, brown"); + attributes.put("dog", "lazy"); + + verifyEquals("${fox:substring( ${ 'dog' :substring(2):length()}, 5 ):equals( 'ick' )}", attributes, true); + verifyEquals("${fox:substring( ${ 'dog' :substring(2):length()}, 5 ):equals( 'ick' )}", attributes, true); + } + + @Test + public void testQuotingQuotes() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("xx", "say 'hi'"); + + String query = "${xx:replaceAll( \"'.*'\", '\\\"hello\\\"' )}"; + verifyEquals(query, attributes, "say \"hello\""); + + query = "${xx:replace( \"'\", '\"')}"; + verifyEquals(query, attributes, "say \"hi\""); + + query = "${xx:replace( '\\'', '\"')}"; + System.out.println(query); + verifyEquals(query, attributes, "say \"hi\""); + } + + @Test + public void testDoubleQuotesWithinSingleQuotes() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("xx", "say 'hi'"); + + String query = "${xx:replace( \"'hi'\", '\\\"hello\\\"' )}"; + System.out.println(query); + verifyEquals(query, attributes, "say \"hello\""); + } + + @Test + public void testEscapeQuotes() { + final long timestamp = 1403620278642L; + final Map<String, String> attributes = new HashMap<>(); + attributes.put("date", String.valueOf(timestamp)); + + final String format = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + + final String query = "startDateTime=\"${date:toNumber():toDate():format(\"" + format + "\")}\""; + final String result = Query.evaluateExpressions(query, attributes, null); + + final String expectedTime = new SimpleDateFormat(format).format(timestamp); + assertEquals("startDateTime=\"" + expectedTime + "\"", result); + + final List<Range> ranges = Query.extractExpressionRanges(query); + assertEquals(1, ranges.size()); + } + + @Test + public void testDateConversion() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("date", "1403620278642"); + + verifyEquals("${date:format('yyyy')}", attributes, "2014"); + verifyEquals("${date:toDate():format('yyyy')}", attributes, "2014"); + verifyEquals("${date:toNumber():format('yyyy')}", attributes, "2014"); + verifyEquals("${date:toNumber():toDate():format('yyyy')}", attributes, "2014"); + verifyEquals("${date:toDate():toNumber():format('yyyy')}", attributes, "2014"); + verifyEquals("${date:toDate():toNumber():toDate():toNumber():toDate():toNumber():format('yyyy')}", attributes, "2014"); + } + + @Test + public void testSingleLetterAttribute() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("A", "0123456789"); + + verifyEquals("${A}", attributes, "0123456789"); + verifyEquals("${'A'}", attributes, "0123456789"); + } + + + @Test + public void testImplicitConversions() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("A", "0123456789"); + attributes.put("b", "true"); + attributes.put("c", "false"); + attributes.put("d", "Quick Brown Fox"); + attributes.put("F", "-48"); + attributes.put("n", "2014/04/04 00:00:00"); + + final Calendar cal = Calendar.getInstance(); + cal.set(Calendar.YEAR, 2014); + cal.set(Calendar.MONTH, 3); + cal.set(Calendar.DAY_OF_MONTH, 4); + cal.set(Calendar.HOUR, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 45); + + final String dateString = cal.getTime().toString(); + attributes.put("z", dateString); + + + verifyEquals("${A:plus(4)}", attributes, 123456793L); + verifyEquals("${A:plus( ${F} )}", attributes, 123456741L); + + verifyEquals("${F:lt( ${A} )}", attributes, true); + verifyEquals("${A:substring(2,3):plus(21):substring(1,2):plus(0)}", attributes, 3L); + verifyEquals("${n:format( 'yyyy' )}", attributes, "2014"); + verifyEquals("${z:format( 'yyyy' )}", attributes, "2014"); + + attributes.put("n", "2014/04/04 00:00:00.045"); + verifyEquals("${n:format( 'yyyy' ):append(','):append( ${n:format( 'SSS' )} )}", attributes, "2014,045"); + } + + @Test + public void testNewLinesAndTabsInQuery() { + 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)); + } + + @Test + public void testAttributeReferencesWithWhiteSpace() { + final Map<String, String> attrs = new HashMap<>(); + attrs.put("a b c,d", "abc"); + + final String query = "${ 'a b c,d':equals('abc') }"; + verifyEquals(query, attrs, true); + } + + @Test + public void testComments() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("abc", "xyz"); + + final String expression = + "# hello, world\n" + + "${# ref attr\n" + + "\t" + + "abc" + + "\t" + + "#end ref attr\n" + + "}"; + + Query query = Query.compile(expression); + QueryResult<?> result = query.evaluate(attributes); + assertEquals(ResultType.STRING, result.getResultType()); + assertEquals("xyz", result.getValue()); + + query = Query.compile("${abc:append('# hello') #good-bye \n}"); + result = query.evaluate(attributes); + assertEquals(ResultType.STRING, result.getResultType()); + assertEquals("xyz# hello", result.getValue()); + } + + @Test + public void testAppendPrepend() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("attr", "XX"); + attributes.put("YXXX", "bingo"); + + verifyEquals("${${attr:append('X'):prepend('Y')}}", attributes, "bingo"); + } + + @Test + public void testIsNull() { + final Map<String, String> attributes = new HashMap<>(); + verifyEquals("${attr:isNull()}", attributes, true); + } + + @Test + public void testNotNull() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("attr", ""); + + verifyEquals("${attr:notNull()}", attributes, true); + } + + @Test + public void testIsNullOrLengthEquals0() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("abc", ""); + attributes.put("xyz", "xyz"); + attributes.put("xx", " "); + + verifyEquals("${abc:isNull():or( ${abc:length():equals(0)} )}", attributes, true); + verifyEquals("${xyz:isNull():or( ${xyz:length():equals(0)} )}", attributes, false); + verifyEquals("${none:isNull():or( ${none:length():equals(0)} )}", attributes, true); + verifyEquals("${xx:isNull():or( ${xx:trim():length():equals(0)} )}", attributes, true); + } + + @Test + public void testReplaceNull() { + final Map<String, String> attributes = new HashMap<>(); + verifyEquals("${attr:replaceNull('hello')}", attributes, "hello"); + } + + @Test + public void testReplace() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("attr", "hello"); + verifyEquals("${attr:replace('hell', 'yell')}", attributes, "yello"); + } + + @Test + public void testReplaceAll() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("attr", "hello"); + attributes.put("xyz", "00-00TEST.2014_01_01_000000_value"); + + verifyEquals("${xyz:replaceAll(\"^([^.]+)\\.([0-9]{4})_([0-9]{2})_([0-9]{2}).*$\", \"$3\")}", attributes, "01"); + verifyEquals("${attr:replaceAll('l+', 'r')}", attributes, "hero"); + + attributes.clear(); + attributes.put("filename1", "abc.gz"); + attributes.put("filename2", "abc.g"); + attributes.put("filename3", "abc.gz.gz"); + attributes.put("filename4", "abc.gz.g"); + attributes.put("abc", "hello world"); + + verifyEquals("${filename3:replaceAll('\\\\\\.gz$', '')}", attributes, "abc.gz.gz"); + verifyEquals("${filename3:replaceAll('\\\\\\\\.gz$', '')}", attributes, "abc.gz.gz"); + verifyEquals("${filename1:replaceAll('\\.gz$', '')}", attributes, "abc"); + verifyEquals("${filename2:replaceAll('\\.gz$', '')}", attributes, "abc.g"); + verifyEquals("${filename4:replaceAll('\\\\.gz$', '')}", attributes, "abc.gz.g"); + + verifyEquals("${abc:replaceAll( 'lo wor(ld)', '$0')}", attributes, "hello world"); + verifyEquals("${abc:replaceAll( 'he(llo) world', '$1')}", attributes, "llo"); + verifyEquals("${abc:replaceAll( 'xx', '$0')}", attributes, "hello world"); + verifyEquals("${abc:replaceAll( '(xx)', '$1')}", attributes, "hello world"); + verifyEquals("${abc:replaceAll( 'lo wor(ld)', '$1')}", attributes, "helld"); + + } + + + @Test + public void testReplaceAllWithOddNumberOfBackslashPairs() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("filename", "C:\\temp\\.txt"); + + verifyEquals("${filename:replace('\\\\', '/')}", attributes, "C:/temp/.txt"); + verifyEquals("${filename:replaceAll('\\\\\\\\', '/')}", attributes, "C:/temp/.txt"); + verifyEquals("${filename:replaceAll('\\\\\\.txt$', '')}", attributes, "C:\\temp"); + } + + @Test + public void testReplaceAllWithMatchingGroup() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("attr", "hello"); + + verifyEquals("${attr:replaceAll('.*?(l+).*', '$1')}", attributes, "ll"); + } + + @Test + public void testMathOperations() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("one", "1"); + attributes.put("two", "2"); + attributes.put("three", "3"); + attributes.put("four", "4"); + attributes.put("five", "5"); + attributes.put("hundred", "100"); + + verifyEquals("${hundred:toNumber():multiply(2):divide(3):plus(1):mod(5)}", attributes, 2L); + } + + @Test + public void testIndexOf() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("attr", "https://abc.go"); + verifyEquals("${attr:indexOf('/')}", attributes, 6L); + } + + @Test + public void testDate() { + final Calendar now = Calendar.getInstance(); + final int year = now.get(Calendar.YEAR); + final Map<String, String> attributes = new HashMap<>(); + attributes.put("entryDate", String.valueOf(now.getTimeInMillis())); + + verifyEquals("${entryDate:toNumber():toDate():format('yyyy')}", attributes, String.valueOf(year)); + + attributes.clear(); + attributes.put("month", "3"); + attributes.put("day", "4"); + attributes.put("year", "2013"); + assertEquals("63", Query.evaluateExpressions("${year:append('/'):append(${month}):append('/'):append(${day}):toDate('yyyy/MM/dd'):format('D')}", attributes, null)); + assertEquals("63", Query.evaluateExpressions("${year:append('/'):append('${month}'):append('/'):append('${day}'):toDate('yyyy/MM/dd'):format('D')}", attributes, null)); + + verifyEquals("${year:append('/'):append(${month}):append('/'):append(${day}):toDate('yyyy/MM/dd'):format('D')}", attributes, "63"); + } + + @Test + public void testSystemProperty() { + System.setProperty("hello", "good-bye"); + assertEquals("good-bye", Query.evaluateExpressions("${hello}")); + assertEquals("good-bye", Query.compile("${hello}").evaluate().getValue()); + } + + @Test + public void testAnyAttribute() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("abc", "zzz"); + attributes.put("xyz", "abc"); + + verifyEquals("${anyAttribute('abc', 'xyz', 'missingAttr'):substring(1,2):equals('b')}", attributes, true); + verifyEquals("${anyAttribute('abc', 'xyz'):substring(1,2):equals('b')}", attributes, true); + verifyEquals("${anyAttribute('xyz', 'abc'):substring(1,2):equals('b')}", attributes, true); + verifyEquals("${anyAttribute('zz'):substring(1,2):equals('b')}", attributes, false); + verifyEquals("${anyAttribute('abc', 'zz'):isNull()}", attributes, true); + } + + @Test + public void testAnyMatchingAttribute() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("abc", "zzz"); + attributes.put("xyz", "abc"); + attributes.put("123.cba", "hello"); + + verifyEquals("${anyMatchingAttribute('.{2}x', '.{2}z'):substring(1,2):equals('b')}", attributes, true); + verifyEquals("${anyMatchingAttribute('.*'):substring(1,2):equals('b')}", attributes, true); + verifyEquals("${anyMatchingAttribute('x{44}'):substring(1,2):equals('b')}", attributes, false); + verifyEquals("${anyMatchingAttribute('abc'):substring(1,2):equals('b')}", attributes, false); + verifyEquals("${anyMatchingAttribute('xyz'):substring(1,2):equals('b')}", attributes, true); + verifyEquals("${anyMatchingAttribute('xyz'):notNull()}", attributes, true); + verifyEquals("${anyMatchingAttribute('xyz'):isNull()}", attributes, false); + verifyEquals("${anyMatchingAttribute('xxxxxxxxx'):notNull()}", attributes, false); + verifyEquals("${anyMatchingAttribute('123\\.c.*'):matches('hello')}", attributes, true); + verifyEquals("${anyMatchingAttribute('123\\.c.*|a.c'):matches('zzz')}", attributes, true); + } + + + @Test + public void testAnyDelineatedValue() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("abc", "a,b,c"); + attributes.put("xyz", "abc"); + + final String query = "${anyDelineatedValue('${abc}', ','):equals('b')}"; + assertEquals(ResultType.BOOLEAN, Query.getResultType(query)); + + assertEquals("true", Query.evaluateExpressions(query, attributes, null)); + assertEquals("true", Query.evaluateExpressions("${anyDelineatedValue('${abc}', ','):equals('a')}", attributes, null)); + assertEquals("true", Query.evaluateExpressions("${anyDelineatedValue('${abc}', ','):equals('c')}", attributes, null)); + assertEquals("false", Query.evaluateExpressions("${anyDelineatedValue('${abc}', ','):equals('d')}", attributes, null)); + + verifyEquals("${anyDelineatedValue(${abc}, ','):equals('b')}", attributes, true); + verifyEquals("${anyDelineatedValue(${abc}, ','):equals('a')}", attributes, true); + verifyEquals("${anyDelineatedValue(${abc}, ','):equals('c')}", attributes, true); + verifyEquals("${anyDelineatedValue(${abc}, ','):equals('d')}", attributes, false); + } + + @Test + public void testAllDelineatedValues() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("abc", "a,b,c"); + attributes.put("xyz", "abc"); + + final String query = "${allDelineatedValues('${abc}', ','):matches('[abc]')}"; + + assertEquals(ResultType.BOOLEAN, Query.getResultType(query)); + assertEquals("true", Query.evaluateExpressions(query, attributes, null)); + assertEquals("true", Query.evaluateExpressions(query, attributes, null)); + assertEquals("false", Query.evaluateExpressions("${allDelineatedValues('${abc}', ','):matches('[abd]')}", attributes, null)); + assertEquals("false", Query.evaluateExpressions("${allDelineatedValues('${abc}', ','):equals('a'):not()}", attributes, null)); + + verifyEquals("${allDelineatedValues(${abc}, ','):matches('[abc]')}", attributes, true); + verifyEquals("${allDelineatedValues(${abc}, ','):matches('[abd]')}", attributes, false); + verifyEquals("${allDelineatedValues(${abc}, ','):equals('a'):not()}", attributes, false); + } + + + @Test + public void testAllAttributes() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("abc", "1234"); + attributes.put("xyz", "4132"); + attributes.put("hello", "world!"); + + verifyEquals("${allAttributes('abc', 'xyz'):matches('\\d+')}", attributes, true); + verifyEquals("${allAttributes('abc', 'xyz'):toNumber():lt(99999)}", attributes, true); + verifyEquals("${allAttributes('abc', 'hello'):length():gt(3)}", attributes, true); + verifyEquals("${allAttributes('abc', 'hello'):length():equals(4)}", attributes, false); + verifyEquals("${allAttributes('abc', 'xyz'):length():equals(4)}", attributes, true); + verifyEquals("${allAttributes('abc', 'xyz', 'other'):isNull()}", attributes, false); + + try { + Query.compile("${allAttributes('#ah'):equals('hello')"); + Assert.fail("Was able to compile with allAttributes and an invalid attribute name"); + } catch (final AttributeExpressionLanguageParsingException e) { + // expected behavior + } + } + + + @Test + public void testMathOperators() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("abc", "1234"); + attributes.put("xyz", "4132"); + attributes.put("hello", "world!"); + + verifyEquals("${xyz:toNumber():gt( ${abc:toNumber()} )}", attributes, true); + } + + @Test + public void testAllMatchingAttributes() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("abc", "1234"); + attributes.put("xyz", "4132"); + attributes.put("hello", "world!"); + attributes.put("123.cba", "hell.o"); + + System.out.println( printTree(Query.compile("${allMatchingAttributes('(abc|xyz)'):matches('\\\\d+')}").getTree()) ); + + verifyEquals("${'123.cba':matches('hell\\.o')}", attributes, true); + verifyEquals("${allMatchingAttributes('123\\.cba'):equals('hell.o')}", attributes, true); + verifyEquals("${allMatchingAttributes('(abc|xyz)'):matches('\\d+')}", attributes, true); + verifyEquals("${allMatchingAttributes('[ax].*'):toNumber():lt(99999)}", attributes, true); + verifyEquals("${allMatchingAttributes('hell.'):length():gt(3)}", attributes, true); + + verifyEquals("${allMatchingAttributes('123\\.cba'):equals('no')}", attributes, false); + } + + @Test + public void testMatches() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("abc", "1234xyz4321"); + attributes.put("end", "xyz"); + attributes.put("xyz", "4132"); + attributes.put("hello", "world!"); + attributes.put("dotted", "abc.xyz"); + + final String evaluated = Query.evaluateExpressions("${abc:matches('1234${end}4321')}", attributes, null); + assertEquals("true", evaluated); + + attributes.put("end", "888"); + final String secondEvaluation = Query.evaluateExpressions("${abc:matches('1234${end}4321')}", attributes, null); + assertEquals("false", secondEvaluation); + + verifyEquals("${dotted:matches('abc\\.xyz')}", attributes, true); + } + + + @Test + public void testFind() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("abc", "1234xyz4321"); + attributes.put("end", "xyz"); + attributes.put("xyz", "4132"); + attributes.put("hello", "world!"); + attributes.put("dotted", "abc.xyz"); + + final String evaluated = Query.evaluateExpressions("${abc:find('1234${end}4321')}", attributes, null); + assertEquals("true", evaluated); + + attributes.put("end", "888"); + final String secondEvaluation = Query.evaluateExpressions("${abc:find('${end}4321')}", attributes, null); + assertEquals("false", secondEvaluation); + + verifyEquals("${dotted:find('\\.')}", attributes, true); + } + + @Test + public void testSubstringAfter() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("filename", "file-255"); + + verifyEquals("${filename:substringAfter('')}", attributes, "file-255"); + verifyEquals("${filename:substringAfterLast('')}", attributes, "file-255"); + verifyEquals("${filename:substringBefore('')}", attributes, "file-255"); + verifyEquals("${filename:substringBeforeLast('')}", attributes, "file-255"); + verifyEquals("${filename:substringBefore('file')}", attributes, ""); + + attributes.put("uri", "sftp://some.uri"); + verifyEquals("${uri:substringAfter('sftp')}", attributes, "://some.uri"); + } + + @Test + public void testSubstringAfterLast() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("filename", "file-file-255"); + + verifyEquals("${filename:substringAfterLast('file-')}", attributes, "255"); + verifyEquals("${filename:substringAfterLast('5')}", attributes, ""); + verifyEquals("${filename:substringAfterLast('x')}", attributes, "file-file-255"); + } + + @Test + public void testSubstringBefore() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("something", "some {} or other"); + + verifyEquals("${something:substringBefore('}')}", attributes, "some {"); + } + + @Test + public void testSubstring() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("filename", "file-255"); + + verifyEquals("${filename:substring(1, 2)}", attributes, "i"); + verifyEquals("${filename:substring(4)}", attributes, "-255"); + } + + @Test + public void testToRadix() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("filename", "file-255"); + attributes.put("filename2", "file-99999"); + + + verifyEquals("${filename:substringAfter('-'):toNumber():toRadix(16):toUpper()}", attributes, "FF"); + verifyEquals("${filename:substringAfter('-'):toNumber():toRadix(16, 4):toUpper()}", attributes, "00FF"); + verifyEquals("${filename:substringAfter('-'):toNumber():toRadix(36, 3):toUpper()}", attributes, "073"); + } + + @Test + public void testDateFormatConversion() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("blue", "20130917162643"); + verifyEquals("${blue:toDate('yyyyMMddHHmmss'):format(\"yyyy/MM/dd HH:mm:ss.SSS'Z'\")}", attributes, "2013/09/17 16:26:43.000Z"); + } + + + @Test + public void testNot() { + verifyEquals("${ab:notNull():not()}", new HashMap<String, String>(), true); + } + + @Test + public void testAttributesWithSpaces() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("ab", "abc"); + attributes.put("a b", "abc"); + + verifyEquals("${ab}", attributes, "abc"); + verifyEquals("${'a b'}", attributes, "abc"); + verifyEquals("${'a b':replaceNull('')}", attributes, ""); + } + + @Test + public void testOr() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("filename1", "xabc"); + attributes.put("filename2", "yabc"); + attributes.put("filename3", "abcxy"); + + verifyEquals("${filename1:startsWith('x'):or(true)}", attributes, true); + verifyEquals("${filename1:startsWith('x'):or( ${filename1:startsWith('y')} )}", attributes, true); + verifyEquals("${filename2:startsWith('x'):or( ${filename2:startsWith('y')} )}", attributes, true); + verifyEquals("${filename3:startsWith('x'):or( ${filename3:startsWith('y')} )}", attributes, false); + verifyEquals("${filename1:startsWith('x'):or( ${filename2:startsWith('y')} )}", attributes, true); + verifyEquals("${filename2:startsWith('x'):or( ${filename1:startsWith('y')} )}", attributes, false); + } + + @Test + public void testAnd() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("filename1", "xabc"); + attributes.put("filename2", "yabc"); + attributes.put("filename 3", "abcxy"); + + verifyEquals("${filename1:startsWith('x'):and(true)}", attributes, true); + verifyEquals("${filename1:startsWith('x') : and( false )}", attributes, false); + verifyEquals("${filename1:startsWith('x'):and( ${filename1:startsWith('y')} )}", attributes, false); + verifyEquals("${filename2:startsWith('x'):and( ${filename2:startsWith('y')} )}", attributes, false); + verifyEquals("${filename3:startsWith('x'):and( ${filename3:startsWith('y')} )}", attributes, false); + verifyEquals("${filename1:startsWith('x'):and( ${filename2:startsWith('y')} )}", attributes, true); + verifyEquals("${filename2:startsWith('x'):and( ${filename1:startsWith('y')} )}", attributes, false); + verifyEquals("${filename1:startsWith('x'):and( ${'filename 3':endsWith('y')} )}", attributes, true); + } + + @Test + public void testAndOrNot() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("filename1", "xabc"); + attributes.put("filename2", "yabc"); + attributes.put("filename 3", "abcxy"); + + final String query = + "${" + + " 'non-existing':notNull():not():and(" + // true AND ( + " ${filename1:startsWith('y')" + // false + " :or(" + // or + " ${ filename1:startsWith('x'):and(false) }" + // false + " ):or(" + // or + " ${ filename2:endsWith('xxxx'):or( ${'filename 3':length():gt(1)} ) }" + // true ) + " )}" + + " )" + + "}"; + + System.out.println(query); + verifyEquals(query, attributes, true); + } + + @Test + public void testAndOrLogicWithAnyAll() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("filename1", "xabc"); + attributes.put("filename2", "yabc"); + attributes.put("filename 3", "abcxy"); + + verifyEquals("${anyMatchingAttribute('filename.*'):contains('abc'):and( ${filename2:equals('yabc')} )}", attributes, true); + verifyEquals("${anyMatchingAttribute('filename.*'):contains('abc'):and( ${filename2:equals('xabc')} )}", attributes, false); + verifyEquals("${anyMatchingAttribute('filename.*'):contains('abc'):not():or( ${filename2:equals('yabc')} )}", attributes, true); + verifyEquals("${anyMatchingAttribute('filename.*'):contains('abc'):not():or( ${filename2:equals('xabc')} )}", attributes, false); + } + + @Test + public void testKeywords() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("UUID", "123"); + verifyEquals("${ 'UUID':toNumber():equals(123) }", attributes, true); + } + + @Test + public void testEqualsNumber() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("abc", "123"); + verifyEquals("${ abc:toNumber():equals(123) }", attributes, true); + } + + @Test + public void testSubjectAsEmbeddedExpressionWithSurroundChars() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("b", "x"); + attributes.put("abcxcba", "hello"); + + final String evaluated = Query.evaluateExpressions("${ 'abc${b}cba':substring(0, 1) }", attributes, null); + assertEquals("h", evaluated); + } + + @Test + public void testToNumberFunctionReturnsNumberType() { + assertEquals(ResultType.NUMBER, Query.getResultType("${header.size:toNumber()}")); + } + + + private void verifyEquals(final String expression, final Map<String, String> attributes, final Object expectedResult) { + Query.validateExpression(expression, false); + assertEquals(String.valueOf(expectedResult), Query.evaluateExpressions(expression, attributes, null)); + + Query query = Query.compile(expression); + QueryResult<?> result = query.evaluate(attributes); + + if ( expectedResult instanceof Number ) { + assertEquals(ResultType.NUMBER, result.getResultType()); + } else if ( expectedResult instanceof Boolean ) { + assertEquals(ResultType.BOOLEAN, result.getResultType()); + } else { + assertEquals(ResultType.STRING, result.getResultType()); + } + + assertEquals(expectedResult, result.getValue()); + } +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestStandardPreparedQuery.java ---------------------------------------------------------------------- diff --git a/commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestStandardPreparedQuery.java b/commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestStandardPreparedQuery.java new file mode 100644 index 0000000..398a23b --- /dev/null +++ b/commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestStandardPreparedQuery.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.attribute.expression.language; + +import static org.junit.Assert.assertEquals; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.junit.Ignore; +import org.junit.Test; + +public class TestStandardPreparedQuery { + + @Test + public void testSimpleReference() { + final Map<String, String> attrs = new HashMap<>(); + attrs.put("xx", "world"); + + assertEquals("world", evaluate("${xx}", attrs)); + assertEquals("hello, world!", evaluate("hello, ${xx}!", attrs)); + } + + @Test + public void testEmbeddedReference() { + final Map<String, String> attrs = new HashMap<>(); + attrs.put("xx", "yy"); + attrs.put("yy", "world"); + + assertEquals("world", evaluate("${${xx}}", attrs)); + } + + @Test + public void test10MIterations() { + final Map<String, String> attrs = new HashMap<>(); + attrs.put("xx", "world"); + + final StandardPreparedQuery prepared = (StandardPreparedQuery) Query.prepare("${xx}"); + final long start = System.nanoTime(); + for (int i=0; i < 10000000; i++) { + assertEquals( "world", prepared.evaluateExpressions(attrs, null) ); + } + final long nanos = System.nanoTime() - start; + System.out.println(TimeUnit.NANOSECONDS.toMillis(nanos)); + } + + @Test + @Ignore("Takes too long") + public void test10MIterationsWithQuery() { + final Map<String, String> attrs = new HashMap<>(); + attrs.put("xx", "world"); + + final long start = System.nanoTime(); + for (int i=0; i < 10000000; i++) { + assertEquals( "world", Query.evaluateExpressions("${xx}", attrs) ); + } + final long nanos = System.nanoTime() - start; + System.out.println(TimeUnit.NANOSECONDS.toMillis(nanos)); + + } + + @Test + public void testSeveralSequentialExpressions() { + final Map<String, String> attributes = new HashMap<>(); + attributes.put("audience", "World"); + attributes.put("comma", ","); + attributes.put("question", " how are you?"); + assertEquals("Hello, World, how are you?!", evaluate("Hello, ${audience}${comma}${question}!", attributes)); + + } + + private String evaluate(final String query, final Map<String, String> attrs) { + final String evaluated = ((StandardPreparedQuery) Query.prepare(query)).evaluateExpressions(attrs, null); + return evaluated; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4d998c12/commons/nifi-file-utils/pom.xml ---------------------------------------------------------------------- diff --git a/commons/nifi-file-utils/pom.xml b/commons/nifi-file-utils/pom.xml new file mode 100644 index 0000000..e3cf792 --- /dev/null +++ b/commons/nifi-file-utils/pom.xml @@ -0,0 +1,35 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <!-- + 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. + --> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-parent</artifactId> + <version>0.0.1-SNAPSHOT</version> + </parent> + + <artifactId>nifi-file-utils</artifactId> + <version>0.0.1-SNAPSHOT</version> + <packaging>jar</packaging> + <name>NiFi File Utils</name> + + <dependencies> + <dependency> + <groupId>commons-codec</groupId> + <artifactId>commons-codec</artifactId> + <version>1.10</version> + </dependency> + </dependencies> +</project>