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