ICU-13634 Centralizing data loading for pattern strings.

X-SVN-Rev: 41230
This commit is contained in:
Shane Carr 2018-04-14 08:10:45 +00:00
parent cbae6dfbaa
commit e0df2775ea
7 changed files with 139 additions and 121 deletions

View File

@ -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<NumberingSystem> 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)

View File

@ -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;

View File

@ -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<int8_t>(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;
}

View File

@ -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 {

View File

@ -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);

View File

@ -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 {

View File

@ -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;