summaryrefslogtreecommitdiff
path: root/deps/icu-small/source/i18n/number_formatimpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'deps/icu-small/source/i18n/number_formatimpl.cpp')
-rw-r--r--deps/icu-small/source/i18n/number_formatimpl.cpp262
1 files changed, 123 insertions, 139 deletions
diff --git a/deps/icu-small/source/i18n/number_formatimpl.cpp b/deps/icu-small/source/i18n/number_formatimpl.cpp
index bc96cb15da..3f887128bc 100644
--- a/deps/icu-small/source/i18n/number_formatimpl.cpp
+++ b/deps/icu-small/source/i18n/number_formatimpl.cpp
@@ -3,7 +3,7 @@
#include "unicode/utypes.h"
-#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
+#if !UCONFIG_NO_FORMATTING
#include "cstring.h"
#include "unicode/ures.h"
@@ -26,88 +26,28 @@ using namespace icu::number::impl;
namespace {
-// NOTE: In Java, the method to get a pattern from the resource bundle exists in NumberFormat.
-// In C++, we have to implement that logic here.
-// TODO: Make Java and C++ consistent?
-
-enum CldrPatternStyle {
- CLDR_PATTERN_STYLE_DECIMAL,
- CLDR_PATTERN_STYLE_CURRENCY,
- CLDR_PATTERN_STYLE_ACCOUNTING,
- CLDR_PATTERN_STYLE_PERCENT
- // TODO: Consider scientific format.
-};
-
-const char16_t *
-doGetPattern(UResourceBundle *res, const char *nsName, const char *patternKey, UErrorCode &publicStatus,
- UErrorCode &localStatus) {
- // Construct the path into the resource bundle
- CharString key;
- key.append("NumberElements/", publicStatus);
- key.append(nsName, publicStatus);
- key.append("/patterns/", publicStatus);
- key.append(patternKey, publicStatus);
- if (U_FAILURE(publicStatus)) {
- return u"";
- }
- return ures_getStringByKeyWithFallback(res, key.data(), nullptr, &localStatus);
-}
-
-const char16_t *getPatternForStyle(const Locale &locale, const char *nsName, CldrPatternStyle style,
- UErrorCode &status) {
- const char *patternKey;
- switch (style) {
- case CLDR_PATTERN_STYLE_DECIMAL:
- patternKey = "decimalFormat";
- break;
- case CLDR_PATTERN_STYLE_CURRENCY:
- patternKey = "currencyFormat";
- break;
- case CLDR_PATTERN_STYLE_ACCOUNTING:
- patternKey = "accountingFormat";
- break;
- case CLDR_PATTERN_STYLE_PERCENT:
- default:
- patternKey = "percentFormat";
- break;
- }
- LocalUResourceBundlePointer res(ures_open(nullptr, locale.getName(), &status));
- if (U_FAILURE(status)) { return u""; }
-
- // Attempt to get the pattern with the native numbering system.
- UErrorCode localStatus = U_ZERO_ERROR;
- const char16_t *pattern;
- pattern = doGetPattern(res.getAlias(), nsName, patternKey, status, localStatus);
- if (U_FAILURE(status)) { return u""; }
-
- // Fall back to latn if native numbering system does not have the right pattern
- if (U_FAILURE(localStatus) && uprv_strcmp("latn", nsName) != 0) {
- localStatus = U_ZERO_ERROR;
- pattern = doGetPattern(res.getAlias(), "latn", patternKey, status, localStatus);
- if (U_FAILURE(status)) { return u""; }
- }
-
- return pattern;
-}
-
struct CurrencyFormatInfoResult {
bool exists;
const char16_t* pattern;
const char16_t* decimalSeparator;
const char16_t* groupingSeparator;
};
-CurrencyFormatInfoResult getCurrencyFormatInfo(const Locale& locale, const char* isoCode, UErrorCode& status) {
+
+CurrencyFormatInfoResult
+getCurrencyFormatInfo(const Locale& locale, const char* isoCode, UErrorCode& status) {
// TODO: Load this data in a centralized location like ICU4J?
+ // TODO: Move this into the CurrencySymbols class?
// TODO: Parts of this same data are loaded in dcfmtsym.cpp; should clean up.
- CurrencyFormatInfoResult result = { false, nullptr, nullptr, nullptr };
- if (U_FAILURE(status)) return result;
+ CurrencyFormatInfoResult result = {false, nullptr, nullptr, nullptr};
+ if (U_FAILURE(status)) { return result; }
CharString key;
key.append("Currencies/", status);
key.append(isoCode, status);
UErrorCode localStatus = status;
LocalUResourceBundlePointer bundle(ures_open(U_ICUDATA_CURR, locale.getName(), &localStatus));
ures_getByKeyWithFallback(bundle.getAlias(), key.data(), bundle.getAlias(), &localStatus);
- if (U_SUCCESS(localStatus) && ures_getSize(bundle.getAlias())>2) { // the length is 3 if more data is present
+ if (U_SUCCESS(localStatus) &&
+ ures_getSize(bundle.getAlias()) > 2) { // the length is 3 if more data is present
ures_getByIndex(bundle.getAlias(), 2, bundle.getAlias(), &localStatus);
int32_t dummy;
result.exists = true;
@@ -121,65 +61,84 @@ CurrencyFormatInfoResult getCurrencyFormatInfo(const Locale& locale, const char*
return result;
}
-inline bool unitIsCurrency(const MeasureUnit &unit) {
- return uprv_strcmp("currency", unit.getType()) == 0;
-}
-
-inline bool unitIsNoUnit(const MeasureUnit &unit) {
- return uprv_strcmp("none", unit.getType()) == 0;
-}
+} // namespace
-inline bool unitIsPercent(const MeasureUnit &unit) {
- return uprv_strcmp("percent", unit.getSubtype()) == 0;
-}
-inline bool unitIsPermille(const MeasureUnit &unit) {
- return uprv_strcmp("permille", unit.getSubtype()) == 0;
-}
+MicroPropsGenerator::~MicroPropsGenerator() = default;
-} // namespace
-NumberFormatterImpl *NumberFormatterImpl::fromMacros(const MacroProps &macros, UErrorCode &status) {
+NumberFormatterImpl* NumberFormatterImpl::fromMacros(const MacroProps& macros, UErrorCode& status) {
return new NumberFormatterImpl(macros, true, status);
}
-void NumberFormatterImpl::applyStatic(const MacroProps &macros, DecimalQuantity &inValue,
- NumberStringBuilder &outString, UErrorCode &status) {
+void NumberFormatterImpl::applyStatic(const MacroProps& macros, DecimalQuantity& inValue,
+ NumberStringBuilder& outString, UErrorCode& status) {
NumberFormatterImpl impl(macros, false, status);
impl.applyUnsafe(inValue, outString, status);
}
+int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, int8_t signum,
+ StandardPlural::Form plural,
+ NumberStringBuilder& outString, UErrorCode& status) {
+ NumberFormatterImpl impl(macros, false, status);
+ return impl.getPrefixSuffixUnsafe(signum, plural, outString, status);
+}
+
// NOTE: C++ SPECIFIC DIFFERENCE FROM JAVA:
// The "safe" apply method uses a new MicroProps. In the MicroPropsGenerator, fMicros is copied into the new instance.
// The "unsafe" method simply re-uses fMicros, eliminating the extra copy operation.
// See MicroProps::processQuantity() for details.
-void NumberFormatterImpl::apply(DecimalQuantity &inValue, NumberStringBuilder &outString,
- UErrorCode &status) const {
+void NumberFormatterImpl::apply(DecimalQuantity& inValue, NumberStringBuilder& outString,
+ UErrorCode& status) const {
if (U_FAILURE(status)) { return; }
MicroProps micros;
+ if (!fMicroPropsGenerator) { return; }
fMicroPropsGenerator->processQuantity(inValue, micros, status);
if (U_FAILURE(status)) { return; }
microsToString(micros, inValue, outString, status);
}
-void NumberFormatterImpl::applyUnsafe(DecimalQuantity &inValue, NumberStringBuilder &outString,
- UErrorCode &status) {
+void NumberFormatterImpl::applyUnsafe(DecimalQuantity& inValue, NumberStringBuilder& outString,
+ UErrorCode& status) {
if (U_FAILURE(status)) { return; }
fMicroPropsGenerator->processQuantity(inValue, fMicros, status);
if (U_FAILURE(status)) { return; }
microsToString(fMicros, inValue, outString, status);
}
-NumberFormatterImpl::NumberFormatterImpl(const MacroProps &macros, bool safe, UErrorCode &status) {
+int32_t NumberFormatterImpl::getPrefixSuffix(int8_t signum, StandardPlural::Form plural,
+ NumberStringBuilder& outString, UErrorCode& status) const {
+ if (U_FAILURE(status)) { return 0; }
+ // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier).
+ // Safe path: use fImmutablePatternModifier.
+ const Modifier* modifier = fImmutablePatternModifier->getModifier(signum, plural);
+ modifier->apply(outString, 0, 0, status);
+ if (U_FAILURE(status)) { return 0; }
+ return modifier->getPrefixLength(status);
+}
+
+int32_t NumberFormatterImpl::getPrefixSuffixUnsafe(int8_t signum, StandardPlural::Form plural,
+ NumberStringBuilder& outString, UErrorCode& status) {
+ if (U_FAILURE(status)) { return 0; }
+ // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier).
+ // Unsafe path: use fPatternModifier.
+ fPatternModifier->setNumberProperties(signum, plural);
+ fPatternModifier->apply(outString, 0, 0, status);
+ if (U_FAILURE(status)) { return 0; }
+ return fPatternModifier->getPrefixLength(status);
+}
+
+NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, bool safe, UErrorCode& status) {
fMicroPropsGenerator = macrosToMicroGenerator(macros, safe, status);
}
//////////
-const MicroPropsGenerator *
-NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe, UErrorCode &status) {
- const MicroPropsGenerator *chain = &fMicros;
+const MicroPropsGenerator*
+NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe, UErrorCode& status) {
+ if (U_FAILURE(status)) { return nullptr; }
+ const MicroPropsGenerator* chain = &fMicros;
// Check that macros is error-free before continuing.
if (macros.copyErrorTo(status)) {
@@ -189,18 +148,26 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe,
// TODO: Accept currency symbols from DecimalFormatSymbols?
// Pre-compute a few values for efficiency.
- bool isCurrency = unitIsCurrency(macros.unit);
- bool isNoUnit = unitIsNoUnit(macros.unit);
- bool isPercent = isNoUnit && unitIsPercent(macros.unit);
- bool isPermille = isNoUnit && unitIsPermille(macros.unit);
+ bool isCurrency = utils::unitIsCurrency(macros.unit);
+ bool isNoUnit = utils::unitIsNoUnit(macros.unit);
+ bool isPercent = isNoUnit && utils::unitIsPercent(macros.unit);
+ bool isPermille = isNoUnit && utils::unitIsPermille(macros.unit);
bool isCldrUnit = !isCurrency && !isNoUnit;
- bool isAccounting = macros.sign == UNUM_SIGN_ACCOUNTING
- || macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS
- || macros.sign == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO;
- CurrencyUnit currency(kDefaultCurrency, status);
+ bool isAccounting =
+ macros.sign == UNUM_SIGN_ACCOUNTING || macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS ||
+ macros.sign == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO;
+ CurrencyUnit currency(nullptr, status);
if (isCurrency) {
currency = CurrencyUnit(macros.unit, status); // Restore CurrencyUnit from MeasureUnit
}
+ const CurrencySymbols* currencySymbols;
+ if (macros.currencySymbols != nullptr) {
+ // Used by the DecimalFormat code path
+ currencySymbols = macros.currencySymbols;
+ } else {
+ fWarehouse.fCurrencySymbols = {currency, macros.locale, status};
+ currencySymbols = &fWarehouse.fCurrencySymbols;
+ }
UNumberUnitWidth unitWidth = UNUM_UNIT_WIDTH_SHORT;
if (macros.unitWidth != UNUM_UNIT_WIDTH_COUNT) {
unitWidth = macros.unitWidth;
@@ -208,7 +175,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe,
// Select the numbering system.
LocalPointer<const NumberingSystem> nsLocal;
- const NumberingSystem *ns;
+ const NumberingSystem* ns;
if (macros.symbols.isNumberingSystem()) {
ns = macros.symbols.getNumberingSystem();
} else {
@@ -217,7 +184,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe,
// Give ownership to the function scope.
nsLocal.adoptInstead(ns);
}
- const char *nsName = U_SUCCESS(status) ? ns->getName() : "latn";
+ const char* nsName = U_SUCCESS(status) ? ns->getName() : "latn";
// Resolve the symbols. Do this here because currency may need to customize them.
if (macros.symbols.isDecimalFormatSymbols()) {
@@ -232,21 +199,22 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe,
// If we are formatting currency, check for a currency-specific pattern.
const char16_t* pattern = nullptr;
if (isCurrency) {
- CurrencyFormatInfoResult info = getCurrencyFormatInfo(macros.locale, currency.getSubtype(), status);
+ CurrencyFormatInfoResult info = getCurrencyFormatInfo(
+ macros.locale, currency.getSubtype(), status);
if (info.exists) {
pattern = info.pattern;
// It's clunky to clone an object here, but this code is not frequently executed.
- DecimalFormatSymbols* symbols = new DecimalFormatSymbols(*fMicros.symbols);
+ auto* symbols = new DecimalFormatSymbols(*fMicros.symbols);
fMicros.symbols = symbols;
fSymbols.adoptInstead(symbols);
symbols->setSymbol(
- DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol,
- UnicodeString(info.decimalSeparator),
- FALSE);
+ DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol,
+ UnicodeString(info.decimalSeparator),
+ FALSE);
symbols->setSymbol(
- DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol,
- UnicodeString(info.groupingSeparator),
- FALSE);
+ DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol,
+ UnicodeString(info.groupingSeparator),
+ FALSE);
}
}
if (pattern == nullptr) {
@@ -262,7 +230,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe,
} else {
patternStyle = CLDR_PATTERN_STYLE_CURRENCY;
}
- pattern = getPatternForStyle(macros.locale, nsName, patternStyle, status);
+ pattern = utils::getPatternForStyle(macros.locale, nsName, patternStyle, status);
}
auto patternInfo = new ParsedPatternInfo();
fPatternInfo.adoptInstead(patternInfo);
@@ -272,17 +240,31 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe,
/// START POPULATING THE DEFAULT MICROPROPS AND BUILDING THE MICROPROPS GENERATOR ///
/////////////////////////////////////////////////////////////////////////////////////
+ // Multiplier
+ if (macros.scale.isValid()) {
+ fMicros.helpers.multiplier.setAndChain(macros.scale, chain);
+ chain = &fMicros.helpers.multiplier;
+ }
+
// Rounding strategy
- if (!macros.rounder.isBogus()) {
- fMicros.rounding = macros.rounder;
+ Precision precision;
+ if (!macros.precision.isBogus()) {
+ precision = macros.precision;
} else if (macros.notation.fType == Notation::NTN_COMPACT) {
- fMicros.rounding = Rounder::integer().withMinDigits(2);
+ precision = Precision::integer().withMinDigits(2);
} else if (isCurrency) {
- fMicros.rounding = Rounder::currency(UCURR_USAGE_STANDARD);
+ precision = Precision::currency(UCURR_USAGE_STANDARD);
+ } else {
+ precision = Precision::maxFraction(6);
+ }
+ UNumberFormatRoundingMode roundingMode;
+ if (macros.roundingMode != kDefaultMode) {
+ roundingMode = macros.roundingMode;
} else {
- fMicros.rounding = Rounder::maxFraction(6);
+ // Temporary until ICU 64
+ roundingMode = precision.fRoundingMode;
}
- fMicros.rounding.setLocaleData(currency, status);
+ fMicros.rounder = {precision, roundingMode, currency, status};
// Grouping strategy
if (!macros.grouper.isBogus()) {
@@ -306,7 +288,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe,
if (!macros.integerWidth.isBogus()) {
fMicros.integerWidth = macros.integerWidth;
} else {
- fMicros.integerWidth = IntegerWidth::zeroFillTo(1);
+ fMicros.integerWidth = IntegerWidth::standard();
}
// Sign display
@@ -338,16 +320,18 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe,
// Middle modifier (patterns, positive/negative, currency symbols, percent)
auto patternModifier = new MutablePatternModifier(false);
fPatternModifier.adoptInstead(patternModifier);
- patternModifier->setPatternInfo(fPatternInfo.getAlias());
+ patternModifier->setPatternInfo(
+ macros.affixProvider != nullptr ? macros.affixProvider
+ : static_cast<const AffixPatternProvider*>(fPatternInfo.getAlias()));
patternModifier->setPatternAttributes(fMicros.sign, isPermille);
if (patternModifier->needsPlurals()) {
patternModifier->setSymbols(
fMicros.symbols,
- currency,
+ currencySymbols,
unitWidth,
resolvePluralRules(macros.rules, macros.locale, status));
} else {
- patternModifier->setSymbols(fMicros.symbols, currency, unitWidth, nullptr);
+ patternModifier->setSymbols(fMicros.symbols, currencySymbols, unitWidth, nullptr);
}
if (safe) {
fImmutablePatternModifier.adoptInstead(patternModifier->createImmutableAndChain(chain, status));
@@ -407,9 +391,9 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe,
return chain;
}
-const PluralRules *
-NumberFormatterImpl::resolvePluralRules(const PluralRules *rulesPtr, const Locale &locale,
- UErrorCode &status) {
+const PluralRules*
+NumberFormatterImpl::resolvePluralRules(const PluralRules* rulesPtr, const Locale& locale,
+ UErrorCode& status) {
if (rulesPtr != nullptr) {
return rulesPtr;
}
@@ -420,9 +404,9 @@ NumberFormatterImpl::resolvePluralRules(const PluralRules *rulesPtr, const Local
return fRules.getAlias();
}
-int32_t NumberFormatterImpl::microsToString(const MicroProps &micros, DecimalQuantity &quantity,
- NumberStringBuilder &string, UErrorCode &status) {
- micros.rounding.apply(quantity, status);
+int32_t NumberFormatterImpl::microsToString(const MicroProps& micros, DecimalQuantity& quantity,
+ NumberStringBuilder& string, UErrorCode& status) {
+ micros.rounder.apply(quantity, status);
micros.integerWidth.apply(quantity, status);
int32_t length = writeNumber(micros, quantity, string, status);
// NOTE: When range formatting is added, these modifiers can bubble up.
@@ -439,8 +423,8 @@ int32_t NumberFormatterImpl::microsToString(const MicroProps &micros, DecimalQua
return length;
}
-int32_t NumberFormatterImpl::writeNumber(const MicroProps &micros, DecimalQuantity &quantity,
- NumberStringBuilder &string, UErrorCode &status) {
+int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuantity& quantity,
+ NumberStringBuilder& string, UErrorCode& status) {
int32_t length = 0;
if (quantity.isInfinite()) {
length += string.insert(
@@ -480,8 +464,8 @@ int32_t NumberFormatterImpl::writeNumber(const MicroProps &micros, DecimalQuanti
return length;
}
-int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps &micros, DecimalQuantity &quantity,
- NumberStringBuilder &string, UErrorCode &status) {
+int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, DecimalQuantity& quantity,
+ NumberStringBuilder& string, UErrorCode& status) {
int length = 0;
int integerCount = quantity.getUpperDisplayMagnitude() + 1;
for (int i = 0; i < integerCount; i++) {
@@ -499,21 +483,21 @@ int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps &micros, Decima
// Get and append the next digit value
int8_t nextDigit = quantity.getDigit(i);
- length += string.insert(
- 0, getDigitFromSymbols(nextDigit, *micros.symbols), UNUM_INTEGER_FIELD, status);
+ length += utils::insertDigitFromSymbols(
+ string, 0, nextDigit, *micros.symbols, UNUM_INTEGER_FIELD, status);
}
return length;
}
-int32_t NumberFormatterImpl::writeFractionDigits(const MicroProps &micros, DecimalQuantity &quantity,
- NumberStringBuilder &string, UErrorCode &status) {
+int32_t NumberFormatterImpl::writeFractionDigits(const MicroProps& micros, DecimalQuantity& quantity,
+ NumberStringBuilder& string, UErrorCode& status) {
int length = 0;
int fractionCount = -quantity.getLowerDisplayMagnitude();
for (int i = 0; i < fractionCount; i++) {
// Get and append the next digit value
int8_t nextDigit = quantity.getDigit(-i - 1);
- length += string.append(
- getDigitFromSymbols(nextDigit, *micros.symbols), UNUM_FRACTION_FIELD, status);
+ length += utils::insertDigitFromSymbols(
+ string, string.length(), nextDigit, *micros.symbols, UNUM_FRACTION_FIELD, status);
}
return length;
}