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