]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/quantityformatter.cpp
ICU-66108.tar.gz
[apple/icu.git] / icuSources / i18n / quantityformatter.cpp
index d0dc378bd9bebfed35b247de65c6fe5c01b07a0a..e88b70fbd71795c0c85cc651473ca6a35737418e 100644 (file)
@@ -1,40 +1,35 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /*
 ******************************************************************************
-* Copyright (C) 2014, International Business Machines
+* Copyright (C) 2014-2016, International Business Machines
 * Corporation and others.  All Rights Reserved.
 ******************************************************************************
 * quantityformatter.cpp
 */
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "unicode/simpleformatter.h"
 #include "quantityformatter.h"
-#include "simplepatternformatter.h"
 #include "uassert.h"
 #include "unicode/unistr.h"
 #include "unicode/decimfmt.h"
 #include "cstring.h"
-#include "plurrule_impl.h"
 #include "unicode/plurrule.h"
 #include "charstr.h"
 #include "unicode/fmtable.h"
 #include "unicode/fieldpos.h"
-
-#if !UCONFIG_NO_FORMATTING
+#include "standardplural.h"
+#include "uassert.h"
+#include "number_decimalquantity.h"
+#include "number_utypes.h"
+#include "formatted_string_builder.h"
 
 U_NAMESPACE_BEGIN
 
-// other must always be first.
-static const char * const gPluralForms[] = {
-        "other", "zero", "one", "two", "few", "many"};
-
-static int32_t getPluralIndex(const char *pluralForm) {
-    int32_t len = UPRV_LENGTHOF(gPluralForms);
-    for (int32_t i = 0; i < len; ++i) {
-        if (uprv_strcmp(pluralForm, gPluralForms[i]) == 0) {
-            return i;
-        }
-    }
-    return -1;
-}
-
 QuantityFormatter::QuantityFormatter() {
     for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
         formatters[i] = NULL;
@@ -46,7 +41,7 @@ QuantityFormatter::QuantityFormatter(const QuantityFormatter &other) {
         if (other.formatters[i] == NULL) {
             formatters[i] = NULL;
         } else {
-            formatters[i] = new SimplePatternFormatter(*other.formatters[i]);
+            formatters[i] = new SimpleFormatter(*other.formatters[i]);
         }
     }
 }
@@ -61,7 +56,7 @@ QuantityFormatter &QuantityFormatter::operator=(
         if (other.formatters[i] == NULL) {
             formatters[i] = NULL;
         } else {
-            formatters[i] = new SimplePatternFormatter(*other.formatters[i]);
+            formatters[i] = new SimpleFormatter(*other.formatters[i]);
         }
     }
     return *this;
@@ -80,107 +75,163 @@ void QuantityFormatter::reset() {
     }
 }
 
