]> granicus.if.org Git - icu/commitdiff
ICU-11276 Plural ranges loaded from data; first implementations of ModifierStore.
authorShane Carr <shane@unicode.org>
Fri, 14 Sep 2018 07:53:32 +0000 (00:53 -0700)
committerShane Carr <shane@unicode.org>
Thu, 27 Sep 2018 21:27:40 +0000 (14:27 -0700)
icu4c/source/i18n/number_longnames.cpp
icu4c/source/i18n/number_longnames.h
icu4c/source/i18n/number_modifiers.cpp
icu4c/source/i18n/number_modifiers.h
icu4c/source/i18n/number_scientific.cpp
icu4c/source/i18n/number_types.h
icu4c/source/i18n/numrange_impl.cpp
icu4c/source/i18n/numrange_impl.h

index 26f9af4c9bdbfe378e2110a0b039821cb3539c81..f3ed5d117090300289746a8d7a98c6ca0d076565 100644 (file)
@@ -265,4 +265,8 @@ void LongNameHandler::processQuantity(DecimalQuantity &quantity, MicroProps &mic
     micros.modOuter = &fModifiers[utils::getStandardPlural(rules, copy)];
 }
 
+const Modifier* LongNameHandler::getModifier(int8_t /*signum*/, StandardPlural::Form plural) const {
+    return &fModifiers[plural];
+}
+
 #endif /* #if !UCONFIG_NO_FORMATTING */
