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