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