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