X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/b331163bffd790ced0e88b73f44f86d49ccc48a5..HEAD:/icuSources/i18n/quantityformatter.cpp?ds=sidebyside diff --git a/icuSources/i18n/quantityformatter.cpp b/icuSources/i18n/quantityformatter.cpp index d0dc378b..e88b70fb 100644 --- a/icuSources/i18n/quantityformatter.cpp +++ b/icuSources/i18n/quantityformatter.cpp @@ -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(&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(&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(¶m, 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;