1 /* 2 ******************************************************************************* 3 * Copyright (C) 1997-2010, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 * 7 * File DCFMTSYM.CPP 8 * 9 * Modification History: 10 * 11 * Date Name Description 12 * 02/19/97 aliu Converted from java. 13 * 03/18/97 clhuang Implemented with C++ APIs. 14 * 03/27/97 helena Updated to pass the simple test after code review. 15 * 08/26/97 aliu Added currency/intl currency symbol support. 16 * 07/20/98 stephen Slightly modified initialization of monetarySeparator 17 ******************************************************************************** 18 */ 19 20 #include "unicode/utypes.h" 21 22 #if !UCONFIG_NO_FORMATTING 23 24 #include "unicode/dcfmtsym.h" 25 #include "unicode/ures.h" 26 #include "unicode/decimfmt.h" 27 #include "unicode/ucurr.h" 28 #include "unicode/choicfmt.h" 29 #include "unicode/unistr.h" 30 #include "unicode/numsys.h" 31 #include "unicode/unum.h" 32 #include "ucurrimp.h" 33 #include "cstring.h" 34 #include "locbased.h" 35 #include "uresimp.h" 36 #include "ureslocs.h" 37 38 // ***************************************************************************** 39 // class DecimalFormatSymbols 40 // ***************************************************************************** 41 42 U_NAMESPACE_BEGIN 43 44 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormatSymbols) 45 46 static const char gNumberElements[] = "NumberElements"; 47 static const char gCurrencySpacingTag[] = "currencySpacing"; 48 static const char gBeforeCurrencyTag[] = "beforeCurrency"; 49 static const char gAfterCurrencyTag[] = "afterCurrency"; 50 static const char gCurrencyMatchTag[] = "currencyMatch"; 51 static const char gCurrencySudMatchTag[] = "surroundingMatch"; 52 static const char gCurrencyInsertBtnTag[] = "insertBetween"; 53 54 55 static const UChar INTL_CURRENCY_SYMBOL_STR[] = {0xa4, 0xa4, 0}; 56 57 // ------------------------------------- 58 // Initializes this with the decimal format symbols in the default locale. 59 60 DecimalFormatSymbols::DecimalFormatSymbols(UErrorCode& status) 61 : UObject(), 62 locale() 63 { 64 initialize(locale, status, TRUE); 65 } 66 67 // ------------------------------------- 68 // Initializes this with the decimal format symbols in the desired locale. 69 70 DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, UErrorCode& status) 71 : UObject(), 72 locale(loc) 73 { 74 initialize(locale, status); 75 } 76 77 // BEGIN android-added: we need a default constructor for performance. 78 // ------------------------------------- 79 80 DecimalFormatSymbols::DecimalFormatSymbols() 81 { 82 initialize(); 83 } 84 // END android-added 85 86 // ------------------------------------- 87 88 DecimalFormatSymbols::~DecimalFormatSymbols() 89 { 90 } 91 92 // ------------------------------------- 93 // copy constructor 94 95 DecimalFormatSymbols::DecimalFormatSymbols(const DecimalFormatSymbols &source) 96 : UObject(source) 97 { 98 *this = source; 99 } 100 101 // ------------------------------------- 102 // assignment operator 103 104 DecimalFormatSymbols& 105 DecimalFormatSymbols::operator=(const DecimalFormatSymbols& rhs) 106 { 107 if (this != &rhs) { 108 for(int32_t i = 0; i < (int32_t)kFormatSymbolCount; ++i) { 109 // fastCopyFrom is safe, see docs on fSymbols 110 fSymbols[(ENumberFormatSymbol)i].fastCopyFrom(rhs.fSymbols[(ENumberFormatSymbol)i]); 111 } 112 for(int32_t i = 0; i < (int32_t)UNUM_CURRENCY_SPACING_COUNT; ++i) { 113 currencySpcBeforeSym[i].fastCopyFrom(rhs.currencySpcBeforeSym[i]); 114 currencySpcAfterSym[i].fastCopyFrom(rhs.currencySpcAfterSym[i]); 115 } 116 locale = rhs.locale; 117 uprv_strcpy(validLocale, rhs.validLocale); 118 uprv_strcpy(actualLocale, rhs.actualLocale); 119 } 120 return *this; 121 } 122 123 // ------------------------------------- 124 125 UBool 126 DecimalFormatSymbols::operator==(const DecimalFormatSymbols& that) const 127 { 128 if (this == &that) { 129 return TRUE; 130 } 131 for(int32_t i = 0; i < (int32_t)kFormatSymbolCount; ++i) { 132 if(fSymbols[(ENumberFormatSymbol)i] != that.fSymbols[(ENumberFormatSymbol)i]) { 133 return FALSE; 134 } 135 } 136 for(int32_t i = 0; i < (int32_t)UNUM_CURRENCY_SPACING_COUNT; ++i) { 137 if(currencySpcBeforeSym[i] != that.currencySpcBeforeSym[i]) { 138 return FALSE; 139 } 140 if(currencySpcAfterSym[i] != that.currencySpcAfterSym[i]) { 141 return FALSE; 142 } 143 } 144 return locale == that.locale && 145 uprv_strcmp(validLocale, that.validLocale) == 0 && 146 uprv_strcmp(actualLocale, that.actualLocale) == 0; 147 } 148 149 // ------------------------------------- 150 151 void 152 DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool useLastResortData) 153 { 154 static const char *gNumberElementKeys[kFormatSymbolCount] = { 155 "decimal", 156 "group", 157 "list", 158 "percentSign", 159 NULL, /* Native zero digit is deprecated from CLDR - get it from the numbering system */ 160 NULL, /* Pattern digit character is deprecated from CLDR - use # by default always */ 161 "minusSign", 162 "plusSign", 163 NULL, /* currency symbol - We don't really try to load this directly from CLDR until we know the currency */ 164 NULL, /* intl currency symbol - We don't really try to load this directly from CLDR until we know the currency */ 165 "currencyDecimal", 166 "exponential", 167 "perMille", 168 NULL, /* Escape padding character - not in CLDR */ 169 "infinity", 170 "nan", 171 NULL, /* Significant digit symbol - not in CLDR */ 172 "currencyGroup", 173 NULL, /* one digit - get it from the numbering system */ 174 NULL, /* two digit - get it from the numbering system */ 175 NULL, /* three digit - get it from the numbering system */ 176 NULL, /* four digit - get it from the numbering system */ 177 NULL, /* five digit - get it from the numbering system */ 178 NULL, /* six digit - get it from the numbering system */ 179 NULL, /* seven digit - get it from the numbering system */ 180 NULL, /* eight digit - get it from the numbering system */ 181 NULL, /* nine digit - get it from the numbering system */ 182 }; 183 184 static const char *gLatn = "latn"; 185 static const char *gSymbols = "symbols"; 186 const char *nsName; 187 const UChar *sym = NULL; 188 int32_t len = 0; 189 190 *validLocale = *actualLocale = 0; 191 currPattern = NULL; 192 if (U_FAILURE(status)) 193 return; 194 195 const char* locStr = loc.getName(); 196 UResourceBundle *resource = ures_open((char *)0, locStr, &status); 197 UResourceBundle *numberElementsRes = ures_getByKeyWithFallback(resource, gNumberElements, NULL, &status); 198 199 if (U_FAILURE(status)) { 200 if ( useLastResortData ) { 201 status = U_USING_FALLBACK_WARNING; 202 initialize(); 203 } 204 return; 205 } else { 206 207 // First initialize all the symbols to the fallbacks for anything we can't find 208 initialize(); 209 210 // 211 // Next get the numbering system for this locale and set zero digit 212 // and the digit string based on the numbering system for the locale 213 // 214 215 NumberingSystem* ns = NumberingSystem::createInstance(loc,status); 216 if (U_SUCCESS(status) && ns->getRadix() == 10 && !ns->isAlgorithmic()) { 217 nsName = ns->getName(); 218 UnicodeString *DigitString = new UnicodeString(ns->getDescription()); 219 setSymbol(kZeroDigitSymbol,DigitString->charAt(0),FALSE); 220 setSymbol(kOneDigitSymbol,DigitString->charAt(1),FALSE); 221 setSymbol(kTwoDigitSymbol,DigitString->charAt(2),FALSE); 222 setSymbol(kThreeDigitSymbol,DigitString->charAt(3),FALSE); 223 setSymbol(kFourDigitSymbol,DigitString->charAt(4),FALSE); 224 setSymbol(kFiveDigitSymbol,DigitString->charAt(5),FALSE); 225 setSymbol(kSixDigitSymbol,DigitString->charAt(6),FALSE); 226 setSymbol(kSevenDigitSymbol,DigitString->charAt(7),FALSE); 227 setSymbol(kEightDigitSymbol,DigitString->charAt(8),FALSE); 228 setSymbol(kNineDigitSymbol,DigitString->charAt(9),FALSE); 229 delete DigitString; 230 } else { 231 nsName = gLatn; 232 } 233 234 UBool isLatn = !uprv_strcmp(nsName,gLatn); 235 236 UErrorCode nlStatus = U_ZERO_ERROR; 237 UResourceBundle *nonLatnSymbols = NULL; 238 if ( !isLatn ) { 239 nonLatnSymbols = ures_getByKeyWithFallback(numberElementsRes, nsName, NULL, &nlStatus); 240 nonLatnSymbols = ures_getByKeyWithFallback(nonLatnSymbols, gSymbols, nonLatnSymbols, &nlStatus); 241 } 242 243 UResourceBundle *latnSymbols = ures_getByKeyWithFallback(numberElementsRes, gLatn, NULL, &status); 244 latnSymbols = ures_getByKeyWithFallback(latnSymbols, gSymbols, latnSymbols, &status); 245 246 UBool kMonetaryDecimalSet = FALSE; 247 UBool kMonetaryGroupingSet = FALSE; 248 for(int32_t i = 0; i<kFormatSymbolCount; i++) { 249 if ( gNumberElementKeys[i] != NULL ) { 250 UErrorCode localStatus = U_ZERO_ERROR; 251 if ( !isLatn ) { 252 sym = ures_getStringByKeyWithFallback(nonLatnSymbols,gNumberElementKeys[i],&len,&localStatus); 253 // If we can't find the symbol in the numbering system specific resources, 254 // use the "latn" numbering system as the fallback. 255 if ( U_FAILURE(localStatus) ) { 256 localStatus = U_ZERO_ERROR; 257 sym = ures_getStringByKeyWithFallback(latnSymbols,gNumberElementKeys[i],&len,&localStatus); 258 } 259 } else { 260 sym = ures_getStringByKeyWithFallback(latnSymbols,gNumberElementKeys[i],&len,&localStatus); 261 } 262 263 if ( U_SUCCESS(localStatus) ) { 264 setSymbol((ENumberFormatSymbol)i,sym); 265 if ( i == kMonetarySeparatorSymbol ) { 266 kMonetaryDecimalSet = TRUE; 267 } else if ( i == kMonetaryGroupingSeparatorSymbol ) { 268 kMonetaryGroupingSet = TRUE; 269 } 270 } 271 } 272 } 273 274 ures_close(latnSymbols); 275 if ( !isLatn ) { 276 ures_close(nonLatnSymbols); 277 } 278 279 // If monetary decimal or grouping were not explicitly set, then set them to be the 280 // same as their non-monetary counterparts. 281 282 if ( !kMonetaryDecimalSet ) { 283 setSymbol(kMonetarySeparatorSymbol,fSymbols[kDecimalSeparatorSymbol]); 284 } 285 if ( !kMonetaryGroupingSet ) { 286 setSymbol(kMonetaryGroupingSeparatorSymbol,fSymbols[kGroupingSeparatorSymbol]); 287 } 288 289 if (ns) { 290 delete ns; 291 } 292 293 // Obtain currency data from the currency API. This is strictly 294 // for backward compatibility; we don't use DecimalFormatSymbols 295 // for currency data anymore. 296 UErrorCode internalStatus = U_ZERO_ERROR; // don't propagate failures out 297 UChar curriso[4]; 298 UnicodeString tempStr; 299 ucurr_forLocale(locStr, curriso, 4, &internalStatus); 300 301 // Reuse numberElements[0] as a temporary buffer 302 uprv_getStaticCurrencyName(curriso, locStr, tempStr, internalStatus); 303 if (U_SUCCESS(internalStatus)) { 304 fSymbols[kIntlCurrencySymbol] = curriso; 305 fSymbols[kCurrencySymbol] = tempStr; 306 } 307 /* else use the default values. */ 308 309 U_LOCALE_BASED(locBased, *this); 310 locBased.setLocaleIDs(ures_getLocaleByType(numberElementsRes, 311 ULOC_VALID_LOCALE, &status), 312 ures_getLocaleByType(numberElementsRes, 313 ULOC_ACTUAL_LOCALE, &status)); 314 315 //load the currency data 316 UChar ucc[4]={0}; //Currency Codes are always 3 chars long 317 int32_t uccLen = 4; 318 const char* locName = loc.getName(); 319 UErrorCode localStatus = U_ZERO_ERROR; 320 uccLen = ucurr_forLocale(locName, ucc, uccLen, &localStatus); 321 322 if(U_SUCCESS(localStatus) && uccLen > 0) { 323 char cc[4]={0}; 324 u_UCharsToChars(ucc, cc, uccLen); 325 /* An explicit currency was requested */ 326 UResourceBundle *currencyResource = ures_open(U_ICUDATA_CURR, locStr, &localStatus); 327 UResourceBundle *currency = ures_getByKeyWithFallback(currencyResource, "Currencies", NULL, &localStatus); 328 currency = ures_getByKeyWithFallback(currency, cc, currency, &localStatus); 329 if(U_SUCCESS(localStatus) && ures_getSize(currency)>2) { // the length is 3 if more data is present 330 currency = ures_getByIndex(currency, 2, currency, &localStatus); 331 int32_t currPatternLen = 0; 332 currPattern = ures_getStringByIndex(currency, (int32_t)0, &currPatternLen, &localStatus); 333 UnicodeString decimalSep = ures_getStringByIndex(currency, (int32_t)1, NULL, &localStatus); 334 UnicodeString groupingSep = ures_getStringByIndex(currency, (int32_t)2, NULL, &localStatus); 335 if(U_SUCCESS(localStatus)){ 336 fSymbols[kMonetaryGroupingSeparatorSymbol] = groupingSep; 337 fSymbols[kMonetarySeparatorSymbol] = decimalSep; 338 //pattern.setTo(TRUE, currPattern, currPatternLen); 339 status = localStatus; 340 } 341 } 342 ures_close(currency); 343 ures_close(currencyResource); 344 /* else An explicit currency was requested and is unknown or locale data is malformed. */ 345 /* ucurr_* API will get the correct value later on. */ 346 } 347 // else ignore the error if no currency 348 349 // Currency Spacing. 350 localStatus = U_ZERO_ERROR; 351 UResourceBundle *currencyResource = ures_open(U_ICUDATA_CURR, locStr, &localStatus); 352 UResourceBundle *currencySpcRes = ures_getByKeyWithFallback(currencyResource, 353 gCurrencySpacingTag, NULL, &localStatus); 354 355 if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) { 356 const char* keywords[UNUM_CURRENCY_SPACING_COUNT] = { 357 gCurrencyMatchTag, gCurrencySudMatchTag, gCurrencyInsertBtnTag 358 }; 359 localStatus = U_ZERO_ERROR; 360 UResourceBundle *dataRes = ures_getByKeyWithFallback(currencySpcRes, 361 gBeforeCurrencyTag, NULL, &localStatus); 362 if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) { 363 localStatus = U_ZERO_ERROR; 364 for (int32_t i = 0; i < UNUM_CURRENCY_SPACING_COUNT; i++) { 365 currencySpcBeforeSym[i] = ures_getStringByKey(dataRes, keywords[i], 366 NULL, &localStatus); 367 } 368 ures_close(dataRes); 369 } 370 dataRes = ures_getByKeyWithFallback(currencySpcRes, 371 gAfterCurrencyTag, NULL, &localStatus); 372 if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) { 373 localStatus = U_ZERO_ERROR; 374 for (int32_t i = 0; i < UNUM_CURRENCY_SPACING_COUNT; i++) { 375 currencySpcAfterSym[i] = ures_getStringByKey(dataRes, keywords[i], 376 NULL, &localStatus); 377 } 378 ures_close(dataRes); 379 } 380 ures_close(currencySpcRes); 381 ures_close(currencyResource); 382 } 383 } 384 ures_close(resource); 385 ures_close(numberElementsRes); 386 387 } 388 389 void 390 DecimalFormatSymbols::initialize() { 391 /* 392 * These strings used to be in static arrays, but the HP/UX aCC compiler 393 * cannot initialize a static array with class constructors. 394 * markus 2000may25 395 */ 396 fSymbols[kDecimalSeparatorSymbol] = (UChar)0x2e; // '.' decimal separator 397 fSymbols[kGroupingSeparatorSymbol].remove(); // group (thousands) separator 398 fSymbols[kPatternSeparatorSymbol] = (UChar)0x3b; // ';' pattern separator 399 fSymbols[kPercentSymbol] = (UChar)0x25; // '%' percent sign 400 fSymbols[kZeroDigitSymbol] = (UChar)0x30; // '0' native 0 digit 401 fSymbols[kOneDigitSymbol] = (UChar)0x31; // '1' native 1 digit 402 fSymbols[kTwoDigitSymbol] = (UChar)0x32; // '2' native 2 digit 403 fSymbols[kThreeDigitSymbol] = (UChar)0x33; // '3' native 3 digit 404 fSymbols[kFourDigitSymbol] = (UChar)0x34; // '4' native 4 digit 405 fSymbols[kFiveDigitSymbol] = (UChar)0x35; // '5' native 5 digit 406 fSymbols[kSixDigitSymbol] = (UChar)0x36; // '6' native 6 digit 407 fSymbols[kSevenDigitSymbol] = (UChar)0x37; // '7' native 7 digit 408 fSymbols[kEightDigitSymbol] = (UChar)0x38; // '8' native 8 digit 409 fSymbols[kNineDigitSymbol] = (UChar)0x39; // '9' native 9 digit 410 fSymbols[kDigitSymbol] = (UChar)0x23; // '#' pattern digit 411 fSymbols[kPlusSignSymbol] = (UChar)0x002b; // '+' plus sign 412 fSymbols[kMinusSignSymbol] = (UChar)0x2d; // '-' minus sign 413 fSymbols[kCurrencySymbol] = (UChar)0xa4; // 'OX' currency symbol 414 fSymbols[kIntlCurrencySymbol] = INTL_CURRENCY_SYMBOL_STR; 415 fSymbols[kMonetarySeparatorSymbol] = (UChar)0x2e; // '.' monetary decimal separator 416 fSymbols[kExponentialSymbol] = (UChar)0x45; // 'E' exponential 417 fSymbols[kPerMillSymbol] = (UChar)0x2030; // '%o' per mill 418 fSymbols[kPadEscapeSymbol] = (UChar)0x2a; // '*' pad escape symbol 419 fSymbols[kInfinitySymbol] = (UChar)0x221e; // 'oo' infinite 420 fSymbols[kNaNSymbol] = (UChar)0xfffd; // SUB NaN 421 fSymbols[kSignificantDigitSymbol] = (UChar)0x0040; // '@' significant digit 422 fSymbols[kMonetaryGroupingSeparatorSymbol].remove(); // 423 } 424 425 Locale 426 DecimalFormatSymbols::getLocale(ULocDataLocaleType type, UErrorCode& status) const { 427 U_LOCALE_BASED(locBased, *this); 428 return locBased.getLocale(type, status); 429 } 430 431 const UnicodeString& 432 DecimalFormatSymbols::getPatternForCurrencySpacing(UCurrencySpacing type, 433 UBool beforeCurrency, 434 UErrorCode& status) const { 435 if (U_FAILURE(status)) { 436 return fNoSymbol; // always empty. 437 } 438 if (beforeCurrency) { 439 return currencySpcBeforeSym[(int32_t)type]; 440 } else { 441 return currencySpcAfterSym[(int32_t)type]; 442 } 443 } 444 445 void 446 DecimalFormatSymbols::setPatternForCurrencySpacing(UCurrencySpacing type, 447 UBool beforeCurrency, 448 const UnicodeString& pattern) { 449 if (beforeCurrency) { 450 currencySpcBeforeSym[(int32_t)type] = pattern; 451 } else { 452 currencySpcAfterSym[(int32_t)type] = pattern; 453 } 454 } 455 U_NAMESPACE_END 456 457 #endif /* #if !UCONFIG_NO_FORMATTING */ 458 459 //eof 460