+// © 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;
if (other.formatters[i] == NULL) {
formatters[i] = NULL;
} else {
- formatters[i] = new SimplePatternFormatter(*other.formatters[i]);
+ formatters[i] = new SimpleFormatter(*other.formatters[i]);
}
}
}
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;
}
}
-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(¶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;