1 /* 2 ******************************************************************************* 3 * Copyright (C) 2009-2011, International Business Machines Corporation and 4 * others. All Rights Reserved. 5 ******************************************************************************* 6 */ 7 8 #include "unicode/currpinf.h" 9 10 #if !UCONFIG_NO_FORMATTING 11 12 //#define CURRENCY_PLURAL_INFO_DEBUG 1 13 14 #ifdef CURRENCY_PLURAL_INFO_DEBUG 15 #include <iostream> 16 #endif 17 18 19 #include "unicode/locid.h" 20 #include "unicode/plurrule.h" 21 #include "unicode/ures.h" 22 #include "unicode/numsys.h" 23 #include "cstring.h" 24 #include "hash.h" 25 #include "uresimp.h" 26 #include "ureslocs.h" 27 28 U_NAMESPACE_BEGIN 29 30 31 static const UChar gNumberPatternSeparator = 0x3B; // ; 32 33 U_CDECL_BEGIN 34 35 /** 36 * @internal ICU 4.2 37 */ 38 static UBool U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2); 39 40 UBool 41 U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2) { 42 const UnicodeString* affix_1 = (UnicodeString*)val1.pointer; 43 const UnicodeString* affix_2 = (UnicodeString*)val2.pointer; 44 return *affix_1 == *affix_2; 45 } 46 47 U_CDECL_END 48 49 50 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyPluralInfo) 51 52 static const UChar gDefaultCurrencyPluralPattern[] = {'0', '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4, 0}; 53 static const UChar gTripleCurrencySign[] = {0xA4, 0xA4, 0xA4, 0}; 54 static const UChar gPluralCountOther[] = {0x6F, 0x74, 0x68, 0x65, 0x72, 0}; 55 static const UChar gPart0[] = {0x7B, 0x30, 0x7D, 0}; 56 static const UChar gPart1[] = {0x7B, 0x31, 0x7D, 0}; 57 58 static const char gNumberElementsTag[]="NumberElements"; 59 static const char gLatnTag[]="latn"; 60 static const char gPatternsTag[]="patterns"; 61 static const char gDecimalFormatTag[]="decimalFormat"; 62 static const char gCurrUnitPtnTag[]="CurrencyUnitPatterns"; 63 64 CurrencyPluralInfo::CurrencyPluralInfo(UErrorCode& status) 65 : fPluralCountToCurrencyUnitPattern(NULL), 66 fPluralRules(NULL), 67 fLocale(NULL) { 68 initialize(Locale::getDefault(), status); 69 } 70 71 CurrencyPluralInfo::CurrencyPluralInfo(const Locale& locale, UErrorCode& status) 72 : fPluralCountToCurrencyUnitPattern(NULL), 73 fPluralRules(NULL), 74 fLocale(NULL) { 75 initialize(locale, status); 76 } 77 78 CurrencyPluralInfo::CurrencyPluralInfo(const CurrencyPluralInfo& info) 79 : UObject(info), 80 fPluralCountToCurrencyUnitPattern(NULL), 81 fPluralRules(NULL), 82 fLocale(NULL) { 83 *this = info; 84 } 85 86 87 CurrencyPluralInfo& 88 CurrencyPluralInfo::operator=(const CurrencyPluralInfo& info) { 89 if (this == &info) { 90 return *this; 91 } 92 93 deleteHash(fPluralCountToCurrencyUnitPattern); 94 UErrorCode status = U_ZERO_ERROR; 95 fPluralCountToCurrencyUnitPattern = initHash(status); 96 copyHash(info.fPluralCountToCurrencyUnitPattern, 97 fPluralCountToCurrencyUnitPattern, status); 98 if ( U_FAILURE(status) ) { 99 return *this; 100 } 101 102 delete fPluralRules; 103 delete fLocale; 104 if (info.fPluralRules) { 105 fPluralRules = info.fPluralRules->clone(); 106 } else { 107 fPluralRules = NULL; 108 } 109 if (info.fLocale) { 110 fLocale = info.fLocale->clone(); 111 } else { 112 fLocale = NULL; 113 } 114 return *this; 115 } 116 117 118 CurrencyPluralInfo::~CurrencyPluralInfo() { 119 deleteHash(fPluralCountToCurrencyUnitPattern); 120 fPluralCountToCurrencyUnitPattern = NULL; 121 delete fPluralRules; 122 delete fLocale; 123 fPluralRules = NULL; 124 fLocale = NULL; 125 } 126 127 UBool 128 CurrencyPluralInfo::operator==(const CurrencyPluralInfo& info) const { 129 #ifdef CURRENCY_PLURAL_INFO_DEBUG 130 if (*fPluralRules == *info.fPluralRules) { 131 std::cout << "same plural rules\n"; 132 } 133 if (*fLocale == *info.fLocale) { 134 std::cout << "same locale\n"; 135 } 136 if (fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern)) { 137 std::cout << "same pattern\n"; 138 } 139 #endif 140 return *fPluralRules == *info.fPluralRules && 141 *fLocale == *info.fLocale && 142 fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern); 143 } 144 145 146 CurrencyPluralInfo* 147 CurrencyPluralInfo::clone() const { 148 return new CurrencyPluralInfo(*this); 149 } 150 151 const PluralRules* 152 CurrencyPluralInfo::getPluralRules() const { 153 return fPluralRules; 154 } 155 156 UnicodeString& 157 CurrencyPluralInfo::getCurrencyPluralPattern(const UnicodeString& pluralCount, 158 UnicodeString& result) const { 159 const UnicodeString* currencyPluralPattern = 160 (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(pluralCount); 161 if (currencyPluralPattern == NULL) { 162 // fall back to "other" 163 if (pluralCount.compare(gPluralCountOther, 5)) { 164 currencyPluralPattern = 165 (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(UnicodeString(TRUE, gPluralCountOther, 5)); 166 } 167 if (currencyPluralPattern == NULL) { 168 // no currencyUnitPatterns defined, 169 // fallback to predefined defult. 170 // This should never happen when ICU resource files are 171 // available, since currencyUnitPattern of "other" is always 172 // defined in root. 173 result = UnicodeString(gDefaultCurrencyPluralPattern); 174 return result; 175 } 176 } 177 result = *currencyPluralPattern; 178 return result; 179 } 180 181 const Locale& 182 CurrencyPluralInfo::getLocale() const { 183 return *fLocale; 184 } 185 186 void 187 CurrencyPluralInfo::setPluralRules(const UnicodeString& ruleDescription, 188 UErrorCode& status) { 189 if (U_SUCCESS(status)) { 190 if (fPluralRules) { 191 delete fPluralRules; 192 } 193 fPluralRules = PluralRules::createRules(ruleDescription, status); 194 } 195 } 196 197 198 void 199 CurrencyPluralInfo::setCurrencyPluralPattern(const UnicodeString& pluralCount, 200 const UnicodeString& pattern, 201 UErrorCode& status) { 202 if (U_SUCCESS(status)) { 203 fPluralCountToCurrencyUnitPattern->put(pluralCount, new UnicodeString(pattern), status); 204 } 205 } 206 207 208 void 209 CurrencyPluralInfo::setLocale(const Locale& loc, UErrorCode& status) { 210 initialize(loc, status); 211 } 212 213 214 void 215 CurrencyPluralInfo::initialize(const Locale& loc, UErrorCode& status) { 216 if (U_FAILURE(status)) { 217 return; 218 } 219 delete fLocale; 220 fLocale = loc.clone(); 221 if (fPluralRules) { 222 delete fPluralRules; 223 } 224 fPluralRules = PluralRules::forLocale(loc, status); 225 setupCurrencyPluralPattern(loc, status); 226 } 227 228 229 void 230 CurrencyPluralInfo::setupCurrencyPluralPattern(const Locale& loc, UErrorCode& status) { 231 if (U_FAILURE(status)) { 232 return; 233 } 234 235 if (fPluralCountToCurrencyUnitPattern) { 236 deleteHash(fPluralCountToCurrencyUnitPattern); 237 } 238 fPluralCountToCurrencyUnitPattern = initHash(status); 239 if (U_FAILURE(status)) { 240 return; 241 } 242 243 NumberingSystem *ns = NumberingSystem::createInstance(loc,status); 244 UErrorCode ec = U_ZERO_ERROR; 245 UResourceBundle *rb = ures_open(NULL, loc.getName(), &ec); 246 UResourceBundle *numElements = ures_getByKeyWithFallback(rb, gNumberElementsTag, NULL, &ec); 247 rb = ures_getByKeyWithFallback(numElements, ns->getName(), rb, &ec); 248 rb = ures_getByKeyWithFallback(rb, gPatternsTag, rb, &ec); 249 int32_t ptnLen; 250 const UChar* numberStylePattern = ures_getStringByKeyWithFallback(rb, gDecimalFormatTag, &ptnLen, &ec); 251 // Fall back to "latn" if num sys specific pattern isn't there. 252 if ( ec == U_MISSING_RESOURCE_ERROR && uprv_strcmp(ns->getName(),gLatnTag)) { 253 ec = U_ZERO_ERROR; 254 rb = ures_getByKeyWithFallback(numElements, gLatnTag, rb, &ec); 255 rb = ures_getByKeyWithFallback(rb, gPatternsTag, rb, &ec); 256 numberStylePattern = ures_getStringByKeyWithFallback(rb, gDecimalFormatTag, &ptnLen, &ec); 257 } 258 int32_t numberStylePatternLen = ptnLen; 259 const UChar* negNumberStylePattern = NULL; 260 int32_t negNumberStylePatternLen = 0; 261 // TODO: Java 262 // parse to check whether there is ";" separator in the numberStylePattern 263 UBool hasSeparator = false; 264 if (U_SUCCESS(ec)) { 265 for (int32_t styleCharIndex = 0; styleCharIndex < ptnLen; ++styleCharIndex) { 266 if (numberStylePattern[styleCharIndex] == gNumberPatternSeparator) { 267 hasSeparator = true; 268 // split the number style pattern into positive and negative 269 negNumberStylePattern = numberStylePattern + styleCharIndex + 1; 270 negNumberStylePatternLen = ptnLen - styleCharIndex - 1; 271 numberStylePatternLen = styleCharIndex; 272 } 273 } 274 } 275 276 ures_close(numElements); 277 ures_close(rb); 278 delete ns; 279 280 if (U_FAILURE(ec)) { 281 return; 282 } 283 284 UResourceBundle *currRb = ures_open(U_ICUDATA_CURR, loc.getName(), &ec); 285 UResourceBundle *currencyRes = ures_getByKeyWithFallback(currRb, gCurrUnitPtnTag, NULL, &ec); 286 287 #ifdef CURRENCY_PLURAL_INFO_DEBUG 288 std::cout << "in set up\n"; 289 #endif 290 StringEnumeration* keywords = fPluralRules->getKeywords(ec); 291 if (U_SUCCESS(ec)) { 292 const char* pluralCount; 293 while ((pluralCount = keywords->next(NULL, ec)) != NULL) { 294 if ( U_SUCCESS(ec) ) { 295 int32_t ptnLen; 296 UErrorCode err = U_ZERO_ERROR; 297 const UChar* patternChars = ures_getStringByKeyWithFallback( 298 currencyRes, pluralCount, &ptnLen, &err); 299 if (U_SUCCESS(err) && ptnLen > 0) { 300 UnicodeString* pattern = new UnicodeString(patternChars, ptnLen); 301 #ifdef CURRENCY_PLURAL_INFO_DEBUG 302 char result_1[1000]; 303 pattern->extract(0, pattern->length(), result_1, "UTF-8"); 304 std::cout << "pluralCount: " << pluralCount << "; pattern: " << result_1 << "\n"; 305 #endif 306 pattern->findAndReplace(UnicodeString(TRUE, gPart0, 3), 307 UnicodeString(numberStylePattern, numberStylePatternLen)); 308 pattern->findAndReplace(UnicodeString(TRUE, gPart1, 3), UnicodeString(TRUE, gTripleCurrencySign, 3)); 309 310 if (hasSeparator) { 311 UnicodeString negPattern(patternChars, ptnLen); 312 negPattern.findAndReplace(UnicodeString(TRUE, gPart0, 3), 313 UnicodeString(negNumberStylePattern, negNumberStylePatternLen)); 314 negPattern.findAndReplace(UnicodeString(TRUE, gPart1, 3), UnicodeString(TRUE, gTripleCurrencySign, 3)); 315 pattern->append(gNumberPatternSeparator); 316 pattern->append(negPattern); 317 } 318 #ifdef CURRENCY_PLURAL_INFO_DEBUG 319 pattern->extract(0, pattern->length(), result_1, "UTF-8"); 320 std::cout << "pluralCount: " << pluralCount << "; pattern: " << result_1 << "\n"; 321 #endif 322 323 fPluralCountToCurrencyUnitPattern->put(UnicodeString(pluralCount, -1, US_INV), pattern, status); 324 } 325 } 326 } 327 } 328 delete keywords; 329 ures_close(currencyRes); 330 ures_close(currRb); 331 } 332 333 334 335 void 336 CurrencyPluralInfo::deleteHash(Hashtable* hTable) 337 { 338 if ( hTable == NULL ) { 339 return; 340 } 341 int32_t pos = -1; 342 const UHashElement* element = NULL; 343 while ( (element = hTable->nextElement(pos)) != NULL ) { 344 const UHashTok valueTok = element->value; 345 const UnicodeString* value = (UnicodeString*)valueTok.pointer; 346 delete value; 347 } 348 delete hTable; 349 hTable = NULL; 350 } 351 352 353 Hashtable* 354 CurrencyPluralInfo::initHash(UErrorCode& status) { 355 if ( U_FAILURE(status) ) { 356 return NULL; 357 } 358 Hashtable* hTable; 359 if ( (hTable = new Hashtable(TRUE, status)) == NULL ) { 360 status = U_MEMORY_ALLOCATION_ERROR; 361 return NULL; 362 } 363 if ( U_FAILURE(status) ) { 364 delete hTable; 365 return NULL; 366 } 367 hTable->setValueComparator(ValueComparator); 368 return hTable; 369 } 370 371 372 void 373 CurrencyPluralInfo::copyHash(const Hashtable* source, 374 Hashtable* target, 375 UErrorCode& status) { 376 if ( U_FAILURE(status) ) { 377 return; 378 } 379 int32_t pos = -1; 380 const UHashElement* element = NULL; 381 if ( source ) { 382 while ( (element = source->nextElement(pos)) != NULL ) { 383 const UHashTok keyTok = element->key; 384 const UnicodeString* key = (UnicodeString*)keyTok.pointer; 385 const UHashTok valueTok = element->value; 386 const UnicodeString* value = (UnicodeString*)valueTok.pointer; 387 UnicodeString* copy = new UnicodeString(*value); 388 target->put(UnicodeString(*key), copy, status); 389 if ( U_FAILURE(status) ) { 390 return; 391 } 392 } 393 } 394 } 395 396 397 U_NAMESPACE_END 398 399 #endif 400