MINIFICPP-331 Implemented string replacement functions
This closes #226.
Signed-off-by: Marc Parisi <phrocker@apache.org>
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 @@
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 @@
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);
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>
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 @@
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