1 /* 2 ******************************************************************************* 3 * Copyright (C) 1997-2012, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 * 7 * File COMPACTDECIMALFORMAT.CPP 8 * 9 ******************************************************************************** 10 */ 11 #include "unicode/utypes.h" 12 13 #if !UCONFIG_NO_FORMATTING 14 15 #include "charstr.h" 16 #include "cstring.h" 17 #include "digitlst.h" 18 #include "mutex.h" 19 #include "unicode/compactdecimalformat.h" 20 #include "unicode/numsys.h" 21 #include "unicode/plurrule.h" 22 #include "unicode/ures.h" 23 #include "ucln_in.h" 24 #include "uhash.h" 25 #include "umutex.h" 26 #include "unicode/ures.h" 27 #include "uresimp.h" 28 29 #define LENGTHOF(array) (int32_t)(sizeof(array) / sizeof((array)[0])) 30 31 // Maps locale name to CDFLocaleData struct. 32 static UHashtable* gCompactDecimalData = NULL; 33 static UMutex gCompactDecimalMetaLock = U_MUTEX_INITIALIZER; 34 35 U_NAMESPACE_BEGIN 36 37 static const int32_t MAX_DIGITS = 15; 38 static const char gOther[] = "other"; 39 static const char gLatnTag[] = "latn"; 40 static const char gNumberElementsTag[] = "NumberElements"; 41 static const char gDecimalFormatTag[] = "decimalFormat"; 42 static const char gPatternsShort[] = "patternsShort"; 43 static const char gPatternsLong[] = "patternsLong"; 44 static const char gRoot[] = "root"; 45 46 static const UChar u_0 = 0x30; 47 static const UChar u_apos = 0x27; 48 49 static const UChar kZero[] = {u_0}; 50 51 // Used to unescape single quotes. 52 enum QuoteState { 53 OUTSIDE, 54 INSIDE_EMPTY, 55 INSIDE_FULL 56 }; 57 58 enum FallbackFlags { 59 ANY = 0, 60 MUST = 1, 61 NOT_ROOT = 2 62 // Next one will be 4 then 6 etc. 63 }; 64 65 66 // CDFUnit represents a prefix-suffix pair for a particular variant 67 // and log10 value. 68 struct CDFUnit : public UMemory { 69 UnicodeString prefix; 70 UnicodeString suffix; 71 inline CDFUnit() : prefix(), suffix() { 72 prefix.setToBogus(); 73 } 74 inline ~CDFUnit() {} 75 inline UBool isSet() const { 76 return !prefix.isBogus(); 77 } 78 inline void markAsSet() { 79 prefix.remove(); 80 } 81 }; 82 83 // CDFLocaleStyleData contains formatting data for a particular locale 84 // and style. 85 class CDFLocaleStyleData : public UMemory { 86 public: 87 // What to divide by for each log10 value when formatting. These values 88 // will be powers of 10. For English, would be: 89 // 1, 1, 1, 1000, 1000, 1000, 1000000, 1000000, 1000000, 1000000000 ... 90 double divisors[MAX_DIGITS]; 91 // Maps plural variants to CDFUnit[MAX_DIGITS] arrays. 92 // To format a number x, 93 // first compute log10(x). Compute displayNum = (x / divisors[log10(x)]). 94 // Compute the plural variant for displayNum 95 // (e.g zero, one, two, few, many, other). 96 // Compute cdfUnits = unitsByVariant[pluralVariant]. 97 // Prefix and suffix to use at cdfUnits[log10(x)] 98 UHashtable* unitsByVariant; 99 inline CDFLocaleStyleData() : unitsByVariant(NULL) {} 100 ~CDFLocaleStyleData(); 101 // Init initializes this object. 102 void Init(UErrorCode& status); 103 inline UBool isBogus() const { 104 return unitsByVariant == NULL; 105 } 106 void setToBogus(); 107 private: 108 CDFLocaleStyleData(const CDFLocaleStyleData&); 109 CDFLocaleStyleData& operator=(const CDFLocaleStyleData&); 110 }; 111 112 // CDFLocaleData contains formatting data for a particular locale. 113 struct CDFLocaleData : public UMemory { 114 CDFLocaleStyleData shortData; 115 CDFLocaleStyleData longData; 116 inline CDFLocaleData() : shortData(), longData() { } 117 inline ~CDFLocaleData() { } 118 // Init initializes this object. 119 void Init(UErrorCode& status); 120 }; 121 122 U_NAMESPACE_END 123 124 U_CDECL_BEGIN 125 126 static UBool U_CALLCONV cdf_cleanup(void) { 127 if (gCompactDecimalData != NULL) { 128 uhash_close(gCompactDecimalData); 129 gCompactDecimalData = NULL; 130 } 131 return TRUE; 132 } 133 134 static void U_CALLCONV deleteCDFUnits(void* ptr) { 135 delete [] (icu::CDFUnit*) ptr; 136 } 137 138 static void U_CALLCONV deleteCDFLocaleData(void* ptr) { 139 delete (icu::CDFLocaleData*) ptr; 140 } 141 142 U_CDECL_END 143 144 U_NAMESPACE_BEGIN 145 146 static UBool divisors_equal(const double* lhs, const double* rhs); 147 static const CDFLocaleStyleData* getCDFLocaleStyleData(const Locale& inLocale, UNumberCompactStyle style, UErrorCode& status); 148 149 static const CDFLocaleStyleData* extractDataByStyleEnum(const CDFLocaleData& data, UNumberCompactStyle style, UErrorCode& status); 150 static CDFLocaleData* loadCDFLocaleData(const Locale& inLocale, UErrorCode& status); 151 static void initCDFLocaleData(const Locale& inLocale, CDFLocaleData* result, UErrorCode& status); 152 static UResourceBundle* tryGetDecimalFallback(const UResourceBundle* numberSystemResource, const char* style, UResourceBundle** fillIn, FallbackFlags flags, UErrorCode& status); 153 static UResourceBundle* tryGetByKeyWithFallback(const UResourceBundle* rb, const char* path, UResourceBundle** fillIn, FallbackFlags flags, UErrorCode& status); 154 static UBool isRoot(const UResourceBundle* rb, UErrorCode& status); 155 static void initCDFLocaleStyleData(const UResourceBundle* decimalFormatBundle, CDFLocaleStyleData* result, UErrorCode& status); 156 static void populatePower10(const UResourceBundle* power10Bundle, CDFLocaleStyleData* result, UErrorCode& status); 157 static int32_t populatePrefixSuffix(const char* variant, int32_t log10Value, const UnicodeString& formatStr, UHashtable* result, UErrorCode& status); 158 static UBool onlySpaces(UnicodeString u); 159 static void fixQuotes(UnicodeString& s); 160 static void fillInMissing(CDFLocaleStyleData* result); 161 static int32_t computeLog10(double x, UBool inRange); 162 static CDFUnit* createCDFUnit(const char* variant, int32_t log10Value, UHashtable* table, UErrorCode& status); 163 static const CDFUnit* getCDFUnitFallback(const UHashtable* table, const UnicodeString& variant, int32_t log10Value); 164 165 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CompactDecimalFormat) 166 167 CompactDecimalFormat::CompactDecimalFormat( 168 const DecimalFormat& decimalFormat, 169 const UHashtable* unitsByVariant, 170 const double* divisors, 171 PluralRules* pluralRules) 172 : DecimalFormat(decimalFormat), _unitsByVariant(unitsByVariant), _divisors(divisors), _pluralRules(pluralRules) { 173 } 174 175 CompactDecimalFormat::CompactDecimalFormat(const CompactDecimalFormat& source) 176 : DecimalFormat(source), _unitsByVariant(source._unitsByVariant), _divisors(source._divisors), _pluralRules(source._pluralRules->clone()) { 177 } 178 179 CompactDecimalFormat* U_EXPORT2 180 CompactDecimalFormat::createInstance( 181 const Locale& inLocale, UNumberCompactStyle style, UErrorCode& status) { 182 LocalPointer<DecimalFormat> decfmt((DecimalFormat*) NumberFormat::makeInstance(inLocale, UNUM_DECIMAL, TRUE, status)); 183 if (U_FAILURE(status)) { 184 return NULL; 185 } 186 LocalPointer<PluralRules> pluralRules(PluralRules::forLocale(inLocale, status)); 187 if (U_FAILURE(status)) { 188 return NULL; 189 } 190 const CDFLocaleStyleData* data = getCDFLocaleStyleData(inLocale, style, status); 191 if (U_FAILURE(status)) { 192 return NULL; 193 } 194 CompactDecimalFormat* result = 195 new CompactDecimalFormat(*decfmt, data->unitsByVariant, data->divisors, pluralRules.getAlias()); 196 if (result == NULL) { 197 status = U_MEMORY_ALLOCATION_ERROR; 198 return NULL; 199 } 200 pluralRules.orphan(); 201 result->setMaximumSignificantDigits(3); 202 result->setSignificantDigitsUsed(TRUE); 203 result->setGroupingUsed(FALSE); 204 return result; 205 } 206 207 CompactDecimalFormat& 208 CompactDecimalFormat::operator=(const CompactDecimalFormat& rhs) { 209 if (this != &rhs) { 210 DecimalFormat::operator=(rhs); 211 _unitsByVariant = rhs._unitsByVariant; 212 _divisors = rhs._divisors; 213 delete _pluralRules; 214 _pluralRules = rhs._pluralRules->clone(); 215 } 216 return *this; 217 } 218 219 CompactDecimalFormat::~CompactDecimalFormat() { 220 delete _pluralRules; 221 } 222 223 224 Format* 225 CompactDecimalFormat::clone(void) const { 226 return new CompactDecimalFormat(*this); 227 } 228 229 UBool 230 CompactDecimalFormat::operator==(const Format& that) const { 231 if (this == &that) { 232 return TRUE; 233 } 234 return (DecimalFormat::operator==(that) && eqHelper((const CompactDecimalFormat&) that)); 235 } 236 237 UBool 238 CompactDecimalFormat::eqHelper(const CompactDecimalFormat& that) const { 239 return uhash_equals(_unitsByVariant, that._unitsByVariant) && divisors_equal(_divisors, that._divisors) && (*_pluralRules == *that._pluralRules); 240 } 241 242 UnicodeString& 243 CompactDecimalFormat::format( 244 double number, 245 UnicodeString& appendTo, 246 FieldPosition& pos) const { 247 DigitList orig, rounded; 248 orig.set(number); 249 UBool isNegative; 250 UErrorCode status = U_ZERO_ERROR; 251 _round(orig, rounded, isNegative, status); 252 if (U_FAILURE(status)) { 253 return appendTo; 254 } 255 double roundedDouble = rounded.getDouble(); 256 if (isNegative) { 257 roundedDouble = -roundedDouble; 258 } 259 int32_t baseIdx = computeLog10(roundedDouble, TRUE); 260 double numberToFormat = roundedDouble / _divisors[baseIdx]; 261 UnicodeString variant = _pluralRules->select(numberToFormat); 262 if (isNegative) { 263 numberToFormat = -numberToFormat; 264 } 265 const CDFUnit* unit = getCDFUnitFallback(_unitsByVariant, variant, baseIdx); 266 appendTo += unit->prefix; 267 DecimalFormat::format(numberToFormat, appendTo, pos); 268 appendTo += unit->suffix; 269 return appendTo; 270 } 271 272 UnicodeString& 273 CompactDecimalFormat::format( 274 double /* number */, 275 UnicodeString& appendTo, 276 FieldPositionIterator* /* posIter */, 277 UErrorCode& status) const { 278 status = U_UNSUPPORTED_ERROR; 279 return appendTo; 280 } 281 282 UnicodeString& 283 CompactDecimalFormat::format( 284 int64_t number, 285 UnicodeString& appendTo, 286 FieldPosition& pos) const { 287 return format((double) number, appendTo, pos); 288 } 289 290 UnicodeString& 291 CompactDecimalFormat::format( 292 int64_t /* number */, 293 UnicodeString& appendTo, 294 FieldPositionIterator* /* posIter */, 295 UErrorCode& status) const { 296 status = U_UNSUPPORTED_ERROR; 297 return appendTo; 298 } 299 300 UnicodeString& 301 CompactDecimalFormat::format( 302 const StringPiece& /* number */, 303 UnicodeString& appendTo, 304 FieldPositionIterator* /* posIter */, 305 UErrorCode& status) const { 306 status = U_UNSUPPORTED_ERROR; 307 return appendTo; 308 } 309 310 UnicodeString& 311 CompactDecimalFormat::format( 312 const DigitList& /* number */, 313 UnicodeString& appendTo, 314 FieldPositionIterator* /* posIter */, 315 UErrorCode& status) const { 316 status = U_UNSUPPORTED_ERROR; 317 return appendTo; 318 } 319 320 UnicodeString& 321 CompactDecimalFormat::format(const DigitList& /* number */, 322 UnicodeString& appendTo, 323 FieldPosition& /* pos */, 324 UErrorCode& status) const { 325 status = U_UNSUPPORTED_ERROR; 326 return appendTo; 327 } 328 329 void 330 CompactDecimalFormat::parse( 331 const UnicodeString& /* text */, 332 Formattable& /* result */, 333 ParsePosition& /* parsePosition */) const { 334 } 335 336 void 337 CompactDecimalFormat::parse( 338 const UnicodeString& /* text */, 339 Formattable& /* result */, 340 UErrorCode& status) const { 341 status = U_UNSUPPORTED_ERROR; 342 } 343 344 CurrencyAmount* 345 CompactDecimalFormat::parseCurrency( 346 const UnicodeString& /* text */, 347 ParsePosition& /* pos */) const { 348 return NULL; 349 } 350 351 void CDFLocaleStyleData::Init(UErrorCode& status) { 352 if (unitsByVariant != NULL) { 353 return; 354 } 355 unitsByVariant = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); 356 if (U_FAILURE(status)) { 357 return; 358 } 359 uhash_setKeyDeleter(unitsByVariant, uprv_free); 360 uhash_setValueDeleter(unitsByVariant, deleteCDFUnits); 361 } 362 363 CDFLocaleStyleData::~CDFLocaleStyleData() { 364 setToBogus(); 365 } 366 367 void CDFLocaleStyleData::setToBogus() { 368 if (unitsByVariant != NULL) { 369 uhash_close(unitsByVariant); 370 unitsByVariant = NULL; 371 } 372 } 373 374 void CDFLocaleData::Init(UErrorCode& status) { 375 shortData.Init(status); 376 if (U_FAILURE(status)) { 377 return; 378 } 379 longData.Init(status); 380 } 381 382 // Helper method for operator= 383 static UBool divisors_equal(const double* lhs, const double* rhs) { 384 for (int32_t i = 0; i < MAX_DIGITS; ++i) { 385 if (lhs[i] != rhs[i]) { 386 return FALSE; 387 } 388 } 389 return TRUE; 390 } 391 392 // getCDFLocaleStyleData returns pointer to formatting data for given locale and 393 // style within the global cache. On cache miss, getCDFLocaleStyleData loads 394 // the data from CLDR into the global cache before returning the pointer. If a 395 // UNUM_LONG data is requested for a locale, and that locale does not have 396 // UNUM_LONG data, getCDFLocaleStyleData will fall back to UNUM_SHORT data for 397 // that locale. 398 static const CDFLocaleStyleData* getCDFLocaleStyleData(const Locale& inLocale, UNumberCompactStyle style, UErrorCode& status) { 399 if (U_FAILURE(status)) { 400 return NULL; 401 } 402 CDFLocaleData* result = NULL; 403 const char* key = inLocale.getName(); 404 { 405 Mutex lock(&gCompactDecimalMetaLock); 406 if (gCompactDecimalData == NULL) { 407 gCompactDecimalData = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); 408 if (U_FAILURE(status)) { 409 return NULL; 410 } 411 uhash_setKeyDeleter(gCompactDecimalData, uprv_free); 412 uhash_setValueDeleter(gCompactDecimalData, deleteCDFLocaleData); 413 ucln_i18n_registerCleanup(UCLN_I18N_CDFINFO, cdf_cleanup); 414 } else { 415 result = (CDFLocaleData*) uhash_get(gCompactDecimalData, key); 416 } 417 } 418 if (result != NULL) { 419 return extractDataByStyleEnum(*result, style, status); 420 } 421 422 result = loadCDFLocaleData(inLocale, status); 423 if (U_FAILURE(status)) { 424 return NULL; 425 } 426 427 { 428 Mutex lock(&gCompactDecimalMetaLock); 429 CDFLocaleData* temp = (CDFLocaleData*) uhash_get(gCompactDecimalData, key); 430 if (temp != NULL) { 431 delete result; 432 result = temp; 433 } else { 434 uhash_put(gCompactDecimalData, uprv_strdup(key), (void*) result, &status); 435 if (U_FAILURE(status)) { 436 return NULL; 437 } 438 } 439 } 440 return extractDataByStyleEnum(*result, style, status); 441 } 442 443 static const CDFLocaleStyleData* extractDataByStyleEnum(const CDFLocaleData& data, UNumberCompactStyle style, UErrorCode& status) { 444 switch (style) { 445 case UNUM_SHORT: 446 return &data.shortData; 447 case UNUM_LONG: 448 if (!data.longData.isBogus()) { 449 return &data.longData; 450 } 451 return &data.shortData; 452 default: 453 status = U_ILLEGAL_ARGUMENT_ERROR; 454 return NULL; 455 } 456 } 457 458 // loadCDFLocaleData loads formatting data from CLDR for a given locale. The 459 // caller owns the returned pointer. 460 static CDFLocaleData* loadCDFLocaleData(const Locale& inLocale, UErrorCode& status) { 461 if (U_FAILURE(status)) { 462 return NULL; 463 } 464 CDFLocaleData* result = new CDFLocaleData; 465 if (result == NULL) { 466 status = U_MEMORY_ALLOCATION_ERROR; 467 return NULL; 468 } 469 result->Init(status); 470 if (U_FAILURE(status)) { 471 delete result; 472 return NULL; 473 } 474 475 initCDFLocaleData(inLocale, result, status); 476 if (U_FAILURE(status)) { 477 delete result; 478 return NULL; 479 } 480 return result; 481 } 482 483 // initCDFLocaleData initializes result with data from CLDR. 484 // inLocale is the locale, the CLDR data is stored in result. 485 // We load the UNUM_SHORT and UNUM_LONG data looking first in local numbering 486 // system and not including root locale in fallback. Next we try in the latn 487 // numbering system where we fallback all the way to root. If we don't find 488 // UNUM_SHORT data in these three places, we report an error. If we find 489 // UNUM_SHORT data before finding UNUM_LONG data we make UNUM_LONG data fall 490 // back to UNUM_SHORT data. 491 static void initCDFLocaleData(const Locale& inLocale, CDFLocaleData* result, UErrorCode& status) { 492 LocalPointer<NumberingSystem> ns(NumberingSystem::createInstance(inLocale, status)); 493 if (U_FAILURE(status)) { 494 return; 495 } 496 const char* numberingSystemName = ns->getName(); 497 UResourceBundle* rb = ures_open(NULL, inLocale.getName(), &status); 498 rb = ures_getByKeyWithFallback(rb, gNumberElementsTag, rb, &status); 499 if (U_FAILURE(status)) { 500 ures_close(rb); 501 return; 502 } 503 UResourceBundle* shortDataFillIn = NULL; 504 UResourceBundle* longDataFillIn = NULL; 505 UResourceBundle* shortData = NULL; 506 UResourceBundle* longData = NULL; 507 508 if (uprv_strcmp(numberingSystemName, gLatnTag) != 0) { 509 LocalUResourceBundlePointer localResource( 510 tryGetByKeyWithFallback(rb, numberingSystemName, NULL, NOT_ROOT, status)); 511 shortData = tryGetDecimalFallback( 512 localResource.getAlias(), gPatternsShort, &shortDataFillIn, NOT_ROOT, status); 513 longData = tryGetDecimalFallback( 514 localResource.getAlias(), gPatternsLong, &longDataFillIn, NOT_ROOT, status); 515 } 516 if (U_FAILURE(status)) { 517 ures_close(shortDataFillIn); 518 ures_close(longDataFillIn); 519 ures_close(rb); 520 return; 521 } 522 523 // If we haven't found UNUM_SHORT look in latn numbering system. We must 524 // succeed at finding UNUM_SHORT here. 525 if (shortData == NULL) { 526 LocalUResourceBundlePointer latnResource(tryGetByKeyWithFallback(rb, gLatnTag, NULL, MUST, status)); 527 shortData = tryGetDecimalFallback(latnResource.getAlias(), gPatternsShort, &shortDataFillIn, MUST, status); 528 if (longData == NULL) { 529 longData = tryGetDecimalFallback(latnResource.getAlias(), gPatternsLong, &longDataFillIn, ANY, status); 530 if (longData != NULL && isRoot(longData, status) && !isRoot(shortData, status)) { 531 longData = NULL; 532 } 533 } 534 } 535 initCDFLocaleStyleData(shortData, &result->shortData, status); 536 ures_close(shortDataFillIn); 537 if (U_FAILURE(status)) { 538 ures_close(longDataFillIn); 539 ures_close(rb); 540 } 541 542 if (longData == NULL) { 543 result->longData.setToBogus(); 544 } else { 545 initCDFLocaleStyleData(longData, &result->longData, status); 546 } 547 ures_close(longDataFillIn); 548 ures_close(rb); 549 } 550 551 /** 552 * tryGetDecimalFallback attempts to fetch the "decimalFormat" resource bundle 553 * with a particular style. style is either "patternsShort" or "patternsLong." 554 * FillIn, flags, and status work in the same way as in tryGetByKeyWithFallback. 555 */ 556 static UResourceBundle* tryGetDecimalFallback(const UResourceBundle* numberSystemResource, const char* style, UResourceBundle** fillIn, FallbackFlags flags, UErrorCode& status) { 557 UResourceBundle* first = tryGetByKeyWithFallback(numberSystemResource, style, fillIn, flags, status); 558 UResourceBundle* second = tryGetByKeyWithFallback(first, gDecimalFormatTag, fillIn, flags, status); 559 if (fillIn == NULL) { 560 ures_close(first); 561 } 562 return second; 563 } 564 565 // tryGetByKeyWithFallback returns a sub-resource bundle that matches given 566 // criteria or NULL if none found. rb is the resource bundle that we are 567 // searching. If rb == NULL then this function behaves as if no sub-resource 568 // is found; path is the key of the sub-resource, 569 // (i.e "foo" but not "foo/bar"); If fillIn is NULL, caller must always call 570 // ures_close() on returned resource. See below for example when fillIn is 571 // not NULL. flags is ANY or NOT_ROOT. Optionally, these values 572 // can be ored with MUST. MUST by itself is the same as ANY | MUST. 573 // The locale of the returned sub-resource will either match the 574 // flags or the returned sub-resouce will be NULL. If MUST is included in 575 // flags, and not suitable sub-resource is found then in addition to returning 576 // NULL, this function also sets status to U_MISSING_RESOURCE_ERROR. If MUST 577 // is not included in flags, then this function just returns NULL if no 578 // such sub-resource is found and will never set status to 579 // U_MISSING_RESOURCE_ERROR. 580 // 581 // Example: This code first searches for "foo/bar" sub-resource without falling 582 // back to ROOT. Then searches for "baz" sub-resource as last resort. 583 // 584 // UResourcebundle* fillIn = NULL; 585 // UResourceBundle* data = tryGetByKeyWithFallback(rb, "foo", &fillIn, NON_ROOT, status); 586 // data = tryGetByKeyWithFallback(data, "bar", &fillIn, NON_ROOT, status); 587 // if (!data) { 588 // data = tryGetbyKeyWithFallback(rb, "baz", &fillIn, MUST, status); 589 // } 590 // if (U_FAILURE(status)) { 591 // ures_close(fillIn); 592 // return; 593 // } 594 // doStuffWithNonNullSubresource(data); 595 // 596 // /* Wrong! don't do the following as it can leak memory if fillIn gets set 597 // to NULL. */ 598 // fillIn = tryGetByKeyWithFallback(rb, "wrong", &fillIn, ANY, status); 599 // 600 // ures_close(fillIn); 601 // 602 static UResourceBundle* tryGetByKeyWithFallback(const UResourceBundle* rb, const char* path, UResourceBundle** fillIn, FallbackFlags flags, UErrorCode& status) { 603 if (U_FAILURE(status)) { 604 return NULL; 605 } 606 UBool must = (flags & MUST); 607 if (rb == NULL) { 608 if (must) { 609 status = U_MISSING_RESOURCE_ERROR; 610 } 611 return NULL; 612 } 613 UResourceBundle* result = NULL; 614 UResourceBundle* ownedByUs = NULL; 615 if (fillIn == NULL) { 616 ownedByUs = ures_getByKeyWithFallback(rb, path, NULL, &status); 617 result = ownedByUs; 618 } else { 619 *fillIn = ures_getByKeyWithFallback(rb, path, *fillIn, &status); 620 result = *fillIn; 621 } 622 if (U_FAILURE(status)) { 623 ures_close(ownedByUs); 624 if (status == U_MISSING_RESOURCE_ERROR && !must) { 625 status = U_ZERO_ERROR; 626 } 627 return NULL; 628 } 629 flags = (FallbackFlags) (flags & ~MUST); 630 switch (flags) { 631 case NOT_ROOT: 632 { 633 UBool bRoot = isRoot(result, status); 634 if (bRoot || U_FAILURE(status)) { 635 ures_close(ownedByUs); 636 if (must && (status == U_ZERO_ERROR)) { 637 status = U_MISSING_RESOURCE_ERROR; 638 } 639 return NULL; 640 } 641 return result; 642 } 643 case ANY: 644 return result; 645 default: 646 ures_close(ownedByUs); 647 status = U_ILLEGAL_ARGUMENT_ERROR; 648 return NULL; 649 } 650 } 651 652 static UBool isRoot(const UResourceBundle* rb, UErrorCode& status) { 653 const char* actualLocale = ures_getLocaleByType( 654 rb, ULOC_ACTUAL_LOCALE, &status); 655 if (U_FAILURE(status)) { 656 return FALSE; 657 } 658 return uprv_strcmp(actualLocale, gRoot) == 0; 659 } 660 661 662 // initCDFLocaleStyleData loads formatting data for a particular style. 663 // decimalFormatBundle is the "decimalFormat" resource bundle in CLDR. 664 // Loaded data stored in result. 665 static void initCDFLocaleStyleData(const UResourceBundle* decimalFormatBundle, CDFLocaleStyleData* result, UErrorCode& status) { 666 if (U_FAILURE(status)) { 667 return; 668 } 669 // Iterate through all the powers of 10. 670 int32_t size = ures_getSize(decimalFormatBundle); 671 UResourceBundle* power10 = NULL; 672 for (int32_t i = 0; i < size; ++i) { 673 power10 = ures_getByIndex(decimalFormatBundle, i, power10, &status); 674 if (U_FAILURE(status)) { 675 ures_close(power10); 676 return; 677 } 678 populatePower10(power10, result, status); 679 if (U_FAILURE(status)) { 680 ures_close(power10); 681 return; 682 } 683 } 684 ures_close(power10); 685 fillInMissing(result); 686 } 687 688 // populatePower10 grabs data for a particular power of 10 from CLDR. 689 // The loaded data is stored in result. 690 static void populatePower10(const UResourceBundle* power10Bundle, CDFLocaleStyleData* result, UErrorCode& status) { 691 if (U_FAILURE(status)) { 692 return; 693 } 694 char* endPtr = NULL; 695 double power10 = uprv_strtod(ures_getKey(power10Bundle), &endPtr); 696 if (*endPtr != 0) { 697 status = U_INTERNAL_PROGRAM_ERROR; 698 return; 699 } 700 int32_t log10Value = computeLog10(power10, FALSE); 701 // Silently ignore divisors that are too big. 702 if (log10Value == MAX_DIGITS) { 703 return; 704 } 705 int32_t size = ures_getSize(power10Bundle); 706 int32_t numZeros = 0; 707 UBool otherVariantDefined = FALSE; 708 UResourceBundle* variantBundle = NULL; 709 // Iterate over all the plural variants for the power of 10 710 for (int32_t i = 0; i < size; ++i) { 711 variantBundle = ures_getByIndex(power10Bundle, i, variantBundle, &status); 712 if (U_FAILURE(status)) { 713 ures_close(variantBundle); 714 return; 715 } 716 const char* variant = ures_getKey(variantBundle); 717 int32_t resLen; 718 const UChar* formatStrP = ures_getString(variantBundle, &resLen, &status); 719 if (U_FAILURE(status)) { 720 ures_close(variantBundle); 721 return; 722 } 723 UnicodeString formatStr(false, formatStrP, resLen); 724 if (uprv_strcmp(variant, gOther) == 0) { 725 otherVariantDefined = TRUE; 726 } 727 int32_t nz = populatePrefixSuffix( 728 variant, log10Value, formatStr, result->unitsByVariant, status); 729 if (U_FAILURE(status)) { 730 ures_close(variantBundle); 731 return; 732 } 733 if (nz != numZeros) { 734 // We expect all format strings to have the same number of 0's 735 // left of the decimal point. 736 if (numZeros != 0) { 737 status = U_INTERNAL_PROGRAM_ERROR; 738 ures_close(variantBundle); 739 return; 740 } 741 numZeros = nz; 742 } 743 } 744 ures_close(variantBundle); 745 // We expect to find an OTHER variant for each power of 10. 746 if (!otherVariantDefined) { 747 status = U_INTERNAL_PROGRAM_ERROR; 748 return; 749 } 750 double divisor = power10; 751 for (int32_t i = 1; i < numZeros; ++i) { 752 divisor /= 10.0; 753 } 754 result->divisors[log10Value] = divisor; 755 } 756 757 // populatePrefixSuffix Adds a specific prefix-suffix pair to result for a 758 // given variant and log10 value. 759 // variant is 'zero', 'one', 'two', 'few', 'many', or 'other'. 760 // formatStr is the format string from which the prefix and suffix are 761 // extracted. It is usually of form 'Pefix 000 suffix'. 762 // populatePrefixSuffix returns the number of 0's found in formatStr 763 // before the decimal point. 764 // In the special case that formatStr contains only spaces for prefix 765 // and suffix, populatePrefixSuffix returns log10Value + 1. 766 static int32_t populatePrefixSuffix( 767 const char* variant, int32_t log10Value, const UnicodeString& formatStr, UHashtable* result, UErrorCode& status) { 768 if (U_FAILURE(status)) { 769 return 0; 770 } 771 int32_t firstIdx = formatStr.indexOf(kZero, LENGTHOF(kZero), 0); 772 // We must have 0's in format string. 773 if (firstIdx == -1) { 774 status = U_INTERNAL_PROGRAM_ERROR; 775 return 0; 776 } 777 int32_t lastIdx = formatStr.lastIndexOf(kZero, LENGTHOF(kZero), firstIdx); 778 CDFUnit* unit = createCDFUnit(variant, log10Value, result, status); 779 if (U_FAILURE(status)) { 780 return 0; 781 } 782 // Everything up to first 0 is the prefix 783 unit->prefix = formatStr.tempSubString(0, firstIdx); 784 fixQuotes(unit->prefix); 785 // Everything beyond the last 0 is the suffix 786 unit->suffix = formatStr.tempSubString(lastIdx + 1); 787 fixQuotes(unit->suffix); 788 789 // If there is effectively no prefix or suffix, ignore the actual number of 790 // 0's and act as if the number of 0's matches the size of the number. 791 if (onlySpaces(unit->prefix) && onlySpaces(unit->suffix)) { 792 return log10Value + 1; 793 } 794 795 // Calculate number of zeros before decimal point 796 int32_t idx = firstIdx + 1; 797 while (idx <= lastIdx && formatStr.charAt(idx) == u_0) { 798 ++idx; 799 } 800 return (idx - firstIdx); 801 } 802 803 static UBool onlySpaces(UnicodeString u) { 804 return u.trim().length() == 0; 805 } 806 807 // fixQuotes unescapes single quotes. Don''t -> Don't. Letter 'j' -> Letter j. 808 // Modifies s in place. 809 static void fixQuotes(UnicodeString& s) { 810 QuoteState state = OUTSIDE; 811 int32_t len = s.length(); 812 int32_t dest = 0; 813 for (int32_t i = 0; i < len; ++i) { 814 UChar ch = s.charAt(i); 815 if (ch == u_apos) { 816 if (state == INSIDE_EMPTY) { 817 s.setCharAt(dest, ch); 818 ++dest; 819 } 820 } else { 821 s.setCharAt(dest, ch); 822 ++dest; 823 } 824 825 // Update state 826 switch (state) { 827 case OUTSIDE: 828 state = ch == u_apos ? INSIDE_EMPTY : OUTSIDE; 829 break; 830 case INSIDE_EMPTY: 831 case INSIDE_FULL: 832 state = ch == u_apos ? OUTSIDE : INSIDE_FULL; 833 break; 834 default: 835 break; 836 } 837 } 838 s.truncate(dest); 839 } 840 841 // fillInMissing ensures that the data in result is complete. 842 // result data is complete if for each variant in result, there exists 843 // a prefix-suffix pair for each log10 value and there also exists 844 // a divisor for each log10 value. 845 // 846 // First this function figures out for which log10 values, the other 847 // variant already had data. These are the same log10 values defined 848 // in CLDR. 849 // 850 // For each log10 value not defined in CLDR, it uses the divisor for 851 // the last defined log10 value or 1. 852 // 853 // Then for each variant, it does the following. For each log10 854 // value not defined in CLDR, copy the prefix-suffix pair from the 855 // previous log10 value. If log10 value is defined in CLDR but is 856 // missing from given variant, copy the prefix-suffix pair for that 857 // log10 value from the 'other' variant. 858 static void fillInMissing(CDFLocaleStyleData* result) { 859 const CDFUnit* otherUnits = 860 (const CDFUnit*) uhash_get(result->unitsByVariant, gOther); 861 UBool definedInCLDR[MAX_DIGITS]; 862 double lastDivisor = 1.0; 863 for (int32_t i = 0; i < MAX_DIGITS; ++i) { 864 if (!otherUnits[i].isSet()) { 865 result->divisors[i] = lastDivisor; 866 definedInCLDR[i] = FALSE; 867 } else { 868 lastDivisor = result->divisors[i]; 869 definedInCLDR[i] = TRUE; 870 } 871 } 872 // Iterate over each variant. 873 int32_t pos = -1; 874 const UHashElement* element = uhash_nextElement(result->unitsByVariant, &pos); 875 for (;element != NULL; element = uhash_nextElement(result->unitsByVariant, &pos)) { 876 CDFUnit* units = (CDFUnit*) element->value.pointer; 877 for (int32_t i = 0; i < MAX_DIGITS; ++i) { 878 if (definedInCLDR[i]) { 879 if (!units[i].isSet()) { 880 units[i] = otherUnits[i]; 881 } 882 } else { 883 if (i == 0) { 884 units[0].markAsSet(); 885 } else { 886 units[i] = units[i - 1]; 887 } 888 } 889 } 890 } 891 } 892 893 // computeLog10 computes floor(log10(x)). If inRange is TRUE, the biggest 894 // value computeLog10 will return MAX_DIGITS -1 even for 895 // numbers > 10^MAX_DIGITS. If inRange is FALSE, computeLog10 will return 896 // up to MAX_DIGITS. 897 static int32_t computeLog10(double x, UBool inRange) { 898 int32_t result = 0; 899 int32_t max = inRange ? MAX_DIGITS - 1 : MAX_DIGITS; 900 while (x >= 10.0) { 901 x /= 10.0; 902 ++result; 903 if (result == max) { 904 break; 905 } 906 } 907 return result; 908 } 909 910 // createCDFUnit returns a pointer to the prefix-suffix pair for a given 911 // variant and log10 value within table. If no such prefix-suffix pair is 912 // stored in table, one is created within table before returning pointer. 913 static CDFUnit* createCDFUnit(const char* variant, int32_t log10Value, UHashtable* table, UErrorCode& status) { 914 if (U_FAILURE(status)) { 915 return NULL; 916 } 917 CDFUnit *cdfUnit = (CDFUnit*) uhash_get(table, variant); 918 if (cdfUnit == NULL) { 919 cdfUnit = new CDFUnit[MAX_DIGITS]; 920 if (cdfUnit == NULL) { 921 status = U_MEMORY_ALLOCATION_ERROR; 922 return NULL; 923 } 924 uhash_put(table, uprv_strdup(variant), cdfUnit, &status); 925 if (U_FAILURE(status)) { 926 return NULL; 927 } 928 } 929 CDFUnit* result = &cdfUnit[log10Value]; 930 result->markAsSet(); 931 return result; 932 } 933 934 // getCDFUnitFallback returns a pointer to the prefix-suffix pair for a given 935 // variant and log10 value within table. If the given variant doesn't exist, it 936 // falls back to the OTHER variant. Therefore, this method will always return 937 // some non-NULL value. 938 static const CDFUnit* getCDFUnitFallback(const UHashtable* table, const UnicodeString& variant, int32_t log10Value) { 939 CharString cvariant; 940 UErrorCode status = U_ZERO_ERROR; 941 const CDFUnit *cdfUnit = NULL; 942 cvariant.appendInvariantChars(variant, status); 943 if (!U_FAILURE(status)) { 944 cdfUnit = (const CDFUnit*) uhash_get(table, cvariant.data()); 945 } 946 if (cdfUnit == NULL) { 947 cdfUnit = (const CDFUnit*) uhash_get(table, gOther); 948 } 949 return &cdfUnit[log10Value]; 950 } 951 952 U_NAMESPACE_END 953 #endif 954