1 // 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ******************************************************************************* 5 * Copyright (C) 2007-2016, International Business Machines Corporation and 6 * others. All Rights Reserved. 7 ******************************************************************************* 8 * 9 * File plurrule.cpp 10 */ 11 12 #include <math.h> 13 #include <stdio.h> 14 15 #include "unicode/utypes.h" 16 #include "unicode/localpointer.h" 17 #include "unicode/plurrule.h" 18 #include "unicode/upluralrules.h" 19 #include "unicode/ures.h" 20 #include "unicode/numfmt.h" 21 #include "unicode/decimfmt.h" 22 #include "charstr.h" 23 #include "cmemory.h" 24 #include "cstring.h" 25 #include "digitlst.h" 26 #include "hash.h" 27 #include "locutil.h" 28 #include "mutex.h" 29 #include "patternprops.h" 30 #include "plurrule_impl.h" 31 #include "putilimp.h" 32 #include "ucln_in.h" 33 #include "ustrfmt.h" 34 #include "uassert.h" 35 #include "uvectr32.h" 36 #include "sharedpluralrules.h" 37 #include "unifiedcache.h" 38 #include "digitinterval.h" 39 #include "visibledigits.h" 40 41 #if !UCONFIG_NO_FORMATTING 42 43 U_NAMESPACE_BEGIN 44 45 static const UChar PLURAL_KEYWORD_OTHER[]={LOW_O,LOW_T,LOW_H,LOW_E,LOW_R,0}; 46 static const UChar PLURAL_DEFAULT_RULE[]={LOW_O,LOW_T,LOW_H,LOW_E,LOW_R,COLON,SPACE,LOW_N,0}; 47 static const UChar PK_IN[]={LOW_I,LOW_N,0}; 48 static const UChar PK_NOT[]={LOW_N,LOW_O,LOW_T,0}; 49 static const UChar PK_IS[]={LOW_I,LOW_S,0}; 50 static const UChar PK_MOD[]={LOW_M,LOW_O,LOW_D,0}; 51 static const UChar PK_AND[]={LOW_A,LOW_N,LOW_D,0}; 52 static const UChar PK_OR[]={LOW_O,LOW_R,0}; 53 static const UChar PK_VAR_N[]={LOW_N,0}; 54 static const UChar PK_VAR_I[]={LOW_I,0}; 55 static const UChar PK_VAR_F[]={LOW_F,0}; 56 static const UChar PK_VAR_T[]={LOW_T,0}; 57 static const UChar PK_VAR_V[]={LOW_V,0}; 58 static const UChar PK_WITHIN[]={LOW_W,LOW_I,LOW_T,LOW_H,LOW_I,LOW_N,0}; 59 static const UChar PK_DECIMAL[]={LOW_D,LOW_E,LOW_C,LOW_I,LOW_M,LOW_A,LOW_L,0}; 60 static const UChar PK_INTEGER[]={LOW_I,LOW_N,LOW_T,LOW_E,LOW_G,LOW_E,LOW_R,0}; 61 62 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralRules) 63 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralKeywordEnumeration) 64 65 PluralRules::PluralRules(UErrorCode& /*status*/) 66 : UObject(), 67 mRules(NULL) 68 { 69 } 70 71 PluralRules::PluralRules(const PluralRules& other) 72 : UObject(other), 73 mRules(NULL) 74 { 75 *this=other; 76 } 77 78 PluralRules::~PluralRules() { 79 delete mRules; 80 } 81 82 SharedPluralRules::~SharedPluralRules() { 83 delete ptr; 84 } 85 86 PluralRules* 87 PluralRules::clone() const { 88 return new PluralRules(*this); 89 } 90 91 PluralRules& 92 PluralRules::operator=(const PluralRules& other) { 93 if (this != &other) { 94 delete mRules; 95 if (other.mRules==NULL) { 96 mRules = NULL; 97 } 98 else { 99 mRules = new RuleChain(*other.mRules); 100 } 101 } 102 103 return *this; 104 } 105 106 StringEnumeration* PluralRules::getAvailableLocales(UErrorCode &status) { 107 StringEnumeration *result = new PluralAvailableLocalesEnumeration(status); 108 if (result == NULL && U_SUCCESS(status)) { 109 status = U_MEMORY_ALLOCATION_ERROR; 110 } 111 if (U_FAILURE(status)) { 112 delete result; 113 result = NULL; 114 } 115 return result; 116 } 117 118 119 PluralRules* U_EXPORT2 120 PluralRules::createRules(const UnicodeString& description, UErrorCode& status) { 121 if (U_FAILURE(status)) { 122 return NULL; 123 } 124 125 PluralRuleParser parser; 126 PluralRules *newRules = new PluralRules(status); 127 if (U_SUCCESS(status) && newRules == NULL) { 128 status = U_MEMORY_ALLOCATION_ERROR; 129 } 130 parser.parse(description, newRules, status); 131 if (U_FAILURE(status)) { 132 delete newRules; 133 newRules = NULL; 134 } 135 return newRules; 136 } 137 138 139 PluralRules* U_EXPORT2 140 PluralRules::createDefaultRules(UErrorCode& status) { 141 return createRules(UnicodeString(TRUE, PLURAL_DEFAULT_RULE, -1), status); 142 } 143 144 /******************************************************************************/ 145 /* Create PluralRules cache */ 146 147 template<> U_I18N_API 148 const SharedPluralRules *LocaleCacheKey<SharedPluralRules>::createObject( 149 const void * /*unused*/, UErrorCode &status) const { 150 const char *localeId = fLoc.getName(); 151 PluralRules *pr = PluralRules::internalForLocale( 152 localeId, UPLURAL_TYPE_CARDINAL, status); 153 if (U_FAILURE(status)) { 154 return NULL; 155 } 156 SharedPluralRules *result = new SharedPluralRules(pr); 157 if (result == NULL) { 158 status = U_MEMORY_ALLOCATION_ERROR; 159 delete pr; 160 return NULL; 161 } 162 result->addRef(); 163 return result; 164 } 165 166 /* end plural rules cache */ 167 /******************************************************************************/ 168 169 const SharedPluralRules* U_EXPORT2 170 PluralRules::createSharedInstance( 171 const Locale& locale, UPluralType type, UErrorCode& status) { 172 if (U_FAILURE(status)) { 173 return NULL; 174 } 175 if (type != UPLURAL_TYPE_CARDINAL) { 176 status = U_UNSUPPORTED_ERROR; 177 return NULL; 178 } 179 const SharedPluralRules *result = NULL; 180 UnifiedCache::getByLocale(locale, result, status); 181 return result; 182 } 183 184 PluralRules* U_EXPORT2 185 PluralRules::forLocale(const Locale& locale, UErrorCode& status) { 186 return forLocale(locale, UPLURAL_TYPE_CARDINAL, status); 187 } 188 189 PluralRules* U_EXPORT2 190 PluralRules::forLocale(const Locale& locale, UPluralType type, UErrorCode& status) { 191 if (type != UPLURAL_TYPE_CARDINAL) { 192 return internalForLocale(locale, type, status); 193 } 194 const SharedPluralRules *shared = createSharedInstance( 195 locale, type, status); 196 if (U_FAILURE(status)) { 197 return NULL; 198 } 199 PluralRules *result = (*shared)->clone(); 200 shared->removeRef(); 201 if (result == NULL) { 202 status = U_MEMORY_ALLOCATION_ERROR; 203 } 204 return result; 205 } 206 207 PluralRules* U_EXPORT2 208 PluralRules::internalForLocale(const Locale& locale, UPluralType type, UErrorCode& status) { 209 if (U_FAILURE(status)) { 210 return NULL; 211 } 212 if (type >= UPLURAL_TYPE_COUNT) { 213 status = U_ILLEGAL_ARGUMENT_ERROR; 214 return NULL; 215 } 216 PluralRules *newObj = new PluralRules(status); 217 if (newObj==NULL || U_FAILURE(status)) { 218 delete newObj; 219 return NULL; 220 } 221 UnicodeString locRule = newObj->getRuleFromResource(locale, type, status); 222 // TODO: which errors, if any, should be returned? 223 if (locRule.length() == 0) { 224 // Locales with no specific rules (all numbers have the "other" category 225 // will return a U_MISSING_RESOURCE_ERROR at this point. This is not 226 // an error. 227 locRule = UnicodeString(PLURAL_DEFAULT_RULE); 228 status = U_ZERO_ERROR; 229 } 230 PluralRuleParser parser; 231 parser.parse(locRule, newObj, status); 232 // TODO: should rule parse errors be returned, or 233 // should we silently use default rules? 234 // Original impl used default rules. 235 // Ask the question to ICU Core. 236 237 return newObj; 238 } 239 240 UnicodeString 241 PluralRules::select(int32_t number) const { 242 return select(FixedDecimal(number)); 243 } 244 245 UnicodeString 246 PluralRules::select(double number) const { 247 return select(FixedDecimal(number)); 248 } 249 250 UnicodeString 251 PluralRules::select(const Formattable& obj, const NumberFormat& fmt, UErrorCode& status) const { 252 if (U_SUCCESS(status)) { 253 const DecimalFormat *decFmt = dynamic_cast<const DecimalFormat *>(&fmt); 254 if (decFmt != NULL) { 255 VisibleDigitsWithExponent digits; 256 decFmt->initVisibleDigitsWithExponent(obj, digits, status); 257 if (U_SUCCESS(status)) { 258 return select(digits); 259 } 260 } else { 261 double number = obj.getDouble(status); 262 if (U_SUCCESS(status)) { 263 return select(number); 264 } 265 } 266 } 267 return UnicodeString(); 268 } 269 270 UnicodeString 271 PluralRules::select(const IFixedDecimal &number) const { 272 if (mRules == NULL) { 273 return UnicodeString(TRUE, PLURAL_DEFAULT_RULE, -1); 274 } 275 else { 276 return mRules->select(number); 277 } 278 } 279 280 UnicodeString 281 PluralRules::select(const VisibleDigitsWithExponent &number) const { 282 if (number.getExponent() != NULL) { 283 return UnicodeString(TRUE, PLURAL_DEFAULT_RULE, -1); 284 } 285 return select(FixedDecimal(number.getMantissa())); 286 } 287 288 289 290 StringEnumeration* 291 PluralRules::getKeywords(UErrorCode& status) const { 292 if (U_FAILURE(status)) return NULL; 293 StringEnumeration* nameEnumerator = new PluralKeywordEnumeration(mRules, status); 294 if (U_FAILURE(status)) { 295 delete nameEnumerator; 296 return NULL; 297 } 298 299 return nameEnumerator; 300 } 301 302 double 303 PluralRules::getUniqueKeywordValue(const UnicodeString& /* keyword */) { 304 // Not Implemented. 305 return UPLRULES_NO_UNIQUE_VALUE; 306 } 307 308 int32_t 309 PluralRules::getAllKeywordValues(const UnicodeString & /* keyword */, double * /* dest */, 310 int32_t /* destCapacity */, UErrorCode& error) { 311 error = U_UNSUPPORTED_ERROR; 312 return 0; 313 } 314 315 316 static double scaleForInt(double d) { 317 double scale = 1.0; 318 while (d != floor(d)) { 319 d = d * 10.0; 320 scale = scale * 10.0; 321 } 322 return scale; 323 } 324 325 static int32_t 326 getSamplesFromString(const UnicodeString &samples, double *dest, 327 int32_t destCapacity, UErrorCode& status) { 328 int32_t sampleCount = 0; 329 int32_t sampleStartIdx = 0; 330 int32_t sampleEndIdx = 0; 331 332 //std::string ss; // TODO: debugging. 333 // std::cout << "PluralRules::getSamples(), samples = \"" << samples.toUTF8String(ss) << "\"\n"; 334 for (sampleCount = 0; sampleCount < destCapacity && sampleStartIdx < samples.length(); ) { 335 sampleEndIdx = samples.indexOf(COMMA, sampleStartIdx); 336 if (sampleEndIdx == -1) { 337 sampleEndIdx = samples.length(); 338 } 339 const UnicodeString &sampleRange = samples.tempSubStringBetween(sampleStartIdx, sampleEndIdx); 340 // ss.erase(); 341 // std::cout << "PluralRules::getSamples(), samplesRange = \"" << sampleRange.toUTF8String(ss) << "\"\n"; 342 int32_t tildeIndex = sampleRange.indexOf(TILDE); 343 if (tildeIndex < 0) { 344 FixedDecimal fixed(sampleRange, status); 345 double sampleValue = fixed.source; 346 if (fixed.visibleDecimalDigitCount == 0 || sampleValue != floor(sampleValue)) { 347 dest[sampleCount++] = sampleValue; 348 } 349 } else { 350 351 FixedDecimal fixedLo(sampleRange.tempSubStringBetween(0, tildeIndex), status); 352 FixedDecimal fixedHi(sampleRange.tempSubStringBetween(tildeIndex+1), status); 353 double rangeLo = fixedLo.source; 354 double rangeHi = fixedHi.source; 355 if (U_FAILURE(status)) { 356 break; 357 } 358 if (rangeHi < rangeLo) { 359 status = U_INVALID_FORMAT_ERROR; 360 break; 361 } 362 363 // For ranges of samples with fraction decimal digits, scale the number up so that we 364 // are adding one in the units place. Avoids roundoffs from repetitive adds of tenths. 365 366 double scale = scaleForInt(rangeLo); 367 double t = scaleForInt(rangeHi); 368 if (t > scale) { 369 scale = t; 370 } 371 rangeLo *= scale; 372 rangeHi *= scale; 373 for (double n=rangeLo; n<=rangeHi; n+=1) { 374 // Hack Alert: don't return any decimal samples with integer values that 375 // originated from a format with trailing decimals. 376 // This API is returning doubles, which can't distinguish having displayed 377 // zeros to the right of the decimal. 378 // This results in test failures with values mapping back to a different keyword. 379 double sampleValue = n/scale; 380 if (!(sampleValue == floor(sampleValue) && fixedLo.visibleDecimalDigitCount > 0)) { 381 dest[sampleCount++] = sampleValue; 382 } 383 if (sampleCount >= destCapacity) { 384 break; 385 } 386 } 387 } 388 sampleStartIdx = sampleEndIdx + 1; 389 } 390 return sampleCount; 391 } 392 393 394 int32_t 395 PluralRules::getSamples(const UnicodeString &keyword, double *dest, 396 int32_t destCapacity, UErrorCode& status) { 397 RuleChain *rc = rulesForKeyword(keyword); 398 if (rc == NULL || destCapacity == 0 || U_FAILURE(status)) { 399 return 0; 400 } 401 int32_t numSamples = getSamplesFromString(rc->fIntegerSamples, dest, destCapacity, status); 402 if (numSamples == 0) { 403 numSamples = getSamplesFromString(rc->fDecimalSamples, dest, destCapacity, status); 404 } 405 return numSamples; 406 } 407 408 409 RuleChain *PluralRules::rulesForKeyword(const UnicodeString &keyword) const { 410 RuleChain *rc; 411 for (rc = mRules; rc != NULL; rc = rc->fNext) { 412 if (rc->fKeyword == keyword) { 413 break; 414 } 415 } 416 return rc; 417 } 418 419 420 UBool 421 PluralRules::isKeyword(const UnicodeString& keyword) const { 422 if (0 == keyword.compare(PLURAL_KEYWORD_OTHER, 5)) { 423 return true; 424 } 425 return rulesForKeyword(keyword) != NULL; 426 } 427 428 UnicodeString 429 PluralRules::getKeywordOther() const { 430 return UnicodeString(TRUE, PLURAL_KEYWORD_OTHER, 5); 431 } 432 433 UBool 434 PluralRules::operator==(const PluralRules& other) const { 435 const UnicodeString *ptrKeyword; 436 UErrorCode status= U_ZERO_ERROR; 437 438 if ( this == &other ) { 439 return TRUE; 440 } 441 LocalPointer<StringEnumeration> myKeywordList(getKeywords(status)); 442 LocalPointer<StringEnumeration> otherKeywordList(other.getKeywords(status)); 443 if (U_FAILURE(status)) { 444 return FALSE; 445 } 446 447 if (myKeywordList->count(status)!=otherKeywordList->count(status)) { 448 return FALSE; 449 } 450 myKeywordList->reset(status); 451 while ((ptrKeyword=myKeywordList->snext(status))!=NULL) { 452 if (!other.isKeyword(*ptrKeyword)) { 453 return FALSE; 454 } 455 } 456 otherKeywordList->reset(status); 457 while ((ptrKeyword=otherKeywordList->snext(status))!=NULL) { 458 if (!this->isKeyword(*ptrKeyword)) { 459 return FALSE; 460 } 461 } 462 if (U_FAILURE(status)) { 463 return FALSE; 464 } 465 466 return TRUE; 467 } 468 469 470 void 471 PluralRuleParser::parse(const UnicodeString& ruleData, PluralRules *prules, UErrorCode &status) 472 { 473 if (U_FAILURE(status)) { 474 return; 475 } 476 U_ASSERT(ruleIndex == 0); // Parsers are good for a single use only! 477 ruleSrc = &ruleData; 478 479 while (ruleIndex< ruleSrc->length()) { 480 getNextToken(status); 481 if (U_FAILURE(status)) { 482 return; 483 } 484 checkSyntax(status); 485 if (U_FAILURE(status)) { 486 return; 487 } 488 switch (type) { 489 case tAnd: 490 U_ASSERT(curAndConstraint != NULL); 491 curAndConstraint = curAndConstraint->add(); 492 break; 493 case tOr: 494 { 495 U_ASSERT(currentChain != NULL); 496 OrConstraint *orNode=currentChain->ruleHeader; 497 while (orNode->next != NULL) { 498 orNode = orNode->next; 499 } 500 orNode->next= new OrConstraint(); 501 orNode=orNode->next; 502 orNode->next=NULL; 503 curAndConstraint = orNode->add(); 504 } 505 break; 506 case tIs: 507 U_ASSERT(curAndConstraint != NULL); 508 U_ASSERT(curAndConstraint->value == -1); 509 U_ASSERT(curAndConstraint->rangeList == NULL); 510 break; 511 case tNot: 512 U_ASSERT(curAndConstraint != NULL); 513 curAndConstraint->negated=TRUE; 514 break; 515 516 case tNotEqual: 517 curAndConstraint->negated=TRUE; 518 U_FALLTHROUGH; 519 case tIn: 520 case tWithin: 521 case tEqual: 522 U_ASSERT(curAndConstraint != NULL); 523 curAndConstraint->rangeList = new UVector32(status); 524 curAndConstraint->rangeList->addElement(-1, status); // range Low 525 curAndConstraint->rangeList->addElement(-1, status); // range Hi 526 rangeLowIdx = 0; 527 rangeHiIdx = 1; 528 curAndConstraint->value=PLURAL_RANGE_HIGH; 529 curAndConstraint->integerOnly = (type != tWithin); 530 break; 531 case tNumber: 532 U_ASSERT(curAndConstraint != NULL); 533 if ( (curAndConstraint->op==AndConstraint::MOD)&& 534 (curAndConstraint->opNum == -1 ) ) { 535 curAndConstraint->opNum=getNumberValue(token); 536 } 537 else { 538 if (curAndConstraint->rangeList == NULL) { 539 // this is for an 'is' rule 540 curAndConstraint->value = getNumberValue(token); 541 } else { 542 // this is for an 'in' or 'within' rule 543 if (curAndConstraint->rangeList->elementAti(rangeLowIdx) == -1) { 544 curAndConstraint->rangeList->setElementAt(getNumberValue(token), rangeLowIdx); 545 curAndConstraint->rangeList->setElementAt(getNumberValue(token), rangeHiIdx); 546 } 547 else { 548 curAndConstraint->rangeList->setElementAt(getNumberValue(token), rangeHiIdx); 549 if (curAndConstraint->rangeList->elementAti(rangeLowIdx) > 550 curAndConstraint->rangeList->elementAti(rangeHiIdx)) { 551 // Range Lower bound > Range Upper bound. 552 // U_UNEXPECTED_TOKEN seems a little funny, but it is consistently 553 // used for all plural rule parse errors. 554 status = U_UNEXPECTED_TOKEN; 555 break; 556 } 557 } 558 } 559 } 560 break; 561 case tComma: 562 // TODO: rule syntax checking is inadequate, can happen with badly formed rules. 563 // Catch cases like "n mod 10, is 1" here instead. 564 if (curAndConstraint == NULL || curAndConstraint->rangeList == NULL) { 565 status = U_UNEXPECTED_TOKEN; 566 break; 567 } 568 U_ASSERT(curAndConstraint->rangeList->size() >= 2); 569 rangeLowIdx = curAndConstraint->rangeList->size(); 570 curAndConstraint->rangeList->addElement(-1, status); // range Low 571 rangeHiIdx = curAndConstraint->rangeList->size(); 572 curAndConstraint->rangeList->addElement(-1, status); // range Hi 573 break; 574 case tMod: 575 U_ASSERT(curAndConstraint != NULL); 576 curAndConstraint->op=AndConstraint::MOD; 577 break; 578 case tVariableN: 579 case tVariableI: 580 case tVariableF: 581 case tVariableT: 582 case tVariableV: 583 U_ASSERT(curAndConstraint != NULL); 584 curAndConstraint->digitsType = type; 585 break; 586 case tKeyword: 587 { 588 RuleChain *newChain = new RuleChain; 589 if (newChain == NULL) { 590 status = U_MEMORY_ALLOCATION_ERROR; 591 break; 592 } 593 newChain->fKeyword = token; 594 if (prules->mRules == NULL) { 595 prules->mRules = newChain; 596 } else { 597 // The new rule chain goes at the end of the linked list of rule chains, 598 // unless there is an "other" keyword & chain. "other" must remain last. 599 RuleChain *insertAfter = prules->mRules; 600 while (insertAfter->fNext!=NULL && 601 insertAfter->fNext->fKeyword.compare(PLURAL_KEYWORD_OTHER, 5) != 0 ){ 602 insertAfter=insertAfter->fNext; 603 } 604 newChain->fNext = insertAfter->fNext; 605 insertAfter->fNext = newChain; 606 } 607 OrConstraint *orNode = new OrConstraint(); 608 newChain->ruleHeader = orNode; 609 curAndConstraint = orNode->add(); 610 currentChain = newChain; 611 } 612 break; 613 614 case tInteger: 615 for (;;) { 616 getNextToken(status); 617 if (U_FAILURE(status) || type == tSemiColon || type == tEOF || type == tAt) { 618 break; 619 } 620 if (type == tEllipsis) { 621 currentChain->fIntegerSamplesUnbounded = TRUE; 622 continue; 623 } 624 currentChain->fIntegerSamples.append(token); 625 } 626 break; 627 628 case tDecimal: 629 for (;;) { 630 getNextToken(status); 631 if (U_FAILURE(status) || type == tSemiColon || type == tEOF || type == tAt) { 632 break; 633 } 634 if (type == tEllipsis) { 635 currentChain->fDecimalSamplesUnbounded = TRUE; 636 continue; 637 } 638 currentChain->fDecimalSamples.append(token); 639 } 640 break; 641 642 default: 643 break; 644 } 645 prevType=type; 646 if (U_FAILURE(status)) { 647 break; 648 } 649 } 650 } 651 652 UnicodeString 653 PluralRules::getRuleFromResource(const Locale& locale, UPluralType type, UErrorCode& errCode) { 654 UnicodeString emptyStr; 655 656 if (U_FAILURE(errCode)) { 657 return emptyStr; 658 } 659 LocalUResourceBundlePointer rb(ures_openDirect(NULL, "plurals", &errCode)); 660 if(U_FAILURE(errCode)) { 661 return emptyStr; 662 } 663 const char *typeKey; 664 switch (type) { 665 case UPLURAL_TYPE_CARDINAL: 666 typeKey = "locales"; 667 break; 668 case UPLURAL_TYPE_ORDINAL: 669 typeKey = "locales_ordinals"; 670 break; 671 default: 672 // Must not occur: The caller should have checked for valid types. 673 errCode = U_ILLEGAL_ARGUMENT_ERROR; 674 return emptyStr; 675 } 676 LocalUResourceBundlePointer locRes(ures_getByKey(rb.getAlias(), typeKey, NULL, &errCode)); 677 if(U_FAILURE(errCode)) { 678 return emptyStr; 679 } 680 int32_t resLen=0; 681 const char *curLocaleName=locale.getName(); 682 const UChar* s = ures_getStringByKey(locRes.getAlias(), curLocaleName, &resLen, &errCode); 683 684 if (s == NULL) { 685 // Check parent locales. 686 UErrorCode status = U_ZERO_ERROR; 687 char parentLocaleName[ULOC_FULLNAME_CAPACITY]; 688 const char *curLocaleName=locale.getName(); 689 uprv_strcpy(parentLocaleName, curLocaleName); 690 691 while (uloc_getParent(parentLocaleName, parentLocaleName, 692 ULOC_FULLNAME_CAPACITY, &status) > 0) { 693 resLen=0; 694 s = ures_getStringByKey(locRes.getAlias(), parentLocaleName, &resLen, &status); 695 if (s != NULL) { 696 errCode = U_ZERO_ERROR; 697 break; 698 } 699 status = U_ZERO_ERROR; 700 } 701 } 702 if (s==NULL) { 703 return emptyStr; 704 } 705 706 char setKey[256]; 707 u_UCharsToChars(s, setKey, resLen + 1); 708 // printf("\n PluralRule: %s\n", setKey); 709 710 LocalUResourceBundlePointer ruleRes(ures_getByKey(rb.getAlias(), "rules", NULL, &errCode)); 711 if(U_FAILURE(errCode)) { 712 return emptyStr; 713 } 714 LocalUResourceBundlePointer setRes(ures_getByKey(ruleRes.getAlias(), setKey, NULL, &errCode)); 715 if (U_FAILURE(errCode)) { 716 return emptyStr; 717 } 718 719 int32_t numberKeys = ures_getSize(setRes.getAlias()); 720 UnicodeString result; 721 const char *key=NULL; 722 for(int32_t i=0; i<numberKeys; ++i) { // Keys are zero, one, few, ... 723 UnicodeString rules = ures_getNextUnicodeString(setRes.getAlias(), &key, &errCode); 724 UnicodeString uKey(key, -1, US_INV); 725 result.append(uKey); 726 result.append(COLON); 727 result.append(rules); 728 result.append(SEMI_COLON); 729 } 730 return result; 731 } 732 733 734 UnicodeString 735 PluralRules::getRules() const { 736 UnicodeString rules; 737 if (mRules != NULL) { 738 mRules->dumpRules(rules); 739 } 740 return rules; 741 } 742 743 744 AndConstraint::AndConstraint() { 745 op = AndConstraint::NONE; 746 opNum=-1; 747 value = -1; 748 rangeList = NULL; 749 negated = FALSE; 750 integerOnly = FALSE; 751 digitsType = none; 752 next=NULL; 753 } 754 755 756 AndConstraint::AndConstraint(const AndConstraint& other) { 757 this->op = other.op; 758 this->opNum=other.opNum; 759 this->value=other.value; 760 this->rangeList=NULL; 761 if (other.rangeList != NULL) { 762 UErrorCode status = U_ZERO_ERROR; 763 this->rangeList = new UVector32(status); 764 this->rangeList->assign(*other.rangeList, status); 765 } 766 this->integerOnly=other.integerOnly; 767 this->negated=other.negated; 768 this->digitsType = other.digitsType; 769 if (other.next==NULL) { 770 this->next=NULL; 771 } 772 else { 773 this->next = new AndConstraint(*other.next); 774 } 775 } 776 777 AndConstraint::~AndConstraint() { 778 delete rangeList; 779 if (next!=NULL) { 780 delete next; 781 } 782 } 783 784 785 UBool 786 AndConstraint::isFulfilled(const IFixedDecimal &number) { 787 UBool result = TRUE; 788 if (digitsType == none) { 789 // An empty AndConstraint, created by a rule with a keyword but no following expression. 790 return TRUE; 791 } 792 793 PluralOperand operand = tokenTypeToPluralOperand(digitsType); 794 double n = number.getPluralOperand(operand); // pulls n | i | v | f value for the number. 795 // Will always be positive. 796 // May be non-integer (n option only) 797 do { 798 if (integerOnly && n != uprv_floor(n)) { 799 result = FALSE; 800 break; 801 } 802 803 if (op == MOD) { 804 n = fmod(n, opNum); 805 } 806 if (rangeList == NULL) { 807 result = value == -1 || // empty rule 808 n == value; // 'is' rule 809 break; 810 } 811 result = FALSE; // 'in' or 'within' rule 812 for (int32_t r=0; r<rangeList->size(); r+=2) { 813 if (rangeList->elementAti(r) <= n && n <= rangeList->elementAti(r+1)) { 814 result = TRUE; 815 break; 816 } 817 } 818 } while (FALSE); 819 820 if (negated) { 821 result = !result; 822 } 823 return result; 824 } 825 826 827 AndConstraint* 828 AndConstraint::add() 829 { 830 this->next = new AndConstraint(); 831 return this->next; 832 } 833 834 OrConstraint::OrConstraint() { 835 childNode=NULL; 836 next=NULL; 837 } 838 839 OrConstraint::OrConstraint(const OrConstraint& other) { 840 if ( other.childNode == NULL ) { 841 this->childNode = NULL; 842 } 843 else { 844 this->childNode = new AndConstraint(*(other.childNode)); 845 } 846 if (other.next == NULL ) { 847 this->next = NULL; 848 } 849 else { 850 this->next = new OrConstraint(*(other.next)); 851 } 852 } 853 854 OrConstraint::~OrConstraint() { 855 if (childNode!=NULL) { 856 delete childNode; 857 } 858 if (next!=NULL) { 859 delete next; 860 } 861 } 862 863 AndConstraint* 864 OrConstraint::add() 865 { 866 OrConstraint *curOrConstraint=this; 867 { 868 while (curOrConstraint->next!=NULL) { 869 curOrConstraint = curOrConstraint->next; 870 } 871 U_ASSERT(curOrConstraint->childNode == NULL); 872 curOrConstraint->childNode = new AndConstraint(); 873 } 874 return curOrConstraint->childNode; 875 } 876 877 UBool 878 OrConstraint::isFulfilled(const IFixedDecimal &number) { 879 OrConstraint* orRule=this; 880 UBool result=FALSE; 881 882 while (orRule!=NULL && !result) { 883 result=TRUE; 884 AndConstraint* andRule = orRule->childNode; 885 while (andRule!=NULL && result) { 886 result = andRule->isFulfilled(number); 887 andRule=andRule->next; 888 } 889 orRule = orRule->next; 890 } 891 892 return result; 893 } 894 895 896 RuleChain::RuleChain(): fKeyword(), fNext(NULL), ruleHeader(NULL), fDecimalSamples(), fIntegerSamples(), 897 fDecimalSamplesUnbounded(FALSE), fIntegerSamplesUnbounded(FALSE) { 898 } 899 900 RuleChain::RuleChain(const RuleChain& other) : 901 fKeyword(other.fKeyword), fNext(NULL), ruleHeader(NULL), fDecimalSamples(other.fDecimalSamples), 902 fIntegerSamples(other.fIntegerSamples), fDecimalSamplesUnbounded(other.fDecimalSamplesUnbounded), 903 fIntegerSamplesUnbounded(other.fIntegerSamplesUnbounded) { 904 if (other.ruleHeader != NULL) { 905 this->ruleHeader = new OrConstraint(*(other.ruleHeader)); 906 } 907 if (other.fNext != NULL ) { 908 this->fNext = new RuleChain(*other.fNext); 909 } 910 } 911 912 RuleChain::~RuleChain() { 913 delete fNext; 914 delete ruleHeader; 915 } 916 917 918 UnicodeString 919 RuleChain::select(const IFixedDecimal &number) const { 920 if (!number.isNaN() && !number.isInfinite()) { 921 for (const RuleChain *rules = this; rules != NULL; rules = rules->fNext) { 922 if (rules->ruleHeader->isFulfilled(number)) { 923 return rules->fKeyword; 924 } 925 } 926 } 927 return UnicodeString(TRUE, PLURAL_KEYWORD_OTHER, 5); 928 } 929 930 static UnicodeString tokenString(tokenType tok) { 931 UnicodeString s; 932 switch (tok) { 933 case tVariableN: 934 s.append(LOW_N); break; 935 case tVariableI: 936 s.append(LOW_I); break; 937 case tVariableF: 938 s.append(LOW_F); break; 939 case tVariableV: 940 s.append(LOW_V); break; 941 case tVariableT: 942 s.append(LOW_T); break; 943 default: 944 s.append(TILDE); 945 } 946 return s; 947 } 948 949 void 950 RuleChain::dumpRules(UnicodeString& result) { 951 UChar digitString[16]; 952 953 if ( ruleHeader != NULL ) { 954 result += fKeyword; 955 result += COLON; 956 result += SPACE; 957 OrConstraint* orRule=ruleHeader; 958 while ( orRule != NULL ) { 959 AndConstraint* andRule=orRule->childNode; 960 while ( andRule != NULL ) { 961 if ((andRule->op==AndConstraint::NONE) && (andRule->rangeList==NULL) && (andRule->value == -1)) { 962 // Empty Rules. 963 } else if ( (andRule->op==AndConstraint::NONE) && (andRule->rangeList==NULL) ) { 964 result += tokenString(andRule->digitsType); 965 result += UNICODE_STRING_SIMPLE(" is "); 966 if (andRule->negated) { 967 result += UNICODE_STRING_SIMPLE("not "); 968 } 969 uprv_itou(digitString,16, andRule->value,10,0); 970 result += UnicodeString(digitString); 971 } 972 else { 973 result += tokenString(andRule->digitsType); 974 result += SPACE; 975 if (andRule->op==AndConstraint::MOD) { 976 result += UNICODE_STRING_SIMPLE("mod "); 977 uprv_itou(digitString,16, andRule->opNum,10,0); 978 result += UnicodeString(digitString); 979 } 980 if (andRule->rangeList==NULL) { 981 if (andRule->negated) { 982 result += UNICODE_STRING_SIMPLE(" is not "); 983 uprv_itou(digitString,16, andRule->value,10,0); 984 result += UnicodeString(digitString); 985 } 986 else { 987 result += UNICODE_STRING_SIMPLE(" is "); 988 uprv_itou(digitString,16, andRule->value,10,0); 989 result += UnicodeString(digitString); 990 } 991 } 992 else { 993 if (andRule->negated) { 994 if ( andRule->integerOnly ) { 995 result += UNICODE_STRING_SIMPLE(" not in "); 996 } 997 else { 998 result += UNICODE_STRING_SIMPLE(" not within "); 999 } 1000 } 1001 else { 1002 if ( andRule->integerOnly ) { 1003 result += UNICODE_STRING_SIMPLE(" in "); 1004 } 1005 else { 1006 result += UNICODE_STRING_SIMPLE(" within "); 1007 } 1008 } 1009 for (int32_t r=0; r<andRule->rangeList->size(); r+=2) { 1010 int32_t rangeLo = andRule->rangeList->elementAti(r); 1011 int32_t rangeHi = andRule->rangeList->elementAti(r+1); 1012 uprv_itou(digitString,16, rangeLo, 10, 0); 1013 result += UnicodeString(digitString); 1014 result += UNICODE_STRING_SIMPLE(".."); 1015 uprv_itou(digitString,16, rangeHi, 10,0); 1016 result += UnicodeString(digitString); 1017 if (r+2 < andRule->rangeList->size()) { 1018 result += UNICODE_STRING_SIMPLE(", "); 1019 } 1020 } 1021 } 1022 } 1023 if ( (andRule=andRule->next) != NULL) { 1024 result += UNICODE_STRING_SIMPLE(" and "); 1025 } 1026 } 1027 if ( (orRule = orRule->next) != NULL ) { 1028 result += UNICODE_STRING_SIMPLE(" or "); 1029 } 1030 } 1031 } 1032 if ( fNext != NULL ) { 1033 result += UNICODE_STRING_SIMPLE("; "); 1034 fNext->dumpRules(result); 1035 } 1036 } 1037 1038 1039 UErrorCode 1040 RuleChain::getKeywords(int32_t capacityOfKeywords, UnicodeString* keywords, int32_t& arraySize) const { 1041 if ( arraySize < capacityOfKeywords-1 ) { 1042 keywords[arraySize++]=fKeyword; 1043 } 1044 else { 1045 return U_BUFFER_OVERFLOW_ERROR; 1046 } 1047 1048 if ( fNext != NULL ) { 1049 return fNext->getKeywords(capacityOfKeywords, keywords, arraySize); 1050 } 1051 else { 1052 return U_ZERO_ERROR; 1053 } 1054 } 1055 1056 UBool 1057 RuleChain::isKeyword(const UnicodeString& keywordParam) const { 1058 if ( fKeyword == keywordParam ) { 1059 return TRUE; 1060 } 1061 1062 if ( fNext != NULL ) { 1063 return fNext->isKeyword(keywordParam); 1064 } 1065 else { 1066 return FALSE; 1067 } 1068 } 1069 1070 1071 PluralRuleParser::PluralRuleParser() : 1072 ruleIndex(0), token(), type(none), prevType(none), 1073 curAndConstraint(NULL), currentChain(NULL), rangeLowIdx(-1), rangeHiIdx(-1) 1074 { 1075 } 1076 1077 PluralRuleParser::~PluralRuleParser() { 1078 } 1079 1080 1081 int32_t 1082 PluralRuleParser::getNumberValue(const UnicodeString& token) { 1083 int32_t i; 1084 char digits[128]; 1085 1086 i = token.extract(0, token.length(), digits, UPRV_LENGTHOF(digits), US_INV); 1087 digits[i]='\0'; 1088 1089 return((int32_t)atoi(digits)); 1090 } 1091 1092 1093 void 1094 PluralRuleParser::checkSyntax(UErrorCode &status) 1095 { 1096 if (U_FAILURE(status)) { 1097 return; 1098 } 1099 if (!(prevType==none || prevType==tSemiColon)) { 1100 type = getKeyType(token, type); // Switch token type from tKeyword if we scanned a reserved word, 1101 // and we are not at the start of a rule, where a 1102 // keyword is expected. 1103 } 1104 1105 switch(prevType) { 1106 case none: 1107 case tSemiColon: 1108 if (type!=tKeyword && type != tEOF) { 1109 status = U_UNEXPECTED_TOKEN; 1110 } 1111 break; 1112 case tVariableN: 1113 case tVariableI: 1114 case tVariableF: 1115 case tVariableT: 1116 case tVariableV: 1117 if (type != tIs && type != tMod && type != tIn && 1118 type != tNot && type != tWithin && type != tEqual && type != tNotEqual) { 1119 status = U_UNEXPECTED_TOKEN; 1120 } 1121 break; 1122 case tKeyword: 1123 if (type != tColon) { 1124 status = U_UNEXPECTED_TOKEN; 1125 } 1126 break; 1127 case tColon: 1128 if (!(type == tVariableN || 1129 type == tVariableI || 1130 type == tVariableF || 1131 type == tVariableT || 1132 type == tVariableV || 1133 type == tAt)) { 1134 status = U_UNEXPECTED_TOKEN; 1135 } 1136 break; 1137 case tIs: 1138 if ( type != tNumber && type != tNot) { 1139 status = U_UNEXPECTED_TOKEN; 1140 } 1141 break; 1142 case tNot: 1143 if (type != tNumber && type != tIn && type != tWithin) { 1144 status = U_UNEXPECTED_TOKEN; 1145 } 1146 break; 1147 case tMod: 1148 case tDot2: 1149 case tIn: 1150 case tWithin: 1151 case tEqual: 1152 case tNotEqual: 1153 if (type != tNumber) { 1154 status = U_UNEXPECTED_TOKEN; 1155 } 1156 break; 1157 case tAnd: 1158 case tOr: 1159 if ( type != tVariableN && 1160 type != tVariableI && 1161 type != tVariableF && 1162 type != tVariableT && 1163 type != tVariableV) { 1164 status = U_UNEXPECTED_TOKEN; 1165 } 1166 break; 1167 case tComma: 1168 if (type != tNumber) { 1169 status = U_UNEXPECTED_TOKEN; 1170 } 1171 break; 1172 case tNumber: 1173 if (type != tDot2 && type != tSemiColon && type != tIs && type != tNot && 1174 type != tIn && type != tEqual && type != tNotEqual && type != tWithin && 1175 type != tAnd && type != tOr && type != tComma && type != tAt && 1176 type != tEOF) 1177 { 1178 status = U_UNEXPECTED_TOKEN; 1179 } 1180 // TODO: a comma following a number that is not part of a range will be allowed. 1181 // It's not the only case of this sort of thing. Parser needs a re-write. 1182 break; 1183 case tAt: 1184 if (type != tDecimal && type != tInteger) { 1185 status = U_UNEXPECTED_TOKEN; 1186 } 1187 break; 1188 default: 1189 status = U_UNEXPECTED_TOKEN; 1190 break; 1191 } 1192 } 1193 1194 1195 /* 1196 * Scan the next token from the input rules. 1197 * rules and returned token type are in the parser state variables. 1198 */ 1199 void 1200 PluralRuleParser::getNextToken(UErrorCode &status) 1201 { 1202 if (U_FAILURE(status)) { 1203 return; 1204 } 1205 1206 UChar ch; 1207 while (ruleIndex < ruleSrc->length()) { 1208 ch = ruleSrc->charAt(ruleIndex); 1209 type = charType(ch); 1210 if (type != tSpace) { 1211 break; 1212 } 1213 ++(ruleIndex); 1214 } 1215 if (ruleIndex >= ruleSrc->length()) { 1216 type = tEOF; 1217 return; 1218 } 1219 int32_t curIndex= ruleIndex; 1220 1221 switch (type) { 1222 case tColon: 1223 case tSemiColon: 1224 case tComma: 1225 case tEllipsis: 1226 case tTilde: // scanned '~' 1227 case tAt: // scanned '@' 1228 case tEqual: // scanned '=' 1229 case tMod: // scanned '%' 1230 // Single character tokens. 1231 ++curIndex; 1232 break; 1233 1234 case tNotEqual: // scanned '!' 1235 if (ruleSrc->charAt(curIndex+1) == EQUALS) { 1236 curIndex += 2; 1237 } else { 1238 type = none; 1239 curIndex += 1; 1240 } 1241 break; 1242 1243 case tKeyword: 1244 while (type == tKeyword && ++curIndex < ruleSrc->length()) { 1245 ch = ruleSrc->charAt(curIndex); 1246 type = charType(ch); 1247 } 1248 type = tKeyword; 1249 break; 1250 1251 case tNumber: 1252 while (type == tNumber && ++curIndex < ruleSrc->length()) { 1253 ch = ruleSrc->charAt(curIndex); 1254 type = charType(ch); 1255 } 1256 type = tNumber; 1257 break; 1258 1259 case tDot: 1260 // We could be looking at either ".." in a range, or "..." at the end of a sample. 1261 if (curIndex+1 >= ruleSrc->length() || ruleSrc->charAt(curIndex+1) != DOT) { 1262 ++curIndex; 1263 break; // Single dot 1264 } 1265 if (curIndex+2 >= ruleSrc->length() || ruleSrc->charAt(curIndex+2) != DOT) { 1266 curIndex += 2; 1267 type = tDot2; 1268 break; // double dot 1269 } 1270 type = tEllipsis; 1271 curIndex += 3; 1272 break; // triple dot 1273 1274 default: 1275 status = U_UNEXPECTED_TOKEN; 1276 ++curIndex; 1277 break; 1278 } 1279 1280 U_ASSERT(ruleIndex <= ruleSrc->length()); 1281 U_ASSERT(curIndex <= ruleSrc->length()); 1282 token=UnicodeString(*ruleSrc, ruleIndex, curIndex-ruleIndex); 1283 ruleIndex = curIndex; 1284 } 1285 1286 tokenType 1287 PluralRuleParser::charType(UChar ch) { 1288 if ((ch>=U_ZERO) && (ch<=U_NINE)) { 1289 return tNumber; 1290 } 1291 if (ch>=LOW_A && ch<=LOW_Z) { 1292 return tKeyword; 1293 } 1294 switch (ch) { 1295 case COLON: 1296 return tColon; 1297 case SPACE: 1298 return tSpace; 1299 case SEMI_COLON: 1300 return tSemiColon; 1301 case DOT: 1302 return tDot; 1303 case COMMA: 1304 return tComma; 1305 case EXCLAMATION: 1306 return tNotEqual; 1307 case EQUALS: 1308 return tEqual; 1309 case PERCENT_SIGN: 1310 return tMod; 1311 case AT: 1312 return tAt; 1313 case ELLIPSIS: 1314 return tEllipsis; 1315 case TILDE: 1316 return tTilde; 1317 default : 1318 return none; 1319 } 1320 } 1321 1322 1323 // Set token type for reserved words in the Plural Rule syntax. 1324 1325 tokenType 1326 PluralRuleParser::getKeyType(const UnicodeString &token, tokenType keyType) 1327 { 1328 if (keyType != tKeyword) { 1329 return keyType; 1330 } 1331 1332 if (0 == token.compare(PK_VAR_N, 1)) { 1333 keyType = tVariableN; 1334 } else if (0 == token.compare(PK_VAR_I, 1)) { 1335 keyType = tVariableI; 1336 } else if (0 == token.compare(PK_VAR_F, 1)) { 1337 keyType = tVariableF; 1338 } else if (0 == token.compare(PK_VAR_T, 1)) { 1339 keyType = tVariableT; 1340 } else if (0 == token.compare(PK_VAR_V, 1)) { 1341 keyType = tVariableV; 1342 } else if (0 == token.compare(PK_IS, 2)) { 1343 keyType = tIs; 1344 } else if (0 == token.compare(PK_AND, 3)) { 1345 keyType = tAnd; 1346 } else if (0 == token.compare(PK_IN, 2)) { 1347 keyType = tIn; 1348 } else if (0 == token.compare(PK_WITHIN, 6)) { 1349 keyType = tWithin; 1350 } else if (0 == token.compare(PK_NOT, 3)) { 1351 keyType = tNot; 1352 } else if (0 == token.compare(PK_MOD, 3)) { 1353 keyType = tMod; 1354 } else if (0 == token.compare(PK_OR, 2)) { 1355 keyType = tOr; 1356 } else if (0 == token.compare(PK_DECIMAL, 7)) { 1357 keyType = tDecimal; 1358 } else if (0 == token.compare(PK_INTEGER, 7)) { 1359 keyType = tInteger; 1360 } 1361 return keyType; 1362 } 1363 1364 1365 PluralKeywordEnumeration::PluralKeywordEnumeration(RuleChain *header, UErrorCode& status) 1366 : pos(0), fKeywordNames(status) { 1367 if (U_FAILURE(status)) { 1368 return; 1369 } 1370 fKeywordNames.setDeleter(uprv_deleteUObject); 1371 UBool addKeywordOther=TRUE; 1372 RuleChain *node=header; 1373 while(node!=NULL) { 1374 fKeywordNames.addElement(new UnicodeString(node->fKeyword), status); 1375 if (U_FAILURE(status)) { 1376 return; 1377 } 1378 if (0 == node->fKeyword.compare(PLURAL_KEYWORD_OTHER, 5)) { 1379 addKeywordOther= FALSE; 1380 } 1381 node=node->fNext; 1382 } 1383 1384 if (addKeywordOther) { 1385 fKeywordNames.addElement(new UnicodeString(PLURAL_KEYWORD_OTHER), status); 1386 } 1387 } 1388 1389 const UnicodeString* 1390 PluralKeywordEnumeration::snext(UErrorCode& status) { 1391 if (U_SUCCESS(status) && pos < fKeywordNames.size()) { 1392 return (const UnicodeString*)fKeywordNames.elementAt(pos++); 1393 } 1394 return NULL; 1395 } 1396 1397 void 1398 PluralKeywordEnumeration::reset(UErrorCode& /*status*/) { 1399 pos=0; 1400 } 1401 1402 int32_t 1403 PluralKeywordEnumeration::count(UErrorCode& /*status*/) const { 1404 return fKeywordNames.size(); 1405 } 1406 1407 PluralKeywordEnumeration::~PluralKeywordEnumeration() { 1408 } 1409 1410 PluralOperand tokenTypeToPluralOperand(tokenType tt) { 1411 switch(tt) { 1412 case tVariableN: 1413 return PLURAL_OPERAND_N; 1414 case tVariableI: 1415 return PLURAL_OPERAND_I; 1416 case tVariableF: 1417 return PLURAL_OPERAND_F; 1418 case tVariableV: 1419 return PLURAL_OPERAND_V; 1420 case tVariableT: 1421 return PLURAL_OPERAND_T; 1422 default: 1423 U_ASSERT(FALSE); // unexpected. 1424 return PLURAL_OPERAND_N; 1425 } 1426 } 1427 1428 IFixedDecimal::~IFixedDecimal() = default; 1429 1430 FixedDecimal::FixedDecimal(const VisibleDigits &digits) { 1431 digits.getFixedDecimal( 1432 source, intValue, decimalDigits, 1433 decimalDigitsWithoutTrailingZeros, 1434 visibleDecimalDigitCount, hasIntegerValue); 1435 isNegative = digits.isNegative(); 1436 _isNaN = digits.isNaN(); 1437 _isInfinite = digits.isInfinite(); 1438 } 1439 1440 FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f) { 1441 init(n, v, f); 1442 // check values. TODO make into unit test. 1443 // 1444 // long visiblePower = (int) Math.pow(10, v); 1445 // if (decimalDigits > visiblePower) { 1446 // throw new IllegalArgumentException(); 1447 // } 1448 // double fraction = intValue + (decimalDigits / (double) visiblePower); 1449 // if (fraction != source) { 1450 // double diff = Math.abs(fraction - source)/(Math.abs(fraction) + Math.abs(source)); 1451 // if (diff > 0.00000001d) { 1452 // throw new IllegalArgumentException(); 1453 // } 1454 // } 1455 } 1456 1457 FixedDecimal::FixedDecimal(double n, int32_t v) { 1458 // Ugly, but for samples we don't care. 1459 init(n, v, getFractionalDigits(n, v)); 1460 } 1461 1462 FixedDecimal::FixedDecimal(double n) { 1463 init(n); 1464 } 1465 1466 FixedDecimal::FixedDecimal() { 1467 init(0, 0, 0); 1468 } 1469 1470 1471 // Create a FixedDecimal from a UnicodeString containing a number. 1472 // Inefficient, but only used for samples, so simplicity trumps efficiency. 1473 1474 FixedDecimal::FixedDecimal(const UnicodeString &num, UErrorCode &status) { 1475 CharString cs; 1476 cs.appendInvariantChars(num, status); 1477 DigitList dl; 1478 dl.set(cs.toStringPiece(), status); 1479 if (U_FAILURE(status)) { 1480 init(0, 0, 0); 1481 return; 1482 } 1483 int32_t decimalPoint = num.indexOf(DOT); 1484 double n = dl.getDouble(); 1485 if (decimalPoint == -1) { 1486 init(n, 0, 0); 1487 } else { 1488 int32_t v = num.length() - decimalPoint - 1; 1489 init(n, v, getFractionalDigits(n, v)); 1490 } 1491 } 1492 1493 1494 FixedDecimal::FixedDecimal(const FixedDecimal &other) { 1495 source = other.source; 1496 visibleDecimalDigitCount = other.visibleDecimalDigitCount; 1497 decimalDigits = other.decimalDigits; 1498 decimalDigitsWithoutTrailingZeros = other.decimalDigitsWithoutTrailingZeros; 1499 intValue = other.intValue; 1500 hasIntegerValue = other.hasIntegerValue; 1501 isNegative = other.isNegative; 1502 _isNaN = other._isNaN; 1503 _isInfinite = other._isInfinite; 1504 } 1505 1506 FixedDecimal::~FixedDecimal() = default; 1507 1508 1509 void FixedDecimal::init(double n) { 1510 int32_t numFractionDigits = decimals(n); 1511 init(n, numFractionDigits, getFractionalDigits(n, numFractionDigits)); 1512 } 1513 1514 1515 void FixedDecimal::init(double n, int32_t v, int64_t f) { 1516 isNegative = n < 0.0; 1517 source = fabs(n); 1518 _isNaN = uprv_isNaN(source); 1519 _isInfinite = uprv_isInfinite(source); 1520 if (_isNaN || _isInfinite) { 1521 v = 0; 1522 f = 0; 1523 intValue = 0; 1524 hasIntegerValue = FALSE; 1525 } else { 1526 intValue = (int64_t)source; 1527 hasIntegerValue = (source == intValue); 1528 } 1529 1530 visibleDecimalDigitCount = v; 1531 decimalDigits = f; 1532 if (f == 0) { 1533 decimalDigitsWithoutTrailingZeros = 0; 1534 } else { 1535 int64_t fdwtz = f; 1536 while ((fdwtz%10) == 0) { 1537 fdwtz /= 10; 1538 } 1539 decimalDigitsWithoutTrailingZeros = fdwtz; 1540 } 1541 } 1542 1543 1544 // Fast path only exact initialization. Return true if successful. 1545 // Note: Do not multiply by 10 each time through loop, rounding cruft can build 1546 // up that makes the check for an integer result fail. 1547 // A single multiply of the original number works more reliably. 1548 static int32_t p10[] = {1, 10, 100, 1000, 10000}; 1549 UBool FixedDecimal::quickInit(double n) { 1550 UBool success = FALSE; 1551 n = fabs(n); 1552 int32_t numFractionDigits; 1553 for (numFractionDigits = 0; numFractionDigits <= 3; numFractionDigits++) { 1554 double scaledN = n * p10[numFractionDigits]; 1555 if (scaledN == floor(scaledN)) { 1556 success = TRUE; 1557 break; 1558 } 1559 } 1560 if (success) { 1561 init(n, numFractionDigits, getFractionalDigits(n, numFractionDigits)); 1562 } 1563 return success; 1564 } 1565 1566 1567 1568 int32_t FixedDecimal::decimals(double n) { 1569 // Count the number of decimal digits in the fraction part of the number, excluding trailing zeros. 1570 // fastpath the common cases, integers or fractions with 3 or fewer digits 1571 n = fabs(n); 1572 for (int ndigits=0; ndigits<=3; ndigits++) { 1573 double scaledN = n * p10[ndigits]; 1574 if (scaledN == floor(scaledN)) { 1575 return ndigits; 1576 } 1577 } 1578 1579 // Slow path, convert with sprintf, parse converted output. 1580 char buf[30] = {0}; 1581 sprintf(buf, "%1.15e", n); 1582 // formatted number looks like this: 1.234567890123457e-01 1583 int exponent = atoi(buf+18); 1584 int numFractionDigits = 15; 1585 for (int i=16; ; --i) { 1586 if (buf[i] != '0') { 1587 break; 1588 } 1589 --numFractionDigits; 1590 } 1591 numFractionDigits -= exponent; // Fraction part of fixed point representation. 1592 return numFractionDigits; 1593 } 1594 1595 1596 // Get the fraction digits of a double, represented as an integer. 1597 // v is the number of visible fraction digits in the displayed form of the number. 1598 // Example: n = 1001.234, v = 6, result = 234000 1599 // TODO: need to think through how this is used in the plural rule context. 1600 // This function can easily encounter integer overflow, 1601 // and can easily return noise digits when the precision of a double is exceeded. 1602 1603 int64_t FixedDecimal::getFractionalDigits(double n, int32_t v) { 1604 if (v == 0 || n == floor(n) || uprv_isNaN(n) || uprv_isPositiveInfinity(n)) { 1605 return 0; 1606 } 1607 n = fabs(n); 1608 double fract = n - floor(n); 1609 switch (v) { 1610 case 1: return (int64_t)(fract*10.0 + 0.5); 1611 case 2: return (int64_t)(fract*100.0 + 0.5); 1612 case 3: return (int64_t)(fract*1000.0 + 0.5); 1613 default: 1614 double scaled = floor(fract * pow(10.0, (double)v) + 0.5); 1615 if (scaled > U_INT64_MAX) { 1616 return U_INT64_MAX; 1617 } else { 1618 return (int64_t)scaled; 1619 } 1620 } 1621 } 1622 1623 1624 void FixedDecimal::adjustForMinFractionDigits(int32_t minFractionDigits) { 1625 int32_t numTrailingFractionZeros = minFractionDigits - visibleDecimalDigitCount; 1626 if (numTrailingFractionZeros > 0) { 1627 for (int32_t i=0; i<numTrailingFractionZeros; i++) { 1628 // Do not let the decimalDigits value overflow if there are many trailing zeros. 1629 // Limit the value to 18 digits, the most that a 64 bit int can fully represent. 1630 if (decimalDigits >= 100000000000000000LL) { 1631 break; 1632 } 1633 decimalDigits *= 10; 1634 } 1635 visibleDecimalDigitCount += numTrailingFractionZeros; 1636 } 1637 } 1638 1639 1640 double FixedDecimal::getPluralOperand(PluralOperand operand) const { 1641 switch(operand) { 1642 case PLURAL_OPERAND_N: return source; 1643 case PLURAL_OPERAND_I: return static_cast<double>(intValue); 1644 case PLURAL_OPERAND_F: return static_cast<double>(decimalDigits); 1645 case PLURAL_OPERAND_T: return static_cast<double>(decimalDigitsWithoutTrailingZeros); 1646 case PLURAL_OPERAND_V: return visibleDecimalDigitCount; 1647 default: 1648 U_ASSERT(FALSE); // unexpected. 1649 return source; 1650 } 1651 } 1652 1653 bool FixedDecimal::isNaN() const { 1654 return _isNaN; 1655 } 1656 1657 bool FixedDecimal::isInfinite() const { 1658 return _isInfinite; 1659 } 1660 1661 bool FixedDecimal::isNanOrInfinity() const { 1662 return _isNaN || _isInfinite; 1663 } 1664 1665 int32_t FixedDecimal::getVisibleFractionDigitCount() const { 1666 return visibleDecimalDigitCount; 1667 } 1668 1669 1670 1671 PluralAvailableLocalesEnumeration::PluralAvailableLocalesEnumeration(UErrorCode &status) { 1672 fLocales = NULL; 1673 fRes = NULL; 1674 fOpenStatus = status; 1675 if (U_FAILURE(status)) { 1676 return; 1677 } 1678 fOpenStatus = U_ZERO_ERROR; 1679 LocalUResourceBundlePointer rb(ures_openDirect(NULL, "plurals", &fOpenStatus)); 1680 fLocales = ures_getByKey(rb.getAlias(), "locales", NULL, &fOpenStatus); 1681 } 1682 1683 PluralAvailableLocalesEnumeration::~PluralAvailableLocalesEnumeration() { 1684 ures_close(fLocales); 1685 ures_close(fRes); 1686 fLocales = NULL; 1687 fRes = NULL; 1688 } 1689 1690 const char *PluralAvailableLocalesEnumeration::next(int32_t *resultLength, UErrorCode &status) { 1691 if (U_FAILURE(status)) { 1692 return NULL; 1693 } 1694 if (U_FAILURE(fOpenStatus)) { 1695 status = fOpenStatus; 1696 return NULL; 1697 } 1698 fRes = ures_getNextResource(fLocales, fRes, &status); 1699 if (fRes == NULL || U_FAILURE(status)) { 1700 if (status == U_INDEX_OUTOFBOUNDS_ERROR) { 1701 status = U_ZERO_ERROR; 1702 } 1703 return NULL; 1704 } 1705 const char *result = ures_getKey(fRes); 1706 if (resultLength != NULL) { 1707 *resultLength = static_cast<int32_t>(uprv_strlen(result)); 1708 } 1709 return result; 1710 } 1711 1712 1713 void PluralAvailableLocalesEnumeration::reset(UErrorCode &status) { 1714 if (U_FAILURE(status)) { 1715 return; 1716 } 1717 if (U_FAILURE(fOpenStatus)) { 1718 status = fOpenStatus; 1719 return; 1720 } 1721 ures_resetIterator(fLocales); 1722 } 1723 1724 int32_t PluralAvailableLocalesEnumeration::count(UErrorCode &status) const { 1725 if (U_FAILURE(status)) { 1726 return 0; 1727 } 1728 if (U_FAILURE(fOpenStatus)) { 1729 status = fOpenStatus; 1730 return 0; 1731 } 1732 return ures_getSize(fLocales); 1733 } 1734 1735 U_NAMESPACE_END 1736 1737 1738 #endif /* #if !UCONFIG_NO_FORMATTING */ 1739 1740 //eof 1741