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