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