]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/plurfmt.cpp
ICU-66108.tar.gz
[apple/icu.git] / icuSources / i18n / plurfmt.cpp
index 510b832453e8d4bd9f24159293cf5fa899eaa1a4..b99437630e67b5d2dc6d99ae1a1a9c8cb40c7100 100644 (file)
@@ -1,31 +1,36 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /*
 *******************************************************************************
-* Copyright (C) 2009-2012, International Business Machines Corporation and
+* Copyright (C) 2009-2015, International Business Machines Corporation and
 * others. All Rights Reserved.
 *******************************************************************************
 *
 * File PLURFMT.CPP
-*
-* Modification History:
-*
-*   Date        Name        Description
 *******************************************************************************
 */
 
+#include "unicode/decimfmt.h"
 #include "unicode/messagepattern.h"
 #include "unicode/plurfmt.h"
 #include "unicode/plurrule.h"
 #include "unicode/utypes.h"
 #include "cmemory.h"
 #include "messageimpl.h"
+#include "nfrule.h"
 #include "plurrule_impl.h"
 #include "uassert.h"
 #include "uhash.h"
+#include "number_decimalquantity.h"
+#include "number_utils.h"
+#include "number_utypes.h"
 
 #if !UCONFIG_NO_FORMATTING
 
 U_NAMESPACE_BEGIN
 
+using number::impl::DecimalQuantity;
+
 static const UChar OTHER_STRING[] = {
     0x6F, 0x74, 0x68, 0x65, 0x72, 0  // "other"
 };
@@ -37,7 +42,7 @@ PluralFormat::PluralFormat(UErrorCode& status)
           msgPattern(status),
           numberFormat(NULL),
           offset(0) {
-    init(NULL, status);
+    init(NULL, UPLURAL_TYPE_CARDINAL, status);
 }
 
 PluralFormat::PluralFormat(const Locale& loc, UErrorCode& status)
@@ -45,7 +50,7 @@ PluralFormat::PluralFormat(const Locale& loc, UErrorCode& status)
           msgPattern(status),
           numberFormat(NULL),
           offset(0) {
-    init(NULL, status);
+    init(NULL, UPLURAL_TYPE_CARDINAL, status);
 }
 
 PluralFormat::PluralFormat(const PluralRules& rules, UErrorCode& status)
