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 import java.util.ArrayList; 13 import java.util.LinkedList; 14 import java.util.List; 15 import java.util.Objects; 16 17 import com.ibm.icu.impl.PatternProps; 18 19 /** 20 * A collection of rules used by a RuleBasedNumberFormat to format and 21 * parse numbers. It is the responsibility of a RuleSet to select an 22 * appropriate rule for formatting a particular number and dispatch 23 * control to it, and to arbitrate between different rules when parsing 24 * a number. 25 */ 26 27 final class NFRuleSet { 28 //----------------------------------------------------------------------- 29 // data members 30 //----------------------------------------------------------------------- 31 32 /** 33 * The rule set's name 34 */ 35 private final String name; 36 37 /** 38 * The rule set's regular rules 39 */ 40 private NFRule[] rules; 41 42 /** 43 * The rule set's non-numerical rules like negative, fractions, infinity and NaN 44 */ 45 final NFRule[] nonNumericalRules = new NFRule[6]; 46 47 /** 48 * These are a pile of fraction rules in declared order. They may have alternate 49 * ways to represent fractions. 50 */ 51 LinkedList<NFRule> fractionRules; 52 53 /** -x */ 54 static final int NEGATIVE_RULE_INDEX = 0; 55 /** x.x */ 56 static final int IMPROPER_FRACTION_RULE_INDEX = 1; 57 /** 0.x */ 58 static final int PROPER_FRACTION_RULE_INDEX = 2; 59 /** x.0 */ 60 static final int MASTER_RULE_INDEX = 3; 61 /** Inf */ 62 static final int INFINITY_RULE_INDEX = 4; 63 /** NaN */ 64 static final int NAN_RULE_INDEX = 5; 65 66 /** 67 * The RuleBasedNumberFormat that owns this rule 68 */ 69 final RuleBasedNumberFormat owner; 70 71 /** 72 * True if the rule set is a fraction rule set. A fraction rule set 73 * is a rule set that is used to format the fractional part of a 74 * number. It is called from a >> substitution in another rule set's 75 * fraction rule, and is only called upon to format values between 76 * 0 and 1. A fraction rule set has different rule-selection 77 * behavior than a regular rule set. 78 */ 79 private boolean isFractionRuleSet = false; 80 81 /** 82 * True if the rule set is parseable. 83 */ 84 private final boolean isParseable; 85 86 /** 87 * Limit of recursion. It's about a 64 bit number formatted in base 2. 88 */ 89 private static final int RECURSION_LIMIT = 64; 90 91 //----------------------------------------------------------------------- 92 // construction 93 //----------------------------------------------------------------------- 94 95 /** 96 * Constructs a rule set. 97 * @param owner The formatter that owns this rule set 98 * @param descriptions An array of Strings representing rule set 99 * descriptions. On exit, this rule set's entry in the array will 100 * have been stripped of its rule set name and any trailing whitespace. 101 * @param index The index into "descriptions" of the description 102 * for the rule to be constructed 103 */ NFRuleSet(RuleBasedNumberFormat owner, String[] descriptions, int index)104 public NFRuleSet(RuleBasedNumberFormat owner, String[] descriptions, int index) throws IllegalArgumentException { 105 this.owner = owner; 106 String description = descriptions[index]; 107 108 if (description.length() == 0) { 109 throw new IllegalArgumentException("Empty rule set description"); 110 } 111 112 // if the description begins with a rule set name (the rule set 113 // name can be omitted in formatter descriptions that consist 114 // of only one rule set), copy it out into our "name" member 115 // and delete it from the description 116 if (description.charAt(0) == '%') { 117 int pos = description.indexOf(':'); 118 if (pos == -1) { 119 throw new IllegalArgumentException("Rule set name doesn't end in colon"); 120 } 121 else { 122 String name = description.substring(0, pos); 123 this.isParseable = !name.endsWith("@noparse"); 124 if (!this.isParseable) { 125 name = name.substring(0,name.length()-8); // Remove the @noparse from the name 126 } 127 this.name = name; 128 129 //noinspection StatementWithEmptyBody 130 while (pos < description.length() && PatternProps.isWhiteSpace(description.charAt(++pos))) { 131 } 132 description = description.substring(pos); 133 descriptions[index] = description; 134 } 135 } 136 else { 137 // if the description doesn't begin with a rule set name, its 138 // name is "%default" 139 name = "%default"; 140 isParseable = true; 141 } 142 143 if (description.length() == 0) { 144 throw new IllegalArgumentException("Empty rule set description"); 145 } 146 147 // all of the other members of NFRuleSet are initialized 148 // by parseRules() 149 } 150 151 /** 152 * Construct the subordinate data structures used by this object. 153 * This function is called by the RuleBasedNumberFormat constructor 154 * after all the rule sets have been created to actually parse 155 * the description and build rules from it. Since any rule set 156 * can refer to any other rule set, we have to have created all of 157 * them before we can create anything else. 158 * @param description The textual description of this rule set 159 */ parseRules(String description)160 public void parseRules(String description) { 161 // (the number of elements in the description list isn't necessarily 162 // the number of rules-- some descriptions may expend into two rules) 163 List<NFRule> tempRules = new ArrayList<>(); 164 165 // we keep track of the rule before the one we're currently working 166 // on solely to support >>> substitutions 167 NFRule predecessor = null; 168 169 // Iterate through the rules. The rules 170 // are separated by semicolons (there's no escape facility: ALL 171 // semicolons are rule delimiters) 172 int oldP = 0; 173 int descriptionLen = description.length(); 174 int p; 175 do { 176 p = description.indexOf(';', oldP); 177 if (p < 0) { 178 p = descriptionLen; 179 } 180 181 // makeRules (a factory method on NFRule) will return either 182 // a single rule or an array of rules. Either way, add them 183 // to our rule vector 184 NFRule.makeRules(description.substring(oldP, p), 185 this, predecessor, owner, tempRules); 186 if (!tempRules.isEmpty()) { 187 predecessor = tempRules.get(tempRules.size() - 1); 188 } 189 190 oldP = p + 1; 191 } 192 while (oldP < descriptionLen); 193 194 // for rules that didn't specify a base value, their base values 195 // were initialized to 0. Make another pass through the list and 196 // set all those rules' base values. We also remove any special 197 // rules from the list and put them into their own member variables 198 long defaultBaseValue = 0; 199 200 for (NFRule rule : tempRules) { 201 long baseValue = rule.getBaseValue(); 202 if (baseValue == 0) { 203 // if the rule's base value is 0, fill in a default 204 // base value (this will be 1 plus the preceding 205 // rule's base value for regular rule sets, and the 206 // same as the preceding rule's base value in fraction 207 // rule sets) 208 rule.setBaseValue(defaultBaseValue); 209 } 210 else { 211 // if it's a regular rule that already knows its base value, 212 // check to make sure the rules are in order, and update 213 // the default base value for the next rule 214 if (baseValue < defaultBaseValue) { 215 throw new IllegalArgumentException("Rules are not in order, base: " + 216 baseValue + " < " + defaultBaseValue); 217 } 218 defaultBaseValue = baseValue; 219 } 220 if (!isFractionRuleSet) { 221 ++defaultBaseValue; 222 } 223 } 224 225 // finally, we can copy the rules from the vector into a 226 // fixed-length array 227 rules = new NFRule[tempRules.size()]; 228 tempRules.toArray(rules); 229 } 230 231 /** 232 * Set one of the non-numerical rules. 233 * @param rule The rule to set. 234 */ setNonNumericalRule(NFRule rule)235 void setNonNumericalRule(NFRule rule) { 236 long baseValue = rule.getBaseValue(); 237 if (baseValue == NFRule.NEGATIVE_NUMBER_RULE) { 238 nonNumericalRules[NFRuleSet.NEGATIVE_RULE_INDEX] = rule; 239 } 240 else if (baseValue == NFRule.IMPROPER_FRACTION_RULE) { 241 setBestFractionRule(NFRuleSet.IMPROPER_FRACTION_RULE_INDEX, rule, true); 242 } 243 else if (baseValue == NFRule.PROPER_FRACTION_RULE) { 244 setBestFractionRule(NFRuleSet.PROPER_FRACTION_RULE_INDEX, rule, true); 245 } 246 else if (baseValue == NFRule.MASTER_RULE) { 247 setBestFractionRule(NFRuleSet.MASTER_RULE_INDEX, rule, true); 248 } 249 else if (baseValue == NFRule.INFINITY_RULE) { 250 nonNumericalRules[NFRuleSet.INFINITY_RULE_INDEX] = rule; 251 } 252 else if (baseValue == NFRule.NAN_RULE) { 253 nonNumericalRules[NFRuleSet.NAN_RULE_INDEX] = rule; 254 } 255 } 256 257 /** 258 * Determine the best fraction rule to use. Rules matching the decimal point from 259 * DecimalFormatSymbols become the main set of rules to use. 260 * @param originalIndex The index into nonNumericalRules 261 * @param newRule The new rule to consider 262 * @param rememberRule Should the new rule be added to fractionRules. 263 */ setBestFractionRule(int originalIndex, NFRule newRule, boolean rememberRule)264 private void setBestFractionRule(int originalIndex, NFRule newRule, boolean rememberRule) { 265 if (rememberRule) { 266 if (fractionRules == null) { 267 fractionRules = new LinkedList<>(); 268 } 269 fractionRules.add(newRule); 270 } 271 NFRule bestResult = nonNumericalRules[originalIndex]; 272 if (bestResult == null) { 273 nonNumericalRules[originalIndex] = newRule; 274 } 275 else { 276 // We have more than one. Which one is better? 277 DecimalFormatSymbols decimalFormatSymbols = owner.getDecimalFormatSymbols(); 278 if (decimalFormatSymbols.getDecimalSeparator() == newRule.getDecimalPoint()) { 279 nonNumericalRules[originalIndex] = newRule; 280 } 281 // else leave it alone 282 } 283 } 284 285 /** 286 * Flags this rule set as a fraction rule set. This function is 287 * called during the construction process once we know this rule 288 * set is a fraction rule set. We don't know a rule set is a 289 * fraction rule set until we see it used somewhere. This function 290 * is not ad must not be called at any time other than during 291 * construction of a RuleBasedNumberFormat. 292 */ makeIntoFractionRuleSet()293 public void makeIntoFractionRuleSet() { 294 isFractionRuleSet = true; 295 } 296 297 //----------------------------------------------------------------------- 298 // boilerplate 299 //----------------------------------------------------------------------- 300 301 /** 302 * Compares two rule sets for equality. 303 * @param that The other rule set 304 * @return true if the two rule sets are functionally equivalent. 305 */ 306 @Override equals(Object that)307 public boolean equals(Object that) { 308 // if different classes, they're not equal 309 if (!(that instanceof NFRuleSet)) { 310 return false; 311 } else { 312 // otherwise, compare the members one by one... 313 NFRuleSet that2 = (NFRuleSet)that; 314 315 if (!name.equals(that2.name) 316 || rules.length != that2.rules.length 317 || isFractionRuleSet != that2.isFractionRuleSet) 318 { 319 return false; 320 } 321 322 // ...then compare the non-numerical rule lists... 323 for (int i = 0; i < nonNumericalRules.length; i++) { 324 if (!Objects.equals(nonNumericalRules[i], that2.nonNumericalRules[i])) { 325 return false; 326 } 327 } 328 329 // ...then compare the rule lists... 330 for (int i = 0; i < rules.length; i++) { 331 if (!rules[i].equals(that2.rules[i])) { 332 return false; 333 } 334 } 335 336 // ...and if we make it here, they're equal 337 return true; 338 } 339 } 340 341 @Override hashCode()342 public int hashCode() { 343 assert false : "hashCode not designed"; 344 return 42; 345 } 346 347 348 /** 349 * Builds a textual representation of a rule set. 350 * @return A textual representation of a rule set. This won't 351 * necessarily be the same description that the rule set was 352 * constructed with, but it will produce the same results. 353 */ 354 @Override toString()355 public String toString() { 356 StringBuilder result = new StringBuilder(); 357 358 // the rule set name goes first... 359 result.append(name).append(":\n"); 360 361 // followed by the regular rules... 362 for (NFRule rule : rules) { 363 result.append(rule.toString()).append("\n"); 364 } 365 366 // followed by the special rules (if they exist) 367 for (NFRule rule : nonNumericalRules) { 368 if (rule != null) { 369 if (rule.getBaseValue() == NFRule.IMPROPER_FRACTION_RULE 370 || rule.getBaseValue() == NFRule.PROPER_FRACTION_RULE 371 || rule.getBaseValue() == NFRule.MASTER_RULE) 372 { 373 for (NFRule fractionRule : fractionRules) { 374 if (fractionRule.getBaseValue() == rule.getBaseValue()) { 375 result.append(fractionRule.toString()).append("\n"); 376 } 377 } 378 } 379 else { 380 result.append(rule.toString()).append("\n"); 381 } 382 } 383 } 384 385 return result.toString(); 386 } 387 388 //----------------------------------------------------------------------- 389 // simple accessors 390 //----------------------------------------------------------------------- 391 392 /** 393 * Says whether this rule set is a fraction rule set. 394 * @return true if this rule is a fraction rule set; false if it isn't 395 */ isFractionSet()396 public boolean isFractionSet() { 397 return isFractionRuleSet; 398 } 399 400 /** 401 * Returns the rule set's name 402 * @return The rule set's name 403 */ getName()404 public String getName() { 405 return name; 406 } 407 408 /** 409 * Return true if the rule set is public. 410 * @return true if the rule set is public 411 */ isPublic()412 public boolean isPublic() { 413 return !name.startsWith("%%"); 414 } 415 416 /** 417 * Return true if the rule set can be used for parsing. 418 * @return true if the rule set can be used for parsing. 419 */ isParseable()420 public boolean isParseable() { 421 return isParseable; 422 } 423 424 //----------------------------------------------------------------------- 425 // formatting 426 //----------------------------------------------------------------------- 427 428 /** 429 * Formats a long. Selects an appropriate rule and dispatches 430 * control to it. 431 * @param number The number being formatted 432 * @param toInsertInto The string where the result is to be placed 433 * @param pos The position in toInsertInto where the result of 434 * this operation is to be inserted 435 */ format(long number, StringBuilder toInsertInto, int pos, int recursionCount)436 public void format(long number, StringBuilder toInsertInto, int pos, int recursionCount) { 437 if (recursionCount >= RECURSION_LIMIT) { 438 throw new IllegalStateException("Recursion limit exceeded when applying ruleSet " + name); 439 } 440 NFRule applicableRule = findNormalRule(number); 441 applicableRule.doFormat(number, toInsertInto, pos, ++recursionCount); 442 } 443 444 /** 445 * Formats a double. Selects an appropriate rule and dispatches 446 * control to it. 447 * @param number The number being formatted 448 * @param toInsertInto The string where the result is to be placed 449 * @param pos The position in toInsertInto where the result of 450 * this operation is to be inserted 451 */ format(double number, StringBuilder toInsertInto, int pos, int recursionCount)452 public void format(double number, StringBuilder toInsertInto, int pos, int recursionCount) { 453 if (recursionCount >= RECURSION_LIMIT) { 454 throw new IllegalStateException("Recursion limit exceeded when applying ruleSet " + name); 455 } 456 NFRule applicableRule = findRule(number); 457 applicableRule.doFormat(number, toInsertInto, pos, ++recursionCount); 458 } 459 460 /** 461 * Selects an appropriate rule for formatting the number. 462 * @param number The number being formatted. 463 * @return The rule that should be used to format it 464 */ findRule(double number)465 NFRule findRule(double number) { 466 // if this is a fraction rule set, use findFractionRuleSetRule() 467 if (isFractionRuleSet) { 468 return findFractionRuleSetRule(number); 469 } 470 471 if (Double.isNaN(number)) { 472 NFRule rule = nonNumericalRules[NAN_RULE_INDEX]; 473 if (rule == null) { 474 rule = owner.getDefaultNaNRule(); 475 } 476 return rule; 477 } 478 479 // if the number is negative, return the negative number rule 480 // (if there isn't a negative-number rule, we pretend it's a 481 // positive number) 482 if (number < 0) { 483 if (nonNumericalRules[NEGATIVE_RULE_INDEX] != null) { 484 return nonNumericalRules[NEGATIVE_RULE_INDEX]; 485 } else { 486 number = -number; 487 } 488 } 489 490 if (Double.isInfinite(number)) { 491 NFRule rule = nonNumericalRules[INFINITY_RULE_INDEX]; 492 if (rule == null) { 493 rule = owner.getDefaultInfinityRule(); 494 } 495 return rule; 496 } 497 498 // if the number isn't an integer, we use one f the fraction rules... 499 if (number != Math.floor(number)) { 500 if (number < 1 && nonNumericalRules[PROPER_FRACTION_RULE_INDEX] != null) { 501 // if the number is between 0 and 1, return the proper 502 // fraction rule 503 return nonNumericalRules[PROPER_FRACTION_RULE_INDEX]; 504 } 505 else if (nonNumericalRules[IMPROPER_FRACTION_RULE_INDEX] != null) { 506 // otherwise, return the improper fraction rule 507 return nonNumericalRules[IMPROPER_FRACTION_RULE_INDEX]; 508 } 509 } 510 511 // if there's a master rule, use it to format the number 512 if (nonNumericalRules[MASTER_RULE_INDEX] != null) { 513 return nonNumericalRules[MASTER_RULE_INDEX]; 514 } 515 else { 516 // and if we haven't yet returned a rule, use findNormalRule() 517 // to find the applicable rule 518 return findNormalRule(Math.round(number)); 519 } 520 } 521 522 /** 523 * If the value passed to findRule() is a positive integer, findRule() 524 * uses this function to select the appropriate rule. The result will 525 * generally be the rule with the highest base value less than or equal 526 * to the number. There is one exception to this: If that rule has 527 * two substitutions and a base value that is not an even multiple of 528 * its divisor, and the number itself IS an even multiple of the rule's 529 * divisor, then the result will be the rule that preceded the original 530 * result in the rule list. (This behavior is known as the "rollback 531 * rule", and is used to handle optional text: a rule with optional 532 * text is represented internally as two rules, and the rollback rule 533 * selects appropriate between them. This avoids things like "two 534 * hundred zero".) 535 * @param number The number being formatted 536 * @return The rule to use to format this number 537 */ findNormalRule(long number)538 private NFRule findNormalRule(long number) { 539 // if this is a fraction rule set, use findFractionRuleSetRule() 540 // to find the rule (we should only go into this clause if the 541 // value is 0) 542 if (isFractionRuleSet) { 543 return findFractionRuleSetRule(number); 544 } 545 546 // if the number is negative, return the negative-number rule 547 // (if there isn't one, pretend the number is positive) 548 if (number < 0) { 549 if (nonNumericalRules[NEGATIVE_RULE_INDEX] != null) { 550 return nonNumericalRules[NEGATIVE_RULE_INDEX]; 551 } else { 552 number = -number; 553 } 554 } 555 556 // we have to repeat the preceding two checks, even though we 557 // do them in findRule(), because the version of format() that 558 // takes a long bypasses findRule() and goes straight to this 559 // function. This function does skip the fraction rules since 560 // we know the value is an integer (it also skips the master 561 // rule, since it's considered a fraction rule. Skipping the 562 // master rule in this function is also how we avoid infinite 563 // recursion) 564 565 // binary-search the rule list for the applicable rule 566 // (a rule is used for all values from its base value to 567 // the next rule's base value) 568 int lo = 0; 569 int hi = rules.length; 570 if (hi > 0) { 571 while (lo < hi) { 572 int mid = (lo + hi) >>> 1; 573 long ruleBaseValue = rules[mid].getBaseValue(); 574 if (ruleBaseValue == number) { 575 return rules[mid]; 576 } 577 else if (ruleBaseValue > number) { 578 hi = mid; 579 } 580 else { 581 lo = mid + 1; 582 } 583 } 584 if (hi == 0) { // bad rule set 585 throw new IllegalStateException("The rule set " + name + " cannot format the value " + number); 586 } 587 NFRule result = rules[hi - 1]; 588 589 // use shouldRollBack() to see whether we need to invoke the 590 // rollback rule (see shouldRollBack()'s documentation for 591 // an explanation of the rollback rule). If we do, roll back 592 // one rule and return that one instead of the one we'd normally 593 // return 594 if (result.shouldRollBack(number)) { 595 if (hi == 1) { // bad rule set 596 throw new IllegalStateException("The rule set " + name + " cannot roll back from the rule '" + 597 result + "'"); 598 } 599 result = rules[hi - 2]; 600 } 601 return result; 602 } 603 // else use the master rule 604 return nonNumericalRules[MASTER_RULE_INDEX]; 605 } 606 607 /** 608 * If this rule is a fraction rule set, this function is used by 609 * findRule() to select the most appropriate rule for formatting 610 * the number. Basically, the base value of each rule in the rule 611 * set is treated as the denominator of a fraction. Whichever 612 * denominator can produce the fraction closest in value to the 613 * number passed in is the result. If there's a tie, the earlier 614 * one in the list wins. (If there are two rules in a row with the 615 * same base value, the first one is used when the numerator of the 616 * fraction would be 1, and the second rule is used the rest of the 617 * time. 618 * @param number The number being formatted (which will always be 619 * a number between 0 and 1) 620 * @return The rule to use to format this number 621 */ findFractionRuleSetRule(double number)622 private NFRule findFractionRuleSetRule(double number) { 623 // the obvious way to do this (multiply the value being formatted 624 // by each rule's base value until you get an integral result) 625 // doesn't work because of rounding error. This method is more 626 // accurate 627 628 // find the least common multiple of the rules' base values 629 // and multiply this by the number being formatted. This is 630 // all the precision we need, and we can do all of the rest 631 // of the math using integer arithmetic 632 long leastCommonMultiple = rules[0].getBaseValue(); 633 for (int i = 1; i < rules.length; i++) { 634 leastCommonMultiple = lcm(leastCommonMultiple, rules[i].getBaseValue()); 635 } 636 long numerator = Math.round(number * leastCommonMultiple); 637 638 // for each rule, do the following... 639 long tempDifference; 640 long difference = Long.MAX_VALUE; 641 int winner = 0; 642 for (int i = 0; i < rules.length; i++) { 643 // "numerator" is the numerator of the fraction is the 644 // denominator is the LCD. The numerator if the the rule's 645 // base value is the denominator is "numerator" times the 646 // base value divided by the LCD. Here we check to see if 647 // that's an integer, and if not, how close it is to being 648 // an integer. 649 tempDifference = numerator * rules[i].getBaseValue() % leastCommonMultiple; 650 651 // normalize the result of the above calculation: we want 652 // the numerator's distance from the CLOSEST multiple 653 // of the LCD 654 if (leastCommonMultiple - tempDifference < tempDifference) { 655 tempDifference = leastCommonMultiple - tempDifference; 656 } 657 658 // if this is as close as we've come, keep track of how close 659 // that is, and the line number of the rule that did it. If 660 // we've scored a direct hit, we don't have to look at any more 661 // rules 662 if (tempDifference < difference) { 663 difference = tempDifference; 664 winner = i; 665 if (difference == 0) { 666 break; 667 } 668 } 669 } 670 671 // if we have two successive rules that both have the winning base 672 // value, then the first one (the one we found above) is used if 673 // the numerator of the fraction is 1 and the second one is used if 674 // the numerator of the fraction is anything else (this lets us 675 // do things like "one third"/"two thirds" without having to define 676 // a whole bunch of extra rule sets) 677 if (winner + 1 < rules.length 678 && rules[winner + 1].getBaseValue() == rules[winner].getBaseValue()) { 679 if (Math.round(number * rules[winner].getBaseValue()) < 1 680 || Math.round(number * rules[winner].getBaseValue()) >= 2) { 681 ++winner; 682 } 683 } 684 685 // finally, return the winning rule 686 return rules[winner]; 687 } 688 689 /** 690 * Calculates the least common multiple of x and y. 691 */ lcm(long x, long y)692 private static long lcm(long x, long y) { 693 // binary gcd algorithm from Knuth, "The Art of Computer Programming," 694 // vol. 2, 1st ed., pp. 298-299 695 long x1 = x; 696 long y1 = y; 697 698 int p2 = 0; 699 while ((x1 & 1) == 0 && (y1 & 1) == 0) { 700 ++p2; 701 x1 >>= 1; 702 y1 >>= 1; 703 } 704 705 long t; 706 if ((x1 & 1) == 1) { 707 t = -y1; 708 } else { 709 t = x1; 710 } 711 712 while (t != 0) { 713 while ((t & 1) == 0) { 714 t >>= 1; 715 } 716 if (t > 0) { 717 x1 = t; 718 } else { 719 y1 = -t; 720 } 721 t = x1 - y1; 722 } 723 long gcd = x1 << p2; 724 725 // x * y == gcd(x, y) * lcm(x, y) 726 return x / gcd * y; 727 } 728 729 //----------------------------------------------------------------------- 730 // parsing 731 //----------------------------------------------------------------------- 732 733 /** 734 * Parses a string. Matches the string to be parsed against each 735 * of its rules (with a base value less than upperBound) and returns 736 * the value produced by the rule that matched the most characters 737 * in the source string. 738 * @param text The string to parse 739 * @param parsePosition The initial position is ignored and assumed 740 * to be 0. On exit, this object has been updated to point to the 741 * first character position this rule set didn't consume. 742 * @param upperBound Limits the rules that can be allowed to match. 743 * Only rules whose base values are strictly less than upperBound 744 * are considered. 745 * @return The numerical result of parsing this string. This will 746 * be the matching rule's base value, composed appropriately with 747 * the results of matching any of its substitutions. The object 748 * will be an instance of Long if it's an integral value; otherwise, 749 * it will be an instance of Double. This function always returns 750 * a valid object: If nothing matched the input string at all, 751 * this function returns new Long(0), and the parse position is 752 * left unchanged. 753 */ parse(String text, ParsePosition parsePosition, double upperBound, int nonNumericalExecutedRuleMask)754 public Number parse(String text, ParsePosition parsePosition, double upperBound, int nonNumericalExecutedRuleMask) { 755 // try matching each rule in the rule set against the text being 756 // parsed. Whichever one matches the most characters is the one 757 // that determines the value we return. 758 759 ParsePosition highWaterMark = new ParsePosition(0); 760 Number result = NFRule.ZERO; 761 Number tempResult; 762 763 // dump out if there's no text to parse 764 if (text.length() == 0) { 765 return result; 766 } 767 768 // Try each of the negative rules, fraction rules, infinity rules and NaN rules 769 for (int nonNumericalRuleIdx = 0; nonNumericalRuleIdx < nonNumericalRules.length; nonNumericalRuleIdx++) { 770 NFRule nonNumericalRule = nonNumericalRules[nonNumericalRuleIdx]; 771 if (nonNumericalRule != null && ((nonNumericalExecutedRuleMask >> nonNumericalRuleIdx) & 1) == 0) { 772 // Mark this rule as being executed so that we don't try to execute it again. 773 nonNumericalExecutedRuleMask |= 1 << nonNumericalRuleIdx; 774 775 tempResult = nonNumericalRule.doParse(text, parsePosition, false, upperBound, nonNumericalExecutedRuleMask); 776 if (parsePosition.getIndex() > highWaterMark.getIndex()) { 777 result = tempResult; 778 highWaterMark.setIndex(parsePosition.getIndex()); 779 } 780 // commented out because the error-index API on ParsePosition isn't there in 1.1.x 781 // if (parsePosition.getErrorIndex() > highWaterMark.getErrorIndex()) { 782 // highWaterMark.setErrorIndex(parsePosition.getErrorIndex()); 783 // } 784 parsePosition.setIndex(0); 785 } 786 } 787 788 // finally, go through the regular rules one at a time. We start 789 // at the end of the list because we want to try matching the most 790 // significant rule first (this helps ensure that we parse 791 // "five thousand three hundred six" as 792 // "(five thousand) (three hundred) (six)" rather than 793 // "((five thousand three) hundred) (six)"). Skip rules whose 794 // base values are higher than the upper bound (again, this helps 795 // limit ambiguity by making sure the rules that match a rule's 796 // are less significant than the rule containing the substitutions)/ 797 for (int i = rules.length - 1; i >= 0 && highWaterMark.getIndex() < text.length(); i--) { 798 if (!isFractionRuleSet && rules[i].getBaseValue() >= upperBound) { 799 continue; 800 } 801 802 tempResult = rules[i].doParse(text, parsePosition, isFractionRuleSet, upperBound, nonNumericalExecutedRuleMask); 803 if (parsePosition.getIndex() > highWaterMark.getIndex()) { 804 result = tempResult; 805 highWaterMark.setIndex(parsePosition.getIndex()); 806 } 807 // commented out because the error-index API on ParsePosition isn't there in 1.1.x 808 // if (parsePosition.getErrorIndex() > highWaterMark.getErrorIndex()) { 809 // highWaterMark.setErrorIndex(parsePosition.getErrorIndex()); 810 // } 811 parsePosition.setIndex(0); 812 } 813 814 // finally, update the parse position we were passed to point to the 815 // first character we didn't use, and return the result that 816 // corresponds to that string of characters 817 parsePosition.setIndex(highWaterMark.getIndex()); 818 // commented out because the error-index API on ParsePosition isn't there in 1.1.x 819 // if (parsePosition.getIndex() == 0) { 820 // parsePosition.setErrorIndex(highWaterMark.getErrorIndex()); 821 // } 822 823 return result; 824 } 825 setDecimalFormatSymbols(DecimalFormatSymbols newSymbols)826 public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) { 827 for (NFRule rule : rules) { 828 rule.setDecimalFormatSymbols(newSymbols); 829 } 830 // Switch the fraction rules to mirror the DecimalFormatSymbols. 831 if (fractionRules != null) { 832 for (int nonNumericalIdx = IMPROPER_FRACTION_RULE_INDEX; nonNumericalIdx <= MASTER_RULE_INDEX; nonNumericalIdx++) { 833 if (nonNumericalRules[nonNumericalIdx] != null) { 834 for (NFRule rule : fractionRules) { 835 if (nonNumericalRules[nonNumericalIdx].getBaseValue() == rule.getBaseValue()) { 836 setBestFractionRule(nonNumericalIdx, rule, false); 837 } 838 } 839 } 840 } 841 } 842 843 for (NFRule rule : nonNumericalRules) { 844 if (rule != null) { 845 rule.setDecimalFormatSymbols(newSymbols); 846 } 847 } 848 } 849 } 850