ICU-7392 DecimalFormatSymbols::createWithLastResortData(UErrorCode), and clean up the real initialize() for supplementary digits and memory management

X-SVN-Rev: 34288
This commit is contained in:
Markus Scherer 2013-09-12 20:33:29 +00:00
parent ed9d094e61
commit 308b16079b
4 changed files with 256 additions and 192 deletions

View File

@ -1,7 +1,7 @@
/*
*******************************************************************************
* Copyright (C) 1997-2011, International Business Machines Corporation and *
* others. All Rights Reserved. *
* Copyright (C) 1997-2013, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*
* File DCFMTSYM.CPP
@ -29,6 +29,7 @@
#include "unicode/unistr.h"
#include "unicode/numsys.h"
#include "unicode/unum.h"
#include "unicode/utf16.h"
#include "ucurrimp.h"
#include "cstring.h"
#include "locbased.h"
@ -74,6 +75,24 @@ DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, UErrorCode& status
initialize(locale, status);
}
DecimalFormatSymbols::DecimalFormatSymbols()
: UObject(),
locale(Locale::getRoot()),
currPattern(NULL) {
*validLocale = *actualLocale = 0;
initialize();
}
DecimalFormatSymbols*
DecimalFormatSymbols::createWithLastResortData(UErrorCode& status) {
if (U_FAILURE(status)) { return NULL; }
DecimalFormatSymbols* sym = new DecimalFormatSymbols();
if (sym == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
}
return sym;
}
// -------------------------------------
DecimalFormatSymbols::~DecimalFormatSymbols()
@ -184,8 +203,9 @@ DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool us
return;
const char* locStr = loc.getName();
UResourceBundle *resource = ures_open((char *)0, locStr, &status);
UResourceBundle *numberElementsRes = ures_getByKeyWithFallback(resource, gNumberElements, NULL, &status);
LocalUResourceBundlePointer resource(ures_open(NULL, locStr, &status));
LocalUResourceBundlePointer numberElementsRes(
ures_getByKeyWithFallback(resource.getAlias(), gNumberElements, NULL, &status));
if (U_FAILURE(status)) {
if ( useLastResortData ) {
@ -193,7 +213,7 @@ DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool us
initialize();
}
return;
} else {
}
// First initialize all the symbols to the fallbacks for anything we can't find
initialize();
@ -203,20 +223,18 @@ DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool us
// and the digit string based on the numbering system for the locale
//
NumberingSystem* ns = NumberingSystem::createInstance(loc,status);
LocalPointer<NumberingSystem> ns(NumberingSystem::createInstance(loc, status));
if (U_SUCCESS(status) && ns->getRadix() == 10 && !ns->isAlgorithmic()) {
nsName = ns->getName();
UnicodeString digitString(ns->getDescription());
setSymbol(kZeroDigitSymbol, digitString.tempSubString(0, 1), FALSE);
setSymbol(kOneDigitSymbol, digitString.tempSubString(1, 1), FALSE);
setSymbol(kTwoDigitSymbol, digitString.tempSubString(2, 1), FALSE);
setSymbol(kThreeDigitSymbol, digitString.tempSubString(3, 1), FALSE);
setSymbol(kFourDigitSymbol, digitString.tempSubString(4, 1), FALSE);
setSymbol(kFiveDigitSymbol, digitString.tempSubString(5, 1), FALSE);
setSymbol(kSixDigitSymbol, digitString.tempSubString(6, 1), FALSE);
setSymbol(kSevenDigitSymbol, digitString.tempSubString(7, 1), FALSE);
setSymbol(kEightDigitSymbol, digitString.tempSubString(8, 1), FALSE);
setSymbol(kNineDigitSymbol, digitString.tempSubString(9, 1), FALSE);
int32_t digitIndex = 0;
UChar32 digit = digitString.char32At(0);
fSymbols[kZeroDigitSymbol].setTo(digit);
for (int32_t i = kOneDigitSymbol; i <= kNineDigitSymbol; ++i) {
digitIndex += U16_LENGTH(digit);
digit = digitString.char32At(digitIndex);
fSymbols[i].setTo(digit);
}
} else {
nsName = gLatn;
}
@ -224,14 +242,16 @@ DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool us
UBool isLatn = !uprv_strcmp(nsName,gLatn);
UErrorCode nlStatus = U_ZERO_ERROR;
UResourceBundle *nonLatnSymbols = NULL;
LocalUResourceBundlePointer nonLatnSymbols;
if ( !isLatn ) {
nonLatnSymbols = ures_getByKeyWithFallback(numberElementsRes, nsName, NULL, &nlStatus);
nonLatnSymbols = ures_getByKeyWithFallback(nonLatnSymbols, gSymbols, nonLatnSymbols, &nlStatus);
nonLatnSymbols.adoptInstead(
ures_getByKeyWithFallback(numberElementsRes.getAlias(), nsName, NULL, &nlStatus));
ures_getByKeyWithFallback(nonLatnSymbols.getAlias(), gSymbols, nonLatnSymbols.getAlias(), &nlStatus);
}
UResourceBundle *latnSymbols = ures_getByKeyWithFallback(numberElementsRes, gLatn, NULL, &status);
latnSymbols = ures_getByKeyWithFallback(latnSymbols, gSymbols, latnSymbols, &status);
LocalUResourceBundlePointer latnSymbols(
ures_getByKeyWithFallback(numberElementsRes.getAlias(), gLatn, NULL, &status));
ures_getByKeyWithFallback(latnSymbols.getAlias(), gSymbols, latnSymbols.getAlias(), &status);
UBool kMonetaryDecimalSet = FALSE;
UBool kMonetaryGroupingSet = FALSE;
@ -239,15 +259,18 @@ DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool us
if ( gNumberElementKeys[i] != NULL ) {
UErrorCode localStatus = U_ZERO_ERROR;
if ( !isLatn ) {
sym = ures_getStringByKeyWithFallback(nonLatnSymbols,gNumberElementKeys[i],&len,&localStatus);
sym = ures_getStringByKeyWithFallback(nonLatnSymbols.getAlias(),
gNumberElementKeys[i], &len, &localStatus);
// If we can't find the symbol in the numbering system specific resources,
// use the "latn" numbering system as the fallback.
if ( U_FAILURE(localStatus) ) {
localStatus = U_ZERO_ERROR;
sym = ures_getStringByKeyWithFallback(latnSymbols,gNumberElementKeys[i],&len,&localStatus);
sym = ures_getStringByKeyWithFallback(latnSymbols.getAlias(),
gNumberElementKeys[i], &len, &localStatus);
}
} else {
sym = ures_getStringByKeyWithFallback(latnSymbols,gNumberElementKeys[i],&len,&localStatus);
sym = ures_getStringByKeyWithFallback(latnSymbols.getAlias(),
gNumberElementKeys[i], &len, &localStatus);
}
if ( U_SUCCESS(localStatus) ) {
@ -261,11 +284,6 @@ DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool us
}
}
ures_close(latnSymbols);
if ( !isLatn ) {
ures_close(nonLatnSymbols);
}
// If monetary decimal or grouping were not explicitly set, then set them to be the
// same as their non-monetary counterparts.
@ -276,10 +294,6 @@ DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool us
setSymbol(kMonetaryGroupingSeparatorSymbol,fSymbols[kGroupingSeparatorSymbol]);
}
if (ns) {
delete ns;
}
// Obtain currency data from the currency API. This is strictly
// for backward compatibility; we don't use DecimalFormatSymbols
// for currency data anymore.
@ -288,7 +302,6 @@ DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool us
UnicodeString tempStr;
ucurr_forLocale(locStr, curriso, 4, &internalStatus);
// Reuse numberElements[0] as a temporary buffer
uprv_getStaticCurrencyName(curriso, locStr, tempStr, internalStatus);
if (U_SUCCESS(internalStatus)) {
fSymbols[kIntlCurrencySymbol].setTo(curriso, -1);
@ -297,9 +310,9 @@ DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool us
/* else use the default values. */
U_LOCALE_BASED(locBased, *this);
locBased.setLocaleIDs(ures_getLocaleByType(numberElementsRes,
locBased.setLocaleIDs(ures_getLocaleByType(numberElementsRes.getAlias(),
ULOC_VALID_LOCALE, &status),
ures_getLocaleByType(numberElementsRes,
ures_getLocaleByType(numberElementsRes.getAlias(),
ULOC_ACTUAL_LOCALE, &status));
//load the currency data
@ -313,15 +326,19 @@ DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool us
char cc[4]={0};
u_UCharsToChars(ucc, cc, uccLen);
/* An explicit currency was requested */
UResourceBundle *currencyResource = ures_open(U_ICUDATA_CURR, locStr, &localStatus);
UResourceBundle *currency = ures_getByKeyWithFallback(currencyResource, "Currencies", NULL, &localStatus);
currency = ures_getByKeyWithFallback(currency, cc, currency, &localStatus);
if(U_SUCCESS(localStatus) && ures_getSize(currency)>2) { // the length is 3 if more data is present
currency = ures_getByIndex(currency, 2, currency, &localStatus);
LocalUResourceBundlePointer currencyResource(ures_open(U_ICUDATA_CURR, locStr, &localStatus));
LocalUResourceBundlePointer currency(
ures_getByKeyWithFallback(currencyResource.getAlias(), "Currencies", NULL, &localStatus));
ures_getByKeyWithFallback(currency.getAlias(), cc, currency.getAlias(), &localStatus);
if(U_SUCCESS(localStatus) && ures_getSize(currency.getAlias())>2) { // the length is 3 if more data is present
ures_getByIndex(currency.getAlias(), 2, currency.getAlias(), &localStatus);
int32_t currPatternLen = 0;
currPattern = ures_getStringByIndex(currency, (int32_t)0, &currPatternLen, &localStatus);
UnicodeString decimalSep = ures_getUnicodeStringByIndex(currency, (int32_t)1, &localStatus);
UnicodeString groupingSep = ures_getUnicodeStringByIndex(currency, (int32_t)2, &localStatus);
currPattern =
ures_getStringByIndex(currency.getAlias(), (int32_t)0, &currPatternLen, &localStatus);
UnicodeString decimalSep =
ures_getUnicodeStringByIndex(currency.getAlias(), (int32_t)1, &localStatus);
UnicodeString groupingSep =
ures_getUnicodeStringByIndex(currency.getAlias(), (int32_t)2, &localStatus);
if(U_SUCCESS(localStatus)){
fSymbols[kMonetaryGroupingSeparatorSymbol] = groupingSep;
fSymbols[kMonetarySeparatorSymbol] = decimalSep;
@ -329,8 +346,6 @@ DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool us
status = localStatus;
}
}
ures_close(currency);
ures_close(currencyResource);
/* else An explicit currency was requested and is unknown or locale data is malformed. */
/* ucurr_* API will get the correct value later on. */
}
@ -338,40 +353,37 @@ DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool us
// Currency Spacing.
localStatus = U_ZERO_ERROR;
UResourceBundle *currencyResource = ures_open(U_ICUDATA_CURR, locStr, &localStatus);
UResourceBundle *currencySpcRes = ures_getByKeyWithFallback(currencyResource,
gCurrencySpacingTag, NULL, &localStatus);
LocalUResourceBundlePointer currencyResource(ures_open(U_ICUDATA_CURR, locStr, &localStatus));
LocalUResourceBundlePointer currencySpcRes(
ures_getByKeyWithFallback(currencyResource.getAlias(),
gCurrencySpacingTag, NULL, &localStatus));
if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) {
const char* keywords[UNUM_CURRENCY_SPACING_COUNT] = {
gCurrencyMatchTag, gCurrencySudMatchTag, gCurrencyInsertBtnTag
};
localStatus = U_ZERO_ERROR;
UResourceBundle *dataRes = ures_getByKeyWithFallback(currencySpcRes,
gBeforeCurrencyTag, NULL, &localStatus);
LocalUResourceBundlePointer dataRes(
ures_getByKeyWithFallback(currencySpcRes.getAlias(),
gBeforeCurrencyTag, NULL, &localStatus));
if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) {
localStatus = U_ZERO_ERROR;
for (int32_t i = 0; i < UNUM_CURRENCY_SPACING_COUNT; i++) {
currencySpcBeforeSym[i] = ures_getUnicodeStringByKey(dataRes, keywords[i], &localStatus);
currencySpcBeforeSym[i] =
ures_getUnicodeStringByKey(dataRes.getAlias(), keywords[i], &localStatus);
}
ures_close(dataRes);
}
dataRes = ures_getByKeyWithFallback(currencySpcRes,
gAfterCurrencyTag, NULL, &localStatus);
dataRes.adoptInstead(
ures_getByKeyWithFallback(currencySpcRes.getAlias(),
gAfterCurrencyTag, NULL, &localStatus));
if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) {
localStatus = U_ZERO_ERROR;
for (int32_t i = 0; i < UNUM_CURRENCY_SPACING_COUNT; i++) {
currencySpcAfterSym[i] = ures_getUnicodeStringByKey(dataRes, keywords[i], &localStatus);
}
ures_close(dataRes);
}
ures_close(currencySpcRes);
ures_close(currencyResource);
currencySpcAfterSym[i] =
ures_getUnicodeStringByKey(dataRes.getAlias(), keywords[i], &localStatus);
}
}
}
ures_close(resource);
ures_close(numberElementsRes);
}
void

