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