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