new CLI interface: --modify relation-name, --modify attribute-name v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Sun, 16 May 2021 17:33:35 +0200
branchv_0
changeset 26 576d4965434f
parent 25 0cfbaf5c57a6
child 27 69eda87848ff
new CLI interface: --modify relation-name, --modify attribute-name
bash-completion.sh
src/CLIParser.h
src/Configuration.h
src/SedHandler.h
--- a/bash-completion.sh	Sat May 15 18:18:10 2021 +0200
+++ b/bash-completion.sh	Sun May 16 17:33:35 2021 +0200
@@ -33,11 +33,17 @@
 		"value"
 	)
 
-	if   [[ "$w1" == "--relation"                      && "x$w0" == "x" ]];    then COMPREPLY=("'.*'")
-	elif [[ "$w1" == "--attribute"                     && "x$w0" == "x" ]];    then COMPREPLY=("'.*'")
-	elif [[ "$w1" == "--value"                         && "x$w0" == "x" ]];    then COMPREPLY=("''")
-	elif [[ "$w1" == "--replacement"                   && "x$w0" == "x" ]];    then COMPREPLY=("''")
-	elif [[ "$w1" == "--modify"                                         ]];    then COMPREPLY=($(compgen -W "${ENTITY_VALUES[*]}"  -- "$w0"))
+	MODIFY_TARGETS=(
+		"relation-name"
+		"attribute-name"
+		"value"
+	)
+
+	if   [[ "$w1" == "--relation"                      && "x$w0" == "x" ]];    then COMPREPLY=("'.+'")
+	elif [[ "$w1" == "--attribute"                     && "x$w0" == "x" ]];    then COMPREPLY=("'.+'")
+	elif [[ "$w1" == "--value"                         && "x$w0" == "x" ]];    then COMPREPLY=("'.+'")
+	elif [[ "$w1" == "--replacement"                   && "x$w0" == "x" ]];    then COMPREPLY=("'\$0'")
+	elif [[ "$w1" == "--modify"                                         ]];    then COMPREPLY=($(compgen -W "${MODIFY_TARGETS[*]}"  -- "$w0"))
 	elif [[ "$w1" == "--case-sensitive"                                 ]];    then COMPREPLY=($(compgen -W "${ENTITY_VALUES[*]}"  -- "$w0"))
 	elif [[ "$w2" == "--case-sensitive"                                 ]];    then COMPREPLY=($(compgen -W "${BOOLEAN_VALUES[*]}" -- "$w0"))
 	elif [[ "$w1" == "--invert-match"                                   ]];    then COMPREPLY=($(compgen -W "${ENTITY_VALUES[*]}"  -- "$w0"))
--- a/src/CLIParser.h	Sat May 15 18:18:10 2021 +0200
+++ b/src/CLIParser.h	Sun May 16 17:33:35 2021 +0200
@@ -52,8 +52,19 @@
 		else throw relpipe::cli::RelpipeCLIException(L"Unable to parse entity value: " + value + L" (expecting „relation“, „attribute“ or „value“)", relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
 	}
 
