compactdecimalformat.cpp revision 1b7d32f919554dda9c193b32188251337bc756f1
1/* 2******************************************************************************* 3* Copyright (C) 1997-2014, 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 DigitList orig, rounded; 246 orig.set(number); 247 UBool isNegative; 248 UErrorCode status = U_ZERO_ERROR; 249 _round(orig, rounded, isNegative, status); 250 if (U_FAILURE(status)) { 251 return appendTo; 252 } 253 double roundedDouble = rounded.getDouble(); 254 if (isNegative) { 255 roundedDouble = -roundedDouble; 256 } 257 int32_t baseIdx = computeLog10(roundedDouble, TRUE); 258 double numberToFormat = roundedDouble / _divisors[baseIdx]; 259 UnicodeString variant = _pluralRules->select(numberToFormat); 260 if (isNegative) { 261 numberToFormat = -numberToFormat; 262 } 263 const CDFUnit* unit = getCDFUnitFallback(_unitsByVariant, variant, baseIdx); 264 appendTo += unit->prefix; 265 DecimalFormat::format(numberToFormat, appendTo, pos); 266 appendTo += unit->suffix; 267 return appendTo; 268} 269 270UnicodeString& 271CompactDecimalFormat::format( 272 double /* number */, 273 UnicodeString& appendTo, 274 FieldPositionIterator* /* posIter */, 275 UErrorCode& status) const { 276 status = U_UNSUPPORTED_ERROR; 277 return appendTo; 278} 279 280UnicodeString& 281CompactDecimalFormat::format( 282 int64_t number, 283 UnicodeString& appendTo, 284 FieldPosition& pos) const { 285 return format((double) number, appendTo, pos); 286} 287 288UnicodeString& 289CompactDecimalFormat::format( 290 int64_t /* number */, 291 UnicodeString& appendTo, 292 FieldPositionIterator* /* posIter */, 293 UErrorCode& status) const { 294 status = U_UNSUPPORTED_ERROR; 295 return appendTo; 296} 297 298UnicodeString& 299CompactDecimalFormat::format( 300 const StringPiece& /* number */, 301 UnicodeString& appendTo, 302 FieldPositionIterator* /* posIter */, 303 UErrorCode& status) const { 304 status = U_UNSUPPORTED_ERROR; 305 return appendTo; 306} 307 308UnicodeString& 309CompactDecimalFormat::format( 310 const DigitList& /* number */, 311 UnicodeString& appendTo, 312 FieldPositionIterator* /* posIter */, 313 UErrorCode& status) const { 314 status = U_UNSUPPORTED_ERROR; 315 return appendTo; 316} 317 318UnicodeString& 319CompactDecimalFormat::format(const DigitList& /* number */, 320 UnicodeString& appendTo, 321 FieldPosition& /* pos */, 322 UErrorCode& status) const { 323 status = U_UNSUPPORTED_ERROR; 324 return appendTo; 325} 326 327void 328CompactDecimalFormat::parse( 329 const UnicodeString& /* text */, 330 Formattable& /* result */, 331 ParsePosition& /* parsePosition */) const { 332} 333 334void 335CompactDecimalFormat::parse( 336 const UnicodeString& /* text */, 337 Formattable& /* result */, 338 UErrorCode& status) const { 339 status = U_UNSUPPORTED_ERROR; 340} 341 342CurrencyAmount* 343CompactDecimalFormat::parseCurrency( 344 const UnicodeString& /* text */, 345 ParsePosition& /* pos */) const { 346 return NULL; 347} 348 349void CDFLocaleStyleData::Init(UErrorCode& status) { 350 if (unitsByVariant != NULL) { 351 return; 352 } 353 unitsByVariant = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); 354 if (U_FAILURE(status)) { 355 return; 356 } 357 uhash_setKeyDeleter(unitsByVariant, uprv_free); 358 uhash_setValueDeleter(unitsByVariant, deleteCDFUnits); 359} 360 361CDFLocaleStyleData::~CDFLocaleStyleData() { 362 setToBogus(); 363} 364 365void CDFLocaleStyleData::setToBogus() { 366 if (unitsByVariant != NULL) { 367 uhash_close(unitsByVariant); 368 unitsByVariant = NULL; 369 } 370} 371 372void CDFLocaleData::Init(UErrorCode& status) { 373 shortData.Init(status); 374 if (U_FAILURE(status)) { 375 return; 376 } 377 longData.Init(status); 378} 379 380// Helper method for operator= 381static UBool divisors_equal(const double* lhs, const double* rhs) { 382 for (int32_t i = 0; i < MAX_DIGITS; ++i) { 383 if (lhs[i] != rhs[i]) { 384 return FALSE; 385 } 386 } 387 return TRUE; 388} 389 390// getCDFLocaleStyleData returns pointer to formatting data for given locale and 391// style within the global cache. On cache miss, getCDFLocaleStyleData loads 392// the data from CLDR into the global cache before returning the pointer. If a 393// UNUM_LONG data is requested for a locale, and that locale does not have 394// UNUM_LONG data, getCDFLocaleStyleData will fall back to UNUM_SHORT data for 395// that locale. 396static const CDFLocaleStyleData* getCDFLocaleStyleData(const Locale& inLocale, UNumberCompactStyle style, UErrorCode& status) { 397 if (U_FAILURE(status)) { 398 return NULL; 399 } 400 CDFLocaleData* result = NULL; 401 const char* key = inLocale.getName(); 402 { 403 Mutex lock(&gCompactDecimalMetaLock); 404 if (gCompactDecimalData == NULL) { 405 gCompactDecimalData = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); 406 if (U_FAILURE(status)) { 407 return NULL; 408 } 409 uhash_setKeyDeleter(gCompactDecimalData, uprv_free); 410 uhash_setValueDeleter(gCompactDecimalData, deleteCDFLocaleData); 411 ucln_i18n_registerCleanup(UCLN_I18N_CDFINFO, cdf_cleanup); 412 } else { 413 result = (CDFLocaleData*) uhash_get(gCompactDecimalData, key); 414 } 415 } 416 if (result != NULL) { 417 return extractDataByStyleEnum(*result, style, status); 418 } 419 420 result = loadCDFLocaleData(inLocale, status); 421 if (U_FAILURE(status)) { 422 return NULL; 423 } 424 425 { 426 Mutex lock(&gCompactDecimalMetaLock); 427 CDFLocaleData* temp = (CDFLocaleData*) uhash_get(gCompactDecimalData, key); 428 if (temp != NULL) { 429 delete result; 430 result = temp; 431 } else { 432 uhash_put(gCompactDecimalData, uprv_strdup(key), (void*) result, &status); 433 if (U_FAILURE(status)) { 434 return NULL; 435 } 436 } 437 } 438 return extractDataByStyleEnum(*result, style, status); 439} 440 441static const CDFLocaleStyleData* extractDataByStyleEnum(const CDFLocaleData& data, UNumberCompactStyle style, UErrorCode& status) { 442 switch (style) { 443 case UNUM_SHORT: 444 return &data.shortData; 445 case UNUM_LONG: 446 if (!data.longData.isBogus()) { 447 return &data.longData; 448 } 449 return &data.shortData; 450 default: 451 status = U_ILLEGAL_ARGUMENT_ERROR; 452 return NULL; 453 } 454} 455 456// loadCDFLocaleData loads formatting data from CLDR for a given locale. The 457// caller owns the returned pointer. 458static CDFLocaleData* loadCDFLocaleData(const Locale& inLocale, UErrorCode& status) { 459 if (U_FAILURE(status)) { 460 return NULL; 461 } 462 CDFLocaleData* result = new CDFLocaleData; 463 if (result == NULL) { 464 status = U_MEMORY_ALLOCATION_ERROR; 465 return NULL; 466 } 467 result->Init(status); 468 if (U_FAILURE(status)) { 469 delete result; 470 return NULL; 471 } 472 473 initCDFLocaleData(inLocale, result, status); 474 if (U_FAILURE(status)) { 475 delete result; 476 return NULL; 477 } 478 return result; 479} 480 481// initCDFLocaleData initializes result with data from CLDR. 482// inLocale is the locale, the CLDR data is stored in result. 483// We load the UNUM_SHORT and UNUM_LONG data looking first in local numbering 484// system and not including root locale in fallback. Next we try in the latn 485// numbering system where we fallback all the way to root. If we don't find 486// UNUM_SHORT data in these three places, we report an error. If we find 487// UNUM_SHORT data before finding UNUM_LONG data we make UNUM_LONG data fall 488// back to UNUM_SHORT data. 489static void initCDFLocaleData(const Locale& inLocale, CDFLocaleData* result, UErrorCode& status) { 490 LocalPointer<NumberingSystem> ns(NumberingSystem::createInstance(inLocale, status)); 491 if (U_FAILURE(status)) { 492 return; 493 } 494 const char* numberingSystemName = ns->getName(); 495 UResourceBundle* rb = ures_open(NULL, inLocale.getName(), &status); 496 rb = ures_getByKeyWithFallback(rb, gNumberElementsTag, rb, &status); 497 if (U_FAILURE(status)) { 498 ures_close(rb); 499 return; 500 } 501 UResourceBundle* shortDataFillIn = NULL; 502 UResourceBundle* longDataFillIn = NULL; 503 UResourceBundle* shortData = NULL; 504 UResourceBundle* longData = NULL; 505 506 if (uprv_strcmp(numberingSystemName, gLatnTag) != 0) { 507 LocalUResourceBundlePointer localResource( 508 tryGetByKeyWithFallback(rb, numberingSystemName, NULL, NOT_ROOT, status)); 509 shortData = tryGetDecimalFallback( 510 localResource.getAlias(), gPatternsShort, &shortDataFillIn, NOT_ROOT, status); 511 longData = tryGetDecimalFallback( 512 localResource.getAlias(), gPatternsLong, &longDataFillIn, NOT_ROOT, status); 513 } 514 if (U_FAILURE(status)) { 515 ures_close(shortDataFillIn); 516 ures_close(longDataFillIn); 517 ures_close(rb); 518 return; 519 } 520 521 // If we haven't found UNUM_SHORT look in latn numbering system. We must 522 // succeed at finding UNUM_SHORT here. 523 if (shortData == NULL) { 524 LocalUResourceBundlePointer latnResource(tryGetByKeyWithFallback(rb, gLatnTag, NULL, MUST, status)); 525 shortData = tryGetDecimalFallback(latnResource.getAlias(), gPatternsShort, &shortDataFillIn, MUST, status); 526 if (longData == NULL) { 527 longData = tryGetDecimalFallback(latnResource.getAlias(), gPatternsLong, &longDataFillIn, ANY, status); 528 if (longData != NULL && isRoot(longData, status) && !isRoot(shortData, status)) { 529 longData = NULL; 530 } 531 } 532 } 533 initCDFLocaleStyleData(shortData, &result->shortData, status); 534 ures_close(shortDataFillIn); 535 if (U_FAILURE(status)) { 536 ures_close(longDataFillIn); 537 ures_close(rb); 538 } 539 540 if (longData == NULL) { 541 result->longData.setToBogus(); 542 } else { 543 initCDFLocaleStyleData(longData, &result->longData, status); 544 } 545 ures_close(longDataFillIn); 546 ures_close(rb); 547} 548 549/** 550 * tryGetDecimalFallback attempts to fetch the "decimalFormat" resource bundle 551 * with a particular style. style is either "patternsShort" or "patternsLong." 552 * FillIn, flags, and status work in the same way as in tryGetByKeyWithFallback. 553 */ 554static UResourceBundle* tryGetDecimalFallback(const UResourceBundle* numberSystemResource, const char* style, UResourceBundle** fillIn, FallbackFlags flags, UErrorCode& status) { 555 UResourceBundle* first = tryGetByKeyWithFallback(numberSystemResource, style, fillIn, flags, status); 556 UResourceBundle* second = tryGetByKeyWithFallback(first, gDecimalFormatTag, fillIn, flags, status); 557 if (fillIn == NULL) { 558 ures_close(first); 559 } 560 return second; 561} 562 563// tryGetByKeyWithFallback returns a sub-resource bundle that matches given 564// criteria or NULL if none found. rb is the resource bundle that we are 565// searching. If rb == NULL then this function behaves as if no sub-resource 566// is found; path is the key of the sub-resource, 567// (i.e "foo" but not "foo/bar"); If fillIn is NULL, caller must always call 568// ures_close() on returned resource. See below for example when fillIn is 569// not NULL. flags is ANY or NOT_ROOT. Optionally, these values 570// can be ored with MUST. MUST by itself is the same as ANY | MUST. 571// The locale of the returned sub-resource will either match the 572// flags or the returned sub-resouce will be NULL. If MUST is included in 573// flags, and not suitable sub-resource is found then in addition to returning 574// NULL, this function also sets status to U_MISSING_RESOURCE_ERROR. If MUST 575// is not included in flags, then this function just returns NULL if no 576// such sub-resource is found and will never set status to 577// U_MISSING_RESOURCE_ERROR. 578// 579// Example: This code first searches for "foo/bar" sub-resource without falling 580// back to ROOT. Then searches for "baz" sub-resource as last resort. 581// 582// UResourcebundle* fillIn = NULL; 583// UResourceBundle* data = tryGetByKeyWithFallback(rb, "foo", &fillIn, NON_ROOT, status); 584// data = tryGetByKeyWithFallback(data, "bar", &fillIn, NON_ROOT, status); 585// if (!data) { 586// data = tryGetbyKeyWithFallback(rb, "baz", &fillIn, MUST, status); 587// } 588// if (U_FAILURE(status)) { 589// ures_close(fillIn); 590// return; 591// } 592// doStuffWithNonNullSubresource(data); 593// 594// /* Wrong! don't do the following as it can leak memory if fillIn gets set 595// to NULL. */ 596// fillIn = tryGetByKeyWithFallback(rb, "wrong", &fillIn, ANY, status); 597// 598// ures_close(fillIn); 599// 600static UResourceBundle* tryGetByKeyWithFallback(const UResourceBundle* rb, const char* path, UResourceBundle** fillIn, FallbackFlags flags, UErrorCode& status) { 601 if (U_FAILURE(status)) { 602 return NULL; 603 } 604 UBool must = (flags & MUST); 605 if (rb == NULL) { 606 if (must) { 607 status = U_MISSING_RESOURCE_ERROR; 608 } 609 return NULL; 610 } 611 UResourceBundle* result = NULL; 612 UResourceBundle* ownedByUs = NULL; 613 if (fillIn == NULL) { 614 ownedByUs = ures_getByKeyWithFallback(rb, path, NULL, &status); 615 result = ownedByUs; 616 } else { 617 *fillIn = ures_getByKeyWithFallback(rb, path, *fillIn, &status); 618 result = *fillIn; 619 } 620 if (U_FAILURE(status)) { 621 ures_close(ownedByUs); 622 if (status == U_MISSING_RESOURCE_ERROR && !must) { 623 status = U_ZERO_ERROR; 624 } 625 return NULL; 626 } 627 flags = (FallbackFlags) (flags & ~MUST); 628 switch (flags) { 629 case NOT_ROOT: 630 { 631 UBool bRoot = isRoot(result, status); 632 if (bRoot || U_FAILURE(status)) { 633 ures_close(ownedByUs); 634 if (must && (status == U_ZERO_ERROR)) { 635 status = U_MISSING_RESOURCE_ERROR; 636 } 637 return NULL; 638 } 639 return result; 640 } 641 case ANY: 642 return result; 643 default: 644 ures_close(ownedByUs); 645 status = U_ILLEGAL_ARGUMENT_ERROR; 646 return NULL; 647 } 648} 649 650static UBool isRoot(const UResourceBundle* rb, UErrorCode& status) { 651 const char* actualLocale = ures_getLocaleByType( 652 rb, ULOC_ACTUAL_LOCALE, &status); 653 if (U_FAILURE(status)) { 654 return FALSE; 655 } 656 return uprv_strcmp(actualLocale, gRoot) == 0; 657} 658 659 660// initCDFLocaleStyleData loads formatting data for a particular style. 661// decimalFormatBundle is the "decimalFormat" resource bundle in CLDR. 662// Loaded data stored in result. 663static void initCDFLocaleStyleData(const UResourceBundle* decimalFormatBundle, CDFLocaleStyleData* result, UErrorCode& status) { 664 if (U_FAILURE(status)) { 665 return; 666 } 667 // Iterate through all the powers of 10. 668 int32_t size = ures_getSize(decimalFormatBundle); 669 UResourceBundle* power10 = NULL; 670 for (int32_t i = 0; i < size; ++i) { 671 power10 = ures_getByIndex(decimalFormatBundle, i, power10, &status); 672 if (U_FAILURE(status)) { 673 ures_close(power10); 674 return; 675 } 676 populatePower10(power10, result, status); 677 if (U_FAILURE(status)) { 678 ures_close(power10); 679 return; 680 } 681 } 682 ures_close(power10); 683 fillInMissing(result); 684} 685 686// populatePower10 grabs data for a particular power of 10 from CLDR. 687// The loaded data is stored in result. 688static void populatePower10(const UResourceBundle* power10Bundle, CDFLocaleStyleData* result, UErrorCode& status) { 689 if (U_FAILURE(status)) { 690 return; 691 } 692 char* endPtr = NULL; 693 double power10 = uprv_strtod(ures_getKey(power10Bundle), &endPtr); 694 if (*endPtr != 0) { 695 status = U_INTERNAL_PROGRAM_ERROR; 696 return; 697 } 698 int32_t log10Value = computeLog10(power10, FALSE); 699 // Silently ignore divisors that are too big. 700 if (log10Value == MAX_DIGITS) { 701 return; 702 } 703 int32_t size = ures_getSize(power10Bundle); 704 int32_t numZeros = 0; 705 UBool otherVariantDefined = FALSE; 706 UResourceBundle* variantBundle = NULL; 707 // Iterate over all the plural variants for the power of 10 708 for (int32_t i = 0; i < size; ++i) { 709 variantBundle = ures_getByIndex(power10Bundle, i, variantBundle, &status); 710 if (U_FAILURE(status)) { 711 ures_close(variantBundle); 712 return; 713 } 714 const char* variant = ures_getKey(variantBundle); 715 int32_t resLen; 716 const UChar* formatStrP = ures_getString(variantBundle, &resLen, &status); 717 if (U_FAILURE(status)) { 718 ures_close(variantBundle); 719 return; 720 } 721 UnicodeString formatStr(false, formatStrP, resLen); 722 if (uprv_strcmp(variant, gOther) == 0) { 723 otherVariantDefined = TRUE; 724 } 725 int32_t nz = populatePrefixSuffix( 726 variant, log10Value, formatStr, result->unitsByVariant, status); 727 if (U_FAILURE(status)) { 728 ures_close(variantBundle); 729 return; 730 } 731 if (nz != numZeros) { 732 // We expect all format strings to have the same number of 0's 733 // left of the decimal point. 734 if (numZeros != 0) { 735 status = U_INTERNAL_PROGRAM_ERROR; 736 ures_close(variantBundle); 737 return; 738 } 739 numZeros = nz; 740 } 741 } 742 ures_close(variantBundle); 743 // We expect to find an OTHER variant for each power of 10. 744 if (!otherVariantDefined) { 745 status = U_INTERNAL_PROGRAM_ERROR; 746 return; 747 } 748 double divisor = power10; 749 for (int32_t i = 1; i < numZeros; ++i) { 750 divisor /= 10.0; 751 } 752 result->divisors[log10Value] = divisor; 753} 754 755// populatePrefixSuffix Adds a specific prefix-suffix pair to result for a 756// given variant and log10 value. 757// variant is 'zero', 'one', 'two', 'few', 'many', or 'other'. 758// formatStr is the format string from which the prefix and suffix are 759// extracted. It is usually of form 'Pefix 000 suffix'. 760// populatePrefixSuffix returns the number of 0's found in formatStr 761// before the decimal point. 762// In the special case that formatStr contains only spaces for prefix 763// and suffix, populatePrefixSuffix returns log10Value + 1. 764static int32_t populatePrefixSuffix( 765 const char* variant, int32_t log10Value, const UnicodeString& formatStr, UHashtable* result, UErrorCode& status) { 766 if (U_FAILURE(status)) { 767 return 0; 768 } 769 int32_t firstIdx = formatStr.indexOf(kZero, UPRV_LENGTHOF(kZero), 0); 770 // We must have 0's in format string. 771 if (firstIdx == -1) { 772 status = U_INTERNAL_PROGRAM_ERROR; 773 return 0; 774 } 775 int32_t lastIdx = formatStr.lastIndexOf(kZero, UPRV_LENGTHOF(kZero), firstIdx); 776 CDFUnit* unit = createCDFUnit(variant, log10Value, result, status); 777 if (U_FAILURE(status)) { 778 return 0; 779 } 780 // Everything up to first 0 is the prefix 781 unit->prefix = formatStr.tempSubString(0, firstIdx); 782 fixQuotes(unit->prefix); 783 // Everything beyond the last 0 is the suffix 784 unit->suffix = formatStr.tempSubString(lastIdx + 1); 785 fixQuotes(unit->suffix); 786 787 // If there is effectively no prefix or suffix, ignore the actual number of 788 // 0's and act as if the number of 0's matches the size of the number. 789 if (onlySpaces(unit->prefix) && onlySpaces(unit->suffix)) { 790 return log10Value + 1; 791 } 792 793 // Calculate number of zeros before decimal point 794 int32_t idx = firstIdx + 1; 795 while (idx <= lastIdx && formatStr.charAt(idx) == u_0) { 796 ++idx; 797 } 798 return (idx - firstIdx); 799} 800 801static UBool onlySpaces(UnicodeString u) { 802 return u.trim().length() == 0; 803} 804 805// fixQuotes unescapes single quotes. Don''t -> Don't. Letter 'j' -> Letter j. 806// Modifies s in place. 807static void fixQuotes(UnicodeString& s) { 808 QuoteState state = OUTSIDE; 809 int32_t len = s.length(); 810 int32_t dest = 0; 811 for (int32_t i = 0; i < len; ++i) { 812 UChar ch = s.charAt(i); 813 if (ch == u_apos) { 814 if (state == INSIDE_EMPTY) { 815 s.setCharAt(dest, ch); 816 ++dest; 817 } 818 } else { 819 s.setCharAt(dest, ch); 820 ++dest; 821 } 822 823 // Update state 824 switch (state) { 825 case OUTSIDE: 826 state = ch == u_apos ? INSIDE_EMPTY : OUTSIDE; 827 break; 828 case INSIDE_EMPTY: 829 case INSIDE_FULL: 830 state = ch == u_apos ? OUTSIDE : INSIDE_FULL; 831 break; 832 default: 833 break; 834 } 835 } 836 s.truncate(dest); 837} 838 839// fillInMissing ensures that the data in result is complete. 840// result data is complete if for each variant in result, there exists 841// a prefix-suffix pair for each log10 value and there also exists 842// a divisor for each log10 value. 843// 844// First this function figures out for which log10 values, the other 845// variant already had data. These are the same log10 values defined 846// in CLDR. 847// 848// For each log10 value not defined in CLDR, it uses the divisor for 849// the last defined log10 value or 1. 850// 851// Then for each variant, it does the following. For each log10 852// value not defined in CLDR, copy the prefix-suffix pair from the 853// previous log10 value. If log10 value is defined in CLDR but is 854// missing from given variant, copy the prefix-suffix pair for that 855// log10 value from the 'other' variant. 856static void fillInMissing(CDFLocaleStyleData* result) { 857 const CDFUnit* otherUnits = 858 (const CDFUnit*) uhash_get(result->unitsByVariant, gOther); 859 UBool definedInCLDR[MAX_DIGITS]; 860 double lastDivisor = 1.0; 861 for (int32_t i = 0; i < MAX_DIGITS; ++i) { 862 if (!otherUnits[i].isSet()) { 863 result->divisors[i] = lastDivisor; 864 definedInCLDR[i] = FALSE; 865 } else { 866 lastDivisor = result->divisors[i]; 867 definedInCLDR[i] = TRUE; 868 } 869 } 870 // Iterate over each variant. 871 int32_t pos = UHASH_FIRST; 872 const UHashElement* element = uhash_nextElement(result->unitsByVariant, &pos); 873 for (;element != NULL; element = uhash_nextElement(result->unitsByVariant, &pos)) { 874 CDFUnit* units = (CDFUnit*) element->value.pointer; 875 for (int32_t i = 0; i < MAX_DIGITS; ++i) { 876 if (definedInCLDR[i]) { 877 if (!units[i].isSet()) { 878 units[i] = otherUnits[i]; 879 } 880 } else { 881 if (i == 0) { 882 units[0].markAsSet(); 883 } else { 884 units[i] = units[i - 1]; 885 } 886 } 887 } 888 } 889} 890 891// computeLog10 computes floor(log10(x)). If inRange is TRUE, the biggest 892// value computeLog10 will return MAX_DIGITS -1 even for 893// numbers > 10^MAX_DIGITS. If inRange is FALSE, computeLog10 will return 894// up to MAX_DIGITS. 895static int32_t computeLog10(double x, UBool inRange) { 896 int32_t result = 0; 897 int32_t max = inRange ? MAX_DIGITS - 1 : MAX_DIGITS; 898 while (x >= 10.0) { 899 x /= 10.0; 900 ++result; 901 if (result == max) { 902 break; 903 } 904 } 905 return result; 906} 907 908// createCDFUnit returns a pointer to the prefix-suffix pair for a given 909// variant and log10 value within table. If no such prefix-suffix pair is 910// stored in table, one is created within table before returning pointer. 911static CDFUnit* createCDFUnit(const char* variant, int32_t log10Value, UHashtable* table, UErrorCode& status) { 912 if (U_FAILURE(status)) { 913 return NULL; 914 } 915 CDFUnit *cdfUnit = (CDFUnit*) uhash_get(table, variant); 916 if (cdfUnit == NULL) { 917 cdfUnit = new CDFUnit[MAX_DIGITS]; 918 if (cdfUnit == NULL) { 919 status = U_MEMORY_ALLOCATION_ERROR; 920 return NULL; 921 } 922 uhash_put(table, uprv_strdup(variant), cdfUnit, &status); 923 if (U_FAILURE(status)) { 924 return NULL; 925 } 926 } 927 CDFUnit* result = &cdfUnit[log10Value]; 928 result->markAsSet(); 929 return result; 930} 931 932// getCDFUnitFallback returns a pointer to the prefix-suffix pair for a given 933// variant and log10 value within table. If the given variant doesn't exist, it 934// falls back to the OTHER variant. Therefore, this method will always return 935// some non-NULL value. 936static const CDFUnit* getCDFUnitFallback(const UHashtable* table, const UnicodeString& variant, int32_t log10Value) { 937 CharString cvariant; 938 UErrorCode status = U_ZERO_ERROR; 939 const CDFUnit *cdfUnit = NULL; 940 cvariant.appendInvariantChars(variant, status); 941 if (!U_FAILURE(status)) { 942 cdfUnit = (const CDFUnit*) uhash_get(table, cvariant.data()); 943 } 944 if (cdfUnit == NULL) { 945 cdfUnit = (const CDFUnit*) uhash_get(table, gOther); 946 } 947 return &cdfUnit[log10Value]; 948} 949 950U_NAMESPACE_END 951#endif 952