1 /* 2 ******************************************************************************* 3 * Copyright (C) 1997-2009, 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 static const UChar INTL_CURRENCY_SYMBOL_STR[] = {0xa4, 0xa4, 0}; 54 55 // ------------------------------------- 56 // Initializes this with the decimal format symbols in the default locale. 57 58 DecimalFormatSymbols::DecimalFormatSymbols(UErrorCode& status) 59 : UObject(), 60 locale() 61 { 62 initialize(locale, status, TRUE); 63 } 64 65 // ------------------------------------- 66 // Initializes this with the decimal format symbols in the desired locale. 67 68 DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, UErrorCode& status) 69 : UObject(), 70 locale(loc) 71 { 72 initialize(locale, status); 73 } 74 75 // BEGIN android-added: we need a default constructor for performance. 76 // ------------------------------------- 77 78 DecimalFormatSymbols::DecimalFormatSymbols() 79 { 80 initialize(); 81 } 82 // END android-added 83 84 // ------------------------------------- 85 86 DecimalFormatSymbols::~DecimalFormatSymbols() 87 { 88 } 89 90 // ------------------------------------- 91 // copy constructor 92 93 DecimalFormatSymbols::DecimalFormatSymbols(const DecimalFormatSymbols &source) 94 : UObject(source) 95 { 96 *this = source; 97 } 98 99 // ------------------------------------- 100 // assignment operator 101 102 DecimalFormatSymbols& 103 DecimalFormatSymbols::operator=(const DecimalFormatSymbols& rhs) 104 { 105 if (this != &rhs) { 106 for(int32_t i = 0; i < (int32_t)kFormatSymbolCount; ++i) { 107 // fastCopyFrom is safe, see docs on fSymbols 108 fSymbols[(ENumberFormatSymbol)i].fastCopyFrom(rhs.fSymbols[(ENumberFormatSymbol)i]); 109 } 110 for(int32_t i = 0; i < (int32_t)kCurrencySpacingCount; ++i) { 111 currencySpcBeforeSym[i].fastCopyFrom(rhs.currencySpcBeforeSym[i]); 112 currencySpcAfterSym[i].fastCopyFrom(rhs.currencySpcAfterSym[i]); 113 } 114 locale = rhs.locale; 115 uprv_strcpy(validLocale, rhs.validLocale); 116 uprv_strcpy(actualLocale, rhs.actualLocale); 117 } 118 return *this; 119 } 120 121 // ------------------------------------- 122 123 UBool 124 DecimalFormatSymbols::operator==(const DecimalFormatSymbols& that) const 125 { 126 if (this == &that) { 127 return TRUE; 128 } 129 for(int32_t i = 0; i < (int32_t)kFormatSymbolCount; ++i) { 130 if(fSymbols[(ENumberFormatSymbol)i] != that.fSymbols[(ENumberFormatSymbol)i]) { 131 return FALSE; 132 } 133 } 134 for(int32_t i = 0; i < (int32_t)kCurrencySpacingCount; ++i) { 135 if(currencySpcBeforeSym[i] != that.currencySpcBeforeSym[i]) { 136 return FALSE; 137 } 138 if(currencySpcAfterSym[i] != that.currencySpcAfterSym[i]) { 139 return FALSE; 140 } 141 } 142 return locale == that.locale && 143 uprv_strcmp(validLocale, that.validLocale) == 0 && 144 uprv_strcmp(actualLocale, that.actualLocale) == 0; 145 } 146 147 // ------------------------------------- 148 149 void 150 DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, 151 UBool useLastResortData) 152 { 153 *validLocale = *actualLocale = 0; 154 currPattern = NULL; 155 if (U_FAILURE(status)) 156 return; 157 158 const char* locStr = loc.getName(); 159 UResourceBundle *resource = ures_open((char *)0, locStr, &status); 160 UResourceBundle *numberElementsRes = ures_getByKey(resource, gNumberElements, NULL, &status); 161 162 if (U_FAILURE(status)) 163 { 164 // Initializes with last resort data if necessary. 165 if (useLastResortData) 166 { 167 status = U_USING_FALLBACK_WARNING; 168 initialize(); 169 } 170 } 171 else { 172 // Gets the number element array. 173 int32_t numberElementsLength = ures_getSize(numberElementsRes); 174 175 if (numberElementsLength > (int32_t)kFormatSymbolCount) { 176 /* Warning: Invalid format. Array too large. */ 177 numberElementsLength = (int32_t)kFormatSymbolCount; 178 } 179 // If the array size is too small, something is wrong with the resource 180 // bundle, returns the failure error code. 181 if (numberElementsLength != 12 || U_FAILURE(status)) { 182 status = U_INVALID_FORMAT_ERROR; 183 } 184 else { 185 const UChar *numberElements[kFormatSymbolCount]; 186 int32_t numberElementsStrLen[kFormatSymbolCount]; 187 int32_t i = 0; 188 for(i = 0; i<numberElementsLength; i++) { 189 numberElements[i] = ures_getStringByIndex(numberElementsRes, i, &numberElementsStrLen[i], &status); 190 } 191 192 if (U_SUCCESS(status)) { 193 initialize(numberElements, numberElementsStrLen, numberElementsLength); 194 195 // Attempt to set the zero digit based on the numbering system for the locale requested 196 // 197 NumberingSystem* ns = NumberingSystem::createInstance(loc,status); 198 if (U_SUCCESS(status) && ns->getRadix() == 10 && !ns->isAlgorithmic()) { 199 UnicodeString zeroDigit(ns->getDescription(),0,1); 200 setSymbol(kZeroDigitSymbol,zeroDigit); 201 } 202 if (ns) { 203 delete ns; 204 } 205 206 // Obtain currency data from the currency API. This is strictly 207 // for backward compatibility; we don't use DecimalFormatSymbols 208 // for currency data anymore. 209 UErrorCode internalStatus = U_ZERO_ERROR; // don't propagate failures out 210 UChar curriso[4]; 211 UnicodeString tempStr; 212 ucurr_forLocale(locStr, curriso, 4, &internalStatus); 213 214 // Reuse numberElements[0] as a temporary buffer 215 uprv_getStaticCurrencyName(curriso, locStr, tempStr, internalStatus); 216 if (U_SUCCESS(internalStatus)) { 217 fSymbols[kIntlCurrencySymbol] = curriso; 218 fSymbols[kCurrencySymbol] = tempStr; 219 } 220 /* else use the default values. */ 221 } 222 223 U_LOCALE_BASED(locBased, *this); 224 locBased.setLocaleIDs(ures_getLocaleByType(numberElementsRes, 225 ULOC_VALID_LOCALE, &status), 226 ures_getLocaleByType(numberElementsRes, 227 ULOC_ACTUAL_LOCALE, &status)); 228 } 229 230 //load the currency data 231 UChar ucc[4]={0}; //Currency Codes are always 3 chars long 232 int32_t uccLen = 4; 233 const char* locName = loc.getName(); 234 UErrorCode localStatus = U_ZERO_ERROR; 235 uccLen = ucurr_forLocale(locName, ucc, uccLen, &localStatus); 236 237 if(U_SUCCESS(localStatus) && uccLen > 0) { 238 char cc[4]={0}; 239 u_UCharsToChars(ucc, cc, uccLen); 240 /* An explicit currency was requested */ 241 UResourceBundle *currencyResource = ures_open(U_ICUDATA_CURR, locStr, &localStatus); 242 UResourceBundle *currency = ures_getByKeyWithFallback(currencyResource, "Currencies", NULL, &localStatus); 243 currency = ures_getByKeyWithFallback(currency, cc, currency, &localStatus); 244 if(U_SUCCESS(localStatus) && ures_getSize(currency)>2) { // the length is 3 if more data is present 245 currency = ures_getByIndex(currency, 2, currency, &localStatus); 246 int32_t currPatternLen = 0; 247 currPattern = ures_getStringByIndex(currency, (int32_t)0, &currPatternLen, &localStatus); 248 UnicodeString decimalSep = ures_getStringByIndex(currency, (int32_t)1, NULL, &localStatus); 249 UnicodeString groupingSep = ures_getStringByIndex(currency, (int32_t)2, NULL, &localStatus); 250 if(U_SUCCESS(localStatus)){ 251 fSymbols[kMonetaryGroupingSeparatorSymbol] = groupingSep; 252 fSymbols[kMonetarySeparatorSymbol] = decimalSep; 253 //pattern.setTo(TRUE, currPattern, currPatternLen); 254 status = localStatus; 255 } 256 } 257 ures_close(currency); 258 ures_close(currencyResource); 259 /* else An explicit currency was requested and is unknown or locale data is malformed. */ 260 /* ucurr_* API will get the correct value later on. */ 261 } 262 // else ignore the error if no currency 263 } 264 ures_close(numberElementsRes); 265 266 // Currency Spacing. 267 UErrorCode localStatus = U_ZERO_ERROR; 268 UResourceBundle *currencyResource = ures_open(U_ICUDATA_CURR, locStr, &localStatus); 269 UResourceBundle *currencySpcRes = ures_getByKeyWithFallback(currencyResource, 270 gCurrencySpacingTag, NULL, &localStatus); 271 272 if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) { 273 const char* keywords[kCurrencySpacingCount] = { 274 gCurrencyMatchTag, gCurrencySudMatchTag, gCurrencyInsertBtnTag 275 }; 276 localStatus = U_ZERO_ERROR; 277 UResourceBundle *dataRes = ures_getByKeyWithFallback(currencySpcRes, 278 gBeforeCurrencyTag, NULL, &localStatus); 279 if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) { 280 localStatus = U_ZERO_ERROR; 281 for (int32_t i = 0; i < kCurrencySpacingCount; i++) { 282 currencySpcBeforeSym[i] = ures_getStringByKey(dataRes, keywords[i], 283 NULL, &localStatus); 284 } 285 ures_close(dataRes); 286 } 287 dataRes = ures_getByKeyWithFallback(currencySpcRes, 288 gAfterCurrencyTag, NULL, &localStatus); 289 if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) { 290 localStatus = U_ZERO_ERROR; 291 for (int32_t i = 0; i < kCurrencySpacingCount; i++) { 292 currencySpcAfterSym[i] = ures_getStringByKey(dataRes, keywords[i], 293 NULL, &localStatus); 294 } 295 ures_close(dataRes); 296 } 297 ures_close(currencySpcRes); 298 ures_close(currencyResource); 299 } 300 ures_close(resource); 301 } 302 303 // Initializes the DecimalFormatSymbol instance with the data obtained 304 // from ResourceBundle in the desired locale. 305 306 void 307 DecimalFormatSymbols::initialize(const UChar** numberElements, int32_t *numberElementsStrLen, int32_t numberElementsLength) 308 { 309 static const int8_t TYPE_MAPPING[][2] = { 310 {kDecimalSeparatorSymbol, 0}, 311 {kGroupingSeparatorSymbol, 1}, 312 {kPatternSeparatorSymbol, 2}, 313 {kPercentSymbol, 3}, 314 {kZeroDigitSymbol, 4}, 315 {kDigitSymbol, 5}, 316 {kMinusSignSymbol, 6}, 317 {kExponentialSymbol, 7}, 318 {kPerMillSymbol, 8}, 319 {kInfinitySymbol, 9}, 320 {kNaNSymbol, 10}, 321 {kPlusSignSymbol, 11}, 322 {kMonetarySeparatorSymbol, 0} 323 }; 324 int32_t idx; 325 326 for (idx = 0; idx < (int32_t)(sizeof(TYPE_MAPPING)/sizeof(TYPE_MAPPING[0])); idx++) { 327 if (TYPE_MAPPING[idx][1] < numberElementsLength) { 328 fSymbols[TYPE_MAPPING[idx][0]].setTo(TRUE, numberElements[TYPE_MAPPING[idx][1]], numberElementsStrLen[TYPE_MAPPING[idx][1]]); 329 } 330 } 331 332 // Default values until it's set later on. 333 fSymbols[kCurrencySymbol] = (UChar)0xa4; // 'OX' currency symbol 334 fSymbols[kIntlCurrencySymbol] = INTL_CURRENCY_SYMBOL_STR; 335 // TODO: read from locale data, if this makes it into CLDR 336 fSymbols[kSignificantDigitSymbol] = (UChar)0x0040; // '@' significant digit 337 fSymbols[kPadEscapeSymbol] = (UChar)0x002a; // TODO: '*' Hard coded for now; get from resource later 338 fSymbols[kMonetaryGroupingSeparatorSymbol] = fSymbols[kGroupingSeparatorSymbol]; 339 } 340 341 // initialize with default values 342 void 343 DecimalFormatSymbols::initialize() { 344 /* 345 * These strings used to be in static arrays, but the HP/UX aCC compiler 346 * cannot initialize a static array with class constructors. 347 * markus 2000may25 348 */ 349 fSymbols[kDecimalSeparatorSymbol] = (UChar)0x2e; // '.' decimal separator 350 fSymbols[kGroupingSeparatorSymbol].remove(); // group (thousands) separator 351 fSymbols[kPatternSeparatorSymbol] = (UChar)0x3b; // ';' pattern separator 352 fSymbols[kPercentSymbol] = (UChar)0x25; // '%' percent sign 353 fSymbols[kZeroDigitSymbol] = (UChar)0x30; // '0' native 0 digit 354 fSymbols[kDigitSymbol] = (UChar)0x23; // '#' pattern digit 355 fSymbols[kPlusSignSymbol] = (UChar)0x002b; // '+' plus sign 356 fSymbols[kMinusSignSymbol] = (UChar)0x2d; // '-' minus sign 357 fSymbols[kCurrencySymbol] = (UChar)0xa4; // 'OX' currency symbol 358 fSymbols[kIntlCurrencySymbol] = INTL_CURRENCY_SYMBOL_STR; 359 fSymbols[kMonetarySeparatorSymbol] = (UChar)0x2e; // '.' monetary decimal separator 360 fSymbols[kExponentialSymbol] = (UChar)0x45; // 'E' exponential 361 fSymbols[kPerMillSymbol] = (UChar)0x2030; // '%o' per mill 362 fSymbols[kPadEscapeSymbol] = (UChar)0x2a; // '*' pad escape symbol 363 fSymbols[kInfinitySymbol] = (UChar)0x221e; // 'oo' infinite 364 fSymbols[kNaNSymbol] = (UChar)0xfffd; // SUB NaN 365 fSymbols[kSignificantDigitSymbol] = (UChar)0x0040; // '@' significant digit 366 } 367 368 Locale 369 DecimalFormatSymbols::getLocale(ULocDataLocaleType type, UErrorCode& status) const { 370 U_LOCALE_BASED(locBased, *this); 371 return locBased.getLocale(type, status); 372 } 373 374 const UnicodeString& 375 DecimalFormatSymbols::getPatternForCurrencySpacing(ECurrencySpacing type, 376 UBool beforeCurrency, 377 UErrorCode& status) const { 378 if (U_FAILURE(status)) { 379 return fNoSymbol; // always empty. 380 } 381 if (beforeCurrency) { 382 return currencySpcBeforeSym[(int32_t)type]; 383 } else { 384 return currencySpcAfterSym[(int32_t)type]; 385 } 386 } 387 388 void 389 DecimalFormatSymbols::setPatternForCurrencySpacing(ECurrencySpacing type, 390 UBool beforeCurrency, 391 const UnicodeString& pattern) { 392 if (beforeCurrency) { 393 currencySpcBeforeSym[(int32_t)type] = pattern; 394 } else { 395 currencySpcAfterSym[(int32_t)type] = pattern; 396 } 397 } 398 U_NAMESPACE_END 399 400 #endif /* #if !UCONFIG_NO_FORMATTING */ 401 402 //eof 403