View File

@ -187,7 +187,24 @@ public:
* failure code upon return.
* @stable ICU 2.0
*/
DecimalFormatSymbols( UErrorCode& status);
DecimalFormatSymbols(UErrorCode& status);
/**
* Creates a DecimalFormatSymbols object with last-resort data.
* Intended for callers who cache the symbols data and
* set all symbols on the resulting object.
*
* The last-resort symbols are similar to those for the root data,
* except that the grouping separators are empty,
* the NaN symbol is U+FFFD rather than "NaN",
* and the CurrencySpacing patterns are empty.
*
* @param status Input/output parameter, set to success or
* failure code upon return.
* @return last-resort symbols
* @draft ICU 52
*/
static DecimalFormatSymbols* createWithLastResortData(UErrorCode& status);
/**
* Copy constructor.
@ -311,7 +328,7 @@ public:
static UClassID U_EXPORT2 getStaticClassID();
private:
DecimalFormatSymbols(); // default constructor not implemented
DecimalFormatSymbols();
/**
* Initializes the symbols from the LocaleElements resource bundle.

View File

@ -1,6 +1,6 @@
/********************************************************************
* COPYRIGHT:
* Copyright (c) 1997-2010, International Business Machines Corporation and
* Copyright (c) 1997-2013, International Business Machines Corporation and
* others. All Rights Reserved.
********************************************************************/
@ -15,17 +15,13 @@
void IntlTestDecimalFormatSymbols::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
{
if (exec) logln("TestSuite DecimalFormatSymbols");
switch (index) {
case 0: name = "DecimalFormatSymbols test";
if (exec) {
logln("DecimalFormatSymbols test---"); logln("");
testSymbols(/*par*/);
}
break;
default: name = ""; break;
logln("TestSuite DecimalFormatSymbols:");
}
TESTCASE_AUTO_BEGIN;
TESTCASE_AUTO(testSymbols);
TESTCASE_AUTO(testLastResortData);
TESTCASE_AUTO_END;
}
/**
@ -213,20 +209,57 @@ void IntlTestDecimalFormatSymbols::testSymbols(/* char *par */)
}
void IntlTestDecimalFormatSymbols::Verify(double value, const UnicodeString& pattern, DecimalFormatSymbols sym, const UnicodeString& expected){
void IntlTestDecimalFormatSymbols::testLastResortData() {
IcuTestErrorCode errorCode(*this, "testLastResortData");
LocalPointer<DecimalFormatSymbols> lastResort(
DecimalFormatSymbols::createWithLastResortData(errorCode));
if(errorCode.logIfFailureAndReset("DecimalFormatSymbols::createWithLastResortData() failed")) {
return;
}
DecimalFormatSymbols root(Locale::getRoot(), errorCode);
if(errorCode.logIfFailureAndReset("DecimalFormatSymbols(root) failed")) {
return;
}
// Note: It is not necessary that the last resort data matches the root locale,
// but it seems weird if most symbols did not match.
// Also, one purpose for calling operator==() is to find uninitialized memory in a debug build.
if(*lastResort == root) {
errln("DecimalFormatSymbols last resort data unexpectedly matches root");
}
// Here we adjust for expected differences.
assertEquals("last-resort grouping separator",
"", lastResort->getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol));
lastResort->setSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol, ",");
assertEquals("last-resort monetary grouping separator",
"", lastResort->getSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol));
lastResort->setSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol, ",");
assertEquals("last-resort NaN",
UnicodeString((UChar)0xfffd), lastResort->getSymbol(DecimalFormatSymbols::kNaNSymbol));
lastResort->setSymbol(DecimalFormatSymbols::kNaNSymbol, "NaN");
// Check that now all of the symbols match root.
for(int32_t i = 0; i < DecimalFormatSymbols::kFormatSymbolCount; ++i) {
DecimalFormatSymbols::ENumberFormatSymbol e = (DecimalFormatSymbols::ENumberFormatSymbol)i;
assertEquals("last-resort symbol vs. root", root.getSymbol(e), lastResort->getSymbol(e));
}
// Also, the CurrencySpacing patterns are empty in the last resort instance,
// but not in root.
Verify(1234567.25, "#,##0.##", *lastResort, "1,234,567.25");
}
void IntlTestDecimalFormatSymbols::Verify(double value, const UnicodeString& pattern,
const DecimalFormatSymbols &sym, const UnicodeString& expected){
UErrorCode status = U_ZERO_ERROR;
DecimalFormat *df = new DecimalFormat(pattern, sym, status);
DecimalFormat df(pattern, sym, status);
if(U_FAILURE(status)){
errln("ERROR: construction of decimal format failed");
errln("ERROR: construction of decimal format failed - %s", u_errorName(status));
}
UnicodeString buffer;
FieldPosition pos(FieldPosition::DONT_CARE);
buffer = df->format(value, buffer, pos);
buffer = df.format(value, buffer, pos);
if(buffer != expected){
errln((UnicodeString)"ERROR: format failed after setSymbols()\n Expected " +
errln((UnicodeString)"ERROR: format() returns wrong result\n Expected " +
expected + ", Got " + buffer);
}
delete df;
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -1,6 +1,6 @@
/********************************************************************
* COPYRIGHT:
* Copyright (c) 1997-2001, International Business Machines Corporation and
* Copyright (c) 1997-2013, International Business Machines Corporation and
* others. All Rights Reserved.
********************************************************************/
@ -25,9 +25,11 @@ private:
* Test the API of DecimalFormatSymbols; primarily a simple get/set set.
*/
void testSymbols(/*char *par*/);
void testLastResortData();
/** helper functions**/
void Verify(double value, const UnicodeString& pattern, DecimalFormatSymbols sym, const UnicodeString& expected);
void Verify(double value, const UnicodeString& pattern,
const DecimalFormatSymbols &sym, const UnicodeString& expected);
};
#endif /* #if !UCONFIG_NO_FORMATTING */