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