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