Repository: nifi-minifi-cpp Updated Branches: refs/heads/master acd8c5538 -> e272487d0
MINIFICPP-331 Implemented string replacement functions This closes #226. Signed-off-by: Marc Parisi <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/repo Commit: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/commit/e272487d Tree: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/tree/e272487d Diff: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/diff/e272487d Branch: refs/heads/master Commit: e272487d0e640ff2740f8429dd56bdbb34cc7f62 Parents: acd8c55 Author: Andy I. Christianson <[email protected]> Authored: Thu Dec 21 12:46:36 2017 -0500 Committer: Marc Parisi <[email protected]> Committed: Tue Jan 16 12:44:01 2018 -0500 ---------------------------------------------------------------------- extensions/expression-language/Expression.cpp | 62 ++++++++++++ .../impl/expression/Expression.h | 7 ++ .../ExpressionLanguageTests.cpp | 100 +++++++++++++++++++ 3 files changed, 169 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/e272487d/extensions/expression-language/Expression.cpp ---------------------------------------------------------------------- diff --git a/extensions/expression-language/Expression.cpp b/extensions/expression-language/Expression.cpp index 806ff6a..2220434 100644 --- a/extensions/expression-language/Expression.cpp +++ b/extensions/expression-language/Expression.cpp @@ -19,6 +19,7 @@ #include <iostream> #include <expression/Expression.h> +#include <regex> #include "Driver.h" namespace org { @@ -96,6 +97,55 @@ std::string expr_substringAfterLast(const std::vector<std::string> &args) { return args[0].substr(last_pos + args[1].length()); } +#ifdef EXPRESSION_LANGUAGE_USE_REGEX + +std::string expr_replace(const std::vector<std::string> &args) { + std::string result = args[0]; + const std::string find = args[1]; + const std::string replace = args[2]; + + std::string::size_type match_pos = 0; + match_pos = result.find(find, match_pos); + + while (match_pos != std::string::npos) { + result.replace(match_pos, find.size(), replace); + match_pos = result.find(find, match_pos + replace.size()); + } + + return result; +} + +std::string expr_replaceFirst(const std::vector<std::string> &args) { + std::string result = args[0]; + const std::regex find(args[1]); + const std::string replace = args[2]; + return std::regex_replace(result, find, replace, std::regex_constants::format_first_only); +} + +std::string expr_replaceAll(const std::vector<std::string> &args) { + std::string result = args[0]; + const std::regex find(args[1]); + const std::string replace = args[2]; + return std::regex_replace(result, find, replace); +} + +std::string expr_replaceNull(const std::vector<std::string> &args) { + if (args[0].empty()) { + return args[1]; + } else { + return args[0]; + } +} + +std::string expr_replaceEmpty(const std::vector<std::string> &args) { + std::string result = args[0]; + const std::regex find("^[ \n\r\t]*$"); + const std::string replace = args[1]; + return std::regex_replace(result, find, replace); +} + +#endif // EXPRESSION_LANGUAGE_USE_REGEX + template<std::string T(const std::vector<std::string> &)> Expression make_dynamic_function_incomplete(const std::string &function_name, const std::vector<Expression> &args, @@ -149,6 +199,18 @@ Expression make_dynamic_function(const std::string &function_name, return make_dynamic_function_incomplete<expr_substringAfter>(function_name, args, 2); } else if (function_name == "substringAfterLast") { return make_dynamic_function_incomplete<expr_substringAfterLast>(function_name, args, 2); +#ifdef EXPRESSION_LANGUAGE_USE_REGEX + } else if (function_name == "replace") { + return make_dynamic_function_incomplete<expr_replace>(function_name, args, 2); + } else if (function_name == "replaceFirst") { + return make_dynamic_function_incomplete<expr_replaceFirst>(function_name, args, 2); + } else if (function_name == "replaceAll") { + return make_dynamic_function_incomplete<expr_replaceAll>(function_name, args, 2); + } else if (function_name == "replaceNull") { + return make_dynamic_function_incomplete<expr_replaceNull>(function_name, args, 1); + } else if (function_name == "replaceEmpty") { + return make_dynamic_function_incomplete<expr_replaceEmpty>(function_name, args, 1); +#endif // EXPRESSION_LANGUAGE_USE_REGEX } else { std::string msg("Unknown expression function: "); msg.append(function_name); http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/e272487d/extensions/expression-language/impl/expression/Expression.h ---------------------------------------------------------------------- diff --git a/extensions/expression-language/impl/expression/Expression.h b/extensions/expression-language/impl/expression/Expression.h index 492bc0b..e245370 100644 --- a/extensions/expression-language/impl/expression/Expression.h +++ b/extensions/expression-language/impl/expression/Expression.h @@ -18,6 +18,13 @@ #ifndef NIFI_MINIFI_CPP_EXPRESSION_H #define NIFI_MINIFI_CPP_EXPRESSION_H +#define EXPRESSION_LANGUAGE_USE_REGEX + +// Disable regex in EL for incompatible compilers +#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 9) +#undef EXPRESSION_LANGUAGE_USE_REGEX +#endif + #include <core/FlowFile.h> #include <string> http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/e272487d/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp ---------------------------------------------------------------------- diff --git a/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp b/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp index 0af816d..8660af5 100644 --- a/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp +++ b/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp @@ -304,3 +304,103 @@ TEST_CASE("Substring After No Args", "[expressionLanguageSubstringAfterNoArgs]") flow_file_a->addAttribute("attr", "__flow_a_attr_value_a__"); REQUIRE_THROWS_WITH(expr({flow_file_a}), "Attempted to call incomplete function"); } + +#ifdef EXPRESSION_LANGUAGE_USE_REGEX + +TEST_CASE("Replace", "[expressionLanguageReplace]") { // NOLINT + auto expr = expression::compile("${attr:replace('.', '_')}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "a brand new filename.txt"); + REQUIRE("a brand new filename_txt" == expr({flow_file_a})); +} + +TEST_CASE("Replace 2", "[expressionLanguageReplace2]") { // NOLINT + auto expr = expression::compile("${attr:replace(' ', '.')}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "a brand new filename.txt"); + REQUIRE("a.brand.new.filename.txt" == expr({flow_file_a})); +} + +TEST_CASE("Replace First", "[expressionLanguageReplaceFirst]") { // NOLINT + auto expr = expression::compile("${attr:replaceFirst('a', 'the')}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "a brand new filename.txt"); + REQUIRE("the brand new filename.txt" == expr({flow_file_a})); +} + +TEST_CASE("Replace First Regex", "[expressionLanguageReplaceFirstRegex]") { // NOLINT + auto expr = expression::compile("${attr:replaceFirst('[br]', 'g')}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "a brand new filename.txt"); + REQUIRE("a grand new filename.txt" == expr({flow_file_a})); +} + +TEST_CASE("Replace All", "[expressionLanguageReplaceAll]") { // NOLINT + auto expr = expression::compile("${attr:replaceAll('\\..*', '')}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "a brand new filename.txt"); + REQUIRE("a brand new filename" == expr({flow_file_a})); +} + +TEST_CASE("Replace All 2", "[expressionLanguageReplaceAll2]") { // NOLINT + auto expr = expression::compile("${attr:replaceAll('a brand (new)', '$1')}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "a brand new filename.txt"); + REQUIRE("new filename.txt" == expr({flow_file_a})); +} + +TEST_CASE("Replace All 3", "[expressionLanguageReplaceAll3]") { // NOLINT + auto expr = expression::compile("${attr:replaceAll('XYZ', 'ZZZ')}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "a brand new filename.txt"); + REQUIRE("a brand new filename.txt" == expr({flow_file_a})); +} + +TEST_CASE("Replace Null", "[expressionLanguageReplaceNull]") { // NOLINT + auto expr = expression::compile("${attr:replaceNull('abc')}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "a brand new filename.txt"); + REQUIRE("a brand new filename.txt" == expr({flow_file_a})); +} + +TEST_CASE("Replace Null 2", "[expressionLanguageReplaceNull2]") { // NOLINT + auto expr = expression::compile("${attr:replaceNull('abc')}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr2", "a brand new filename.txt"); + REQUIRE("abc" == expr({flow_file_a})); +} + +TEST_CASE("Replace Empty", "[expressionLanguageReplaceEmpty]") { // NOLINT + auto expr = expression::compile("${attr:replaceEmpty('abc')}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "a brand new filename.txt"); + REQUIRE("a brand new filename.txt" == expr({flow_file_a})); +} + +TEST_CASE("Replace Empty 2", "[expressionLanguageReplaceEmpty2]") { // NOLINT + auto expr = expression::compile("${attr:replaceEmpty('abc')}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", " \t \r \n "); + REQUIRE("abc" == expr({flow_file_a})); +} + +TEST_CASE("Replace Empty 3", "[expressionLanguageReplaceEmpty2]") { // NOLINT + auto expr = expression::compile("${attr:replaceEmpty('abc')}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr2", "test"); + REQUIRE("abc" == expr({flow_file_a})); +} + +#endif // EXPRESSION_LANGUAGE_USE_REGEX
