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