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