# HG changeset patch # User František Kučera # Date 1621179215 -7200 # Node ID 576d4965434f9191acd45ec6244a3a2f7754b2b1 # Parent 0cfbaf5c57a6b5e8983275295c27e88f0f23355d new CLI interface: --modify relation-name, --modify attribute-name diff -r 0cfbaf5c57a6 -r 576d4965434f bash-completion.sh --- 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")) diff -r 0cfbaf5c57a6 -r 576d4965434f src/CLIParser.h --- 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"; diff -r 0cfbaf5c57a6 -r 576d4965434f src/Configuration.h --- 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; diff -r 0cfbaf5c57a6 -r 576d4965434f src/SedHandler.h --- 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 attributes) override { + + std::vector 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 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);