From 308b16079bd423acf6a4938bb346afde6828cda5 Mon Sep 17 00:00:00 2001 From: Markus Scherer Date: Thu, 12 Sep 2013 20:33:29 +0000 Subject: [PATCH] ICU-7392 DecimalFormatSymbols::createWithLastResortData(UErrorCode), and clean up the real initialize() for supplementary digits and memory management X-SVN-Rev: 34288 --- icu4c/source/i18n/dcfmtsym.cpp | 336 ++++++++++++------------ icu4c/source/i18n/unicode/dcfmtsym.h | 21 +- icu4c/source/test/intltest/tsdcfmsy.cpp | 67 +++-- icu4c/source/test/intltest/tsdcfmsy.h | 6 +- 4 files changed, 247 insertions(+), 183 deletions(-) diff --git a/icu4c/source/i18n/dcfmtsym.cpp b/icu4c/source/i18n/dcfmtsym.cpp index 260f8b17c9e..809e8a19074 100644 --- a/icu4c/source/i18n/dcfmtsym.cpp +++ b/icu4c/source/i18n/dcfmtsym.cpp @@ -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,185 +213,177 @@ 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(); - - // - // Next get the numbering system for this locale and set zero digit - // and the digit string based on the numbering system for the locale - // - - 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); - } else { - nsName = gLatn; - } - - UBool isLatn = !uprv_strcmp(nsName,gLatn); - - UErrorCode nlStatus = U_ZERO_ERROR; - UResourceBundle *nonLatnSymbols = NULL; - if ( !isLatn ) { - nonLatnSymbols = ures_getByKeyWithFallback(numberElementsRes, nsName, NULL, &nlStatus); - nonLatnSymbols = ures_getByKeyWithFallback(nonLatnSymbols, gSymbols, nonLatnSymbols, &nlStatus); + // First initialize all the symbols to the fallbacks for anything we can't find + initialize(); + + // + // Next get the numbering system for this locale and set zero digit + // and the digit string based on the numbering system for the locale + // + + LocalPointer ns(NumberingSystem::createInstance(loc, status)); + if (U_SUCCESS(status) && ns->getRadix() == 10 && !ns->isAlgorithmic()) { + nsName = ns->getName(); + UnicodeString digitString(ns->getDescription()); + 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; + } - UResourceBundle *latnSymbols = ures_getByKeyWithFallback(numberElementsRes, gLatn, NULL, &status); - latnSymbols = ures_getByKeyWithFallback(latnSymbols, gSymbols, latnSymbols, &status); - - UBool kMonetaryDecimalSet = FALSE; - UBool kMonetaryGroupingSet = FALSE; - for(int32_t i = 0; i 0) { - 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); - 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); - if(U_SUCCESS(localStatus)){ - fSymbols[kMonetaryGroupingSeparatorSymbol] = groupingSep; - fSymbols[kMonetarySeparatorSymbol] = decimalSep; - //pattern.setTo(TRUE, currPattern, currPatternLen); - status = localStatus; - } + U_LOCALE_BASED(locBased, *this); + locBased.setLocaleIDs(ures_getLocaleByType(numberElementsRes.getAlias(), + ULOC_VALID_LOCALE, &status), + ures_getLocaleByType(numberElementsRes.getAlias(), + ULOC_ACTUAL_LOCALE, &status)); + + //load the currency data + UChar ucc[4]={0}; //Currency Codes are always 3 chars long + int32_t uccLen = 4; + const char* locName = loc.getName(); + UErrorCode localStatus = U_ZERO_ERROR; + uccLen = ucurr_forLocale(locName, ucc, uccLen, &localStatus); + + if(U_SUCCESS(localStatus) && uccLen > 0) { + char cc[4]={0}; + u_UCharsToChars(ucc, cc, uccLen); + /* An explicit currency was requested */ + 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.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; + //pattern.setTo(TRUE, currPattern, currPatternLen); + 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. */ } - // else ignore the error if no currency - - // Currency Spacing. + /* else An explicit currency was requested and is unknown or locale data is malformed. */ + /* ucurr_* API will get the correct value later on. */ + } + // else ignore the error if no currency + + // Currency Spacing. + localStatus = U_ZERO_ERROR; + 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 *currencyResource = ures_open(U_ICUDATA_CURR, locStr, &localStatus); - UResourceBundle *currencySpcRes = ures_getByKeyWithFallback(currencyResource, - gCurrencySpacingTag, NULL, &localStatus); - + LocalUResourceBundlePointer dataRes( + ures_getByKeyWithFallback(currencySpcRes.getAlias(), + gBeforeCurrencyTag, 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); - 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); - } - ures_close(dataRes); + for (int32_t i = 0; i < UNUM_CURRENCY_SPACING_COUNT; i++) { + currencySpcBeforeSym[i] = + ures_getUnicodeStringByKey(dataRes.getAlias(), keywords[i], &localStatus); } - dataRes = ures_getByKeyWithFallback(currencySpcRes, - 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); + } + 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.getAlias(), keywords[i], &localStatus); } - ures_close(currencySpcRes); - ures_close(currencyResource); } } - ures_close(resource); - ures_close(numberElementsRes); - } void diff --git a/icu4c/source/i18n/unicode/dcfmtsym.h b/icu4c/source/i18n/unicode/dcfmtsym.h index da5c08d915d..bfb14500524 100644 --- a/icu4c/source/i18n/unicode/dcfmtsym.h +++ b/icu4c/source/i18n/unicode/dcfmtsym.h @@ -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. diff --git a/icu4c/source/test/intltest/tsdcfmsy.cpp b/icu4c/source/test/intltest/tsdcfmsy.cpp index 7ddaf3aaee6..0703a4921b3 100644 --- a/icu4c/source/test/intltest/tsdcfmsy.cpp +++ b/icu4c/source/test/intltest/tsdcfmsy.cpp @@ -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; + if (exec) { + 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 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 */ diff --git a/icu4c/source/test/intltest/tsdcfmsy.h b/icu4c/source/test/intltest/tsdcfmsy.h index 828d0f434ad..63e0fd1df8d 100644 --- a/icu4c/source/test/intltest/tsdcfmsy.h +++ b/icu4c/source/test/intltest/tsdcfmsy.h @@ -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 */ -- 2.40.0