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. 32static UHashtable* gCompactDecimalData = NULL; 33static UMutex gCompactDecimalMetaLock = U_MUTEX_INITIALIZER; 34 35U_NAMESPACE_BEGIN 36 37static const int32_t MAX_DIGITS = 15; 38static const char gOther[] = "other"; 39static const char gLatnTag[] = "latn"; 40static const char gNumberElementsTag[] = "NumberElements"; 41static const char gDecimalFormatTag[] = "decimalFormat"; 42static const char gPatternsShort[] = "patternsShort"; 43static const char gPatternsLong[] = "patternsLong"; 44static const char gRoot[] = "root"; 45 46static const UChar u_0 = 0x30; 47static const UChar u_apos = 0x27; 48 49static const UChar kZero[] = {u_0}; 50 51// Used to unescape single quotes. 52enum QuoteState { 53 OUTSIDE, 54 INSIDE_EMPTY, 55 INSIDE_FULL 56}; 57 58enum 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. 68struct 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. 85class 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. 113struct 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 122U_NAMESPACE_END 123 124U_CDECL_BEGIN 125 126static UBool U_CALLCONV cdf_cleanup(void) { 127 if (gCompactDecimalData != NULL) { 128 uhash_close(gCompactDecimalData); 129 gCompactDecimalData = NULL; 130 } 131 return TRUE; 132} 133 134static void U_CALLCONV deleteCDFUnits(void* ptr) { 135 delete [] (icu::CDFUnit*) ptr; 136} 137 138static void U_CALLCONV deleteCDFLocaleData(void* ptr) { 139 delete (icu::CDFLocaleData*) ptr; 140} 141 142U_CDECL_END 143 144U_NAMESPACE_BEGIN 145 146static UBool divisors_equal(const double* lhs, const double* rhs); 147static const CDFLocaleStyleData* getCDFLocaleStyleData(const Locale& inLocale, UNumberCompactStyle style, UErrorCode& status); 148 149static const CDFLocaleStyleData* extractDataByStyleEnum(const CDFLocaleData& data, UNumberCompactStyle style, UErrorCode& status); 150static CDFLocaleData* loadCDFLocaleData(const Locale& inLocale, UErrorCode& status); 151static void initCDFLocaleData(const Locale& inLocale, CDFLocaleData* result, UErrorCode& status); 152static UResourceBundle* tryGetDecimalFallback(const UResourceBundle* numberSystemResource, const char* style, UResourceBundle** fillIn, FallbackFlags flags, UErrorCode& status); 153static UResourceBundle* tryGetByKeyWithFallback(const UResourceBundle* rb, const char* path, UResourceBundle** fillIn, FallbackFlags flags, UErrorCode& status); 154static UBool isRoot(const UResourceBundle* rb, UErrorCode& status); 155static void initCDFLocaleStyleData(const UResourceBundle* decimalFormatBundle, CDFLocaleStyleData* result, UErrorCode& status); 156static void populatePower10(const UResourceBundle* power10Bundle, CDFLocaleStyleData* result, UErrorCode& status); 157static int32_t populatePrefixSuffix(const char* variant, int32_t log10Value, const UnicodeString& formatStr, UHashtable* result, UErrorCode& status); 158static UBool onlySpaces(UnicodeString u); 159static void fixQuotes(UnicodeString& s); 160static void fillInMissing(CDFLocaleStyleData* result); 161static int32_t computeLog10(double x, UBool inRange); 162static CDFUnit* createCDFUnit(const char* variant, int32_t log10Value, UHashtable* table, UErrorCode& status); 163static const CDFUnit* getCDFUnitFallback(const UHashtable* table, const UnicodeString& variant, int32_t log10Value); 164 165UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CompactDecimalFormat) 166 167CompactDecimalFormat::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 175CompactDecimalFormat::CompactDecimalFormat(const CompactDecimalFormat& source) 176 : DecimalFormat(source), _unitsByVariant(source._unitsByVariant), _divisors(source._divisors), _pluralRules(source._pluralRules->clone()) { 177} 178 179CompactDecimalFormat* U_EXPORT2 180CompactDecimalFormat::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 207CompactDecimalFormat& 208CompactDecimalFormat::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 219CompactDecimalFormat::~CompactDecimalFormat() { 220 delete _pluralRules; 221} 222 223 224Format* 225CompactDecimalFormat::clone(void) const { 226 return new CompactDecimalFormat(*this); 227} 228 229UBool 230CompactDecimalFormat::operator==(const Format& that) const { 231 if (this == &that) { 232 return TRUE; 233 } 234 return (DecimalFormat::operator==(that) && eqHelper((const CompactDecimalFormat&) that)); 235} 236 237UBool 238CompactDecimalFormat::eqHelper(const CompactDecimalFormat& that) const { 239 return uhash_equals(_unitsByVariant, that._unitsByVariant) && divisors_equal(_divisors, that._divisors) && (*_pluralRules == *that._pluralRules); 240} 241 242UnicodeString& 243CompactDecimalFormat::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 272UnicodeString& 273CompactDecimalFormat::format( 274 double /* number */, 275 UnicodeString& appendTo, 276 FieldPositionIterator* /* posIter */, 277 UErrorCode& status) const { 278 status = U_UNSUPPORTED_ERROR; 279 return appendTo; 280} 281 282UnicodeString& 283CompactDecimalFormat::format( 284 int64_t number, 285 UnicodeString& appendTo, 286 FieldPosition& pos) const { 287 return format((double) number, appendTo, pos); 288} 289 290UnicodeString& 291CompactDecimalFormat::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 300UnicodeString& 301CompactDecimalFormat::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 310UnicodeString& 311CompactDecimalFormat::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 320UnicodeString& 321CompactDecimalFormat::format(const DigitList& /* number */, 322 UnicodeString& appendTo, 323 FieldPosition& /* pos */, 324 UErrorCode& status) const { 325 status = U_UNSUPPORTED_ERROR; 326 return appendTo; 327} 328 329void 330CompactDecimalFormat::parse( 331 const UnicodeString& /* text */, 332 Formattable& /* result */, 333 ParsePosition& /* parsePosition */) const { 334} 335 336void 337CompactDecimalFormat::parse( 338 const UnicodeString& /* text */, 339 Formattable& /* result */, 340 UErrorCode& status) const { 341 status = U_UNSUPPORTED_ERROR; 342} 343 344CurrencyAmount* 345CompactDecimalFormat::parseCurrency( 346 const UnicodeString& /* text */, 347 ParsePosition& /* pos */) const { 348 return NULL; 349} 350 351void 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 363CDFLocaleStyleData::~CDFLocaleStyleData() { 364 setToBogus(); 365} 366 367void CDFLocaleStyleData::setToBogus() { 368 if (unitsByVariant != NULL) { 369 uhash_close(unitsByVariant); 370 unitsByVariant = NULL; 371 } 372} 373 374void 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= 383static 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. 398static 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 443static 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. 460static 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. 491static 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 */ 556static 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// 602static 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 652static 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. 665static 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. 690static 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. 766static 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 803static 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. 809static 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. 858static 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. 897static 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. 913static 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. 938static 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 952U_NAMESPACE_END 953#endif 954