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