1 // 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ****************************************************************************** 5 * Copyright (C) 1997-2015, International Business Machines 6 * Corporation and others. All Rights Reserved. 7 ****************************************************************************** 8 * file name: nfsubs.cpp 9 * encoding: UTF-8 10 * tab size: 8 (not used) 11 * indentation:4 12 * 13 * Modification history 14 * Date Name Comments 15 * 10/11/2001 Doug Ported from ICU4J 16 */ 17 18 #include <stdio.h> 19 #include "utypeinfo.h" // for 'typeid' to work 20 21 #include "nfsubs.h" 22 #include "digitlst.h" 23 #include "fmtableimp.h" 24 25 #if U_HAVE_RBNF 26 27 static const UChar gLessThan = 0x003c; 28 static const UChar gEquals = 0x003d; 29 static const UChar gGreaterThan = 0x003e; 30 static const UChar gPercent = 0x0025; 31 static const UChar gPound = 0x0023; 32 static const UChar gZero = 0x0030; 33 static const UChar gSpace = 0x0020; 34 35 static const UChar gEqualsEquals[] = 36 { 37 0x3D, 0x3D, 0 38 }; /* "==" */ 39 static const UChar gGreaterGreaterGreaterThan[] = 40 { 41 0x3E, 0x3E, 0x3E, 0 42 }; /* ">>>" */ 43 static const UChar gGreaterGreaterThan[] = 44 { 45 0x3E, 0x3E, 0 46 }; /* ">>" */ 47 48 U_NAMESPACE_BEGIN 49 50 class SameValueSubstitution : public NFSubstitution { 51 public: 52 SameValueSubstitution(int32_t pos, 53 const NFRuleSet* ruleset, 54 const UnicodeString& description, 55 UErrorCode& status); 56 virtual ~SameValueSubstitution(); 57 58 virtual int64_t transformNumber(int64_t number) const { return number; } 59 virtual double transformNumber(double number) const { return number; } 60 virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const { return newRuleValue; } 61 virtual double calcUpperBound(double oldUpperBound) const { return oldUpperBound; } 62 virtual UChar tokenChar() const { return (UChar)0x003d; } // '=' 63 64 public: 65 static UClassID getStaticClassID(void); 66 virtual UClassID getDynamicClassID(void) const; 67 }; 68 69 SameValueSubstitution::~SameValueSubstitution() {} 70 71 class MultiplierSubstitution : public NFSubstitution { 72 int64_t divisor; 73 74 public: 75 MultiplierSubstitution(int32_t _pos, 76 const NFRule *rule, 77 const NFRuleSet* _ruleSet, 78 const UnicodeString& description, 79 UErrorCode& status) 80 : NFSubstitution(_pos, _ruleSet, description, status), divisor(rule->getDivisor()) 81 { 82 if (divisor == 0) { 83 status = U_PARSE_ERROR; 84 } 85 } 86 virtual ~MultiplierSubstitution(); 87 88 virtual void setDivisor(int32_t radix, int16_t exponent, UErrorCode& status) { 89 divisor = util64_pow(radix, exponent); 90 91 if(divisor == 0) { 92 status = U_PARSE_ERROR; 93 } 94 } 95 96 virtual UBool operator==(const NFSubstitution& rhs) const; 97 98 virtual int64_t transformNumber(int64_t number) const { 99 return number / divisor; 100 } 101 102 virtual double transformNumber(double number) const { 103 if (getRuleSet()) { 104 return uprv_floor(number / divisor); 105 } else { 106 return number / divisor; 107 } 108 } 109 110 virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const { 111 return newRuleValue * divisor; 112 } 113 114 virtual double calcUpperBound(double /*oldUpperBound*/) const { return static_cast<double>(divisor); } 115 116 virtual UChar tokenChar() const { return (UChar)0x003c; } // '<' 117 118 public: 119 static UClassID getStaticClassID(void); 120 virtual UClassID getDynamicClassID(void) const; 121 }; 122 123 MultiplierSubstitution::~MultiplierSubstitution() {} 124 125 class ModulusSubstitution : public NFSubstitution { 126 int64_t divisor; 127 const NFRule* ruleToUse; 128 public: 129 ModulusSubstitution(int32_t pos, 130 const NFRule* rule, 131 const NFRule* rulePredecessor, 132 const NFRuleSet* ruleSet, 133 const UnicodeString& description, 134 UErrorCode& status); 135 virtual ~ModulusSubstitution(); 136 137 virtual void setDivisor(int32_t radix, int16_t exponent, UErrorCode& status) { 138 divisor = util64_pow(radix, exponent); 139 140 if (divisor == 0) { 141 status = U_PARSE_ERROR; 142 } 143 } 144 145 virtual UBool operator==(const NFSubstitution& rhs) const; 146 147 virtual void doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const; 148 virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const; 149 150 virtual int64_t transformNumber(int64_t number) const { return number % divisor; } 151 virtual double transformNumber(double number) const { return uprv_fmod(number, static_cast<double>(divisor)); } 152 153 virtual UBool doParse(const UnicodeString& text, 154 ParsePosition& parsePosition, 155 double baseValue, 156 double upperBound, 157 UBool lenientParse, 158 Formattable& result) const; 159 160 virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { 161 return oldRuleValue - uprv_fmod(oldRuleValue, static_cast<double>(divisor)) + newRuleValue; 162 } 163 164 virtual double calcUpperBound(double /*oldUpperBound*/) const { return static_cast<double>(divisor); } 165 166 virtual UBool isModulusSubstitution() const { return TRUE; } 167 168 virtual UChar tokenChar() const { return (UChar)0x003e; } // '>' 169 170 virtual void toString(UnicodeString& result) const; 171 172 public: 173 static UClassID getStaticClassID(void); 174 virtual UClassID getDynamicClassID(void) const; 175 }; 176 177 ModulusSubstitution::~ModulusSubstitution() {} 178 179 class IntegralPartSubstitution : public NFSubstitution { 180 public: 181 IntegralPartSubstitution(int32_t _pos, 182 const NFRuleSet* _ruleSet, 183 const UnicodeString& description, 184 UErrorCode& status) 185 : NFSubstitution(_pos, _ruleSet, description, status) {} 186 virtual ~IntegralPartSubstitution(); 187 188 virtual int64_t transformNumber(int64_t number) const { return number; } 189 virtual double transformNumber(double number) const { return uprv_floor(number); } 190 virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue + oldRuleValue; } 191 virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX; } 192 virtual UChar tokenChar() const { return (UChar)0x003c; } // '<' 193 194 public: 195 static UClassID getStaticClassID(void); 196 virtual UClassID getDynamicClassID(void) const; 197 }; 198 199 IntegralPartSubstitution::~IntegralPartSubstitution() {} 200 201 class FractionalPartSubstitution : public NFSubstitution { 202 UBool byDigits; 203 UBool useSpaces; 204 enum { kMaxDecimalDigits = 8 }; 205 public: 206 FractionalPartSubstitution(int32_t pos, 207 const NFRuleSet* ruleSet, 208 const UnicodeString& description, 209 UErrorCode& status); 210 virtual ~FractionalPartSubstitution(); 211 212 virtual UBool operator==(const NFSubstitution& rhs) const; 213 214 virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const; 215 virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/, int32_t /*recursionCount*/, UErrorCode& /*status*/) const {} 216 virtual int64_t transformNumber(int64_t /*number*/) const { return 0; } 217 virtual double transformNumber(double number) const { return number - uprv_floor(number); } 218 219 virtual UBool doParse(const UnicodeString& text, 220 ParsePosition& parsePosition, 221 double baseValue, 222 double upperBound, 223 UBool lenientParse, 224 Formattable& result) const; 225 226 virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue + oldRuleValue; } 227 virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0.0; } 228 virtual UChar tokenChar() const { return (UChar)0x003e; } // '>' 229 230 public: 231 static UClassID getStaticClassID(void); 232 virtual UClassID getDynamicClassID(void) const; 233 }; 234 235 FractionalPartSubstitution::~FractionalPartSubstitution() {} 236 237 class AbsoluteValueSubstitution : public NFSubstitution { 238 public: 239 AbsoluteValueSubstitution(int32_t _pos, 240 const NFRuleSet* _ruleSet, 241 const UnicodeString& description, 242 UErrorCode& status) 243 : NFSubstitution(_pos, _ruleSet, description, status) {} 244 virtual ~AbsoluteValueSubstitution(); 245 246 virtual int64_t transformNumber(int64_t number) const { return number >= 0 ? number : -number; } 247 virtual double transformNumber(double number) const { return uprv_fabs(number); } 248 virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const { return -newRuleValue; } 249 virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX; } 250 virtual UChar tokenChar() const { return (UChar)0x003e; } // '>' 251 252 public: 253 static UClassID getStaticClassID(void); 254 virtual UClassID getDynamicClassID(void) const; 255 }; 256 257 AbsoluteValueSubstitution::~AbsoluteValueSubstitution() {} 258 259 class NumeratorSubstitution : public NFSubstitution { 260 double denominator; 261 int64_t ldenominator; 262 UBool withZeros; 263 public: 264 static inline UnicodeString fixdesc(const UnicodeString& desc) { 265 if (desc.endsWith(LTLT, 2)) { 266 UnicodeString result(desc, 0, desc.length()-1); 267 return result; 268 } 269 return desc; 270 } 271 NumeratorSubstitution(int32_t _pos, 272 double _denominator, 273 NFRuleSet* _ruleSet, 274 const UnicodeString& description, 275 UErrorCode& status) 276 : NFSubstitution(_pos, _ruleSet, fixdesc(description), status), denominator(_denominator) 277 { 278 ldenominator = util64_fromDouble(denominator); 279 withZeros = description.endsWith(LTLT, 2); 280 } 281 virtual ~NumeratorSubstitution(); 282 283 virtual UBool operator==(const NFSubstitution& rhs) const; 284 285 virtual int64_t transformNumber(int64_t number) const { return number * ldenominator; } 286 virtual double transformNumber(double number) const { return uprv_round(number * denominator); } 287 288 virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/, int32_t /*recursionCount*/, UErrorCode& /*status*/) const {} 289 virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const; 290 virtual UBool doParse(const UnicodeString& text, 291 ParsePosition& parsePosition, 292 double baseValue, 293 double upperBound, 294 UBool /*lenientParse*/, 295 Formattable& result) const; 296 297 virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue / oldRuleValue; } 298 virtual double calcUpperBound(double /*oldUpperBound*/) const { return denominator; } 299 virtual UChar tokenChar() const { return (UChar)0x003c; } // '<' 300 private: 301 static const UChar LTLT[2]; 302 303 public: 304 static UClassID getStaticClassID(void); 305 virtual UClassID getDynamicClassID(void) const; 306 }; 307 308 NumeratorSubstitution::~NumeratorSubstitution() {} 309 310 NFSubstitution* 311 NFSubstitution::makeSubstitution(int32_t pos, 312 const NFRule* rule, 313 const NFRule* predecessor, 314 const NFRuleSet* ruleSet, 315 const RuleBasedNumberFormat* formatter, 316 const UnicodeString& description, 317 UErrorCode& status) 318 { 319 // if the description is empty, return a NullSubstitution 320 if (description.length() == 0) { 321 return NULL; 322 } 323 324 switch (description.charAt(0)) { 325 // if the description begins with '<'... 326 case gLessThan: 327 // throw an exception if the rule is a negative number 328 // rule 329 if (rule->getBaseValue() == NFRule::kNegativeNumberRule) { 330 // throw new IllegalArgumentException("<< not allowed in negative-number rule"); 331 status = U_PARSE_ERROR; 332 return NULL; 333 } 334 335 // if the rule is a fraction rule, return an 336 // IntegralPartSubstitution 337 else if (rule->getBaseValue() == NFRule::kImproperFractionRule 338 || rule->getBaseValue() == NFRule::kProperFractionRule 339 || rule->getBaseValue() == NFRule::kMasterRule) { 340 return new IntegralPartSubstitution(pos, ruleSet, description, status); 341 } 342 343 // if the rule set containing the rule is a fraction 344 // rule set, return a NumeratorSubstitution 345 else if (ruleSet->isFractionRuleSet()) { 346 return new NumeratorSubstitution(pos, (double)rule->getBaseValue(), 347 formatter->getDefaultRuleSet(), description, status); 348 } 349 350 // otherwise, return a MultiplierSubstitution 351 else { 352 return new MultiplierSubstitution(pos, rule, ruleSet, 353 description, status); 354 } 355 356 // if the description begins with '>'... 357 case gGreaterThan: 358 // if the rule is a negative-number rule, return 359 // an AbsoluteValueSubstitution 360 if (rule->getBaseValue() == NFRule::kNegativeNumberRule) { 361 return new AbsoluteValueSubstitution(pos, ruleSet, description, status); 362 } 363 364 // if the rule is a fraction rule, return a 365 // FractionalPartSubstitution 366 else if (rule->getBaseValue() == NFRule::kImproperFractionRule 367 || rule->getBaseValue() == NFRule::kProperFractionRule 368 || rule->getBaseValue() == NFRule::kMasterRule) { 369 return new FractionalPartSubstitution(pos, ruleSet, description, status); 370 } 371 372 // if the rule set owning the rule is a fraction rule set, 373 // throw an exception 374 else if (ruleSet->isFractionRuleSet()) { 375 // throw new IllegalArgumentException(">> not allowed in fraction rule set"); 376 status = U_PARSE_ERROR; 377 return NULL; 378 } 379 380 // otherwise, return a ModulusSubstitution 381 else { 382 return new ModulusSubstitution(pos, rule, predecessor, 383 ruleSet, description, status); 384 } 385 386 // if the description begins with '=', always return a 387 // SameValueSubstitution 388 case gEquals: 389 return new SameValueSubstitution(pos, ruleSet, description, status); 390 391 // and if it's anything else, throw an exception 392 default: 393 // throw new IllegalArgumentException("Illegal substitution character"); 394 status = U_PARSE_ERROR; 395 } 396 return NULL; 397 } 398 399 NFSubstitution::NFSubstitution(int32_t _pos, 400 const NFRuleSet* _ruleSet, 401 const UnicodeString& description, 402 UErrorCode& status) 403 : pos(_pos), ruleSet(NULL), numberFormat(NULL) 404 { 405 // the description should begin and end with the same character. 406 // If it doesn't that's a syntax error. Otherwise, 407 // makeSubstitution() was the only thing that needed to know 408 // about these characters, so strip them off 409 UnicodeString workingDescription(description); 410 if (description.length() >= 2 411 && description.charAt(0) == description.charAt(description.length() - 1)) 412 { 413 workingDescription.remove(description.length() - 1, 1); 414 workingDescription.remove(0, 1); 415 } 416 else if (description.length() != 0) { 417 // throw new IllegalArgumentException("Illegal substitution syntax"); 418 status = U_PARSE_ERROR; 419 return; 420 } 421 422 if (workingDescription.length() == 0) { 423 // if the description was just two paired token characters 424 // (i.e., "<<" or ">>"), it uses the rule set it belongs to to 425 // format its result 426 this->ruleSet = _ruleSet; 427 } 428 else if (workingDescription.charAt(0) == gPercent) { 429 // if the description contains a rule set name, that's the rule 430 // set we use to format the result: get a reference to the 431 // names rule set 432 this->ruleSet = _ruleSet->getOwner()->findRuleSet(workingDescription, status); 433 } 434 else if (workingDescription.charAt(0) == gPound || workingDescription.charAt(0) ==gZero) { 435 // if the description begins with 0 or #, treat it as a 436 // DecimalFormat pattern, and initialize a DecimalFormat with 437 // that pattern (then set it to use the DecimalFormatSymbols 438 // belonging to our formatter) 439 const DecimalFormatSymbols* sym = _ruleSet->getOwner()->getDecimalFormatSymbols(); 440 if (!sym) { 441 status = U_MISSING_RESOURCE_ERROR; 442 return; 443 } 444 DecimalFormat *tempNumberFormat = new DecimalFormat(workingDescription, *sym, status); 445 /* test for NULL */ 446 if (!tempNumberFormat) { 447 status = U_MEMORY_ALLOCATION_ERROR; 448 return; 449 } 450 if (U_FAILURE(status)) { 451 delete tempNumberFormat; 452 return; 453 } 454 this->numberFormat = tempNumberFormat; 455 } 456 else if (workingDescription.charAt(0) == gGreaterThan) { 457 // if the description is ">>>", this substitution bypasses the 458 // usual rule-search process and always uses the rule that precedes 459 // it in its own rule set's rule list (this is used for place-value 460 // notations: formats where you want to see a particular part of 461 // a number even when it's 0) 462 463 // this causes problems when >>> is used in a frationalPartSubstitution 464 // this->ruleSet = NULL; 465 this->ruleSet = _ruleSet; 466 this->numberFormat = NULL; 467 } 468 else { 469 // and of the description is none of these things, it's a syntax error 470 471 // throw new IllegalArgumentException("Illegal substitution syntax"); 472 status = U_PARSE_ERROR; 473 } 474 } 475 476 NFSubstitution::~NFSubstitution() 477 { 478 delete numberFormat; 479 numberFormat = NULL; 480 } 481 482 /** 483 * Set's the substitution's divisor. Used by NFRule.setBaseValue(). 484 * A no-op for all substitutions except multiplier and modulus 485 * substitutions. 486 * @param radix The radix of the divisor 487 * @param exponent The exponent of the divisor 488 */ 489 void 490 NFSubstitution::setDivisor(int32_t /*radix*/, int16_t /*exponent*/, UErrorCode& /*status*/) { 491 // a no-op for all substitutions except multiplier and modulus substitutions 492 } 493 494 void 495 NFSubstitution::setDecimalFormatSymbols(const DecimalFormatSymbols &newSymbols, UErrorCode& /*status*/) { 496 if (numberFormat != NULL) { 497 numberFormat->setDecimalFormatSymbols(newSymbols); 498 } 499 } 500 501 //----------------------------------------------------------------------- 502 // boilerplate 503 //----------------------------------------------------------------------- 504 505 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NFSubstitution) 506 507 /** 508 * Compares two substitutions for equality 509 * @param The substitution to compare this one to 510 * @return true if the two substitutions are functionally equivalent 511 */ 512 UBool 513 NFSubstitution::operator==(const NFSubstitution& rhs) const 514 { 515 // compare class and all of the fields all substitutions have 516 // in common 517 // this should be called by subclasses before their own equality tests 518 return typeid(*this) == typeid(rhs) 519 && pos == rhs.pos 520 && (ruleSet == NULL) == (rhs.ruleSet == NULL) 521 // && ruleSet == rhs.ruleSet causes circularity, other checks to make instead? 522 && (numberFormat == NULL 523 ? (rhs.numberFormat == NULL) 524 : (*numberFormat == *rhs.numberFormat)); 525 } 526 527 /** 528 * Returns a textual description of the substitution 529 * @return A textual description of the substitution. This might 530 * not be identical to the description it was created from, but 531 * it'll produce the same result. 532 */ 533 void 534 NFSubstitution::toString(UnicodeString& text) const 535 { 536 // use tokenChar() to get the character at the beginning and 537 // end of the substitutin token. In between them will go 538 // either the name of the rule set it uses, or the pattern of 539 // the DecimalFormat it uses 540 text.remove(); 541 text.append(tokenChar()); 542 543 UnicodeString temp; 544 if (ruleSet != NULL) { 545 ruleSet->getName(temp); 546 } else if (numberFormat != NULL) { 547 numberFormat->toPattern(temp); 548 } 549 text.append(temp); 550 text.append(tokenChar()); 551 } 552 553 //----------------------------------------------------------------------- 554 // formatting 555 //----------------------------------------------------------------------- 556 557 /** 558 * Performs a mathematical operation on the number, formats it using 559 * either ruleSet or decimalFormat, and inserts the result into 560 * toInsertInto. 561 * @param number The number being formatted. 562 * @param toInsertInto The string we insert the result into 563 * @param pos The position in toInsertInto where the owning rule's 564 * rule text begins (this value is added to this substitution's 565 * position to determine exactly where to insert the new text) 566 */ 567 void 568 NFSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const 569 { 570 if (ruleSet != NULL) { 571 // Perform a transformation on the number that is dependent 572 // on the type of substitution this is, then just call its 573 // rule set's format() method to format the result 574 ruleSet->format(transformNumber(number), toInsertInto, _pos + this->pos, recursionCount, status); 575 } else if (numberFormat != NULL) { 576 if (number <= MAX_INT64_IN_DOUBLE) { 577 // or perform the transformation on the number (preserving 578 // the result's fractional part if the formatter it set 579 // to show it), then use that formatter's format() method 580 // to format the result 581 double numberToFormat = transformNumber((double)number); 582 if (numberFormat->getMaximumFractionDigits() == 0) { 583 numberToFormat = uprv_floor(numberToFormat); 584 } 585 586 UnicodeString temp; 587 numberFormat->format(numberToFormat, temp, status); 588 toInsertInto.insert(_pos + this->pos, temp); 589 } 590 else { 591 // We have gone beyond double precision. Something has to give. 592 // We're favoring accuracy of the large number over potential rules 593 // that round like a CompactDecimalFormat, which is not a common use case. 594 // 595 // Perform a transformation on the number that is dependent 596 // on the type of substitution this is, then just call its 597 // rule set's format() method to format the result 598 int64_t numberToFormat = transformNumber(number); 599 UnicodeString temp; 600 numberFormat->format(numberToFormat, temp, status); 601 toInsertInto.insert(_pos + this->pos, temp); 602 } 603 } 604 } 605 606 /** 607 * Performs a mathematical operation on the number, formats it using 608 * either ruleSet or decimalFormat, and inserts the result into 609 * toInsertInto. 610 * @param number The number being formatted. 611 * @param toInsertInto The string we insert the result into 612 * @param pos The position in toInsertInto where the owning rule's 613 * rule text begins (this value is added to this substitution's 614 * position to determine exactly where to insert the new text) 615 */ 616 void 617 NFSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const { 618 // perform a transformation on the number being formatted that 619 // is dependent on the type of substitution this is 620 double numberToFormat = transformNumber(number); 621 622 if (uprv_isInfinite(numberToFormat)) { 623 // This is probably a minus rule. Combine it with an infinite rule. 624 const NFRule *infiniteRule = ruleSet->findDoubleRule(uprv_getInfinity()); 625 infiniteRule->doFormat(numberToFormat, toInsertInto, _pos + this->pos, recursionCount, status); 626 return; 627 } 628 629 // if the result is an integer, from here on out we work in integer 630 // space (saving time and memory and preserving accuracy) 631 if (numberToFormat == uprv_floor(numberToFormat) && ruleSet != NULL) { 632 ruleSet->format(util64_fromDouble(numberToFormat), toInsertInto, _pos + this->pos, recursionCount, status); 633 634 // if the result isn't an integer, then call either our rule set's 635 // format() method or our DecimalFormat's format() method to 636 // format the result 637 } else { 638 if (ruleSet != NULL) { 639 ruleSet->format(numberToFormat, toInsertInto, _pos + this->pos, recursionCount, status); 640 } else if (numberFormat != NULL) { 641 UnicodeString temp; 642 numberFormat->format(numberToFormat, temp); 643 toInsertInto.insert(_pos + this->pos, temp); 644 } 645 } 646 } 647 648 649 //----------------------------------------------------------------------- 650 // parsing 651 //----------------------------------------------------------------------- 652 653 #ifdef RBNF_DEBUG 654 #include <stdio.h> 655 #endif 656 657 /** 658 * Parses a string using the rule set or DecimalFormat belonging 659 * to this substitution. If there's a match, a mathematical 660 * operation (the inverse of the one used in formatting) is 661 * performed on the result of the parse and the value passed in 662 * and returned as the result. The parse position is updated to 663 * point to the first unmatched character in the string. 664 * @param text The string to parse 665 * @param parsePosition On entry, ignored, but assumed to be 0. 666 * On exit, this is updated to point to the first unmatched 667 * character (or 0 if the substitution didn't match) 668 * @param baseValue A partial parse result that should be 669 * combined with the result of this parse 670 * @param upperBound When searching the rule set for a rule 671 * matching the string passed in, only rules with base values 672 * lower than this are considered 673 * @param lenientParse If true and matching against rules fails, 674 * the substitution will also try matching the text against 675 * numerals using a default-costructed NumberFormat. If false, 676 * no extra work is done. (This value is false whenever the 677 * formatter isn't in lenient-parse mode, but is also false 678 * under some conditions even when the formatter _is_ in 679 * lenient-parse mode.) 680 * @return If there's a match, this is the result of composing 681 * baseValue with whatever was returned from matching the 682 * characters. This will be either a Long or a Double. If there's 683 * no match this is new Long(0) (not null), and parsePosition 684 * is left unchanged. 685 */ 686 UBool 687 NFSubstitution::doParse(const UnicodeString& text, 688 ParsePosition& parsePosition, 689 double baseValue, 690 double upperBound, 691 UBool lenientParse, 692 Formattable& result) const 693 { 694 #ifdef RBNF_DEBUG 695 fprintf(stderr, "<nfsubs> %x bv: %g ub: %g\n", this, baseValue, upperBound); 696 #endif 697 // figure out the highest base value a rule can have and match 698 // the text being parsed (this varies according to the type of 699 // substitutions: multiplier, modulus, and numerator substitutions 700 // restrict the search to rules with base values lower than their 701 // own; same-value substitutions leave the upper bound wherever 702 // it was, and the others allow any rule to match 703 upperBound = calcUpperBound(upperBound); 704 705 // use our rule set to parse the text. If that fails and 706 // lenient parsing is enabled (this is always false if the 707 // formatter's lenient-parsing mode is off, but it may also 708 // be false even when the formatter's lenient-parse mode is 709 // on), then also try parsing the text using a default- 710 // constructed NumberFormat 711 if (ruleSet != NULL) { 712 ruleSet->parse(text, parsePosition, upperBound, result); 713 if (lenientParse && !ruleSet->isFractionRuleSet() && parsePosition.getIndex() == 0) { 714 UErrorCode status = U_ZERO_ERROR; 715 NumberFormat* fmt = NumberFormat::createInstance(status); 716 if (U_SUCCESS(status)) { 717 fmt->parse(text, result, parsePosition); 718 } 719 delete fmt; 720 } 721 722 // ...or use our DecimalFormat to parse the text 723 } else if (numberFormat != NULL) { 724 numberFormat->parse(text, result, parsePosition); 725 } 726 727 // if the parse was successful, we've already advanced the caller's 728 // parse position (this is the one function that doesn't have one 729 // of its own). Derive a parse result and return it as a Long, 730 // if possible, or a Double 731 if (parsePosition.getIndex() != 0) { 732 UErrorCode status = U_ZERO_ERROR; 733 double tempResult = result.getDouble(status); 734 735 // composeRuleValue() produces a full parse result from 736 // the partial parse result passed to this function from 737 // the caller (this is either the owning rule's base value 738 // or the partial result obtained from composing the 739 // owning rule's base value with its other substitution's 740 // parse result) and the partial parse result obtained by 741 // matching the substitution (which will be the same value 742 // the caller would get by parsing just this part of the 743 // text with RuleBasedNumberFormat.parse() ). How the two 744 // values are used to derive the full parse result depends 745 // on the types of substitutions: For a regular rule, the 746 // ultimate result is its multiplier substitution's result 747 // times the rule's divisor (or the rule's base value) plus 748 // the modulus substitution's result (which will actually 749 // supersede part of the rule's base value). For a negative- 750 // number rule, the result is the negative of its substitution's 751 // result. For a fraction rule, it's the sum of its two 752 // substitution results. For a rule in a fraction rule set, 753 // it's the numerator substitution's result divided by 754 // the rule's base value. Results from same-value substitutions 755 // propagate back upard, and null substitutions don't affect 756 // the result. 757 tempResult = composeRuleValue(tempResult, baseValue); 758 result.setDouble(tempResult); 759 return TRUE; 760 // if the parse was UNsuccessful, return 0 761 } else { 762 result.setLong(0); 763 return FALSE; 764 } 765 } 766 767 /** 768 * Returns true if this is a modulus substitution. (We didn't do this 769 * with instanceof partially because it causes source files to 770 * proliferate and partially because we have to port this to C++.) 771 * @return true if this object is an instance of ModulusSubstitution 772 */ 773 UBool 774 NFSubstitution::isModulusSubstitution() const { 775 return FALSE; 776 } 777 778 //=================================================================== 779 // SameValueSubstitution 780 //=================================================================== 781 782 /** 783 * A substitution that passes the value passed to it through unchanged. 784 * Represented by == in rule descriptions. 785 */ 786 SameValueSubstitution::SameValueSubstitution(int32_t _pos, 787 const NFRuleSet* _ruleSet, 788 const UnicodeString& description, 789 UErrorCode& status) 790 : NFSubstitution(_pos, _ruleSet, description, status) 791 { 792 if (0 == description.compare(gEqualsEquals, 2)) { 793 // throw new IllegalArgumentException("== is not a legal token"); 794 status = U_PARSE_ERROR; 795 } 796 } 797 798 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SameValueSubstitution) 799 800 //=================================================================== 801 // MultiplierSubstitution 802 //=================================================================== 803 804 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MultiplierSubstitution) 805 806 UBool MultiplierSubstitution::operator==(const NFSubstitution& rhs) const 807 { 808 return NFSubstitution::operator==(rhs) && 809 divisor == ((const MultiplierSubstitution*)&rhs)->divisor; 810 } 811 812 813 //=================================================================== 814 // ModulusSubstitution 815 //=================================================================== 816 817 /** 818 * A substitution that divides the number being formatted by the its rule's 819 * divisor and formats the remainder. Represented by ">>" in a 820 * regular rule. 821 */ 822 ModulusSubstitution::ModulusSubstitution(int32_t _pos, 823 const NFRule* rule, 824 const NFRule* predecessor, 825 const NFRuleSet* _ruleSet, 826 const UnicodeString& description, 827 UErrorCode& status) 828 : NFSubstitution(_pos, _ruleSet, description, status) 829 , divisor(rule->getDivisor()) 830 , ruleToUse(NULL) 831 { 832 // the owning rule's divisor controls the behavior of this 833 // substitution: rather than keeping a backpointer to the rule, 834 // we keep a copy of the divisor 835 836 if (divisor == 0) { 837 status = U_PARSE_ERROR; 838 } 839 840 if (0 == description.compare(gGreaterGreaterGreaterThan, 3)) { 841 // the >>> token doesn't alter how this substituion calculates the 842 // values it uses for formatting and parsing, but it changes 843 // what's done with that value after it's obtained: >>> short- 844 // circuits the rule-search process and goes straight to the 845 // specified rule to format the substitution value 846 ruleToUse = predecessor; 847 } 848 } 849 850 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ModulusSubstitution) 851 852 UBool ModulusSubstitution::operator==(const NFSubstitution& rhs) const 853 { 854 return NFSubstitution::operator==(rhs) && 855 divisor == ((const ModulusSubstitution*)&rhs)->divisor && 856 ruleToUse == ((const ModulusSubstitution*)&rhs)->ruleToUse; 857 } 858 859 //----------------------------------------------------------------------- 860 // formatting 861 //----------------------------------------------------------------------- 862 863 864 /** 865 * If this is a >>> substitution, use ruleToUse to fill in 866 * the substitution. Otherwise, just use the superclass function. 867 * @param number The number being formatted 868 * @toInsertInto The string to insert the result of this substitution 869 * into 870 * @param pos The position of the rule text in toInsertInto 871 */ 872 void 873 ModulusSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const 874 { 875 // if this isn't a >>> substitution, just use the inherited version 876 // of this function (which uses either a rule set or a DecimalFormat 877 // to format its substitution value) 878 if (ruleToUse == NULL) { 879 NFSubstitution::doSubstitution(number, toInsertInto, _pos, recursionCount, status); 880 881 // a >>> substitution goes straight to a particular rule to 882 // format the substitution value 883 } else { 884 int64_t numberToFormat = transformNumber(number); 885 ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos(), recursionCount, status); 886 } 887 } 888 889 /** 890 * If this is a >>> substitution, use ruleToUse to fill in 891 * the substitution. Otherwise, just use the superclass function. 892 * @param number The number being formatted 893 * @toInsertInto The string to insert the result of this substitution 894 * into 895 * @param pos The position of the rule text in toInsertInto 896 */ 897 void 898 ModulusSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const 899 { 900 // if this isn't a >>> substitution, just use the inherited version 901 // of this function (which uses either a rule set or a DecimalFormat 902 // to format its substitution value) 903 if (ruleToUse == NULL) { 904 NFSubstitution::doSubstitution(number, toInsertInto, _pos, recursionCount, status); 905 906 // a >>> substitution goes straight to a particular rule to 907 // format the substitution value 908 } else { 909 double numberToFormat = transformNumber(number); 910 911 ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos(), recursionCount, status); 912 } 913 } 914 915 //----------------------------------------------------------------------- 916 // parsing 917 //----------------------------------------------------------------------- 918 919 /** 920 * If this is a >>> substitution, match only against ruleToUse. 921 * Otherwise, use the superclass function. 922 * @param text The string to parse 923 * @param parsePosition Ignored on entry, updated on exit to point to 924 * the first unmatched character. 925 * @param baseValue The partial parse result prior to calling this 926 * routine. 927 */ 928 UBool 929 ModulusSubstitution::doParse(const UnicodeString& text, 930 ParsePosition& parsePosition, 931 double baseValue, 932 double upperBound, 933 UBool lenientParse, 934 Formattable& result) const 935 { 936 // if this isn't a >>> substitution, we can just use the 937 // inherited parse() routine to do the parsing 938 if (ruleToUse == NULL) { 939 return NFSubstitution::doParse(text, parsePosition, baseValue, upperBound, lenientParse, result); 940 941 // but if it IS a >>> substitution, we have to do it here: we 942 // use the specific rule's doParse() method, and then we have to 943 // do some of the other work of NFRuleSet.parse() 944 } else { 945 ruleToUse->doParse(text, parsePosition, FALSE, upperBound, result); 946 947 if (parsePosition.getIndex() != 0) { 948 UErrorCode status = U_ZERO_ERROR; 949 double tempResult = result.getDouble(status); 950 tempResult = composeRuleValue(tempResult, baseValue); 951 result.setDouble(tempResult); 952 } 953 954 return TRUE; 955 } 956 } 957 /** 958 * Returns a textual description of the substitution 959 * @return A textual description of the substitution. This might 960 * not be identical to the description it was created from, but 961 * it'll produce the same result. 962 */ 963 void 964 ModulusSubstitution::toString(UnicodeString& text) const 965 { 966 // use tokenChar() to get the character at the beginning and 967 // end of the substitutin token. In between them will go 968 // either the name of the rule set it uses, or the pattern of 969 // the DecimalFormat it uses 970 971 if ( ruleToUse != NULL ) { // Must have been a >>> substitution. 972 text.remove(); 973 text.append(tokenChar()); 974 text.append(tokenChar()); 975 text.append(tokenChar()); 976 } else { // Otherwise just use the super-class function. 977 NFSubstitution::toString(text); 978 } 979 } 980 //=================================================================== 981 // IntegralPartSubstitution 982 //=================================================================== 983 984 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IntegralPartSubstitution) 985 986 987 //=================================================================== 988 // FractionalPartSubstitution 989 //=================================================================== 990 991 992 /** 993 * Constructs a FractionalPartSubstitution. This object keeps a flag 994 * telling whether it should format by digits or not. In addition, 995 * it marks the rule set it calls (if any) as a fraction rule set. 996 */ 997 FractionalPartSubstitution::FractionalPartSubstitution(int32_t _pos, 998 const NFRuleSet* _ruleSet, 999 const UnicodeString& description, 1000 UErrorCode& status) 1001 : NFSubstitution(_pos, _ruleSet, description, status) 1002 , byDigits(FALSE) 1003 , useSpaces(TRUE) 1004 1005 { 1006 // akk, ruleSet can change in superclass constructor 1007 if (0 == description.compare(gGreaterGreaterThan, 2) || 1008 0 == description.compare(gGreaterGreaterGreaterThan, 3) || 1009 _ruleSet == getRuleSet()) { 1010 byDigits = TRUE; 1011 if (0 == description.compare(gGreaterGreaterGreaterThan, 3)) { 1012 useSpaces = FALSE; 1013 } 1014 } else { 1015 // cast away const 1016 ((NFRuleSet*)getRuleSet())->makeIntoFractionRuleSet(); 1017 } 1018 } 1019 1020 //----------------------------------------------------------------------- 1021 // formatting 1022 //----------------------------------------------------------------------- 1023 1024 /** 1025 * If in "by digits" mode, fills in the substitution one decimal digit 1026 * at a time using the rule set containing this substitution. 1027 * Otherwise, uses the superclass function. 1028 * @param number The number being formatted 1029 * @param toInsertInto The string to insert the result of formatting 1030 * the substitution into 1031 * @param pos The position of the owning rule's rule text in 1032 * toInsertInto 1033 */ 1034 void 1035 FractionalPartSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, 1036 int32_t _pos, int32_t recursionCount, UErrorCode& status) const 1037 { 1038 // if we're not in "byDigits" mode, just use the inherited 1039 // doSubstitution() routine 1040 if (!byDigits) { 1041 NFSubstitution::doSubstitution(number, toInsertInto, _pos, recursionCount, status); 1042 1043 // if we're in "byDigits" mode, transform the value into an integer 1044 // by moving the decimal point eight places to the right and 1045 // pulling digits off the right one at a time, formatting each digit 1046 // as an integer using this substitution's owning rule set 1047 // (this is slower, but more accurate, than doing it from the 1048 // other end) 1049 } else { 1050 // int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits)); 1051 // // this flag keeps us from formatting trailing zeros. It starts 1052 // // out false because we're pulling from the right, and switches 1053 // // to true the first time we encounter a non-zero digit 1054 // UBool doZeros = FALSE; 1055 // for (int32_t i = 0; i < kMaxDecimalDigits; i++) { 1056 // int64_t digit = numberToFormat % 10; 1057 // if (digit != 0 || doZeros) { 1058 // if (doZeros && useSpaces) { 1059 // toInsertInto.insert(_pos + getPos(), gSpace); 1060 // } 1061 // doZeros = TRUE; 1062 // getRuleSet()->format(digit, toInsertInto, _pos + getPos()); 1063 // } 1064 // numberToFormat /= 10; 1065 // } 1066 1067 DigitList dl; 1068 dl.set(number); 1069 dl.roundFixedPoint(20); // round to 20 fraction digits. 1070 dl.reduce(); // Removes any trailing zeros. 1071 1072 UBool pad = FALSE; 1073 for (int32_t didx = dl.getCount()-1; didx>=dl.getDecimalAt(); didx--) { 1074 // Loop iterates over fraction digits, starting with the LSD. 1075 // include both real digits from the number, and zeros 1076 // to the left of the MSD but to the right of the decimal point. 1077 if (pad && useSpaces) { 1078 toInsertInto.insert(_pos + getPos(), gSpace); 1079 } else { 1080 pad = TRUE; 1081 } 1082 int64_t digit = didx>=0 ? dl.getDigit(didx) - '0' : 0; 1083 getRuleSet()->format(digit, toInsertInto, _pos + getPos(), recursionCount, status); 1084 } 1085 1086 if (!pad) { 1087 // hack around lack of precision in digitlist. if we would end up with 1088 // "foo point" make sure we add a " zero" to the end. 1089 getRuleSet()->format((int64_t)0, toInsertInto, _pos + getPos(), recursionCount, status); 1090 } 1091 } 1092 } 1093 1094 //----------------------------------------------------------------------- 1095 // parsing 1096 //----------------------------------------------------------------------- 1097 1098 /** 1099 * If in "by digits" mode, parses the string as if it were a string 1100 * of individual digits; otherwise, uses the superclass function. 1101 * @param text The string to parse 1102 * @param parsePosition Ignored on entry, but updated on exit to point 1103 * to the first unmatched character 1104 * @param baseValue The partial parse result prior to entering this 1105 * function 1106 * @param upperBound Only consider rules with base values lower than 1107 * this when filling in the substitution 1108 * @param lenientParse If true, try matching the text as numerals if 1109 * matching as words doesn't work 1110 * @return If the match was successful, the current partial parse 1111 * result; otherwise new Long(0). The result is either a Long or 1112 * a Double. 1113 */ 1114 1115 UBool 1116 FractionalPartSubstitution::doParse(const UnicodeString& text, 1117 ParsePosition& parsePosition, 1118 double baseValue, 1119 double /*upperBound*/, 1120 UBool lenientParse, 1121 Formattable& resVal) const 1122 { 1123 // if we're not in byDigits mode, we can just use the inherited 1124 // doParse() 1125 if (!byDigits) { 1126 return NFSubstitution::doParse(text, parsePosition, baseValue, 0, lenientParse, resVal); 1127 1128 // if we ARE in byDigits mode, parse the text one digit at a time 1129 // using this substitution's owning rule set (we do this by setting 1130 // upperBound to 10 when calling doParse() ) until we reach 1131 // nonmatching text 1132 } else { 1133 UnicodeString workText(text); 1134 ParsePosition workPos(1); 1135 double result = 0; 1136 int32_t digit; 1137 // double p10 = 0.1; 1138 1139 DigitList dl; 1140 NumberFormat* fmt = NULL; 1141 while (workText.length() > 0 && workPos.getIndex() != 0) { 1142 workPos.setIndex(0); 1143 Formattable temp; 1144 getRuleSet()->parse(workText, workPos, 10, temp); 1145 UErrorCode status = U_ZERO_ERROR; 1146 digit = temp.getLong(status); 1147 // digit = temp.getType() == Formattable::kLong ? 1148 // temp.getLong() : 1149 // (int32_t)temp.getDouble(); 1150 1151 if (lenientParse && workPos.getIndex() == 0) { 1152 if (!fmt) { 1153 status = U_ZERO_ERROR; 1154 fmt = NumberFormat::createInstance(status); 1155 if (U_FAILURE(status)) { 1156 delete fmt; 1157 fmt = NULL; 1158 } 1159 } 1160 if (fmt) { 1161 fmt->parse(workText, temp, workPos); 1162 digit = temp.getLong(status); 1163 } 1164 } 1165 1166 if (workPos.getIndex() != 0) { 1167 dl.append((char)('0' + digit)); 1168 // result += digit * p10; 1169 // p10 /= 10; 1170 parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()); 1171 workText.removeBetween(0, workPos.getIndex()); 1172 while (workText.length() > 0 && workText.charAt(0) == gSpace) { 1173 workText.removeBetween(0, 1); 1174 parsePosition.setIndex(parsePosition.getIndex() + 1); 1175 } 1176 } 1177 } 1178 delete fmt; 1179 1180 result = dl.getCount() == 0 ? 0 : dl.getDouble(); 1181 result = composeRuleValue(result, baseValue); 1182 resVal.setDouble(result); 1183 return TRUE; 1184 } 1185 } 1186 1187 UBool 1188 FractionalPartSubstitution::operator==(const NFSubstitution& rhs) const 1189 { 1190 return NFSubstitution::operator==(rhs) && 1191 ((const FractionalPartSubstitution*)&rhs)->byDigits == byDigits; 1192 } 1193 1194 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FractionalPartSubstitution) 1195 1196 1197 //=================================================================== 1198 // AbsoluteValueSubstitution 1199 //=================================================================== 1200 1201 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AbsoluteValueSubstitution) 1202 1203 //=================================================================== 1204 // NumeratorSubstitution 1205 //=================================================================== 1206 1207 void 1208 NumeratorSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t apos, int32_t recursionCount, UErrorCode& status) const { 1209 // perform a transformation on the number being formatted that 1210 // is dependent on the type of substitution this is 1211 1212 double numberToFormat = transformNumber(number); 1213 int64_t longNF = util64_fromDouble(numberToFormat); 1214 1215 const NFRuleSet* aruleSet = getRuleSet(); 1216 if (withZeros && aruleSet != NULL) { 1217 // if there are leading zeros in the decimal expansion then emit them 1218 int64_t nf =longNF; 1219 int32_t len = toInsertInto.length(); 1220 while ((nf *= 10) < denominator) { 1221 toInsertInto.insert(apos + getPos(), gSpace); 1222 aruleSet->format((int64_t)0, toInsertInto, apos + getPos(), recursionCount, status); 1223 } 1224 apos += toInsertInto.length() - len; 1225 } 1226 1227 // if the result is an integer, from here on out we work in integer 1228 // space (saving time and memory and preserving accuracy) 1229 if (numberToFormat == longNF && aruleSet != NULL) { 1230 aruleSet->format(longNF, toInsertInto, apos + getPos(), recursionCount, status); 1231 1232 // if the result isn't an integer, then call either our rule set's 1233 // format() method or our DecimalFormat's format() method to 1234 // format the result 1235 } else { 1236 if (aruleSet != NULL) { 1237 aruleSet->format(numberToFormat, toInsertInto, apos + getPos(), recursionCount, status); 1238 } else { 1239 UnicodeString temp; 1240 getNumberFormat()->format(numberToFormat, temp, status); 1241 toInsertInto.insert(apos + getPos(), temp); 1242 } 1243 } 1244 } 1245 1246 UBool 1247 NumeratorSubstitution::doParse(const UnicodeString& text, 1248 ParsePosition& parsePosition, 1249 double baseValue, 1250 double upperBound, 1251 UBool /*lenientParse*/, 1252 Formattable& result) const 1253 { 1254 // we don't have to do anything special to do the parsing here, 1255 // but we have to turn lenient parsing off-- if we leave it on, 1256 // it SERIOUSLY messes up the algorithm 1257 1258 // if withZeros is true, we need to count the zeros 1259 // and use that to adjust the parse result 1260 UErrorCode status = U_ZERO_ERROR; 1261 int32_t zeroCount = 0; 1262 UnicodeString workText(text); 1263 1264 if (withZeros) { 1265 ParsePosition workPos(1); 1266 Formattable temp; 1267 1268 while (workText.length() > 0 && workPos.getIndex() != 0) { 1269 workPos.setIndex(0); 1270 getRuleSet()->parse(workText, workPos, 1, temp); // parse zero or nothing at all 1271 if (workPos.getIndex() == 0) { 1272 // we failed, either there were no more zeros, or the number was formatted with digits 1273 // either way, we're done 1274 break; 1275 } 1276 1277 ++zeroCount; 1278 parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()); 1279 workText.remove(0, workPos.getIndex()); 1280 while (workText.length() > 0 && workText.charAt(0) == gSpace) { 1281 workText.remove(0, 1); 1282 parsePosition.setIndex(parsePosition.getIndex() + 1); 1283 } 1284 } 1285 1286 workText = text; 1287 workText.remove(0, (int32_t)parsePosition.getIndex()); 1288 parsePosition.setIndex(0); 1289 } 1290 1291 // we've parsed off the zeros, now let's parse the rest from our current position 1292 NFSubstitution::doParse(workText, parsePosition, withZeros ? 1 : baseValue, upperBound, FALSE, result); 1293 1294 if (withZeros) { 1295 // any base value will do in this case. is there a way to 1296 // force this to not bother trying all the base values? 1297 1298 // compute the 'effective' base and prescale the value down 1299 int64_t n = result.getLong(status); // force conversion! 1300 int64_t d = 1; 1301 int32_t pow = 0; 1302 while (d <= n) { 1303 d *= 10; 1304 ++pow; 1305 } 1306 // now add the zeros 1307 while (zeroCount > 0) { 1308 d *= 10; 1309 --zeroCount; 1310 } 1311 // d is now our true denominator 1312 result.setDouble((double)n/(double)d); 1313 } 1314 1315 return TRUE; 1316 } 1317 1318 UBool 1319 NumeratorSubstitution::operator==(const NFSubstitution& rhs) const 1320 { 1321 return NFSubstitution::operator==(rhs) && 1322 denominator == ((const NumeratorSubstitution*)&rhs)->denominator; 1323 } 1324 1325 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumeratorSubstitution) 1326 1327 const UChar NumeratorSubstitution::LTLT[] = { 0x003c, 0x003c }; 1328 1329 U_NAMESPACE_END 1330 1331 /* U_HAVE_RBNF */ 1332 #endif 1333 1334