]> granicus.if.org Git - icu/commitdiff
ICU-11276 Adding ModifierStore, groundwork for plural range support.
authorShane Carr <shane@unicode.org>
Fri, 14 Sep 2018 05:16:29 +0000 (22:16 -0700)
committerShane Carr <shane@unicode.org>
Thu, 27 Sep 2018 21:27:40 +0000 (14:27 -0700)
icu4c/source/i18n/number_modifiers.cpp
icu4c/source/i18n/number_modifiers.h
icu4c/source/i18n/number_patternmodifier.cpp
icu4c/source/i18n/number_patternmodifier.h
icu4c/source/i18n/number_scientific.cpp
icu4c/source/i18n/number_scientific.h
icu4c/source/i18n/number_types.h
icu4c/source/i18n/numrange_impl.cpp

index 6d0096055c8ba5756d32f266c7a08916a72af918..926dbaec205f0d71e67de2e63f00cf3502bffc8a 100644 (file)
@@ -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<const ConstantAffixModifier*>(&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<const SimpleModifier*>(&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<const ConstantMultiFieldModifier*>(&other);
     if (_other == nullptr) {
         return false;
index 9209e3c0f34e758d318e5852025fc2952c114845..221efbe18119bf7a4b828bd0c769e5e27d245184 100644 (file)
@@ -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<int32_t>(plural) * 3 + (signum + 1);
     }
 };
index 58f72f5d3435b9236354457d95ff4e004f8f6f77..4c61a0d35bca828b3dcf9adc3e17970338b9f0f1 100644 (file)
@@ -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);
index 8beafddc5feff391bbd73f7af991f7c7de91d017..ea80d6305e75b4444934f13733f91c27e9a79c75 100644 (file)
 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<number::impl::ParameterizedModifier>;
-template class U_I18N_API LocalPointer<number::impl::ParameterizedModifier>;
+template class U_I18N_API LocalPointerBase<number::impl::AdoptingModifierStore>;
+template class U_I18N_API LocalPointer<number::impl::AdoptingModifierStore>;
 #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<ParameterizedModifier> pm;
+    const LocalPointer<AdoptingModifierStore> 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.
index c318eee6ffbcdc6851cf07a3ec662980d724af72..fc7d629e2963501407ffc48cf90c4c8c73f9db77 100644 (file)
@@ -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<const ScientificModifier*>(&other);
     if (_other == nullptr) {
         return false;
index 88a1c662666a2ef72bbd46c1aba1a15369e52eb0..e377bd941efaeb07ac7d9cdc00cd519cb443d3e5 100644 (file)
@@ -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;
index 844f3cbb6a7d81fcecc11919a0516663d457d7eb..649d365f7231cb8277e20118ed1e20e2d0e65af8 100644 (file)
@@ -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
index d076352bb060ae606159e724ea84a884c127c20e..e36418ad1941f863c0dcb768b8b41e6e6d84c64f 100644 (file)
@@ -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;