From ba13db1ced3157832eecfaae52f9157988a41ee6 Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Fri, 29 Mar 2024 14:54:26 -0700 Subject: [PATCH 1/5] ICU-22261 Refactor MF2 attributes and options parsing code Previously, there were separate overrides for the options and attributes parsing methods in the parser that were used in different context. (Options can appear in Operator and Markup, while attributes can appear in Expression and Markup.) This is a refactoring that eliminates this duplicated code. To enable it, a builder is added for the internal OptionMap type. --- .../source/i18n/messageformat2_data_model.cpp | 165 +++++++------- icu4c/source/i18n/messageformat2_parser.cpp | 202 ++---------------- icu4c/source/i18n/messageformat2_parser.h | 42 +++- .../i18n/unicode/messageformat2_data_model.h | 54 ++--- 4 files changed, 155 insertions(+), 308 deletions(-) diff --git a/icu4c/source/i18n/messageformat2_data_model.cpp b/icu4c/source/i18n/messageformat2_data_model.cpp index b29f29e81342..b8ba4f5a35d7 100644 --- a/icu4c/source/i18n/messageformat2_data_model.cpp +++ b/icu4c/source/i18n/messageformat2_data_model.cpp @@ -304,14 +304,53 @@ int32_t OptionMap::size() const { return len; } -Operator::Builder& Operator::Builder::setOptionMap(OptionMap&& m) { - optionMap = std::move(m); - delete options; - options = nullptr; +OptionMap::~OptionMap() {} + +OptionMap OptionMap::Builder::build(UErrorCode& status) { + return OptionMap(*options, status); +} + +OptionMap::Builder::Builder(UErrorCode& status) { + options = createStringUVector(status); +} + +/* static */ OptionMap::Builder OptionMap::Builder::attributes(UErrorCode& status) { + Builder b(status); + // The same code is re-used for representing attributes and options. + // Duplicate attributes are allowed, while duplicate options are disallowed. + b.checkDuplicates = false; + return b; +} + +static UBool hasOptionNamed(const UVector& v, const UnicodeString& s) { + for (int32_t i = 0; i < v.size(); i++) { + const Option* opt = static_cast(v[i]); + U_ASSERT(opt != nullptr); + if (opt->getName() == s) { + return true; + } + } + return false; +} + +OptionMap::Builder& OptionMap::Builder::add(Option&& opt, UErrorCode& status) { + THIS_ON_ERROR(status); + + // If the option name is already in the map, emit a data model error + if (checkDuplicates && hasOptionNamed(*options, opt.getName())) { + status = U_MF_DUPLICATE_OPTION_NAME_ERROR; + } else { + Option* newOption = create