1 /* GENERATED SOURCE. DO NOT MODIFY. */ 2 // © 2016 and later: Unicode, Inc. and others. 3 // License & terms of use: http://www.unicode.org/copyright.html#License 4 /* 5 ******************************************************************************* 6 * Copyright (C) 1996-2015, International Business Machines Corporation and * 7 * others. All Rights Reserved. * 8 ******************************************************************************* 9 */ 10 package ohos.global.icu.text; 11 12 import java.text.ParsePosition; 13 14 import ohos.global.icu.impl.number.DecimalQuantity_DualStorageBCD; 15 16 //=================================================================== 17 // NFSubstitution (abstract base class) 18 //=================================================================== 19 20 /** 21 * An abstract class defining protocol for substitutions. A substitution 22 * is a section of a rule that inserts text into the rule's rule text 23 * based on some part of the number being formatted. 24 * @author Richard Gillam 25 */ 26 abstract class NFSubstitution { 27 //----------------------------------------------------------------------- 28 // data members 29 //----------------------------------------------------------------------- 30 31 /** 32 * The substitution's position in the rule text of the rule that owns it 33 */ 34 final int pos; 35 36 /** 37 * The rule set this substitution uses to format its result, or null. 38 * (Either this or numberFormat has to be non-null.) 39 */ 40 final NFRuleSet ruleSet; 41 42 /** 43 * The DecimalFormat this substitution uses to format its result, 44 * or null. (Either this or ruleSet has to be non-null.) 45 */ 46 final DecimalFormat numberFormat; 47 48 //----------------------------------------------------------------------- 49 // construction 50 //----------------------------------------------------------------------- 51 52 /** 53 * Parses the description, creates the right kind of substitution, 54 * and initializes it based on the description. 55 * @param pos The substitution's position in the rule text of the 56 * rule that owns it. 57 * @param rule The rule containing this substitution 58 * @param rulePredecessor The rule preceding the one that contains 59 * this substitution in the rule set's rule list (this is used 60 * only for >>> substitutions). 61 * @param ruleSet The rule set containing the rule containing this 62 * substitution 63 * @param formatter The RuleBasedNumberFormat that ultimately owns 64 * this substitution 65 * @param description The description to parse to build the substitution 66 * (this is just the substring of the rule's description containing 67 * the substitution token itself) 68 * @return A new substitution constructed according to the description 69 */ makeSubstitution(int pos, NFRule rule, NFRule rulePredecessor, NFRuleSet ruleSet, RuleBasedNumberFormat formatter, String description)70 public static NFSubstitution makeSubstitution(int pos, 71 NFRule rule, 72 NFRule rulePredecessor, 73 NFRuleSet ruleSet, 74 RuleBasedNumberFormat formatter, 75 String description) { 76 // if the description is empty, return a NullSubstitution 77 if (description.length() == 0) { 78 return null; 79 } 80 81 switch (description.charAt(0)) { 82 case '<': 83 if (rule.getBaseValue() == NFRule.NEGATIVE_NUMBER_RULE) { 84 // throw an exception if the rule is a negative number rule 85 ///CLOVER:OFF 86 // If you look at the call hierarchy of this method, the rule would 87 // never be directly modified by the user and therefore makes the 88 // following pointless unless the user changes the ruleset. 89 throw new IllegalArgumentException("<< not allowed in negative-number rule"); 90 ///CLOVER:ON 91 } 92 else if (rule.getBaseValue() == NFRule.IMPROPER_FRACTION_RULE 93 || rule.getBaseValue() == NFRule.PROPER_FRACTION_RULE 94 || rule.getBaseValue() == NFRule.MASTER_RULE) 95 { 96 // if the rule is a fraction rule, return an IntegralPartSubstitution 97 return new IntegralPartSubstitution(pos, ruleSet, description); 98 } 99 else if (ruleSet.isFractionSet()) { 100 // if the rule set containing the rule is a fraction 101 // rule set, return a NumeratorSubstitution 102 return new NumeratorSubstitution(pos, rule.getBaseValue(), 103 formatter.getDefaultRuleSet(), description); 104 } 105 else { 106 // otherwise, return a MultiplierSubstitution 107 return new MultiplierSubstitution(pos, rule, ruleSet, 108 description); 109 } 110 111 case '>': 112 if (rule.getBaseValue() == NFRule.NEGATIVE_NUMBER_RULE) { 113 // if the rule is a negative-number rule, return 114 // an AbsoluteValueSubstitution 115 return new AbsoluteValueSubstitution(pos, ruleSet, description); 116 } 117 else if (rule.getBaseValue() == NFRule.IMPROPER_FRACTION_RULE 118 || rule.getBaseValue() == NFRule.PROPER_FRACTION_RULE 119 || rule.getBaseValue() == NFRule.MASTER_RULE) 120 { 121 // if the rule is a fraction rule, return a 122 // FractionalPartSubstitution 123 return new FractionalPartSubstitution(pos, ruleSet, description); 124 } 125 else if (ruleSet.isFractionSet()) { 126 // if the rule set owning the rule is a fraction rule set, 127 // throw an exception 128 ///CLOVER:OFF 129 // If you look at the call hierarchy of this method, the rule would 130 // never be directly modified by the user and therefore makes the 131 // following pointless unless the user changes the ruleset. 132 throw new IllegalArgumentException(">> not allowed in fraction rule set"); 133 ///CLOVER:ON 134 } 135 else { 136 // otherwise, return a ModulusSubstitution 137 return new ModulusSubstitution(pos, rule, rulePredecessor, 138 ruleSet, description); 139 } 140 case '=': 141 return new SameValueSubstitution(pos, ruleSet, description); 142 default: 143 // and if it's anything else, throw an exception 144 ///CLOVER:OFF 145 // If you look at the call hierarchy of this method, the rule would 146 // never be directly modified by the user and therefore makes the 147 // following pointless unless the user changes the ruleset. 148 throw new IllegalArgumentException("Illegal substitution character"); 149 ///CLOVER:ON 150 } 151 } 152 153 /** 154 * Base constructor for substitutions. This constructor sets up the 155 * fields which are common to all substitutions. 156 * @param pos The substitution's position in the owning rule's rule 157 * text 158 * @param ruleSet The rule set that owns this substitution 159 * @param description The substitution descriptor (i.e., the text 160 * inside the token characters) 161 */ NFSubstitution(int pos, NFRuleSet ruleSet, String description)162 NFSubstitution(int pos, 163 NFRuleSet ruleSet, 164 String description) { 165 // initialize the substitution's position in its parent rule 166 this.pos = pos; 167 int descriptionLen = description.length(); 168 169 // the description should begin and end with the same character. 170 // If it doesn't that's a syntax error. Otherwise, 171 // makeSubstitution() was the only thing that needed to know 172 // about these characters, so strip them off 173 if (descriptionLen >= 2 && description.charAt(0) == description.charAt(descriptionLen - 1)) { 174 description = description.substring(1, descriptionLen - 1); 175 } 176 else if (descriptionLen != 0) { 177 throw new IllegalArgumentException("Illegal substitution syntax"); 178 } 179 180 // if the description was just two paired token characters 181 // (i.e., "<<" or ">>"), it uses the rule set it belongs to to 182 // format its result 183 if (description.length() == 0) { 184 this.ruleSet = ruleSet; 185 this.numberFormat = null; 186 } 187 else if (description.charAt(0) == '%') { 188 // if the description contains a rule set name, that's the rule 189 // set we use to format the result: get a reference to the 190 // names rule set 191 this.ruleSet = ruleSet.owner.findRuleSet(description); 192 this.numberFormat = null; 193 } 194 else if (description.charAt(0) == '#' || description.charAt(0) == '0') { 195 // if the description begins with 0 or #, treat it as a 196 // DecimalFormat pattern, and initialize a DecimalFormat with 197 // that pattern (then set it to use the DecimalFormatSymbols 198 // belonging to our formatter) 199 this.ruleSet = null; 200 this.numberFormat = (DecimalFormat) ruleSet.owner.getDecimalFormat().clone(); 201 this.numberFormat.applyPattern(description); 202 } 203 else if (description.charAt(0) == '>') { 204 // if the description is ">>>", this substitution bypasses the 205 // usual rule-search process and always uses the rule that precedes 206 // it in its own rule set's rule list (this is used for place-value 207 // notations: formats where you want to see a particular part of 208 // a number even when it's 0) 209 this.ruleSet = ruleSet; // was null, thai rules added to control space 210 this.numberFormat = null; 211 } 212 else { 213 // and of the description is none of these things, it's a syntax error 214 throw new IllegalArgumentException("Illegal substitution syntax"); 215 } 216 } 217 218 /** 219 * Set's the substitution's divisor. Used by NFRule.setBaseValue(). 220 * A no-op for all substitutions except multiplier and modulus 221 * substitutions. 222 * @param radix The radix of the divisor 223 * @param exponent The exponent of the divisor 224 */ setDivisor(int radix, short exponent)225 public void setDivisor(int radix, short exponent) { 226 // a no-op for all substitutions except multiplier and modulus substitutions 227 } 228 229 //----------------------------------------------------------------------- 230 // boilerplate 231 //----------------------------------------------------------------------- 232 233 /** 234 * Compares two substitutions for equality 235 * @param that The substitution to compare this one to 236 * @return true if the two substitutions are functionally equivalent 237 */ 238 @Override equals(Object that)239 public boolean equals(Object that) { 240 // compare class and all of the fields all substitutions have 241 // in common 242 if (that == null) { 243 return false; 244 } 245 if (this == that) { 246 return true; 247 } 248 if (this.getClass() == that.getClass()) { 249 NFSubstitution that2 = (NFSubstitution)that; 250 251 return pos == that2.pos 252 && (ruleSet != null || that2.ruleSet == null) // can't compare tree structure, no .equals or recurse 253 && (numberFormat == null ? (that2.numberFormat == null) : numberFormat.equals(that2.numberFormat)); 254 } 255 return false; 256 } 257 258 @Override hashCode()259 public int hashCode() { 260 assert false : "hashCode not designed"; 261 return 42; 262 } 263 264 /** 265 * Returns a textual description of the substitution 266 * @return A textual description of the substitution. This might 267 * not be identical to the description it was created from, but 268 * it'll produce the same result. 269 */ 270 @Override toString()271 public String toString() { 272 // use tokenChar() to get the character at the beginning and 273 // end of the substitution token. In between them will go 274 // either the name of the rule set it uses, or the pattern of 275 // the DecimalFormat it uses 276 if (ruleSet != null) { 277 return tokenChar() + ruleSet.getName() + tokenChar(); 278 } else { 279 return tokenChar() + numberFormat.toPattern() + tokenChar(); 280 } 281 } 282 283 //----------------------------------------------------------------------- 284 // formatting 285 //----------------------------------------------------------------------- 286 287 private static final long MAX_INT64_IN_DOUBLE = 0x1FFFFFFFFFFFFFL; 288 289 /** 290 * Performs a mathematical operation on the number, formats it using 291 * either ruleSet or decimalFormat, and inserts the result into 292 * toInsertInto. 293 * @param number The number being formatted. 294 * @param toInsertInto The string we insert the result into 295 * @param position The position in toInsertInto where the owning rule's 296 * rule text begins (this value is added to this substitution's 297 * position to determine exactly where to insert the new text) 298 */ doSubstitution(long number, StringBuilder toInsertInto, int position, int recursionCount)299 public void doSubstitution(long number, StringBuilder toInsertInto, int position, int recursionCount) { 300 if (ruleSet != null) { 301 // Perform a transformation on the number that is dependent 302 // on the type of substitution this is, then just call its 303 // rule set's format() method to format the result 304 long numberToFormat = transformNumber(number); 305 306 ruleSet.format(numberToFormat, toInsertInto, position + pos, recursionCount); 307 } else { 308 if (number <= MAX_INT64_IN_DOUBLE) { 309 // or perform the transformation on the number (preserving 310 // the result's fractional part if the formatter it set 311 // to show it), then use that formatter's format() method 312 // to format the result 313 double numberToFormat = transformNumber((double) number); 314 if (numberFormat.getMaximumFractionDigits() == 0) { 315 numberToFormat = Math.floor(numberToFormat); 316 } 317 318 toInsertInto.insert(position + pos, numberFormat.format(numberToFormat)); 319 } 320 else { 321 // We have gone beyond double precision. Something has to give. 322 // We're favoring accuracy of the large number over potential rules 323 // that round like a CompactDecimalFormat, which is not a common use case. 324 // 325 // Perform a transformation on the number that is dependent 326 // on the type of substitution this is, then just call its 327 // rule set's format() method to format the result 328 long numberToFormat = transformNumber(number); 329 toInsertInto.insert(position + pos, numberFormat.format(numberToFormat)); 330 } 331 } 332 } 333 334 /** 335 * Performs a mathematical operation on the number, formats it using 336 * either ruleSet or decimalFormat, and inserts the result into 337 * toInsertInto. 338 * @param number The number being formatted. 339 * @param toInsertInto The string we insert the result into 340 * @param position The position in toInsertInto where the owning rule's 341 * rule text begins (this value is added to this substitution's 342 * position to determine exactly where to insert the new text) 343 */ doSubstitution(double number, StringBuilder toInsertInto, int position, int recursionCount)344 public void doSubstitution(double number, StringBuilder toInsertInto, int position, int recursionCount) { 345 // perform a transformation on the number being formatted that 346 // is dependent on the type of substitution this is 347 double numberToFormat = transformNumber(number); 348 349 if (Double.isInfinite(numberToFormat)) { 350 // This is probably a minus rule. Combine it with an infinite rule. 351 NFRule infiniteRule = ruleSet.findRule(Double.POSITIVE_INFINITY); 352 infiniteRule.doFormat(numberToFormat, toInsertInto, position + pos, recursionCount); 353 return; 354 } 355 356 // if the result is an integer, from here on out we work in integer 357 // space (saving time and memory and preserving accuracy) 358 if (numberToFormat == Math.floor(numberToFormat) && ruleSet != null) { 359 ruleSet.format((long)numberToFormat, toInsertInto, position + pos, recursionCount); 360 361 // if the result isn't an integer, then call either our rule set's 362 // format() method or our DecimalFormat's format() method to 363 // format the result 364 } else { 365 if (ruleSet != null) { 366 ruleSet.format(numberToFormat, toInsertInto, position + pos, recursionCount); 367 } else { 368 toInsertInto.insert(position + this.pos, numberFormat.format(numberToFormat)); 369 } 370 } 371 } 372 373 /** 374 * Subclasses override this function to perform some kind of 375 * mathematical operation on the number. The result of this operation 376 * is formatted using the rule set or DecimalFormat that this 377 * substitution refers to, and the result is inserted into the result 378 * string. 379 * @param number The number being formatted 380 * @return The result of performing the opreration on the number 381 */ transformNumber(long number)382 public abstract long transformNumber(long number); 383 384 /** 385 * Subclasses override this function to perform some kind of 386 * mathematical operation on the number. The result of this operation 387 * is formatted using the rule set or DecimalFormat that this 388 * substitution refers to, and the result is inserted into the result 389 * string. 390 * @param number The number being formatted 391 * @return The result of performing the opreration on the number 392 */ transformNumber(double number)393 public abstract double transformNumber(double number); 394 395 //----------------------------------------------------------------------- 396 // parsing 397 //----------------------------------------------------------------------- 398 399 /** 400 * Parses a string using the rule set or DecimalFormat belonging 401 * to this substitution. If there's a match, a mathematical 402 * operation (the inverse of the one used in formatting) is 403 * performed on the result of the parse and the value passed in 404 * and returned as the result. The parse position is updated to 405 * point to the first unmatched character in the string. 406 * @param text The string to parse 407 * @param parsePosition On entry, ignored, but assumed to be 0. 408 * On exit, this is updated to point to the first unmatched 409 * character (or 0 if the substitution didn't match) 410 * @param baseValue A partial parse result that should be 411 * combined with the result of this parse 412 * @param upperBound When searching the rule set for a rule 413 * matching the string passed in, only rules with base values 414 * lower than this are considered 415 * @param lenientParse If true and matching against rules fails, 416 * the substitution will also try matching the text against 417 * numerals using a default-constructed NumberFormat. If false, 418 * no extra work is done. (This value is false whenever the 419 * formatter isn't in lenient-parse mode, but is also false 420 * under some conditions even when the formatter _is_ in 421 * lenient-parse mode.) 422 * @return If there's a match, this is the result of composing 423 * baseValue with whatever was returned from matching the 424 * characters. This will be either a Long or a Double. If there's 425 * no match this is new Long(0) (not null), and parsePosition 426 * is left unchanged. 427 */ doParse(String text, ParsePosition parsePosition, double baseValue, double upperBound, boolean lenientParse, int nonNumericalExecutedRuleMask)428 public Number doParse(String text, ParsePosition parsePosition, double baseValue, 429 double upperBound, boolean lenientParse, int nonNumericalExecutedRuleMask) { 430 Number tempResult; 431 432 // figure out the highest base value a rule can have and match 433 // the text being parsed (this varies according to the type of 434 // substitutions: multiplier, modulus, and numerator substitutions 435 // restrict the search to rules with base values lower than their 436 // own; same-value substitutions leave the upper bound wherever 437 // it was, and the others allow any rule to match 438 upperBound = calcUpperBound(upperBound); 439 440 // use our rule set to parse the text. If that fails and 441 // lenient parsing is enabled (this is always false if the 442 // formatter's lenient-parsing mode is off, but it may also 443 // be false even when the formatter's lenient-parse mode is 444 // on), then also try parsing the text using a default- 445 // constructed NumberFormat 446 if (ruleSet != null) { 447 tempResult = ruleSet.parse(text, parsePosition, upperBound, nonNumericalExecutedRuleMask); 448 if (lenientParse && !ruleSet.isFractionSet() && parsePosition.getIndex() == 0) { 449 tempResult = ruleSet.owner.getDecimalFormat().parse(text, parsePosition); 450 } 451 452 // ...or use our DecimalFormat to parse the text 453 } else { 454 tempResult = numberFormat.parse(text, parsePosition); 455 } 456 457 // if the parse was successful, we've already advanced the caller's 458 // parse position (this is the one function that doesn't have one 459 // of its own). Derive a parse result and return it as a Long, 460 // if possible, or a Double 461 if (parsePosition.getIndex() != 0) { 462 double result = tempResult.doubleValue(); 463 464 // composeRuleValue() produces a full parse result from 465 // the partial parse result passed to this function from 466 // the caller (this is either the owning rule's base value 467 // or the partial result obtained from composing the 468 // owning rule's base value with its other substitution's 469 // parse result) and the partial parse result obtained by 470 // matching the substitution (which will be the same value 471 // the caller would get by parsing just this part of the 472 // text with RuleBasedNumberFormat.parse() ). How the two 473 // values are used to derive the full parse result depends 474 // on the types of substitutions: For a regular rule, the 475 // ultimate result is its multiplier substitution's result 476 // times the rule's divisor (or the rule's base value) plus 477 // the modulus substitution's result (which will actually 478 // supersede part of the rule's base value). For a negative- 479 // number rule, the result is the negative of its substitution's 480 // result. For a fraction rule, it's the sum of its two 481 // substitution results. For a rule in a fraction rule set, 482 // it's the numerator substitution's result divided by 483 // the rule's base value. Results from same-value substitutions 484 // propagate back upward, and null substitutions don't affect 485 // the result. 486 result = composeRuleValue(result, baseValue); 487 if (result == (long)result) { 488 return Long.valueOf((long)result); 489 } else { 490 return new Double(result); 491 } 492 493 // if the parse was UNsuccessful, return 0 494 } else { 495 return tempResult; 496 } 497 } 498 499 /** 500 * Derives a new value from the two values passed in. The two values 501 * are typically either the base values of two rules (the one containing 502 * the substitution and the one matching the substitution) or partial 503 * parse results derived in some other way. The operation is generally 504 * the inverse of the operation performed by transformNumber(). 505 * @param newRuleValue The value produced by matching this substitution 506 * @param oldRuleValue The value that was passed to the substitution 507 * by the rule that owns it 508 * @return A third value derived from the other two, representing a 509 * partial parse result 510 */ composeRuleValue(double newRuleValue, double oldRuleValue)511 public abstract double composeRuleValue(double newRuleValue, double oldRuleValue); 512 513 /** 514 * Calculates an upper bound when searching for a rule that matches 515 * this substitution. Rules with base values greater than or equal 516 * to upperBound are not considered. 517 * @param oldUpperBound The current upper-bound setting. The new 518 * upper bound can't be any higher. 519 */ calcUpperBound(double oldUpperBound)520 public abstract double calcUpperBound(double oldUpperBound); 521 522 //----------------------------------------------------------------------- 523 // simple accessors 524 //----------------------------------------------------------------------- 525 526 /** 527 * Returns the substitution's position in the rule that owns it. 528 * @return The substitution's position in the rule that owns it. 529 */ getPos()530 public final int getPos() { 531 return pos; 532 } 533 534 /** 535 * Returns the character used in the textual representation of 536 * substitutions of this type. Used by toString(). 537 * @return This substitution's token character. 538 */ tokenChar()539 abstract char tokenChar(); 540 541 /** 542 * Returns true if this is a modulus substitution. (We didn't do this 543 * with instanceof partially because it causes source files to 544 * proliferate and partially because we have to port this to C++.) 545 * @return true if this object is an instance of ModulusSubstitution 546 */ isModulusSubstitution()547 public boolean isModulusSubstitution() { 548 return false; 549 } 550 551 setDecimalFormatSymbols(DecimalFormatSymbols newSymbols)552 public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) { 553 if (numberFormat != null) { 554 numberFormat.setDecimalFormatSymbols(newSymbols); 555 } 556 } 557 } 558 559 //=================================================================== 560 // SameValueSubstitution 561 //=================================================================== 562 563 /** 564 * A substitution that passes the value passed to it through unchanged. 565 * Represented by == in rule descriptions. 566 */ 567 class SameValueSubstitution extends NFSubstitution { 568 //----------------------------------------------------------------------- 569 // construction 570 //----------------------------------------------------------------------- 571 572 /** 573 * Constructs a SameValueSubstution. This function just uses the 574 * superclass constructor, but it performs a check that this 575 * substitution doesn't call the rule set that owns it, since that 576 * would lead to infinite recursion. 577 */ SameValueSubstitution(int pos, NFRuleSet ruleSet, String description)578 SameValueSubstitution(int pos, 579 NFRuleSet ruleSet, 580 String description) { 581 super(pos, ruleSet, description); 582 if (description.equals("==")) { 583 throw new IllegalArgumentException("== is not a legal token"); 584 } 585 } 586 587 //----------------------------------------------------------------------- 588 // formatting 589 //----------------------------------------------------------------------- 590 591 /** 592 * Returns "number" unchanged. 593 * @return "number" 594 */ 595 @Override transformNumber(long number)596 public long transformNumber(long number) { 597 return number; 598 } 599 600 /** 601 * Returns "number" unchanged. 602 * @return "number" 603 */ 604 @Override transformNumber(double number)605 public double transformNumber(double number) { 606 return number; 607 } 608 609 //----------------------------------------------------------------------- 610 // parsing 611 //----------------------------------------------------------------------- 612 613 /** 614 * Returns newRuleValue and ignores oldRuleValue. (The value we got 615 * matching the substitution supersedes the value of the rule 616 * that owns the substitution.) 617 * @param newRuleValue The value resulting from matching the substitution 618 * @param oldRuleValue The value of the rule containing the 619 * substitution. 620 * @return newRuleValue 621 */ 622 @Override composeRuleValue(double newRuleValue, double oldRuleValue)623 public double composeRuleValue(double newRuleValue, double oldRuleValue) { 624 return newRuleValue; 625 } 626 627 /** 628 * SameValueSubstitution doesn't change the upper bound. 629 * @param oldUpperBound The current upper bound. 630 * @return oldUpperBound 631 */ 632 @Override calcUpperBound(double oldUpperBound)633 public double calcUpperBound(double oldUpperBound) { 634 return oldUpperBound; 635 } 636 637 //----------------------------------------------------------------------- 638 // simple accessor 639 //----------------------------------------------------------------------- 640 641 /** 642 * The token character for a SameValueSubstitution is =. 643 * @return '=' 644 */ 645 @Override tokenChar()646 char tokenChar() { 647 return '='; 648 } 649 } 650 651 //=================================================================== 652 // MultiplierSubstitution 653 //=================================================================== 654 655 /** 656 * A substitution that divides the number being formatted by the rule's 657 * divisor and formats the quotient. Represented by << in normal 658 * rules. 659 */ 660 class MultiplierSubstitution extends NFSubstitution { 661 //----------------------------------------------------------------------- 662 // data members 663 //----------------------------------------------------------------------- 664 665 /** 666 * The divisor of the rule that owns this substitution. 667 */ 668 long divisor; 669 670 //----------------------------------------------------------------------- 671 // construction 672 //----------------------------------------------------------------------- 673 674 /** 675 * Constructs a MultiplierSubstitution. This uses the superclass 676 * constructor to initialize most members, but this substitution 677 * also maintains its own copy of its rule's divisor. 678 * @param pos The substitution's position in its rule's rule text 679 * @param rule The rule that owns this substitution 680 * @param ruleSet The ruleSet this substitution uses to format its result 681 * @param description The description describing this substitution 682 */ MultiplierSubstitution(int pos, NFRule rule, NFRuleSet ruleSet, String description)683 MultiplierSubstitution(int pos, 684 NFRule rule, 685 NFRuleSet ruleSet, 686 String description) { 687 super(pos, ruleSet, description); 688 689 // the owning rule's divisor affects the behavior of this 690 // substitution. Rather than keeping a back-pointer to the 691 // rule, we keep a copy of the divisor 692 this.divisor = rule.getDivisor(); 693 694 if (divisor == 0) { // this will cause recursion 695 throw new IllegalStateException("Substitution with divisor 0 " + description.substring(0, pos) + 696 " | " + description.substring(pos)); 697 } 698 } 699 700 /** 701 * Sets the substitution's divisor based on the values passed in. 702 * @param radix The radix of the divisor. 703 * @param exponent The exponent of the divisor. 704 */ 705 @Override setDivisor(int radix, short exponent)706 public void setDivisor(int radix, short exponent) { 707 divisor = NFRule.power(radix, exponent); 708 709 if (divisor == 0) { 710 throw new IllegalStateException("Substitution with divisor 0"); 711 } 712 } 713 714 //----------------------------------------------------------------------- 715 // boilerplate 716 //----------------------------------------------------------------------- 717 718 /** 719 * Augments the superclass's equals() function by comparing divisors. 720 * @param that The other substitution 721 * @return true if the two substitutions are functionally equal 722 */ 723 @Override equals(Object that)724 public boolean equals(Object that) { 725 return super.equals(that) && divisor == ((MultiplierSubstitution) that).divisor; 726 } 727 728 //----------------------------------------------------------------------- 729 // formatting 730 //----------------------------------------------------------------------- 731 732 /** 733 * Divides the number by the rule's divisor and returns the quotient. 734 * @param number The number being formatted. 735 * @return "number" divided by the rule's divisor 736 */ 737 @Override transformNumber(long number)738 public long transformNumber(long number) { 739 return (long)Math.floor(number / divisor); 740 } 741 742 /** 743 * Divides the number by the rule's divisor and returns the quotient. 744 * This is an integral quotient if we're filling in the substitution 745 * using another rule set, but it's the full quotient (integral and 746 * fractional parts) if we're filling in the substitution using 747 * a DecimalFormat. (This allows things such as "1.2 million".) 748 * @param number The number being formatted 749 * @return "number" divided by the rule's divisor 750 */ 751 @Override transformNumber(double number)752 public double transformNumber(double number) { 753 if (ruleSet == null) { 754 return number / divisor; 755 } else { 756 return Math.floor(number / divisor); 757 } 758 } 759 760 //----------------------------------------------------------------------- 761 // parsing 762 //----------------------------------------------------------------------- 763 764 /** 765 * Returns newRuleValue times the divisor. Ignores oldRuleValue. 766 * (The result of matching a << substitution supersedes the base 767 * value of the rule that contains it.) 768 * @param newRuleValue The result of matching the substitution 769 * @param oldRuleValue The base value of the rule containing the 770 * substitution 771 * @return newRuleValue * divisor 772 */ 773 @Override composeRuleValue(double newRuleValue, double oldRuleValue)774 public double composeRuleValue(double newRuleValue, double oldRuleValue) { 775 return newRuleValue * divisor; 776 } 777 778 /** 779 * Sets the upper bound down to the rule's divisor. 780 * @param oldUpperBound Ignored. 781 * @return The rule's divisor. 782 */ 783 @Override calcUpperBound(double oldUpperBound)784 public double calcUpperBound(double oldUpperBound) { 785 return divisor; 786 } 787 788 //----------------------------------------------------------------------- 789 // simple accessor 790 //----------------------------------------------------------------------- 791 792 /** 793 * The token character for a multiplier substitution is <. 794 * @return '<' 795 */ 796 @Override tokenChar()797 char tokenChar() { 798 return '<'; 799 } 800 } 801 802 //=================================================================== 803 // ModulusSubstitution 804 //=================================================================== 805 806 /** 807 * A substitution that divides the number being formatted by the its rule's 808 * divisor and formats the remainder. Represented by ">>" in a 809 * regular rule. 810 */ 811 class ModulusSubstitution extends NFSubstitution { 812 //----------------------------------------------------------------------- 813 // data members 814 //----------------------------------------------------------------------- 815 816 /** 817 * The divisor of the rule owning this substitution 818 */ 819 long divisor; 820 821 /** 822 * If this is a >>> substitution, the rule to use to format 823 * the substitution value. Otherwise, null. 824 */ 825 private final NFRule ruleToUse; 826 827 //----------------------------------------------------------------------- 828 // construction 829 //----------------------------------------------------------------------- 830 831 /** 832 * Constructs a ModulusSubstitution. In addition to the inherited 833 * members, a ModulusSubstitution keeps track of the divisor of the 834 * rule that owns it, and may also keep a reference to the rule 835 * that precedes the rule containing this substitution in the rule 836 * set's rule list. 837 * @param pos The substitution's position in its rule's rule text 838 * @param rule The rule that owns this substitution 839 * @param rulePredecessor The rule that precedes this substitution's 840 * rule in its rule set's rule list 841 * @param description The description for this substitution 842 */ ModulusSubstitution(int pos, NFRule rule, NFRule rulePredecessor, NFRuleSet ruleSet, String description)843 ModulusSubstitution(int pos, 844 NFRule rule, 845 NFRule rulePredecessor, 846 NFRuleSet ruleSet, 847 String description) 848 { 849 super(pos, ruleSet, description); 850 851 // the owning rule's divisor controls the behavior of this 852 // substitution: rather than keeping a backpointer to the rule, 853 // we keep a copy of the divisor 854 this.divisor = rule.getDivisor(); 855 856 if (divisor == 0) { // this will cause recursion 857 throw new IllegalStateException("Substitution with bad divisor (" + divisor + ") "+ description.substring(0, pos) + 858 " | " + description.substring(pos)); 859 } 860 861 // the >>> token doesn't alter how this substitution calculates the 862 // values it uses for formatting and parsing, but it changes 863 // what's done with that value after it's obtained: >>> short- 864 // circuits the rule-search process and goes straight to the 865 // specified rule to format the substitution value 866 if (description.equals(">>>")) { 867 ruleToUse = rulePredecessor; 868 } else { 869 ruleToUse = null; 870 } 871 } 872 873 /** 874 * Makes the substitution's divisor conform to that of the rule 875 * that owns it. Used when the divisor is determined after creation. 876 * @param radix The radix of the divisor. 877 * @param exponent The exponent of the divisor. 878 */ 879 @Override setDivisor(int radix, short exponent)880 public void setDivisor(int radix, short exponent) { 881 divisor = NFRule.power(radix, exponent); 882 883 if (divisor == 0) { // this will cause recursion 884 throw new IllegalStateException("Substitution with bad divisor"); 885 } 886 } 887 888 //----------------------------------------------------------------------- 889 // boilerplate 890 //----------------------------------------------------------------------- 891 892 /** 893 * Augments the inherited equals() function by comparing divisors and 894 * ruleToUse. 895 * @param that The other substitution 896 * @return true if the two substitutions are functionally equivalent 897 */ 898 @Override equals(Object that)899 public boolean equals(Object that) { 900 if (super.equals(that)) { 901 ModulusSubstitution that2 = (ModulusSubstitution)that; 902 903 return divisor == that2.divisor; 904 } else { 905 return false; 906 } 907 } 908 909 //----------------------------------------------------------------------- 910 // formatting 911 //----------------------------------------------------------------------- 912 913 /** 914 * If this is a >>> substitution, use ruleToUse to fill in 915 * the substitution. Otherwise, just use the superclass function. 916 * @param number The number being formatted 917 * @param toInsertInto The string to insert the result of this substitution 918 * into 919 * @param position The position of the rule text in toInsertInto 920 */ 921 @Override doSubstitution(long number, StringBuilder toInsertInto, int position, int recursionCount)922 public void doSubstitution(long number, StringBuilder toInsertInto, int position, int recursionCount) { 923 // if this isn't a >>> substitution, just use the inherited version 924 // of this function (which uses either a rule set or a DecimalFormat 925 // to format its substitution value) 926 if (ruleToUse == null) { 927 super.doSubstitution(number, toInsertInto, position, recursionCount); 928 929 } else { 930 // a >>> substitution goes straight to a particular rule to 931 // format the substitution value 932 long numberToFormat = transformNumber(number); 933 ruleToUse.doFormat(numberToFormat, toInsertInto, position + pos, recursionCount); 934 } 935 } 936 937 /** 938 * If this is a >>> substitution, use ruleToUse to fill in 939 * the substitution. Otherwise, just use the superclass function. 940 * @param number The number being formatted 941 * @param toInsertInto The string to insert the result of this substitution 942 * into 943 * @param position The position of the rule text in toInsertInto 944 */ 945 @Override doSubstitution(double number, StringBuilder toInsertInto, int position, int recursionCount)946 public void doSubstitution(double number, StringBuilder toInsertInto, int position, int recursionCount) { 947 // if this isn't a >>> substitution, just use the inherited version 948 // of this function (which uses either a rule set or a DecimalFormat 949 // to format its substitution value) 950 if (ruleToUse == null) { 951 super.doSubstitution(number, toInsertInto, position, recursionCount); 952 953 } else { 954 // a >>> substitution goes straight to a particular rule to 955 // format the substitution value 956 double numberToFormat = transformNumber(number); 957 958 ruleToUse.doFormat(numberToFormat, toInsertInto, position + pos, recursionCount); 959 } 960 } 961 962 /** 963 * Divides the number being formatted by the rule's divisor and 964 * returns the remainder. 965 * @param number The number being formatted 966 * @return "number" mod divisor 967 */ 968 @Override transformNumber(long number)969 public long transformNumber(long number) { 970 return number % divisor; 971 } 972 973 /** 974 * Divides the number being formatted by the rule's divisor and 975 * returns the remainder. 976 * @param number The number being formatted 977 * @return "number" mod divisor 978 */ 979 @Override transformNumber(double number)980 public double transformNumber(double number) { 981 return Math.floor(number % divisor); 982 } 983 984 //----------------------------------------------------------------------- 985 // parsing 986 //----------------------------------------------------------------------- 987 988 /** 989 * If this is a >>> substitution, match only against ruleToUse. 990 * Otherwise, use the superclass function. 991 * @param text The string to parse 992 * @param parsePosition Ignored on entry, updated on exit to point to 993 * the first unmatched character. 994 * @param baseValue The partial parse result prior to calling this 995 * routine. 996 */ 997 @Override doParse(String text, ParsePosition parsePosition, double baseValue, double upperBound, boolean lenientParse, int nonNumericalExecutedRuleMask)998 public Number doParse(String text, ParsePosition parsePosition, double baseValue, 999 double upperBound, boolean lenientParse, int nonNumericalExecutedRuleMask) { 1000 // if this isn't a >>> substitution, we can just use the 1001 // inherited parse() routine to do the parsing 1002 if (ruleToUse == null) { 1003 return super.doParse(text, parsePosition, baseValue, upperBound, lenientParse, nonNumericalExecutedRuleMask); 1004 1005 } else { 1006 // but if it IS a >>> substitution, we have to do it here: we 1007 // use the specific rule's doParse() method, and then we have to 1008 // do some of the other work of NFRuleSet.parse() 1009 Number tempResult = ruleToUse.doParse(text, parsePosition, false, upperBound, nonNumericalExecutedRuleMask); 1010 1011 if (parsePosition.getIndex() != 0) { 1012 double result = tempResult.doubleValue(); 1013 1014 result = composeRuleValue(result, baseValue); 1015 if (result == (long)result) { 1016 return Long.valueOf((long)result); 1017 } else { 1018 return new Double(result); 1019 } 1020 } else { 1021 return tempResult; 1022 } 1023 } 1024 } 1025 1026 /** 1027 * Returns the highest multiple of the rule's divisor that its less 1028 * than or equal to oldRuleValue, plus newRuleValue. (The result 1029 * is the sum of the result of parsing the substitution plus the 1030 * base value of the rule containing the substitution, but if the 1031 * owning rule's base value isn't an even multiple of its divisor, 1032 * we have to round it down to a multiple of the divisor, or we 1033 * get unwanted digits in the result.) 1034 * @param newRuleValue The result of parsing the substitution 1035 * @param oldRuleValue The base value of the rule containing the 1036 * substitution 1037 */ 1038 @Override composeRuleValue(double newRuleValue, double oldRuleValue)1039 public double composeRuleValue(double newRuleValue, double oldRuleValue) { 1040 return (oldRuleValue - (oldRuleValue % divisor)) + newRuleValue; 1041 } 1042 1043 /** 1044 * Sets the upper bound down to the owning rule's divisor 1045 * @param oldUpperBound Ignored 1046 * @return The owning rule's divisor 1047 */ 1048 @Override calcUpperBound(double oldUpperBound)1049 public double calcUpperBound(double oldUpperBound) { 1050 return divisor; 1051 } 1052 1053 //----------------------------------------------------------------------- 1054 // simple accessors 1055 //----------------------------------------------------------------------- 1056 1057 /** 1058 * Returns true. This _is_ a ModulusSubstitution. 1059 * @return true 1060 */ 1061 @Override isModulusSubstitution()1062 public boolean isModulusSubstitution() { 1063 return true; 1064 } 1065 1066 /** 1067 * The token character of a ModulusSubstitution is >. 1068 * @return '>' 1069 */ 1070 @Override tokenChar()1071 char tokenChar() { 1072 return '>'; 1073 } 1074 } 1075 1076 //=================================================================== 1077 // IntegralPartSubstitution 1078 //=================================================================== 1079 1080 /** 1081 * A substitution that formats the number's integral part. This is 1082 * represented by << in a fraction rule. 1083 */ 1084 class IntegralPartSubstitution extends NFSubstitution { 1085 //----------------------------------------------------------------------- 1086 // construction 1087 //----------------------------------------------------------------------- 1088 1089 /** 1090 * Constructs an IntegralPartSubstitution. This just calls 1091 * the superclass constructor. 1092 */ IntegralPartSubstitution(int pos, NFRuleSet ruleSet, String description)1093 IntegralPartSubstitution(int pos, 1094 NFRuleSet ruleSet, 1095 String description) { 1096 super(pos, ruleSet, description); 1097 } 1098 1099 //----------------------------------------------------------------------- 1100 // formatting 1101 //----------------------------------------------------------------------- 1102 1103 /** 1104 * Returns the number's integral part. (For a long, that's just the 1105 * number unchanged.) 1106 * @param number The number being formatted 1107 * @return "number" unchanged 1108 */ 1109 @Override transformNumber(long number)1110 public long transformNumber(long number) { 1111 return number; 1112 } 1113 1114 /** 1115 * Returns the number's integral part. 1116 * @param number The integral part of the number being formatted 1117 * @return floor(number) 1118 */ 1119 @Override transformNumber(double number)1120 public double transformNumber(double number) { 1121 return Math.floor(number); 1122 } 1123 1124 //----------------------------------------------------------------------- 1125 // parsing 1126 //----------------------------------------------------------------------- 1127 1128 /** 1129 * Returns the sum of the result of parsing the substitution and the 1130 * owning rule's base value. (The owning rule, at best, has an 1131 * integral-part substitution and a fractional-part substitution, 1132 * so we can safely just add them.) 1133 * @param newRuleValue The result of matching the substitution 1134 * @param oldRuleValue The partial result of the parse prior to 1135 * calling this function 1136 * @return oldRuleValue + newRuleValue 1137 */ 1138 @Override composeRuleValue(double newRuleValue, double oldRuleValue)1139 public double composeRuleValue(double newRuleValue, double oldRuleValue) { 1140 return newRuleValue + oldRuleValue; 1141 } 1142 1143 /** 1144 * An IntegralPartSubstitution sets the upper bound back up so all 1145 * potentially matching rules are considered. 1146 * @param oldUpperBound Ignored 1147 * @return Double.MAX_VALUE 1148 */ 1149 @Override calcUpperBound(double oldUpperBound)1150 public double calcUpperBound(double oldUpperBound) { 1151 return Double.MAX_VALUE; 1152 } 1153 1154 //----------------------------------------------------------------------- 1155 // simple accessor 1156 //----------------------------------------------------------------------- 1157 1158 /** 1159 * An IntegralPartSubstitution's token character is < 1160 * @return '<' 1161 */ 1162 @Override tokenChar()1163 char tokenChar() { 1164 return '<'; 1165 } 1166 } 1167 1168 //=================================================================== 1169 // FractionalPartSubstitution 1170 //=================================================================== 1171 1172 /** 1173 * A substitution that formats the fractional part of a number. This is 1174 * represented by >> in a fraction rule. 1175 */ 1176 class FractionalPartSubstitution extends NFSubstitution { 1177 //----------------------------------------------------------------------- 1178 // data members 1179 //----------------------------------------------------------------------- 1180 1181 /** 1182 * true if this substitution should have the default "by digits" 1183 * behavior, false otherwise 1184 */ 1185 private final boolean byDigits; 1186 1187 /** 1188 * true if we automatically insert spaces to separate names of digits 1189 * set to false by '>>>' in fraction rules, used by Thai. 1190 */ 1191 private final boolean useSpaces; 1192 1193 //----------------------------------------------------------------------- 1194 // construction 1195 //----------------------------------------------------------------------- 1196 1197 /** 1198 * Constructs a FractionalPartSubstitution. This object keeps a flag 1199 * telling whether it should format by digits or not. In addition, 1200 * it marks the rule set it calls (if any) as a fraction rule set. 1201 */ FractionalPartSubstitution(int pos, NFRuleSet ruleSet, String description)1202 FractionalPartSubstitution(int pos, 1203 NFRuleSet ruleSet, 1204 String description) { 1205 super(pos, ruleSet, description); 1206 if (description.equals(">>") || description.equals(">>>") || ruleSet == this.ruleSet) { 1207 byDigits = true; 1208 useSpaces = !description.equals(">>>"); 1209 } else { 1210 byDigits = false; 1211 useSpaces = true; 1212 this.ruleSet.makeIntoFractionRuleSet(); 1213 } 1214 } 1215 1216 //----------------------------------------------------------------------- 1217 // formatting 1218 //----------------------------------------------------------------------- 1219 1220 /** 1221 * If in "by digits" mode, fills in the substitution one decimal digit 1222 * at a time using the rule set containing this substitution. 1223 * Otherwise, uses the superclass function. 1224 * @param number The number being formatted 1225 * @param toInsertInto The string to insert the result of formatting 1226 * the substitution into 1227 * @param position The position of the owning rule's rule text in 1228 * toInsertInto 1229 */ 1230 @Override doSubstitution(double number, StringBuilder toInsertInto, int position, int recursionCount)1231 public void doSubstitution(double number, StringBuilder toInsertInto, int position, int recursionCount) { 1232 if (!byDigits) { 1233 // if we're not in "byDigits" mode, just use the inherited 1234 // doSubstitution() routine 1235 super.doSubstitution(number, toInsertInto, position, recursionCount); 1236 } 1237 else { 1238 // if we're in "byDigits" mode, transform the value into an integer 1239 // by moving the decimal point eight places to the right and 1240 // pulling digits off the right one at a time, formatting each digit 1241 // as an integer using this substitution's owning rule set 1242 // (this is slower, but more accurate, than doing it from the 1243 // other end) 1244 1245 DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD(number); 1246 fq.roundToInfinity(); // ensure doubles are resolved using slow path 1247 1248 boolean pad = false; 1249 int mag = fq.getLowerDisplayMagnitude(); 1250 while (mag < 0) { 1251 if (pad && useSpaces) { 1252 toInsertInto.insert(position + pos, ' '); 1253 } else { 1254 pad = true; 1255 } 1256 ruleSet.format(fq.getDigit(mag++), toInsertInto, position + pos, recursionCount); 1257 } 1258 } 1259 } 1260 1261 /** 1262 * Returns the fractional part of the number, which will always be 1263 * zero if it's a long. 1264 * @param number The number being formatted 1265 * @return 0 1266 */ 1267 @Override transformNumber(long number)1268 public long transformNumber(long number) { 1269 return 0; 1270 } 1271 1272 /** 1273 * Returns the fractional part of the number. 1274 * @param number The number being formatted. 1275 * @return number - floor(number) 1276 */ 1277 @Override transformNumber(double number)1278 public double transformNumber(double number) { 1279 return number - Math.floor(number); 1280 } 1281 1282 //----------------------------------------------------------------------- 1283 // parsing 1284 //----------------------------------------------------------------------- 1285 1286 /** 1287 * If in "by digits" mode, parses the string as if it were a string 1288 * of individual digits; otherwise, uses the superclass function. 1289 * @param text The string to parse 1290 * @param parsePosition Ignored on entry, but updated on exit to point 1291 * to the first unmatched character 1292 * @param baseValue The partial parse result prior to entering this 1293 * function 1294 * @param upperBound Only consider rules with base values lower than 1295 * this when filling in the substitution 1296 * @param lenientParse If true, try matching the text as numerals if 1297 * matching as words doesn't work 1298 * @return If the match was successful, the current partial parse 1299 * result; otherwise new Long(0). The result is either a Long or 1300 * a Double. 1301 */ 1302 @Override doParse(String text, ParsePosition parsePosition, double baseValue, double upperBound, boolean lenientParse, int nonNumericalExecutedRuleMask)1303 public Number doParse(String text, ParsePosition parsePosition, double baseValue, 1304 double upperBound, boolean lenientParse, int nonNumericalExecutedRuleMask) { 1305 // if we're not in byDigits mode, we can just use the inherited 1306 // doParse() 1307 if (!byDigits) { 1308 return super.doParse(text, parsePosition, baseValue, 0, lenientParse, nonNumericalExecutedRuleMask); 1309 } 1310 else { 1311 // if we ARE in byDigits mode, parse the text one digit at a time 1312 // using this substitution's owning rule set (we do this by setting 1313 // upperBound to 10 when calling doParse() ) until we reach 1314 // nonmatching text 1315 String workText = text; 1316 ParsePosition workPos = new ParsePosition(1); 1317 double result; 1318 int digit; 1319 1320 DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD(); 1321 int totalDigits = 0; 1322 while (workText.length() > 0 && workPos.getIndex() != 0) { 1323 workPos.setIndex(0); 1324 digit = ruleSet.parse(workText, workPos, 10, nonNumericalExecutedRuleMask).intValue(); 1325 if (lenientParse && workPos.getIndex() == 0) { 1326 Number n = ruleSet.owner.getDecimalFormat().parse(workText, workPos); 1327 if (n != null) { 1328 digit = n.intValue(); 1329 } 1330 } 1331 1332 if (workPos.getIndex() != 0) { 1333 fq.appendDigit((byte) digit, 0, true); 1334 totalDigits++; 1335 1336 parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()); 1337 workText = workText.substring(workPos.getIndex()); 1338 while (workText.length() > 0 && workText.charAt(0) == ' ') { 1339 workText = workText.substring(1); 1340 parsePosition.setIndex(parsePosition.getIndex() + 1); 1341 } 1342 } 1343 } 1344 fq.adjustMagnitude(-totalDigits); 1345 result = fq.toDouble(); 1346 1347 result = composeRuleValue(result, baseValue); 1348 return new Double(result); 1349 } 1350 } 1351 1352 /** 1353 * Returns the sum of the two partial parse results. 1354 * @param newRuleValue The result of parsing the substitution 1355 * @param oldRuleValue The partial parse result prior to calling 1356 * this function 1357 * @return newRuleValue + oldRuleValue 1358 */ 1359 @Override composeRuleValue(double newRuleValue, double oldRuleValue)1360 public double composeRuleValue(double newRuleValue, double oldRuleValue) { 1361 return newRuleValue + oldRuleValue; 1362 } 1363 1364 /** 1365 * Not used. 1366 */ 1367 @Override calcUpperBound(double oldUpperBound)1368 public double calcUpperBound(double oldUpperBound) { 1369 return 0; // this value is ignored 1370 } 1371 1372 //----------------------------------------------------------------------- 1373 // simple accessor 1374 //----------------------------------------------------------------------- 1375 1376 /** 1377 * The token character for a FractionalPartSubstitution is >. 1378 * @return '>' 1379 */ 1380 @Override tokenChar()1381 char tokenChar() { 1382 return '>'; 1383 } 1384 } 1385 1386 //=================================================================== 1387 // AbsoluteValueSubstitution 1388 //=================================================================== 1389 1390 /** 1391 * A substitution that formats the absolute value of the number. 1392 * This substitution is represented by >> in a negative-number rule. 1393 */ 1394 class AbsoluteValueSubstitution extends NFSubstitution { 1395 //----------------------------------------------------------------------- 1396 // construction 1397 //----------------------------------------------------------------------- 1398 1399 /** 1400 * Constructs an AbsoluteValueSubstitution. This just uses the 1401 * superclass constructor. 1402 */ AbsoluteValueSubstitution(int pos, NFRuleSet ruleSet, String description)1403 AbsoluteValueSubstitution(int pos, 1404 NFRuleSet ruleSet, 1405 String description) { 1406 super(pos, ruleSet, description); 1407 } 1408 1409 //----------------------------------------------------------------------- 1410 // formatting 1411 //----------------------------------------------------------------------- 1412 1413 /** 1414 * Returns the absolute value of the number. 1415 * @param number The number being formatted. 1416 * @return abs(number) 1417 */ 1418 @Override transformNumber(long number)1419 public long transformNumber(long number) { 1420 return Math.abs(number); 1421 } 1422 1423 /** 1424 * Returns the absolute value of the number. 1425 * @param number The number being formatted. 1426 * @return abs(number) 1427 */ 1428 @Override transformNumber(double number)1429 public double transformNumber(double number) { 1430 return Math.abs(number); 1431 } 1432 1433 //----------------------------------------------------------------------- 1434 // parsing 1435 //----------------------------------------------------------------------- 1436 1437 /** 1438 * Returns the additive inverse of the result of parsing the 1439 * substitution (this supersedes the earlier partial result) 1440 * @param newRuleValue The result of parsing the substitution 1441 * @param oldRuleValue The partial parse result prior to calling 1442 * this function 1443 * @return -newRuleValue 1444 */ 1445 @Override composeRuleValue(double newRuleValue, double oldRuleValue)1446 public double composeRuleValue(double newRuleValue, double oldRuleValue) { 1447 return -newRuleValue; 1448 } 1449 1450 /** 1451 * Sets the upper bound beck up to consider all rules 1452 * @param oldUpperBound Ignored. 1453 * @return Double.MAX_VALUE 1454 */ 1455 @Override calcUpperBound(double oldUpperBound)1456 public double calcUpperBound(double oldUpperBound) { 1457 return Double.MAX_VALUE; 1458 } 1459 1460 //----------------------------------------------------------------------- 1461 // simple accessor 1462 //----------------------------------------------------------------------- 1463 1464 /** 1465 * The token character for an AbsoluteValueSubstitution is > 1466 * @return '>' 1467 */ 1468 @Override tokenChar()1469 char tokenChar() { 1470 return '>'; 1471 } 1472 } 1473 1474 //=================================================================== 1475 // NumeratorSubstitution 1476 //=================================================================== 1477 1478 /** 1479 * A substitution that multiplies the number being formatted (which is 1480 * between 0 and 1) by the base value of the rule that owns it and 1481 * formats the result. It is represented by << in the rules 1482 * in a fraction rule set. 1483 */ 1484 class NumeratorSubstitution extends NFSubstitution { 1485 //----------------------------------------------------------------------- 1486 // data members 1487 //----------------------------------------------------------------------- 1488 1489 /** 1490 * The denominator of the fraction we're finding the numerator for. 1491 * (The base value of the rule that owns this substitution.) 1492 */ 1493 private final double denominator; 1494 1495 /** 1496 * True if we format leading zeros (this is a hack for Hebrew spellout) 1497 */ 1498 private final boolean withZeros; 1499 1500 //----------------------------------------------------------------------- 1501 // construction 1502 //----------------------------------------------------------------------- 1503 1504 /** 1505 * Constructs a NumeratorSubstitution. In addition to the inherited 1506 * fields, a NumeratorSubstitution keeps track of a denominator, which 1507 * is merely the base value of the rule that owns it. 1508 */ NumeratorSubstitution(int pos, double denominator, NFRuleSet ruleSet, String description)1509 NumeratorSubstitution(int pos, 1510 double denominator, 1511 NFRuleSet ruleSet, 1512 String description) { 1513 super(pos, ruleSet, fixdesc(description)); 1514 1515 // this substitution's behavior depends on the rule's base value 1516 // Rather than keeping a backpointer to the rule, we copy its 1517 // base value here 1518 this.denominator = denominator; 1519 1520 this.withZeros = description.endsWith("<<"); 1521 } 1522 fixdesc(String description)1523 static String fixdesc(String description) { 1524 return description.endsWith("<<") 1525 ? description.substring(0,description.length()-1) 1526 : description; 1527 } 1528 1529 //----------------------------------------------------------------------- 1530 // boilerplate 1531 //----------------------------------------------------------------------- 1532 1533 /** 1534 * Tests two NumeratorSubstitutions for equality 1535 * @param that The other NumeratorSubstitution 1536 * @return true if the two objects are functionally equivalent 1537 */ 1538 @Override equals(Object that)1539 public boolean equals(Object that) { 1540 if (super.equals(that)) { 1541 NumeratorSubstitution that2 = (NumeratorSubstitution)that; 1542 return denominator == that2.denominator && withZeros == that2.withZeros; 1543 } else { 1544 return false; 1545 } 1546 } 1547 1548 //----------------------------------------------------------------------- 1549 // formatting 1550 //----------------------------------------------------------------------- 1551 1552 /** 1553 * Performs a mathematical operation on the number, formats it using 1554 * either ruleSet or decimalFormat, and inserts the result into 1555 * toInsertInto. 1556 * @param number The number being formatted. 1557 * @param toInsertInto The string we insert the result into 1558 * @param position The position in toInsertInto where the owning rule's 1559 * rule text begins (this value is added to this substitution's 1560 * position to determine exactly where to insert the new text) 1561 */ 1562 @Override doSubstitution(double number, StringBuilder toInsertInto, int position, int recursionCount)1563 public void doSubstitution(double number, StringBuilder toInsertInto, int position, int recursionCount) { 1564 // perform a transformation on the number being formatted that 1565 // is dependent on the type of substitution this is 1566 //String s = toInsertInto.toString(); 1567 double numberToFormat = transformNumber(number); 1568 1569 if (withZeros && ruleSet != null) { 1570 // if there are leading zeros in the decimal expansion then emit them 1571 long nf = (long)numberToFormat; 1572 int len = toInsertInto.length(); 1573 while ((nf *= 10) < denominator) { 1574 toInsertInto.insert(position + pos, ' '); 1575 ruleSet.format(0, toInsertInto, position + pos, recursionCount); 1576 } 1577 position += toInsertInto.length() - len; 1578 } 1579 1580 // if the result is an integer, from here on out we work in integer 1581 // space (saving time and memory and preserving accuracy) 1582 if (numberToFormat == Math.floor(numberToFormat) && ruleSet != null) { 1583 ruleSet.format((long)numberToFormat, toInsertInto, position + pos, recursionCount); 1584 1585 // if the result isn't an integer, then call either our rule set's 1586 // format() method or our DecimalFormat's format() method to 1587 // format the result 1588 } else { 1589 if (ruleSet != null) { 1590 ruleSet.format(numberToFormat, toInsertInto, position + pos, recursionCount); 1591 } else { 1592 toInsertInto.insert(position + pos, numberFormat.format(numberToFormat)); 1593 } 1594 } 1595 } 1596 1597 /** 1598 * Returns the number being formatted times the denominator. 1599 * @param number The number being formatted 1600 * @return number * denominator 1601 */ 1602 @Override transformNumber(long number)1603 public long transformNumber(long number) { 1604 return Math.round(number * denominator); 1605 } 1606 1607 /** 1608 * Returns the number being formatted times the denominator. 1609 * @param number The number being formatted 1610 * @return number * denominator 1611 */ 1612 @Override transformNumber(double number)1613 public double transformNumber(double number) { 1614 return Math.round(number * denominator); 1615 } 1616 1617 //----------------------------------------------------------------------- 1618 // parsing 1619 //----------------------------------------------------------------------- 1620 1621 /** 1622 * Dispatches to the inherited version of this function, but makes 1623 * sure that lenientParse is off. 1624 */ 1625 @Override doParse(String text, ParsePosition parsePosition, double baseValue, double upperBound, boolean lenientParse, int nonNumericalExecutedRuleMask)1626 public Number doParse(String text, ParsePosition parsePosition, double baseValue, 1627 double upperBound, boolean lenientParse, int nonNumericalExecutedRuleMask) { 1628 // we don't have to do anything special to do the parsing here, 1629 // but we have to turn lenient parsing off-- if we leave it on, 1630 // it SERIOUSLY messes up the algorithm 1631 1632 // if withZeros is true, we need to count the zeros 1633 // and use that to adjust the parse result 1634 int zeroCount = 0; 1635 if (withZeros) { 1636 String workText = text; 1637 ParsePosition workPos = new ParsePosition(1); 1638 //int digit; 1639 1640 while (workText.length() > 0 && workPos.getIndex() != 0) { 1641 workPos.setIndex(0); 1642 /*digit = */ruleSet.parse(workText, workPos, 1, nonNumericalExecutedRuleMask).intValue(); // parse zero or nothing at all 1643 if (workPos.getIndex() == 0) { 1644 // we failed, either there were no more zeros, or the number was formatted with digits 1645 // either way, we're done 1646 break; 1647 } 1648 1649 ++zeroCount; 1650 parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()); 1651 workText = workText.substring(workPos.getIndex()); 1652 while (workText.length() > 0 && workText.charAt(0) == ' ') { 1653 workText = workText.substring(1); 1654 parsePosition.setIndex(parsePosition.getIndex() + 1); 1655 } 1656 } 1657 1658 text = text.substring(parsePosition.getIndex()); // arrgh! 1659 parsePosition.setIndex(0); 1660 } 1661 1662 // we've parsed off the zeros, now let's parse the rest from our current position 1663 Number result = super.doParse(text, parsePosition, withZeros ? 1 : baseValue, upperBound, false, nonNumericalExecutedRuleMask); 1664 1665 if (withZeros) { 1666 // any base value will do in this case. is there a way to 1667 // force this to not bother trying all the base values? 1668 1669 // compute the 'effective' base and prescale the value down 1670 long n = result.longValue(); 1671 long d = 1; 1672 while (d <= n) { 1673 d *= 10; 1674 } 1675 // now add the zeros 1676 while (zeroCount > 0) { 1677 d *= 10; 1678 --zeroCount; 1679 } 1680 // d is now our true denominator 1681 result = new Double(n/(double)d); 1682 } 1683 1684 return result; 1685 } 1686 1687 /** 1688 * Divides the result of parsing the substitution by the partial 1689 * parse result. 1690 * @param newRuleValue The result of parsing the substitution 1691 * @param oldRuleValue The owning rule's base value 1692 * @return newRuleValue / oldRuleValue 1693 */ 1694 @Override composeRuleValue(double newRuleValue, double oldRuleValue)1695 public double composeRuleValue(double newRuleValue, double oldRuleValue) { 1696 return newRuleValue / oldRuleValue; 1697 } 1698 1699 /** 1700 * Sets the upper bound down to this rule's base value 1701 * @param oldUpperBound Ignored 1702 * @return The base value of the rule owning this substitution 1703 */ 1704 @Override calcUpperBound(double oldUpperBound)1705 public double calcUpperBound(double oldUpperBound) { 1706 return denominator; 1707 } 1708 1709 //----------------------------------------------------------------------- 1710 // simple accessor 1711 //----------------------------------------------------------------------- 1712 1713 /** 1714 * The token character for a NumeratorSubstitution is < 1715 * @return '<' 1716 */ 1717 @Override tokenChar()1718 char tokenChar() { 1719 return '<'; 1720 } 1721 } 1722