index 1d1e7dd3e864a655ecf390f52f32820a4851b54b..a8804659135d622bc358415a6d8e7855120ea1e9 100644 (file)
@@ -14,7 +14,7 @@
 U_NAMESPACE_BEGIN namespace number {
 namespace impl {
 
-class LongNameHandler : public MicroPropsGenerator, public UMemory {
+class LongNameHandler : public MicroPropsGenerator, public ModifierStore, public UMemory {
   public:
     static LongNameHandler
     forCurrencyLongNames(const Locale &loc, const CurrencyUnit &currency, const PluralRules *rules,
@@ -28,6 +28,8 @@ class LongNameHandler : public MicroPropsGenerator, public UMemory {
     void
     processQuantity(DecimalQuantity &quantity, MicroProps &micros, UErrorCode &status) const U_OVERRIDE;
 
+    const Modifier* getModifier(int8_t signum, StandardPlural::Form plural) const U_OVERRIDE;
+
   private:
     SimpleModifier fModifiers[StandardPlural::Form::COUNT];
     const PluralRules *rules;
index 926dbaec205f0d71e67de2e63f00cf3502bffc8a..dacac324efe4394f2ad57a468ca13430553a6bff 100644 (file)
@@ -53,6 +53,12 @@ void U_CALLCONV initDefaultCurrencySpacing(UErrorCode &status) {
 
 Modifier::~Modifier() = default;
 
+Modifier::Parameters Modifier::Parameters::getBogus() {
+    Modifier::Parameters result;
+    result.obj = nullptr;
+    return result;
+}
+
 ModifierStore::~ModifierStore() = default;
 
 AdoptingModifierStore::~AdoptingModifierStore()  {
@@ -62,6 +68,14 @@ AdoptingModifierStore::~AdoptingModifierStore()  {
 }
 
 
+ModifierWithParameters::ModifierWithParameters(const Modifier::Parameters& parameters)
+    : fParameters(parameters) {}
+
+void ModifierWithParameters::getParameters(Parameters& output) const {
+    output = fParameters;
+}
+
+
 int32_t ConstantAffixModifier::apply(NumberStringBuilder &output, int leftIndex, int rightIndex,
                                      UErrorCode &status) const {
     // Insert the suffix first since inserting the prefix will change the rightIndex
@@ -108,7 +122,12 @@ bool ConstantAffixModifier::semanticallyEquivalent(const Modifier& other) const
 
 
 SimpleModifier::SimpleModifier(const SimpleFormatter &simpleFormatter, Field field, bool strong)
-        : fCompiledPattern(simpleFormatter.compiledPattern), fField(field), fStrong(strong) {
+        : SimpleModifier(simpleFormatter, field, strong, Modifier::Parameters::getBogus()) {}
+
+SimpleModifier::SimpleModifier(const SimpleFormatter &simpleFormatter, Field field, bool strong,
+                               const Modifier::Parameters parameters)
+        : ModifierWithParameters(parameters),
+          fCompiledPattern(simpleFormatter.compiledPattern), fField(field), fStrong(strong) {
     int32_t argLimit = SimpleFormatter::getArgumentLimit(
             fCompiledPattern.getBuffer(), fCompiledPattern.length());
     if (argLimit == 0) {
@@ -140,7 +159,8 @@ SimpleModifier::SimpleModifier(const SimpleFormatter &simpleFormatter, Field fie
 }
 
 SimpleModifier::SimpleModifier()
-        : fField(UNUM_FIELD_COUNT), fStrong(false), fPrefixLength(0), fSuffixLength(0) {
+        : ModifierWithParameters(Modifier::Parameters::getBogus()),
+          fField(UNUM_FIELD_COUNT), fStrong(false), fPrefixLength(0), fSuffixLength(0) {
 }
 
 int32_t SimpleModifier::apply(NumberStringBuilder &output, int leftIndex, int rightIndex,
@@ -174,12 +194,6 @@ bool SimpleModifier::containsField(UNumberFormatFields field) const {
     return false;
 }
 
-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) {
@@ -303,12 +317,6 @@ bool ConstantMultiFieldModifier::containsField(UNumberFormatFields field) const
     return fPrefix.containsField(field) || fSuffix.containsField(field);
 }
 
-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) {
index 221efbe18119bf7a4b828bd0c769e5e27d245184..1892ae554953ca9f6926608faea8007fe1605e17 100644 (file)
 U_NAMESPACE_BEGIN namespace number {
 namespace impl {
 
+/**
+ * A base class for modifiers that need to be able to keep a reference to a ModifierStore.
+ */
+class U_I18N_API ModifierWithParameters : public Modifier, public UMemory {
+  public:
+    ModifierWithParameters(const Modifier::Parameters& parameters);
+
+    void getParameters(Parameters& output) const U_OVERRIDE;
+
+  private:
+    Modifier::Parameters fParameters;
+};
+
 /**
  * The canonical implementation of {@link Modifier}, containing a prefix and suffix string.
  * TODO: This is not currently being used by real code and could be removed.
@@ -54,10 +67,13 @@ class U_I18N_API ConstantAffixModifier : public Modifier, public UObject {
  * The second primary implementation of {@link Modifier}, this one consuming a {@link SimpleFormatter}
  * pattern.
  */
-class U_I18N_API SimpleModifier : public Modifier, public UMemory {
+class U_I18N_API SimpleModifier : public ModifierWithParameters {
   public:
     SimpleModifier(const SimpleFormatter &simpleFormatter, Field field, bool strong);
 
+    SimpleModifier(const SimpleFormatter &simpleFormatter, Field field, bool strong,
+                   const Modifier::Parameters parameters);
+
     // Default constructor for LongNameHandler.h
     SimpleModifier();
 
@@ -72,8 +88,6 @@ class U_I18N_API SimpleModifier : public Modifier, public UMemory {
 
     bool containsField(UNumberFormatFields field) const U_OVERRIDE;
 
-    void getParameters(Parameters& output) const U_OVERRIDE;
-
     bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE;
 
     /**
@@ -129,18 +143,27 @@ class U_I18N_API SimpleModifier : public Modifier, public UMemory {
  * An implementation of {@link Modifier} that allows for multiple types of fields in the same modifier. Constructed
  * based on the contents of two {@link NumberStringBuilder} instances (one for the prefix, one for the suffix).
  */
-class U_I18N_API ConstantMultiFieldModifier : public Modifier, public UMemory {
+class U_I18N_API ConstantMultiFieldModifier :public ModifierWithParameters {
   public:
     ConstantMultiFieldModifier(
             const NumberStringBuilder &prefix,
             const NumberStringBuilder &suffix,
             bool overwrite,
-            bool strong)
-      : fPrefix(prefix),
+            bool strong,
+            const Modifier::Parameters parameters)
+      : ModifierWithParameters(parameters),
+        fPrefix(prefix),
         fSuffix(suffix),
         fOverwrite(overwrite),
         fStrong(strong) {}
 
+    ConstantMultiFieldModifier(
+            const NumberStringBuilder &prefix,
+            const NumberStringBuilder &suffix,
+            bool overwrite,
+            bool strong)
+      : ConstantMultiFieldModifier(prefix, suffix, overwrite, strong, Modifier::Parameters::getBogus()) {}
+
     int32_t apply(NumberStringBuilder &output, int32_t leftIndex, int32_t rightIndex,
                   UErrorCode &status) const U_OVERRIDE;
 
@@ -152,8 +175,6 @@ class U_I18N_API ConstantMultiFieldModifier : public Modifier, public UMemory {
 
     bool containsField(UNumberFormatFields field) const U_OVERRIDE;
 
-    void getParameters(Parameters& output) const U_OVERRIDE;
-
     bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE;
 
   protected:
index fc7d629e2963501407ffc48cf90c4c8c73f9db77..009e4dfa5b5d4c0891db587d4e7a26891d8d4b4a 100644 (file)
@@ -101,9 +101,8 @@ bool ScientificModifier::containsField(UNumberFormatFields field) const {
 }
 
 void ScientificModifier::getParameters(Parameters& output) const {
-    (void)output;
-    // This method is not used for inner modifiers.
-    U_ASSERT(false);
+    // Not part of any plural sets
+    output.obj = nullptr;
 }
 
 bool ScientificModifier::semanticallyEquivalent(const Modifier& other) const {
index 649d365f7231cb8277e20118ed1e20e2d0e65af8..c20c8124f425eb59df0924ecee29d95dae17c216 100644 (file)
@@ -194,6 +194,8 @@ class U_I18N_API Modifier {
         const ModifierStore* obj = nullptr;
         int8_t signum;
         StandardPlural::Form plural;
+
+        static Parameters getBogus();
     };
 
     /**
index e36418ad1941f863c0dcb768b8b41e6e6d84c64f..a2493bcf29fd9e6453f9b3e81fd45ba72e39f9db 100644 (file)
@@ -26,6 +26,11 @@ constexpr int8_t identity2d(UNumberRangeIdentityFallback a, UNumberRangeIdentity
 }
 
 
+struct NumberRangeData {
+    SimpleFormatter rangePattern;
+    SimpleFormatter approximatelyPattern;
+};
+
 class NumberRangeDataSink : public ResourceSink {
   public:
     NumberRangeDataSink(NumberRangeData& data) : fData(data) {}
@@ -77,9 +82,94 @@ void getNumberRangeData(const char* localeName, const char* nsName, NumberRangeD
     }
 }
 
+
+class PluralRangesDataSink : public ResourceSink {
+  public:
+    PluralRangesDataSink(StandardPluralRanges& output) : fOutput(output) {}
+
+    void put(const char* /*key*/, ResourceValue& value, UBool /*noFallback*/, UErrorCode& status) U_OVERRIDE {
+        ResourceArray entriesArray = value.getArray(status);
+        if (U_FAILURE(status)) { return; }
+        fOutput.setCapacity(entriesArray.getSize());
+        for (int i = 0; entriesArray.getValue(i, value); i++) {
+            ResourceArray pluralFormsArray = value.getArray(status);
+            if (U_FAILURE(status)) { return; }
+            pluralFormsArray.getValue(0, value);
+            StandardPlural::Form first = StandardPlural::fromString(value.getUnicodeString(status), status);
+            if (U_FAILURE(status)) { return; }
+            pluralFormsArray.getValue(1, value);
+            StandardPlural::Form second = StandardPlural::fromString(value.getUnicodeString(status), status);
+            if (U_FAILURE(status)) { return; }
+            pluralFormsArray.getValue(2, value);
+            StandardPlural::Form result = StandardPlural::fromString(value.getUnicodeString(status), status);
+            if (U_FAILURE(status)) { return; }
+            fOutput.addPluralRange(first, second, result);
+        }
+    }
+
+  private:
+    StandardPluralRanges& fOutput;
+};
+
+void getPluralRangesData(const Locale& locale, StandardPluralRanges& output, UErrorCode& status) {
+    if (U_FAILURE(status)) { return; }
+    LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "pluralRanges", &status));
+    if (U_FAILURE(status)) { return; }
+
+    CharString dataPath;
+    dataPath.append("locales/", -1, status);
+    dataPath.append(locale.getLanguage(), -1, status);
+    if (U_FAILURE(status)) { return; }
+    int32_t setLen;
+    // Not all languages are covered: fail gracefully
+    UErrorCode internalStatus = U_ZERO_ERROR;
+    const UChar* set = ures_getStringByKey(rb.getAlias(), dataPath.data(), &setLen, &internalStatus);
+    if (U_FAILURE(internalStatus)) { return; }
+
+    dataPath.clear();
+    dataPath.append("rules/", -1, status);
+    dataPath.appendInvariantChars(set, setLen, status);
+    if (U_FAILURE(status)) { return; }
+    PluralRangesDataSink sink(output);
+    ures_getAllItemsWithFallback(rb.getAlias(), dataPath.data(), sink, status);
+    if (U_FAILURE(status)) { return; }
+}
+
 } // namespace
 
 
+void StandardPluralRanges::initialize(const Locale& locale, UErrorCode& status) {
+    getPluralRangesData(locale, *this, status);
+}
+
+void StandardPluralRanges::addPluralRange(
+        StandardPlural::Form first,
+        StandardPlural::Form second,
+        StandardPlural::Form result) {
+    U_ASSERT(fTriplesLen < fTriples.getCapacity());
+    fTriples[fTriplesLen] = {first, second, result};
+    fTriplesLen++;
+}
+
+void StandardPluralRanges::setCapacity(int32_t length) {
+    if (length > fTriples.getCapacity()) {
+        fTriples.resize(length, 0);
+    }
+}
+
+StandardPlural::Form
+StandardPluralRanges::resolve(StandardPlural::Form first, StandardPlural::Form second) const {
+    for (int32_t i=0; i<fTriplesLen; i++) {
+        const auto& triple = fTriples[i];
+        if (triple.first == first && triple.second == second) {
+            return triple.result;
+        }
+    }
+    // Default fallback
+    return StandardPlural::OTHER;
+}
+
+
 NumberRangeFormatterImpl::NumberRangeFormatterImpl(const RangeMacroProps& macros, UErrorCode& status)
     : formatterImpl1(macros.formatter1.fMacros, status),
       formatterImpl2(macros.formatter2.fMacros, status),
@@ -98,6 +188,10 @@ NumberRangeFormatterImpl::NumberRangeFormatterImpl(const RangeMacroProps& macros
     if (U_FAILURE(status)) { return; }
     fRangeFormatter = data.rangePattern;
     fApproximatelyModifier = {data.approximatelyPattern, UNUM_FIELD_COUNT, false};
+
+    // TODO: Get locale from PluralRules instead?
+    fPluralRanges.initialize(macros.locale, status);
+    if (U_FAILURE(status)) { return; }
 }
 
 void NumberRangeFormatterImpl::format(UFormattedNumberRangeData& data, bool equalBeforeRounding, UErrorCode& status) const {
@@ -320,7 +414,8 @@ void NumberRangeFormatterImpl::formatRange(UFormattedNumberRangeData& data,
 
     if (collapseInner) {
         // Note: this is actually a mix of prefix and suffix, but adding to infix length works
-        lengthInfix += micros1.modInner->apply(string, UPRV_INDEX_0, UPRV_INDEX_3, status);
+        const Modifier& mod = resolveModifierPlurals(*micros1.modInner, *micros2.modInner);
+        lengthInfix += mod.apply(string, UPRV_INDEX_0, UPRV_INDEX_3, status);
     } else {
         length1 += micros1.modInner->apply(string, UPRV_INDEX_0, UPRV_INDEX_1, status);
         length2 += micros2.modInner->apply(string, UPRV_INDEX_2, UPRV_INDEX_3, status);
@@ -328,7 +423,8 @@ void NumberRangeFormatterImpl::formatRange(UFormattedNumberRangeData& data,
 
     if (collapseMiddle) {
         // Note: this is actually a mix of prefix and suffix, but adding to infix length works
-        lengthInfix += micros1.modMiddle->apply(string, UPRV_INDEX_0, UPRV_INDEX_3, status);
+        const Modifier& mod = resolveModifierPlurals(*micros1.modMiddle, *micros2.modMiddle);
+        lengthInfix += mod.apply(string, UPRV_INDEX_0, UPRV_INDEX_3, status);
     } else {
         length1 += micros1.modMiddle->apply(string, UPRV_INDEX_0, UPRV_INDEX_1, status);
         length2 += micros2.modMiddle->apply(string, UPRV_INDEX_2, UPRV_INDEX_3, status);
@@ -336,7 +432,8 @@ void NumberRangeFormatterImpl::formatRange(UFormattedNumberRangeData& data,
 
     if (collapseOuter) {
         // Note: this is actually a mix of prefix and suffix, but adding to infix length works
-        lengthInfix += micros1.modOuter->apply(string, UPRV_INDEX_0, UPRV_INDEX_3, status);
+        const Modifier& mod = resolveModifierPlurals(*micros1.modOuter, *micros2.modOuter);
+        lengthInfix += mod.apply(string, UPRV_INDEX_0, UPRV_INDEX_3, status);
     } else {
         length1 += micros1.modOuter->apply(string, UPRV_INDEX_0, UPRV_INDEX_1, status);
         length2 += micros2.modOuter->apply(string, UPRV_INDEX_2, UPRV_INDEX_3, status);
@@ -344,5 +441,32 @@ void NumberRangeFormatterImpl::formatRange(UFormattedNumberRangeData& data,
 }
 
 
+const Modifier&
+NumberRangeFormatterImpl::resolveModifierPlurals(const Modifier& first, const Modifier& second) const {
+    Modifier::Parameters parameters;
+    first.getParameters(parameters);
+    if (parameters.obj == nullptr) {
+        // No plural form; return a fallback (e.g., the first)
+        return first;
+    }
+    StandardPlural::Form firstPlural = parameters.plural;
+
+    second.getParameters(parameters);
+    if (parameters.obj == nullptr) {
+        // No plural form; return a fallback (e.g., the first)
+        return first;
+    }
+    StandardPlural::Form secondPlural = parameters.plural;
+
+    // Get the required plural form from data
+    StandardPlural::Form resultPlural = fPluralRanges.resolve(firstPlural, secondPlural);
+
+    // Get and return the new Modifier
+    const Modifier* mod = parameters.obj->getModifier(parameters.signum, resultPlural);
+    U_ASSERT(mod != nullptr);
+    return *mod;
+}
+
+
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
index 5e08fad581ca11c96ed2888c654d9a8083c25383..c763f0c72c58d0b312cf68924e030a9ef74ea20a 100644 (file)
@@ -44,9 +44,33 @@ struct UFormattedNumberRangeData : public UMemory {
 };
 
 
-struct NumberRangeData {
-    SimpleFormatter rangePattern;
-    SimpleFormatter approximatelyPattern;
+class StandardPluralRanges : public UMemory {
+  public:
+    void initialize(const Locale& locale, UErrorCode& status);
+    StandardPlural::Form resolve(StandardPlural::Form first, StandardPlural::Form second) const;
+
+    /** Used for data loading. */
+    void addPluralRange(
+        StandardPlural::Form first,
+        StandardPlural::Form second,
+        StandardPlural::Form result);
+
+    /** Used for data loading. */
+    void setCapacity(int32_t length);
+
+  private:
+    struct StandardPluralRangeTriple {
+        StandardPlural::Form first;
+        StandardPlural::Form second;
+        StandardPlural::Form result;
+    };
+
+    // TODO: An array is simple here, but it results in linear lookup time.
+    // Certain locales have 20-30 entries in this list.
+    // Consider changing to a smarter data structure.
+    typedef MaybeStackArray<StandardPluralRangeTriple, 3> PluralRangeTriples;
+    PluralRangeTriples fTriples;
+    int32_t fTriplesLen = 0;
 };
 
 
@@ -67,6 +91,8 @@ class NumberRangeFormatterImpl : public UMemory {
     SimpleFormatter fRangeFormatter;
     SimpleModifier fApproximatelyModifier;
 
+    StandardPluralRanges fPluralRanges;
+
     void formatSingleValue(UFormattedNumberRangeData& data,
                            MicroProps& micros1, MicroProps& micros2,
                            UErrorCode& status) const;
@@ -78,6 +104,8 @@ class NumberRangeFormatterImpl : public UMemory {
     void formatRange(UFormattedNumberRangeData& data,
                      MicroProps& micros1, MicroProps& micros2,
                      UErrorCode& status) const;
+
+    const Modifier& resolveModifierPlurals(const Modifier& first, const Modifier& second) const;
 };