From e0df2775eac129671423a5417d9932b938246748 Mon Sep 17 00:00:00 2001 From: Shane Carr Date: Sat, 14 Apr 2018 08:10:45 +0000 Subject: [PATCH] ICU-13634 Centralizing data loading for pattern strings. X-SVN-Rev: 41230 --- icu4c/source/i18n/decimfmt.cpp | 10 ++++ icu4c/source/i18n/number_formatimpl.cpp | 78 +++---------------------- icu4c/source/i18n/number_scientific.cpp | 8 ++- icu4c/source/i18n/number_skeletons.cpp | 14 ++--- icu4c/source/i18n/number_utils.cpp | 64 ++++++++++++++++++++ icu4c/source/i18n/number_utils.h | 18 ++++++ icu4c/source/i18n/numfmt.cpp | 68 +++++++++------------ 7 files changed, 139 insertions(+), 121 deletions(-) diff --git a/icu4c/source/i18n/decimfmt.cpp b/icu4c/source/i18n/decimfmt.cpp index 46974b36651..8ca75eb10a9 100644 --- a/icu4c/source/i18n/decimfmt.cpp +++ b/icu4c/source/i18n/decimfmt.cpp @@ -34,6 +34,16 @@ UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormat) DecimalFormat::DecimalFormat(UErrorCode& status) : DecimalFormat(nullptr, status) { + // Use the default locale and decimal pattern. + const char* localeName = Locale::getDefault().getName(); + LocalPointer ns(NumberingSystem::createInstance(status)); + UnicodeString patternString = utils::getPatternForStyle( + localeName, + ns->getName(), + CLDR_PATTERN_STYLE_DECIMAL, + status); + setPropertiesFromPattern(patternString, IGNORE_ROUNDING_IF_CURRENCY, status); + refreshFormatter(status); } DecimalFormat::DecimalFormat(const UnicodeString& pattern, UErrorCode& status) diff --git a/icu4c/source/i18n/number_formatimpl.cpp b/icu4c/source/i18n/number_formatimpl.cpp index c0dee2e6153..f198187921c 100644 --- a/icu4c/source/i18n/number_formatimpl.cpp +++ b/icu4c/source/i18n/number_formatimpl.cpp @@ -26,70 +26,6 @@ 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; @@ -195,10 +131,10 @@ 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 || @@ -277,7 +213,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); @@ -522,7 +458,7 @@ int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, Decima // Get and append the next digit value int8_t nextDigit = quantity.getDigit(i); - length += insertDigitFromSymbols( + length += utils::insertDigitFromSymbols( string, 0, nextDigit, *micros.symbols, UNUM_INTEGER_FIELD, status); } return length; @@ -535,7 +471,7 @@ int32_t NumberFormatterImpl::writeFractionDigits(const MicroProps& micros, Decim for (int i = 0; i < fractionCount; i++) { // Get and append the next digit value int8_t nextDigit = quantity.getDigit(-i - 1); - length += insertDigitFromSymbols( + length += utils::insertDigitFromSymbols( string, string.length(), nextDigit, *micros.symbols, UNUM_FRACTION_FIELD, status); } return length; diff --git a/icu4c/source/i18n/number_scientific.cpp b/icu4c/source/i18n/number_scientific.cpp index 1d8b020e88e..ce7bb3b6893 100644 --- a/icu4c/source/i18n/number_scientific.cpp +++ b/icu4c/source/i18n/number_scientific.cpp @@ -64,7 +64,13 @@ int32_t ScientificModifier::apply(NumberStringBuilder &output, int32_t /*leftInd int32_t disp = std::abs(fExponent); for (int j = 0; j < fHandler->fSettings.fMinExponentDigits || disp > 0; j++, disp /= 10) { auto d = static_cast(disp % 10); - i += insertDigitFromSymbols(output, i - j, d, *fHandler->fSymbols, UNUM_EXPONENT_FIELD, status); + i += utils::insertDigitFromSymbols( + output, + i - j, + d, + *fHandler->fSymbols, + UNUM_EXPONENT_FIELD, + status); } return i - rightIndex; } diff --git a/icu4c/source/i18n/number_skeletons.cpp b/icu4c/source/i18n/number_skeletons.cpp index 81be03b15a4..35de1358cd0 100644 --- a/icu4c/source/i18n/number_skeletons.cpp +++ b/icu4c/source/i18n/number_skeletons.cpp @@ -1256,15 +1256,15 @@ bool GeneratorHelpers::notation(const MacroProps& macros, UnicodeString& sb, UEr } bool GeneratorHelpers::unit(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { - if (unitIsCurrency(macros.unit)) { + if (utils::unitIsCurrency(macros.unit)) { sb.append(u"currency/", -1); blueprint_helpers::generateCurrencyOption({macros.unit, status}, sb, status); return true; - } else if (unitIsNoUnit(macros.unit)) { - if (unitIsPercent(macros.unit)) { + } else if (utils::unitIsNoUnit(macros.unit)) { + if (utils::unitIsPercent(macros.unit)) { sb.append(u"percent", -1); return true; - } else if (unitIsPermille(macros.unit)) { + } else if (utils::unitIsPermille(macros.unit)) { sb.append(u"permille", -1); return true; } else { @@ -1280,15 +1280,15 @@ bool GeneratorHelpers::unit(const MacroProps& macros, UnicodeString& sb, UErrorC bool GeneratorHelpers::perUnit(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { // Per-units are currently expected to be only MeasureUnits. - if (unitIsNoUnit(macros.perUnit)) { - if (unitIsPercent(macros.perUnit) || unitIsPermille(macros.perUnit)) { + if (utils::unitIsNoUnit(macros.perUnit)) { + if (utils::unitIsPercent(macros.perUnit) || utils::unitIsPermille(macros.perUnit)) { status = U_UNSUPPORTED_ERROR; return false; } else { // Default value: ok to ignore return false; } - } else if (unitIsCurrency(macros.perUnit)) { + } else if (utils::unitIsCurrency(macros.perUnit)) { status = U_UNSUPPORTED_ERROR; return false; } else { diff --git a/icu4c/source/i18n/number_utils.cpp b/icu4c/source/i18n/number_utils.cpp index 8cf702dd336..d98f061aa36 100644 --- a/icu4c/source/i18n/number_utils.cpp +++ b/icu4c/source/i18n/number_utils.cpp @@ -17,6 +17,8 @@ #include "decContext.h" #include "decNumber.h" #include "double-conversion.h" +#include "uresimp.h" +#include "ureslocs.h" using namespace icu; using namespace icu::number; @@ -25,6 +27,68 @@ using namespace icu::number::impl; using icu::double_conversion::DoubleToStringConverter; +namespace { + +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* utils::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: + patternKey = "percentFormat"; + break; + case CLDR_PATTERN_STYLE_SCIENTIFIC: + patternKey = "scientificFormat"; + break; + default: + U_ASSERT(false); + } + 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; +} + + DecNum::DecNum() { uprv_decContextDefault(&fContext, DEC_INIT_BASE); uprv_decContextSetRounding(&fContext, DEC_ROUND_HALF_EVEN); diff --git a/icu4c/source/i18n/number_utils.h b/icu4c/source/i18n/number_utils.h index 1afd608ae92..1b4000d78c1 100644 --- a/icu4c/source/i18n/number_utils.h +++ b/icu4c/source/i18n/number_utils.h @@ -105,6 +105,18 @@ struct MicroProps : public MicroPropsGenerator { bool exhausted = false; }; +enum CldrPatternStyle { + CLDR_PATTERN_STYLE_DECIMAL, + CLDR_PATTERN_STYLE_CURRENCY, + CLDR_PATTERN_STYLE_ACCOUNTING, + CLDR_PATTERN_STYLE_PERCENT, + CLDR_PATTERN_STYLE_SCIENTIFIC, + CLDR_PATTERN_STYLE_COUNT, +}; + +// Namespace for naked functions +namespace utils { + inline int32_t insertDigitFromSymbols(NumberStringBuilder& output, int32_t index, int8_t digit, const DecimalFormatSymbols& symbols, Field field, UErrorCode& status) { @@ -130,6 +142,12 @@ inline bool unitIsPermille(const MeasureUnit& unit) { return uprv_strcmp("permille", unit.getSubtype()) == 0; } +// NOTE: In Java, this method is in NumberFormat.java +const char16_t* +getPatternForStyle(const Locale& locale, const char* nsName, CldrPatternStyle style, UErrorCode& status); + +} // namespace utils + /** A very thin C++ wrapper around decNumber.h */ class DecNum : public UMemory { diff --git a/icu4c/source/i18n/numfmt.cpp b/icu4c/source/i18n/numfmt.cpp index 301b9855b11..4b45ce00a19 100644 --- a/icu4c/source/i18n/numfmt.cpp +++ b/icu4c/source/i18n/numfmt.cpp @@ -55,6 +55,7 @@ #include "sharednumberformat.h" #include "unifiedcache.h" #include "number_decimalquantity.h" +#include "number_utils.h" //#define FMT_DEBUG @@ -129,31 +130,28 @@ static const UChar * const gLastResortNumberPatterns[UNUM_FORMAT_STYLE_COUNT] = // Keys used for accessing resource bundles -static const char *gNumberElements = "NumberElements"; -static const char *gLatn = "latn"; -static const char *gPatterns = "patterns"; -static const char *gFormatKeys[UNUM_FORMAT_STYLE_COUNT] = { - NULL, // UNUM_PATTERN_DECIMAL - "decimalFormat", // UNUM_DECIMAL - "currencyFormat", // UNUM_CURRENCY - "percentFormat", // UNUM_PERCENT - "scientificFormat", // UNUM_SCIENTIFIC - NULL, // UNUM_SPELLOUT - NULL, // UNUM_ORDINAL - NULL, // UNUM_DURATION - NULL, // UNUM_NUMBERING_SYSTEM - NULL, // UNUM_PATTERN_RULEBASED +static const icu::number::impl::CldrPatternStyle gFormatCldrStyles[UNUM_FORMAT_STYLE_COUNT] = { + /* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_PATTERN_DECIMAL + icu::number::impl::CLDR_PATTERN_STYLE_DECIMAL, // UNUM_DECIMAL + icu::number::impl::CLDR_PATTERN_STYLE_CURRENCY, // UNUM_CURRENCY + icu::number::impl::CLDR_PATTERN_STYLE_PERCENT, // UNUM_PERCENT + icu::number::impl::CLDR_PATTERN_STYLE_SCIENTIFIC, // UNUM_SCIENTIFIC + /* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_SPELLOUT + /* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_ORDINAL + /* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_DURATION + /* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_NUMBERING_SYSTEM + /* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_PATTERN_RULEBASED // For UNUM_CURRENCY_ISO and UNUM_CURRENCY_PLURAL, // the pattern is the same as the pattern of UNUM_CURRENCY // except for replacing the single currency sign with // double currency sign or triple currency sign. - "currencyFormat", // UNUM_CURRENCY_ISO - "currencyFormat", // UNUM_CURRENCY_PLURAL - "accountingFormat", // UNUM_CURRENCY_ACCOUNTING - "currencyFormat", // UNUM_CASH_CURRENCY - NULL, // UNUM_DECIMAL_COMPACT_SHORT - NULL, // UNUM_DECIMAL_COMPACT_LONG - "currencyFormat", // UNUM_CURRENCY_STANDARD + icu::number::impl::CLDR_PATTERN_STYLE_CURRENCY, // UNUM_CURRENCY_ISO + icu::number::impl::CLDR_PATTERN_STYLE_CURRENCY, // UNUM_CURRENCY_PLURAL + icu::number::impl::CLDR_PATTERN_STYLE_ACCOUNTING, // UNUM_CURRENCY_ACCOUNTING + icu::number::impl::CLDR_PATTERN_STYLE_CURRENCY, // UNUM_CASH_CURRENCY + /* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_DECIMAL_COMPACT_SHORT + /* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_DECIMAL_COMPACT_LONG + icu::number::impl::CLDR_PATTERN_STYLE_CURRENCY, // UNUM_CURRENCY_STANDARD }; // Static hashtable cache of NumberingSystem objects used by NumberFormat @@ -1400,27 +1398,13 @@ NumberFormat::makeInstance(const Locale& desiredLocale, return NULL; } - UResourceBundle *resource = ownedResource.orphan(); - UResourceBundle *numElements = ures_getByKeyWithFallback(resource, gNumberElements, NULL, &status); - resource = ures_getByKeyWithFallback(numElements, ns->getName(), resource, &status); - resource = ures_getByKeyWithFallback(resource, gPatterns, resource, &status); - ownedResource.adoptInstead(resource); - - int32_t patLen = 0; - const UChar *patResStr = ures_getStringByKeyWithFallback(resource, gFormatKeys[style], &patLen, &status); - - // Didn't find a pattern specific to the numbering system, so fall back to "latn" - if ( status == U_MISSING_RESOURCE_ERROR && uprv_strcmp(gLatn,ns->getName())) { - status = U_ZERO_ERROR; - resource = ures_getByKeyWithFallback(numElements, gLatn, resource, &status); - resource = ures_getByKeyWithFallback(resource, gPatterns, resource, &status); - patResStr = ures_getStringByKeyWithFallback(resource, gFormatKeys[style], &patLen, &status); - } - - ures_close(numElements); - - // Creates the specified decimal format style of the desired locale. - pattern.setTo(TRUE, patResStr, patLen); + // Load the pattern from data using the common library function + const char16_t* patternPtr = number::impl::utils::getPatternForStyle( + desiredLocale, + ns->getName(), + gFormatCldrStyles[style], + status); + pattern = UnicodeString(TRUE, patternPtr, -1); } if (U_FAILURE(status)) { return NULL;