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