From: Shane Carr Date: Fri, 14 Sep 2018 05:16:29 +0000 (-0700) Subject: ICU-11276 Adding ModifierStore, groundwork for plural range support. X-Git-Tag: release-63-rc~63^2~11 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3161453c02a1dcc4a56c99caf9c383e6d3af8a12;p=icu ICU-11276 Adding ModifierStore, groundwork for plural range support. --- diff --git a/icu4c/source/i18n/number_modifiers.cpp b/icu4c/source/i18n/number_modifiers.cpp index 6d0096055c8..926dbaec205 100644 --- a/icu4c/source/i18n/number_modifiers.cpp +++ b/icu4c/source/i18n/number_modifiers.cpp @@ -53,6 +53,14 @@ void U_CALLCONV initDefaultCurrencySpacing(UErrorCode &status) { Modifier::~Modifier() = default; +ModifierStore::~ModifierStore() = default; + +AdoptingModifierStore::~AdoptingModifierStore() { + for (const Modifier *mod : mods) { + delete mod; + } +} + int32_t ConstantAffixModifier::apply(NumberStringBuilder &output, int leftIndex, int rightIndex, UErrorCode &status) const { @@ -81,7 +89,13 @@ bool ConstantAffixModifier::containsField(UNumberFormatFields field) const { return false; } -bool ConstantAffixModifier::operator==(const Modifier& other) const { +void ConstantAffixModifier::getParameters(Parameters& output) const { + (void)output; + // This method is not currently used. + U_ASSERT(false); +} + +bool ConstantAffixModifier::semanticallyEquivalent(const Modifier& other) const { auto* _other = dynamic_cast(&other); if (_other == nullptr) { return false; @@ -160,7 +174,13 @@ bool SimpleModifier::containsField(UNumberFormatFields field) const { return false; } -bool SimpleModifier::operator==(const Modifier& other) const { +void SimpleModifier::getParameters(Parameters& output) const { + (void)output; + // This method is not currently used. + U_ASSERT(false); +} + +bool SimpleModifier::semanticallyEquivalent(const Modifier& other) const { auto* _other = dynamic_cast(&other); if (_other == nullptr) { return false; @@ -283,7 +303,13 @@ bool ConstantMultiFieldModifier::containsField(UNumberFormatFields field) const return fPrefix.containsField(field) || fSuffix.containsField(field); } -bool ConstantMultiFieldModifier::operator==(const Modifier& other) const { +void ConstantMultiFieldModifier::getParameters(Parameters& output) const { + (void)output; + // This method is not currently used. + U_ASSERT(false); +} + +bool ConstantMultiFieldModifier::semanticallyEquivalent(const Modifier& other) const { auto* _other = dynamic_cast(&other); if (_other == nullptr) { return false; diff --git a/icu4c/source/i18n/number_modifiers.h b/icu4c/source/i18n/number_modifiers.h index 9209e3c0f34..221efbe1811 100644 --- a/icu4c/source/i18n/number_modifiers.h +++ b/icu4c/source/i18n/number_modifiers.h @@ -39,7 +39,9 @@ class U_I18N_API ConstantAffixModifier : public Modifier, public UObject { bool containsField(UNumberFormatFields field) const U_OVERRIDE; - bool operator==(const Modifier& other) const U_OVERRIDE; + void getParameters(Parameters& output) const U_OVERRIDE; + + bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE; private: UnicodeString fPrefix; @@ -70,7 +72,9 @@ class U_I18N_API SimpleModifier : public Modifier, public UMemory { bool containsField(UNumberFormatFields field) const U_OVERRIDE; - bool operator==(const Modifier& other) const U_OVERRIDE; + void getParameters(Parameters& output) const U_OVERRIDE; + + bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE; /** * TODO: This belongs in SimpleFormatterImpl. The only reason I haven't moved it there yet is because @@ -148,7 +152,9 @@ class U_I18N_API ConstantMultiFieldModifier : public Modifier, public UMemory { bool containsField(UNumberFormatFields field) const U_OVERRIDE; - bool operator==(const Modifier& other) const U_OVERRIDE; + void getParameters(Parameters& output) const U_OVERRIDE; + + bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE; protected: // NOTE: In Java, these are stored as array pointers. In C++, the NumberStringBuilder is stored by @@ -237,7 +243,11 @@ class U_I18N_API EmptyModifier : public Modifier, public UMemory { return false; } - bool operator==(const Modifier& other) const U_OVERRIDE { + void getParameters(Parameters& output) const U_OVERRIDE { + output.obj = nullptr; + } + + bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE { return other.getCodePointCount() == 0; } @@ -246,50 +256,55 @@ class U_I18N_API EmptyModifier : public Modifier, public UMemory { }; /** - * A ParameterizedModifier by itself is NOT a Modifier. Rather, it wraps a data structure containing two or more - * Modifiers and returns the modifier appropriate for the current situation. + * This implementation of ModifierStore adopts Modifer pointers. */ -class U_I18N_API ParameterizedModifier : public UMemory { +class U_I18N_API AdoptingModifierStore : public ModifierStore, public UMemory { public: - // NOTE: mods is zero-initialized (to nullptr) - ParameterizedModifier() : mods() { - } + virtual ~AdoptingModifierStore(); - // No copying! - ParameterizedModifier(const ParameterizedModifier &other) = delete; + static constexpr StandardPlural::Form DEFAULT_STANDARD_PLURAL = StandardPlural::OTHER; - ~ParameterizedModifier() { - for (const Modifier *mod : mods) { - delete mod; - } - } + AdoptingModifierStore() = default; - void adoptPositiveNegativeModifiers( - const Modifier *positive, const Modifier *zero, const Modifier *negative) { - mods[2] = positive; - mods[1] = zero; - mods[0] = negative; - } + // No copying! + AdoptingModifierStore(const AdoptingModifierStore &other) = delete; - /** The modifier is ADOPTED. */ - void adoptSignPluralModifier(int8_t signum, StandardPlural::Form plural, const Modifier *mod) { + /** + * Sets the Modifier with the specified signum and plural form. + */ + void adoptModifier(int8_t signum, StandardPlural::Form plural, const Modifier *mod) { mods[getModIndex(signum, plural)] = mod; } + /** + * Sets the Modifier with the specified signum. + * The modifier will apply to all plural forms. + */ + void adoptModifierWithoutPlural(int8_t signum, const Modifier *mod) { + mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)] = mod; + } + /** Returns a reference to the modifier; no ownership change. */ - const Modifier *getModifier(int8_t signum) const { - return mods[signum + 1]; + const Modifier *getModifier(int8_t signum, StandardPlural::Form plural) const U_OVERRIDE { + const Modifier* modifier = mods[getModIndex(signum, plural)]; + if (modifier == nullptr && plural != DEFAULT_STANDARD_PLURAL) { + modifier = mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)]; + } + return modifier; } /** Returns a reference to the modifier; no ownership change. */ - const Modifier *getModifier(int8_t signum, StandardPlural::Form plural) const { - return mods[getModIndex(signum, plural)]; + const Modifier *getModifierWithoutPlural(int8_t signum) const { + return mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)]; } private: - const Modifier *mods[3 * StandardPlural::COUNT]; + // NOTE: mods is zero-initialized (to nullptr) + const Modifier *mods[3 * StandardPlural::COUNT] = {}; inline static int32_t getModIndex(int8_t signum, StandardPlural::Form plural) { + U_ASSERT(signum >= -1 && signum <= 1); + U_ASSERT(plural >= 0 && plural < StandardPlural::COUNT); return static_cast(plural) * 3 + (signum + 1); } }; diff --git a/icu4c/source/i18n/number_patternmodifier.cpp b/icu4c/source/i18n/number_patternmodifier.cpp index 58f72f5d343..4c61a0d35bc 100644 --- a/icu4c/source/i18n/number_patternmodifier.cpp +++ b/icu4c/source/i18n/number_patternmodifier.cpp @@ -69,7 +69,7 @@ MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator* paren StandardPlural::Form::MANY, StandardPlural::Form::OTHER}; - auto pm = new ParameterizedModifier(); + auto pm = new AdoptingModifierStore(); if (pm == nullptr) { status = U_MEMORY_ALLOCATION_ERROR; return nullptr; @@ -79,11 +79,11 @@ MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator* paren // Slower path when we require the plural keyword. for (StandardPlural::Form plural : STANDARD_PLURAL_VALUES) { setNumberProperties(1, plural); - pm->adoptSignPluralModifier(1, plural, createConstantModifier(status)); + pm->adoptModifier(1, plural, createConstantModifier(status)); setNumberProperties(0, plural); - pm->adoptSignPluralModifier(0, plural, createConstantModifier(status)); + pm->adoptModifier(0, plural, createConstantModifier(status)); setNumberProperties(-1, plural); - pm->adoptSignPluralModifier(-1, plural, createConstantModifier(status)); + pm->adoptModifier(-1, plural, createConstantModifier(status)); } if (U_FAILURE(status)) { delete pm; @@ -93,12 +93,11 @@ MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator* paren } else { // Faster path when plural keyword is not needed. setNumberProperties(1, StandardPlural::Form::COUNT); - Modifier* positive = createConstantModifier(status); + pm->adoptModifierWithoutPlural(1, createConstantModifier(status)); setNumberProperties(0, StandardPlural::Form::COUNT); - Modifier* zero = createConstantModifier(status); + pm->adoptModifierWithoutPlural(0, createConstantModifier(status)); setNumberProperties(-1, StandardPlural::Form::COUNT); - Modifier* negative = createConstantModifier(status); - pm->adoptPositiveNegativeModifiers(positive, zero, negative); + pm->adoptModifierWithoutPlural(-1, createConstantModifier(status)); if (U_FAILURE(status)) { delete pm; return nullptr; @@ -120,7 +119,7 @@ ConstantMultiFieldModifier* MutablePatternModifier::createConstantModifier(UErro } } -ImmutablePatternModifier::ImmutablePatternModifier(ParameterizedModifier* pm, const PluralRules* rules, +ImmutablePatternModifier::ImmutablePatternModifier(AdoptingModifierStore* pm, const PluralRules* rules, const MicroPropsGenerator* parent) : pm(pm), rules(rules), parent(parent) {} @@ -132,7 +131,7 @@ void ImmutablePatternModifier::processQuantity(DecimalQuantity& quantity, MicroP void ImmutablePatternModifier::applyToMicros(MicroProps& micros, DecimalQuantity& quantity) const { if (rules == nullptr) { - micros.modMiddle = pm->getModifier(quantity.signum()); + micros.modMiddle = pm->getModifierWithoutPlural(quantity.signum()); } else { // TODO: Fix this. Avoid the copy. DecimalQuantity copy(quantity); @@ -144,7 +143,7 @@ void ImmutablePatternModifier::applyToMicros(MicroProps& micros, DecimalQuantity const Modifier* ImmutablePatternModifier::getModifier(int8_t signum, StandardPlural::Form plural) const { if (rules == nullptr) { - return pm->getModifier(signum); + return pm->getModifierWithoutPlural(signum); } else { return pm->getModifier(signum, plural); } @@ -241,7 +240,13 @@ bool MutablePatternModifier::containsField(UNumberFormatFields field) const { return false; } -bool MutablePatternModifier::operator==(const Modifier& other) const { +void MutablePatternModifier::getParameters(Parameters& output) const { + (void)output; + // This method is not currently used. + U_ASSERT(false); +} + +bool MutablePatternModifier::semanticallyEquivalent(const Modifier& other) const { (void)other; // This method is not currently used. U_ASSERT(false); diff --git a/icu4c/source/i18n/number_patternmodifier.h b/icu4c/source/i18n/number_patternmodifier.h index 8beafddc5fe..ea80d6305e7 100644 --- a/icu4c/source/i18n/number_patternmodifier.h +++ b/icu4c/source/i18n/number_patternmodifier.h @@ -18,13 +18,13 @@ U_NAMESPACE_BEGIN // Export an explicit template instantiation of the LocalPointer that is used as a -// data member of ParameterizedModifier. +// data member of AdoptingModifierStore. // (When building DLLs for Windows this is required.) #if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN // Ignore warning 4661 as LocalPointerBase does not use operator== or operator!= #pragma warning(suppress: 4661) -template class U_I18N_API LocalPointerBase; -template class U_I18N_API LocalPointer; +template class U_I18N_API LocalPointerBase; +template class U_I18N_API LocalPointer; #endif namespace number { @@ -45,10 +45,10 @@ class U_I18N_API ImmutablePatternModifier : public MicroPropsGenerator, public U const Modifier* getModifier(int8_t signum, StandardPlural::Form plural) const; private: - ImmutablePatternModifier(ParameterizedModifier* pm, const PluralRules* rules, + ImmutablePatternModifier(AdoptingModifierStore* pm, const PluralRules* rules, const MicroPropsGenerator* parent); - const LocalPointer pm; + const LocalPointer pm; const PluralRules* rules; const MicroPropsGenerator* parent; @@ -186,7 +186,9 @@ class U_I18N_API MutablePatternModifier bool containsField(UNumberFormatFields field) const U_OVERRIDE; - bool operator==(const Modifier& other) const U_OVERRIDE; + void getParameters(Parameters& output) const U_OVERRIDE; + + bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE; /** * Returns the string that substitutes a given symbol type in a pattern. diff --git a/icu4c/source/i18n/number_scientific.cpp b/icu4c/source/i18n/number_scientific.cpp index c318eee6ffb..fc7d629e296 100644 --- a/icu4c/source/i18n/number_scientific.cpp +++ b/icu4c/source/i18n/number_scientific.cpp @@ -100,7 +100,13 @@ bool ScientificModifier::containsField(UNumberFormatFields field) const { return false; } -bool ScientificModifier::operator==(const Modifier& other) const { +void ScientificModifier::getParameters(Parameters& output) const { + (void)output; + // This method is not used for inner modifiers. + U_ASSERT(false); +} + +bool ScientificModifier::semanticallyEquivalent(const Modifier& other) const { auto* _other = dynamic_cast(&other); if (_other == nullptr) { return false; diff --git a/icu4c/source/i18n/number_scientific.h b/icu4c/source/i18n/number_scientific.h index 88a1c662666..e377bd941ef 100644 --- a/icu4c/source/i18n/number_scientific.h +++ b/icu4c/source/i18n/number_scientific.h @@ -32,7 +32,9 @@ class U_I18N_API ScientificModifier : public UMemory, public Modifier { bool containsField(UNumberFormatFields field) const U_OVERRIDE; - bool operator==(const Modifier& other) const U_OVERRIDE; + void getParameters(Parameters& output) const U_OVERRIDE; + + bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE; private: int32_t fExponent; diff --git a/icu4c/source/i18n/number_types.h b/icu4c/source/i18n/number_types.h index 844f3cbb6a7..649d365f723 100644 --- a/icu4c/source/i18n/number_types.h +++ b/icu4c/source/i18n/number_types.h @@ -16,6 +16,7 @@ #include "uassert.h" #include "unicode/platform.h" #include "unicode/uniset.h" +#include "standardplural.h" U_NAMESPACE_BEGIN namespace number { namespace impl { @@ -45,6 +46,7 @@ class Modifier; class MutablePatternModifier; class DecimalQuantity; class NumberStringBuilder; +class ModifierStore; struct MicroProps; @@ -133,7 +135,7 @@ class U_I18N_API AffixPatternProvider { * builder. A Modifier usually contains a prefix and a suffix that are applied, but it could contain something else, * like a {@link com.ibm.icu.text.SimpleFormatter} pattern. * - * A Modifier is usually immutable, except in cases such as {@link MurkyModifier}, which are mutable for performance + * A Modifier is usually immutable, except in cases such as {@link MutablePatternModifier}, which are mutable for performance * reasons. * * Exported as U_I18N_API because it is a base class for other exported types @@ -185,11 +187,45 @@ class U_I18N_API Modifier { virtual bool containsField(UNumberFormatFields field) const = 0; /** - * Returns whether the affixes owned by this modifier are equal to the ones owned by the given modifier. + * A fill-in for getParameters(). obj will always be set; if non-null, the other + * two fields are also safe to read. */ - virtual bool operator==(const Modifier& other) const = 0; + struct Parameters { + const ModifierStore* obj = nullptr; + int8_t signum; + StandardPlural::Form plural; + }; + + /** + * Gets a set of "parameters" for this Modifier. + */ + virtual void getParameters(Parameters& output) const = 0; + + /** + * Returns whether this Modifier is *semantically equivalent* to the other Modifier; + * in many cases, this is the same as equal, but parameters should be ignored. + */ + virtual bool semanticallyEquivalent(const Modifier& other) const = 0; }; + +/** + * This is *not* a modifier; rather, it is an object that can return modifiers + * based on given parameters. + * + * Exported as U_I18N_API because it is a base class for other exported types. + */ +class U_I18N_API ModifierStore { + public: + virtual ~ModifierStore(); + + /** + * Returns a Modifier with the given parameters (best-effort). + */ + virtual const Modifier* getModifier(int8_t signum, StandardPlural::Form plural) const = 0; +}; + + /** * This interface is used when all number formatting settings, including the locale, are known, except for the quantity * itself. The {@link #processQuantity} method performs the final step in the number processing pipeline: it uses the diff --git a/icu4c/source/i18n/numrange_impl.cpp b/icu4c/source/i18n/numrange_impl.cpp index d076352bb06..e36418ad194 100644 --- a/icu4c/source/i18n/numrange_impl.cpp +++ b/icu4c/source/i18n/numrange_impl.cpp @@ -119,9 +119,9 @@ void NumberRangeFormatterImpl::format(UFormattedNumberRangeData& data, bool equa // TODO: Write this as MicroProps operator==() ? // TODO: Avoid the redundancy of these equality operations with the // ones in formatRange? - if (!(*micros1.modInner == *micros2.modInner) - || !(*micros1.modMiddle == *micros2.modMiddle) - || !(*micros1.modOuter == *micros2.modOuter)) { + if (!micros1.modInner->semanticallyEquivalent(*micros2.modInner) + || !micros1.modMiddle->semanticallyEquivalent(*micros2.modMiddle) + || !micros1.modOuter->semanticallyEquivalent(*micros2.modOuter)) { formatRange(data, micros1, micros2, status); data.identityResult = UNUM_IDENTITY_RESULT_NOT_EQUAL; return; @@ -220,7 +220,7 @@ void NumberRangeFormatterImpl::formatRange(UFormattedNumberRangeData& data, case UNUM_RANGE_COLLAPSE_UNIT: { // OUTER MODIFIER - collapseOuter = *micros1.modOuter == *micros2.modOuter; + collapseOuter = micros1.modOuter->semanticallyEquivalent(*micros2.modOuter); if (!collapseOuter) { // Never collapse inner mods if outer mods are not collapsable @@ -230,7 +230,7 @@ void NumberRangeFormatterImpl::formatRange(UFormattedNumberRangeData& data, } // MIDDLE MODIFIER - collapseMiddle = *micros1.modMiddle == *micros2.modMiddle; + collapseMiddle = micros1.modMiddle->semanticallyEquivalent(*micros2.modMiddle); if (!collapseMiddle) { // Never collapse inner mods if outer mods are not collapsable @@ -262,7 +262,7 @@ void NumberRangeFormatterImpl::formatRange(UFormattedNumberRangeData& data, } // INNER MODIFIER - collapseInner = *micros1.modInner == *micros2.modInner; + collapseInner = micros1.modInner->semanticallyEquivalent(*micros2.modInner); // All done checking for collapsability. break;