+	ENTITY parseModifyTarget(const relpipe::common::type::StringX& value) {
+		if (value == L"relation-name") return ENTITY::RELATION;
+		else if (value == L"attribute-name") return ENTITY::ATTRIBUTE;
+		else if (value == L"value") return ENTITY::VALUE;
+		else throw relpipe::cli::RelpipeCLIException(L"Unable to parse entity value: " + value + L" (expecting „relation-name“, „attribute-name“ or „value“)", relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
+	}
+
 	void addRule(RelationConfiguration& currentRelation, RewriteRule& currentRule) {
-		if (currentRule.attribute.size()) {
+		if (currentRule.modify != ENTITY::DEFAULT) {
+			// the --value is optional → set defaults if missing:
+			if (currentRule.modify == ENTITY::RELATION && currentRule.value.size() == 0) currentRule.value = currentRelation.relation;
+			if (currentRule.modify == ENTITY::ATTRIBUTE && currentRule.value.size() == 0) currentRule.value = currentRule.attribute;
+
 			currentRule.attributePattern = currentRule.caseSensitive[ENTITY::ATTRIBUTE] ? std::wregex(currentRule.attribute) : std::wregex(currentRule.attribute, std::regex_constants::icase);
 			currentRule.valuePattern = currentRule.caseSensitive[ENTITY::VALUE] ? std::wregex(currentRule.value) : std::wregex(currentRule.value, std::regex_constants::icase);
 			currentRelation.rules.push_back(currentRule);
@@ -73,6 +84,7 @@
 public:
 
 	static const relpipe::common::type::StringX OPTION_RELATION;
+	static const relpipe::common::type::StringX OPTION_MODIFY;
 	static const relpipe::common::type::StringX OPTION_ATTRIBUTE;
 	static const relpipe::common::type::StringX OPTION_VALUE;
 	static const relpipe::common::type::StringX OPTION_REPLACEMENT;
@@ -90,8 +102,17 @@
 			if (option == OPTION_RELATION) {
 				addRelation(c, currentRelation, currentRule); // previous relation
 				currentRelation.relation = readNext(arguments, i);
+			} else if (option == OPTION_MODIFY) {
+				addRule(currentRelation, currentRule); // previous rule
+				currentRule.modify = parseModifyTarget(readNext(arguments, i));
+				if (currentRule.modify == ENTITY::VALUE) currentRule.modify = ENTITY::DEFAULT;
 			} else if (option == OPTION_ATTRIBUTE) {
-				addRule(currentRelation, currentRule); // previous rule
+				if (currentRule.modify == ENTITY::DEFAULT) {
+					currentRule.modify = ENTITY::VALUE;
+				} else if (currentRule.modify == ENTITY::VALUE) {
+					addRule(currentRelation, currentRule); // previous rule
+					currentRule.modify = ENTITY::VALUE;
+				}
 				currentRule.attribute = readNext(arguments, i);
 			} else if (option == OPTION_VALUE) {
 				currentRule.value = readNext(arguments, i);
@@ -100,13 +121,13 @@
 			} else if (option == OPTION_CASE_SENSITIVE) {
 				ENTITY entity = parseEntity(readNext(arguments, i));
 				bool value = parseBoolean(readNext(arguments, i));
-				if (currentRule.attribute.size()) currentRule.caseSensitive[entity] = value;
-				else currentRelation.caseSensitive[entity] = value;
+				if (currentRule.modify == ENTITY::DEFAULT) currentRelation.caseSensitive[entity] = value;
+				currentRule.caseSensitive[entity] = value;
 			} else if (option == OPTION_INVERT_MATCH) {
 				ENTITY entity = parseEntity(readNext(arguments, i));
 				bool value = parseBoolean(readNext(arguments, i));
-				if (currentRule.attribute.size()) currentRule.invertMatch[entity] = value;
-				else currentRelation.invertMatch[entity] = value;
+				if (currentRule.modify == ENTITY::DEFAULT) currentRelation.invertMatch[entity] = value;
+				currentRule.invertMatch[entity] = value;
 			} else throw relpipe::cli::RelpipeCLIException(L"Unsupported CLI option: " + option, relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
 		}
 		addRelation(c, currentRelation, currentRule); // last relation
@@ -119,6 +140,7 @@
 };
 
 const relpipe::common::type::StringX CLIParser::OPTION_RELATION = L"--relation";
+const relpipe::common::type::StringX CLIParser::OPTION_MODIFY = L"--modify";
 const relpipe::common::type::StringX CLIParser::OPTION_ATTRIBUTE = L"--attribute";
 const relpipe::common::type::StringX CLIParser::OPTION_VALUE = L"--value";
 const relpipe::common::type::StringX CLIParser::OPTION_REPLACEMENT = L"--replacement";
--- a/src/Configuration.h	Sat May 15 18:18:10 2021 +0200
+++ b/src/Configuration.h	Sun May 16 17:33:35 2021 +0200
@@ -28,6 +28,7 @@
 namespace sed {
 
 enum class ENTITY {
+	DEFAULT,
 	RELATION,
 	ATTRIBUTE,
 	VALUE
@@ -42,6 +43,14 @@
 	virtual ~RewriteRule() {
 	}
 
+	/**
+	 * What will be modified:
+	 *  - relation: rename relation
+	 *  - attribute: rename attribute
+	 *  - value: rewrite value
+	 *  - default: not yet set (should be set in CLIParser; means error everywhere else)
+	 */
+	ENTITY modify = ENTITY::DEFAULT;
 	relpipe::common::type::StringX attribute;
 	relpipe::common::type::StringX value;
 	relpipe::common::type::StringX replacement;
--- a/src/SedHandler.h	Sat May 15 18:18:10 2021 +0200
+++ b/src/SedHandler.h	Sun May 16 17:33:35 2021 +0200
@@ -60,10 +60,23 @@
 	}
 
 	void startRelation(string_t name, vector<AttributeMetadata> attributes) override {
+
+		std::vector<RewriteRule*> attributeRenamingRules;
+		for (RelationConfiguration& rc : configuration.relationConfigurations) {
+			if (std::regex_match(name, rc.relationPattern) ^ rc.invertMatch[ENTITY::RELATION]) {
+				for (RewriteRule& rule : rc.rules) if (rule.modify == ENTITY::ATTRIBUTE) attributeRenamingRules.push_back(&rule);
+			}
+		}
+
+
 		// TODO: move to a reusable method (or use same metadata on both reader and writer side?)
 		vector<writer::AttributeMetadata> writerMetadata;
 		for (AttributeMetadata readerMetadata : attributes) {
-			writerMetadata.push_back({readerMetadata.getAttributeName(), relationalWriter->toTypeId(readerMetadata.getTypeName())});
+			string_t newName = readerMetadata.getAttributeName();
+			for (RewriteRule* rule : attributeRenamingRules)
+				if (std::regex_match(readerMetadata.getAttributeName(), rule->attributePattern) ^ rule->invertMatch[ENTITY::ATTRIBUTE]) // TODO: optionally: regex_match(newName, rule->attributePattern) ?
+					newName = std::regex_replace(newName, rule->valuePattern, rule->replacement);
+			writerMetadata.push_back({newName, relationalWriter->toTypeId(readerMetadata.getTypeName())});
 		}
 
 
@@ -72,12 +85,18 @@
 			if (std::regex_match(name, rc.relationPattern) ^ rc.invertMatch[ENTITY::RELATION]) {
 				for (int i = 0; i < currentRules.size(); i++) {
 					for (RewriteRule& rule : rc.rules) {
-						if (std::regex_match(attributes[i].getAttributeName(), rule.attributePattern) ^ rule.invertMatch[ENTITY::ATTRIBUTE]) currentRules[i].push_back(&rule);
+						if (rule.modify == ENTITY::VALUE && std::regex_match(attributes[i].getAttributeName(), rule.attributePattern) ^ rule.invertMatch[ENTITY::ATTRIBUTE]) currentRules[i].push_back(&rule);
 					}
 				}
 			}
 		}
 
+		for (RelationConfiguration& rc : configuration.relationConfigurations) {
+			if (std::regex_match(name, rc.relationPattern) ^ rc.invertMatch[ENTITY::RELATION]) {
+				for (RewriteRule rule : rc.rules) if (rule.modify == ENTITY::RELATION) name = std::regex_replace(name, rule.valuePattern, rule.replacement);
+			}
+		}
+
 		relationalWriter->startRelation(name, writerMetadata, true);
 	}
 
@@ -85,7 +104,8 @@
 		string_t newValue = value;
 
 		for (RewriteRule* rule : currentRules[currentAttributeIndex]) {
-			if (rule) newValue = std::regex_replace(value, rule->valuePattern, rule->replacement);
+			if (rule && !rule->invertMatch[ENTITY::VALUE]) newValue = std::regex_replace(newValue, rule->valuePattern, rule->replacement);
+			else if (rule && !std::regex_match(value, rule->valuePattern)) newValue = rule->replacement;
 		}
 
 		relationalWriter->writeAttribute(newValue);