-UBool QuantityFormatter::add(
+UBool QuantityFormatter::addIfAbsent(
         const char *variant,
         const UnicodeString &rawPattern,
         UErrorCode &status) {
+    int32_t pluralIndex = StandardPlural::indexFromString(variant, status);
     if (U_FAILURE(status)) {
         return FALSE;
     }
-    int32_t pluralIndex = getPluralIndex(variant);
-    if (pluralIndex == -1) {
-        status = U_ILLEGAL_ARGUMENT_ERROR;
-        return FALSE;
+    if (formatters[pluralIndex] != NULL) {
+        return TRUE;
     }
-    SimplePatternFormatter *newFmt =
-            new SimplePatternFormatter(rawPattern);
+    SimpleFormatter *newFmt = new SimpleFormatter(rawPattern, 0, 1, status);
     if (newFmt == NULL) {
         status = U_MEMORY_ALLOCATION_ERROR;
         return FALSE;
     }
-    if (newFmt->getPlaceholderCount() > 1) {
+    if (U_FAILURE(status)) {
         delete newFmt;
-        status = U_ILLEGAL_ARGUMENT_ERROR;
         return FALSE;
     }
-    delete formatters[pluralIndex];
     formatters[pluralIndex] = newFmt;
     return TRUE;
 }
 
 UBool QuantityFormatter::isValid() const {
-    return formatters[0] != NULL;
+    return formatters[StandardPlural::OTHER] != NULL;
 }
 
-const SimplePatternFormatter *QuantityFormatter::getByVariant(
+const SimpleFormatter *QuantityFormatter::getByVariant(
         const char *variant) const {
-    int32_t pluralIndex = getPluralIndex(variant);
-    if (pluralIndex == -1) {
-        pluralIndex = 0;
-    }
-    const SimplePatternFormatter *pattern = formatters[pluralIndex];
+    U_ASSERT(isValid());
+    int32_t pluralIndex = StandardPlural::indexOrOtherIndexFromString(variant);
+    const SimpleFormatter *pattern = formatters[pluralIndex];
     if (pattern == NULL) {
-        pattern = formatters[0];
+        pattern = formatters[StandardPlural::OTHER];
     }
     return pattern;
 }
 
 UnicodeString &QuantityFormatter::format(
-            const Formattable& quantity,
+            const Formattable &number,
             const NumberFormat &fmt,
             const PluralRules &rules,
             UnicodeString &appendTo,
             FieldPosition &pos,
             UErrorCode &status) const {
+    UnicodeString formattedNumber;
+    StandardPlural::Form p = selectPlural(number, fmt, rules, formattedNumber, pos, status);
     if (U_FAILURE(status)) {
         return appendTo;
     }
-    UnicodeString count;
+    const SimpleFormatter *pattern = formatters[p];
+    if (pattern == NULL) {
+        pattern = formatters[StandardPlural::OTHER];
+        if (pattern == NULL) {
+            status = U_INVALID_STATE_ERROR;
+            return appendTo;
+        }
+    }
+    return format(*pattern, formattedNumber, appendTo, pos, status);
+}
+
+// The following methods live here so that class PluralRules does not depend on number formatting,
+// and the SimpleFormatter does not depend on FieldPosition.
+
+StandardPlural::Form QuantityFormatter::selectPlural(
+            const Formattable &number,
+            const NumberFormat &fmt,
+            const PluralRules &rules,
+            UnicodeString &formattedNumber,
+            FieldPosition &pos,
+            UErrorCode &status) {
+    if (U_FAILURE(status)) {
+        return StandardPlural::OTHER;
+    }
+    UnicodeString pluralKeyword;
     const DecimalFormat *decFmt = dynamic_cast<const DecimalFormat *>(&fmt);
     if (decFmt != NULL) {
-        FixedDecimal fd = decFmt->getFixedDecimal(quantity, status);
+        number::impl::DecimalQuantity dq;
+        decFmt->formatToDecimalQuantity(number, dq, status);
         if (U_FAILURE(status)) {
-            return appendTo;
+            return StandardPlural::OTHER;
         }
-        count = rules.select(fd);
+        pluralKeyword = rules.select(dq);
+        decFmt->format(number, formattedNumber, pos, status);
     } else {
-        if (quantity.getType() == Formattable::kDouble) {
-            count = rules.select(quantity.getDouble());
-        } else if (quantity.getType() == Formattable::kLong) {
-            count = rules.select(quantity.getLong());
-        } else if (quantity.getType() == Formattable::kInt64) {
-            count = rules.select((double) quantity.getInt64());
+        if (number.getType() == Formattable::kDouble) {
+            pluralKeyword = rules.select(number.getDouble());
+        } else if (number.getType() == Formattable::kLong) {
+            pluralKeyword = rules.select(number.getLong());
+        } else if (number.getType() == Formattable::kInt64) {
+            pluralKeyword = rules.select((double) number.getInt64());
         } else {
             status = U_ILLEGAL_ARGUMENT_ERROR;
-            return appendTo;
+            return StandardPlural::OTHER;
         }
+        fmt.format(number, formattedNumber, pos, status);
     }
-    CharString buffer;
-    buffer.appendInvariantChars(count, status);
-    if (U_FAILURE(status)) {
-        return appendTo;
+    return StandardPlural::orOtherFromString(pluralKeyword);
+}
+
+void QuantityFormatter::formatAndSelect(
+        double quantity,
+        const NumberFormat& fmt,
+        const PluralRules& rules,
+        FormattedStringBuilder& output,
+        StandardPlural::Form& pluralForm,
+        UErrorCode& status) {
+    UnicodeString pluralKeyword;
+    const DecimalFormat* df = dynamic_cast<const DecimalFormat*>(&fmt);
+    if (df != nullptr) {
+        number::impl::UFormattedNumberData fn;
+        fn.quantity.setToDouble(quantity);
+        const number::LocalizedNumberFormatter* lnf = df->toNumberFormatter(status);
+        if (U_FAILURE(status)) {
+            return;
+        }
+        lnf->formatImpl(&fn, status);
+        if (U_FAILURE(status)) {
+            return;
+        }
+        output = std::move(fn.getStringRef());
+        pluralKeyword = rules.select(fn.quantity);
+    } else {
+        UnicodeString result;
+        fmt.format(quantity, result, status);
+        if (U_FAILURE(status)) {
+            return;
+        }
+        output.append(result, UNUM_FIELD_COUNT, status);
+        if (U_FAILURE(status)) {
+            return;
+        }
+        pluralKeyword = rules.select(quantity);
     }
-    const SimplePatternFormatter *pattern = getByVariant(buffer.data());
-    if (pattern == NULL) {
-        status = U_INVALID_STATE_ERROR;
+    pluralForm = StandardPlural::orOtherFromString(pluralKeyword);
+}
+
+UnicodeString &QuantityFormatter::format(
+            const SimpleFormatter &pattern,
+            const UnicodeString &value,
+            UnicodeString &appendTo,
+            FieldPosition &pos,
+            UErrorCode &status) {
+    if (U_FAILURE(status)) {
         return appendTo;
     }
-    UnicodeString formattedNumber;
-    FieldPosition fpos(pos.getField());
-    fmt.format(quantity, formattedNumber, fpos, status);
-    const UnicodeString *params[1] = {&formattedNumber};
-    int32_t offsets[1];
-    pattern->formatAndAppend(
-            params,
-            UPRV_LENGTHOF(params),
-            appendTo,
-            offsets,
-            UPRV_LENGTHOF(offsets),
-            status);
-    if (offsets[0] != -1) {
-        if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
-            pos.setBeginIndex(fpos.getBeginIndex() + offsets[0]);
-            pos.setEndIndex(fpos.getEndIndex() + offsets[0]);
+    const UnicodeString *param = &value;
+    int32_t offset;
+    pattern.formatAndAppend(&param, 1, appendTo, &offset, 1, status);
+    if (pos.getBeginIndex() != 0 || pos.getEndIndex() != 0) {
+        if (offset >= 0) {
+            pos.setBeginIndex(pos.getBeginIndex() + offset);
+            pos.setEndIndex(pos.getEndIndex() + offset);
+        } else {
+            pos.setBeginIndex(0);
+            pos.setEndIndex(0);
         }
     }
     return appendTo;