@@ -53,7 +58,7 @@ PluralFormat::PluralFormat(const PluralRules& rules, UErrorCode& status)
           msgPattern(status),
           numberFormat(NULL),
           offset(0) {
-    init(&rules, status);
+    init(&rules, UPLURAL_TYPE_COUNT, status);
 }
 
 PluralFormat::PluralFormat(const Locale& loc,
@@ -63,7 +68,17 @@ PluralFormat::PluralFormat(const Locale& loc,
           msgPattern(status),
           numberFormat(NULL),
           offset(0) {
-    init(&rules, status);
+    init(&rules, UPLURAL_TYPE_COUNT, status);
+}
+
+PluralFormat::PluralFormat(const Locale& loc,
+                           UPluralType type,
+                           UErrorCode& status)
+        : locale(loc),
+          msgPattern(status),
+          numberFormat(NULL),
+          offset(0) {
+    init(NULL, type, status);
 }
 
 PluralFormat::PluralFormat(const UnicodeString& pat,
@@ -72,7 +87,7 @@ PluralFormat::PluralFormat(const UnicodeString& pat,
           msgPattern(status),
           numberFormat(NULL),
           offset(0) {
-    init(NULL, status);
+    init(NULL, UPLURAL_TYPE_CARDINAL, status);
     applyPattern(pat, status);
 }
 
@@ -83,7 +98,7 @@ PluralFormat::PluralFormat(const Locale& loc,
           msgPattern(status),
           numberFormat(NULL),
           offset(0) {
-    init(NULL, status);
+    init(NULL, UPLURAL_TYPE_CARDINAL, status);
     applyPattern(pat, status);
 }
 
@@ -94,7 +109,7 @@ PluralFormat::PluralFormat(const PluralRules& rules,
           msgPattern(status),
           numberFormat(NULL),
           offset(0) {
-    init(&rules, status);
+    init(&rules, UPLURAL_TYPE_COUNT, status);
     applyPattern(pat, status);
 }
 
@@ -106,7 +121,19 @@ PluralFormat::PluralFormat(const Locale& loc,
           msgPattern(status),
           numberFormat(NULL),
           offset(0) {
-    init(&rules, status);
+    init(&rules, UPLURAL_TYPE_COUNT, status);
+    applyPattern(pat, status);
+}
+
+PluralFormat::PluralFormat(const Locale& loc,
+                           UPluralType type,
+                           const UnicodeString& pat,
+                           UErrorCode& status)
+        : locale(loc),
+          msgPattern(status),
+          numberFormat(NULL),
+          offset(0) {
+    init(NULL, type, status);
     applyPattern(pat, status);
 }
 
@@ -132,7 +159,7 @@ PluralFormat::copyObjects(const PluralFormat& other) {
     if (other.numberFormat == NULL) {
         numberFormat = NumberFormat::createInstance(locale, status);
     } else {
-        numberFormat = (NumberFormat*)other.numberFormat->clone();
+        numberFormat = other.numberFormat->clone();
     }
     if (other.pluralRulesWrapper.pluralRules == NULL) {
         pluralRulesWrapper.pluralRules = PluralRules::forLocale(locale, status);
@@ -147,13 +174,13 @@ PluralFormat::~PluralFormat() {
 }
 
 void
-PluralFormat::init(const PluralRules* rules, UErrorCode& status) {
+PluralFormat::init(const PluralRules* rules, UPluralType type, UErrorCode& status) {
     if (U_FAILURE(status)) {
         return;
     }
 
     if (rules==NULL) {
-        pluralRulesWrapper.pluralRules = PluralRules::forLocale(locale, status);
+        pluralRulesWrapper.pluralRules = PluralRules::forLocale(locale, type, status);
     } else {
         pluralRulesWrapper.pluralRules = rules->clone();
         if (pluralRulesWrapper.pluralRules == NULL) {
@@ -185,7 +212,7 @@ PluralFormat::format(const Formattable& obj,
     if (U_FAILURE(status)) return appendTo;
 
     if (obj.isNumeric()) {
-        return format(obj.getDouble(), appendTo, pos, status);
+        return format(obj, obj.getDouble(), appendTo, pos, status);
     } else {
         status = U_ILLEGAL_ARGUMENT_ERROR;
         return appendTo;
@@ -194,16 +221,16 @@ PluralFormat::format(const Formattable& obj,
 
 UnicodeString
 PluralFormat::format(int32_t number, UErrorCode& status) const {
-    FieldPosition fpos(0);
+    FieldPosition fpos(FieldPosition::DONT_CARE);
     UnicodeString result;
-    return format(number, result, fpos, status);
+    return format(Formattable(number), number, result, fpos, status);
 }
 
 UnicodeString
 PluralFormat::format(double number, UErrorCode& status) const {
-    FieldPosition fpos(0);
+    FieldPosition fpos(FieldPosition::DONT_CARE);
     UnicodeString result;
-    return format(number, result, fpos, status);
+    return format(Formattable(number), number, result, fpos, status);
 }
 
 
@@ -212,7 +239,7 @@ PluralFormat::format(int32_t number,
                      UnicodeString& appendTo,
                      FieldPosition& pos,
                      UErrorCode& status) const {
-    return format((double)number, appendTo, pos, status);
+    return format(Formattable(number), (double)number, appendTo, pos, status);
 }
 
 UnicodeString&
@@ -220,18 +247,58 @@ PluralFormat::format(double number,
                      UnicodeString& appendTo,
                      FieldPosition& pos,
                      UErrorCode& status) const {
+    return format(Formattable(number), (double)number, appendTo, pos, status);
+}
+
+UnicodeString&
+PluralFormat::format(const Formattable& numberObject, double number,
+                     UnicodeString& appendTo,
+                     FieldPosition& pos,
+                     UErrorCode& status) const {
     if (U_FAILURE(status)) {
         return appendTo;
     }
     if (msgPattern.countParts() == 0) {
-        return numberFormat->format(number, appendTo, pos);
+        return numberFormat->format(numberObject, appendTo, pos, status);
     }
+
     // Get the appropriate sub-message.
-    int32_t partIndex = findSubMessage(msgPattern, 0, pluralRulesWrapper, number, status);
+    // Select it based on the formatted number-offset.
+    double numberMinusOffset = number - offset;
+    // Call NumberFormatter to get both the DecimalQuantity and the string.
+    // This call site needs to use more internal APIs than the Java equivalent.
+    number::impl::UFormattedNumberData data;
+    if (offset == 0) {
+        // could be BigDecimal etc.
+        numberObject.populateDecimalQuantity(data.quantity, status);
+    } else {
+        data.quantity.setToDouble(numberMinusOffset);
+    }
+    UnicodeString numberString;
+    auto *decFmt = dynamic_cast<DecimalFormat *>(numberFormat);
+    if(decFmt != nullptr) {
+        const number::LocalizedNumberFormatter* lnf = decFmt->toNumberFormatter(status);
+        if (U_FAILURE(status)) {
+            return appendTo;
+        }
+        lnf->formatImpl(&data, status); // mutates &data
+        if (U_FAILURE(status)) {
+            return appendTo;
+        }
+        numberString = data.getStringRef().toUnicodeString();
+    } else {
+        if (offset == 0) {
+            numberFormat->format(numberObject, numberString, status);
+        } else {
+            numberFormat->format(numberMinusOffset, numberString, status);
+        }
+    }
+
+    int32_t partIndex = findSubMessage(msgPattern, 0, pluralRulesWrapper, &data.quantity, number, status);
+    if (U_FAILURE(status)) { return appendTo; }
     // Replace syntactic # signs in the top level of this sub-message
     // (not in nested arguments) with the formatted number-offset.
     const UnicodeString& pattern = msgPattern.getPatternString();
-    number -= offset;
     int32_t prevIndex = msgPattern.getPart(partIndex).getLimit();
     for (;;) {
         const MessagePattern::Part& part = msgPattern.getPart(++partIndex);
@@ -243,7 +310,7 @@ PluralFormat::format(double number,
             (type == UMSGPAT_PART_TYPE_SKIP_SYNTAX && MessageImpl::jdkAposMode(msgPattern))) {
             appendTo.append(pattern, prevIndex, index - prevIndex);
             if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) {
-                numberFormat->format(number, appendTo);
+                appendTo.append(numberString);
             }
             prevIndex = part.getLimit();
         } else if (type == UMSGPAT_PART_TYPE_ARG_START) {
@@ -278,7 +345,7 @@ PluralFormat::setLocale(const Locale& loc, UErrorCode& status) {
     offset = 0;
     numberFormat = NULL;
     pluralRulesWrapper.reset();
-    init(NULL, status);
+    init(NULL, UPLURAL_TYPE_CARDINAL, status);
 }
 
 void
@@ -286,7 +353,7 @@ PluralFormat::setNumberFormat(const NumberFormat* format, UErrorCode& status) {
     if (U_FAILURE(status)) {
         return;
     }
-    NumberFormat* nf = (NumberFormat*)format->clone();
+    NumberFormat* nf = format->clone();
     if (nf != NULL) {
         delete numberFormat;
         numberFormat = nf;
@@ -295,7 +362,7 @@ PluralFormat::setNumberFormat(const NumberFormat* format, UErrorCode& status) {
     }
 }
 
-Format*
+PluralFormat*
 PluralFormat::clone() const
 {
     return new PluralFormat(*this);
@@ -348,7 +415,8 @@ PluralFormat::parseObject(const UnicodeString& /*source*/,
 }
 
 int32_t PluralFormat::findSubMessage(const MessagePattern& pattern, int32_t partIndex,
-                                     const PluralSelector& selector, double number, UErrorCode& ec) {
+                                     const PluralSelector& selector, void *context,
+                                     double number, UErrorCode& ec) {
     if (U_FAILURE(ec)) {
         return 0;
     }
@@ -361,7 +429,7 @@ int32_t PluralFormat::findSubMessage(const MessagePattern& pattern, int32_t part
     } else {
         offset=0;
     }
-    // The keyword is empty until we need to match against non-explicit, not-"other" value.
+    // The keyword is empty until we need to match against non-explicit, not-"other" value.
     // Then we get the keyword from the selector.
     // (In other words, we never call the selector if we match against an explicit value,
     // or if the only non-explicit keyword is "other".)
@@ -414,7 +482,7 @@ int32_t PluralFormat::findSubMessage(const MessagePattern& pattern, int32_t part
                 }
             } else {
                 if(keyword.isEmpty()) {
-                    keyword=selector.select(number-offset, ec);
+                    keyword=selector.select(context, number-offset, ec);
                     if(msgStart!=0 && (0 == keyword.compare(other))) {
                         // We have already seen an "other" sub-message.
                         // Do not match "other" again.
@@ -435,15 +503,88 @@ int32_t PluralFormat::findSubMessage(const MessagePattern& pattern, int32_t part
     return msgStart;
 }
 
+void PluralFormat::parseType(const UnicodeString& source, const NFRule *rbnfLenientScanner, Formattable& result, FieldPosition& pos) const {
+    // If no pattern was applied, return null.
+    if (msgPattern.countParts() == 0) {
+        pos.setBeginIndex(-1);
+        pos.setEndIndex(-1);
+        return;
+    }
+    int partIndex = 0;
+    int currMatchIndex;
+    int count=msgPattern.countParts();
+    int startingAt = pos.getBeginIndex();
+    if (startingAt < 0) {
+        startingAt = 0;
+    }
+
+    // The keyword is null until we need to match against a non-explicit, not-"other" value.
+    // Then we get the keyword from the selector.
+    // (In other words, we never call the selector if we match against an explicit value,
+    // or if the only non-explicit keyword is "other".)
+    UnicodeString keyword;
+    UnicodeString matchedWord;
+    const UnicodeString& pattern = msgPattern.getPatternString();
+    int matchedIndex = -1;
+    // Iterate over (ARG_SELECTOR ARG_START message ARG_LIMIT) tuples
+    // until the end of the plural-only pattern.
+    while (partIndex < count) {
+        const MessagePattern::Part* partSelector = &msgPattern.getPart(partIndex++);
+        if (partSelector->getType() != UMSGPAT_PART_TYPE_ARG_SELECTOR) {
+            // Bad format
+            continue;
+        }
+
+        const MessagePattern::Part* partStart = &msgPattern.getPart(partIndex++);
+        if (partStart->getType() != UMSGPAT_PART_TYPE_MSG_START) {
+            // Bad format
+            continue;
+        }
+
+        const MessagePattern::Part* partLimit = &msgPattern.getPart(partIndex++);
+        if (partLimit->getType() != UMSGPAT_PART_TYPE_MSG_LIMIT) {
+            // Bad format
+            continue;
+        }
+
+        UnicodeString currArg = pattern.tempSubString(partStart->getLimit(), partLimit->getIndex() - partStart->getLimit());
+        if (rbnfLenientScanner != NULL) {
+            // If lenient parsing is turned ON, we've got some time consuming parsing ahead of us.
+            int32_t length = -1;
+            currMatchIndex = rbnfLenientScanner->findTextLenient(source, currArg, startingAt, &length);
+        }
+        else {
+            currMatchIndex = source.indexOf(currArg, startingAt);
+        }
+        if (currMatchIndex >= 0 && currMatchIndex >= matchedIndex && currArg.length() > matchedWord.length()) {
+            matchedIndex = currMatchIndex;
+            matchedWord = currArg;
+            keyword = pattern.tempSubString(partStart->getLimit(), partLimit->getIndex() - partStart->getLimit());
+        }
+    }
+    if (matchedIndex >= 0) {
+        pos.setBeginIndex(matchedIndex);
+        pos.setEndIndex(matchedIndex + matchedWord.length());
+        result.setString(keyword);
+        return;
+    }
+
+    // Not found!
+    pos.setBeginIndex(-1);
+    pos.setEndIndex(-1);
+}
+
 PluralFormat::PluralSelector::~PluralSelector() {}
 
 PluralFormat::PluralSelectorAdapter::~PluralSelectorAdapter() {
     delete pluralRules;
 }
 
-UnicodeString PluralFormat::PluralSelectorAdapter::select(double number,
+UnicodeString PluralFormat::PluralSelectorAdapter::select(void *context, double number,
                                                           UErrorCode& /*ec*/) const {
-    return pluralRules->select(number);
+    (void)number;  // unused except in the assertion
+    IFixedDecimal *dec=static_cast<IFixedDecimal *>(context);
+    return pluralRules->select(*dec);
 }
 
 void PluralFormat::PluralSelectorAdapter::reset() {