Home | History | Annotate | Download | only in text
      1 /* GENERATED SOURCE. DO NOT MODIFY. */
      2 //  2016 and later: Unicode, Inc. and others.
      3 // License & terms of use: http://www.unicode.org/copyright.html#License
      4 /*
      5  *******************************************************************************
      6  * Copyright (C) 1996-2015, International Business Machines Corporation and    *
      7  * others. All Rights Reserved.                                                *
      8  *******************************************************************************
      9  */
     10 package android.icu.text;
     11 
     12 import java.text.ParsePosition;
     13 import java.util.ArrayList;
     14 import java.util.LinkedList;
     15 import java.util.List;
     16 
     17 import android.icu.impl.PatternProps;
     18 import android.icu.impl.Utility;
     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      */
    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      */
    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<NFRule>();
    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      */
    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      */
    265     private void setBestFractionRule(int originalIndex, NFRule newRule, boolean rememberRule) {
    266         if (rememberRule) {
    267             if (fractionRules == null) {
    268                 fractionRules = new LinkedList<NFRule>();
    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      */
    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     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 (!Utility.objectEquals(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     public int hashCode() {
    342         assert false : "hashCode not designed";
    343         return 42;
    344     }
    345 
    346 
    347     /**
    348      * Builds a textual representation of a rule set.
    349      * @return A textual representation of a rule set.  This won't
    350      * necessarily be the same description that the rule set was
    351      * constructed with, but it will produce the same results.
    352      */
    353     public String toString() {
    354         StringBuilder result = new StringBuilder();
    355 
    356         // the rule set name goes first...
    357         result.append(name).append(":\n");
    358 
    359         // followed by the regular rules...
    360         for (NFRule rule : rules) {
    361             result.append(rule.toString()).append("\n");
    362         }
    363 
    364         // followed by the special rules (if they exist)
    365         for (NFRule rule : nonNumericalRules) {
    366             if (rule != null) {
    367                 if (rule.getBaseValue() == NFRule.IMPROPER_FRACTION_RULE
    368                     || rule.getBaseValue() == NFRule.PROPER_FRACTION_RULE
    369                     || rule.getBaseValue() == NFRule.MASTER_RULE)
    370                 {
    371                     for (NFRule fractionRule : fractionRules) {
    372                         if (fractionRule.getBaseValue() == rule.getBaseValue()) {
    373                             result.append(fractionRule.toString()).append("\n");
    374                         }
    375                     }
    376                 }
    377                 else {
    378                     result.append(rule.toString()).append("\n");
    379                 }
    380             }
    381         }
    382 
    383         return result.toString();
    384     }
    385 
    386     //-----------------------------------------------------------------------
    387     // simple accessors
    388     //-----------------------------------------------------------------------
    389 
    390     /**
    391      * Says whether this rule set is a fraction rule set.
    392      * @return true if this rule is a fraction rule set; false if it isn't
    393      */
    394     public boolean isFractionSet() {
    395         return isFractionRuleSet;
    396     }
    397 
    398     /**
    399      * Returns the rule set's name
    400      * @return The rule set's name
    401      */
    402     public String getName() {
    403         return name;
    404     }
    405 
    406     /**
    407      * Return true if the rule set is public.
    408      * @return true if the rule set is public
    409      */
    410     public boolean isPublic() {
    411         return !name.startsWith("%%");
    412     }
    413 
    414     /**
    415      * Return true if the rule set can be used for parsing.
    416      * @return true if the rule set can be used for parsing.
    417      */
    418     public boolean isParseable() {
    419         return isParseable;
    420     }
    421 
    422     //-----------------------------------------------------------------------
    423     // formatting
    424     //-----------------------------------------------------------------------
    425 
    426     /**
    427      * Formats a long.  Selects an appropriate rule and dispatches
    428      * control to it.
    429      * @param number The number being formatted
    430      * @param toInsertInto The string where the result is to be placed
    431      * @param pos The position in toInsertInto where the result of
    432      * this operation is to be inserted
    433      */
    434     public void format(long number, StringBuilder toInsertInto, int pos, int recursionCount) {
    435         if (recursionCount >= RECURSION_LIMIT) {
    436             throw new IllegalStateException("Recursion limit exceeded when applying ruleSet " + name);
    437         }
    438         NFRule applicableRule = findNormalRule(number);
    439         applicableRule.doFormat(number, toInsertInto, pos, ++recursionCount);
    440     }
    441 
    442     /**
    443      * Formats a double.  Selects an appropriate rule and dispatches
    444      * control to it.
    445      * @param number The number being formatted
    446      * @param toInsertInto The string where the result is to be placed
    447      * @param pos The position in toInsertInto where the result of
    448      * this operation is to be inserted
    449      */
    450     public void format(double number, StringBuilder toInsertInto, int pos, int recursionCount) {
    451         if (recursionCount >= RECURSION_LIMIT) {
    452             throw new IllegalStateException("Recursion limit exceeded when applying ruleSet " + name);
    453         }
    454         NFRule applicableRule = findRule(number);
    455         applicableRule.doFormat(number, toInsertInto, pos, ++recursionCount);
    456     }
    457 
    458     /**
    459      * Selects an appropriate rule for formatting the number.
    460      * @param number The number being formatted.
    461      * @return The rule that should be used to format it
    462      */
    463     NFRule findRule(double number) {
    464         // if this is a fraction rule set, use findFractionRuleSetRule()
    465         if (isFractionRuleSet) {
    466             return findFractionRuleSetRule(number);
    467         }
    468 
    469         if (Double.isNaN(number)) {
    470             NFRule rule = nonNumericalRules[NAN_RULE_INDEX];
    471             if (rule == null) {
    472                 rule = owner.getDefaultNaNRule();
    473             }
    474             return rule;
    475         }
    476 
    477         // if the number is negative, return the negative number rule
    478         // (if there isn't a negative-number rule, we pretend it's a
    479         // positive number)
    480         if (number < 0) {
    481             if (nonNumericalRules[NEGATIVE_RULE_INDEX] != null) {
    482                 return nonNumericalRules[NEGATIVE_RULE_INDEX];
    483             } else {
    484                 number = -number;
    485             }
    486         }
    487 
    488         if (Double.isInfinite(number)) {
    489             NFRule rule = nonNumericalRules[INFINITY_RULE_INDEX];
    490             if (rule == null) {
    491                 rule = owner.getDefaultInfinityRule();
    492             }
    493             return rule;
    494         }
    495 
    496         // if the number isn't an integer, we use one f the fraction rules...
    497         if (number != Math.floor(number)) {
    498             if (number < 1 && nonNumericalRules[PROPER_FRACTION_RULE_INDEX] != null) {
    499                 // if the number is between 0 and 1, return the proper
    500                 // fraction rule
    501                 return nonNumericalRules[PROPER_FRACTION_RULE_INDEX];
    502             }
    503             else if (nonNumericalRules[IMPROPER_FRACTION_RULE_INDEX] != null) {
    504                 // otherwise, return the improper fraction rule
    505                 return nonNumericalRules[IMPROPER_FRACTION_RULE_INDEX];
    506             }
    507         }
    508 
    509         // if there's a master rule, use it to format the number
    510         if (nonNumericalRules[MASTER_RULE_INDEX] != null) {
    511             return nonNumericalRules[MASTER_RULE_INDEX];
    512         }
    513         else {
    514             // and if we haven't yet returned a rule, use findNormalRule()
    515             // to find the applicable rule
    516             return findNormalRule(Math.round(number));
    517         }
    518     }
    519 
    520     /**
    521      * If the value passed to findRule() is a positive integer, findRule()
    522      * uses this function to select the appropriate rule.  The result will
    523      * generally be the rule with the highest base value less than or equal
    524      * to the number.  There is one exception to this: If that rule has
    525      * two substitutions and a base value that is not an even multiple of
    526      * its divisor, and the number itself IS an even multiple of the rule's
    527      * divisor, then the result will be the rule that preceded the original
    528      * result in the rule list.  (This behavior is known as the "rollback
    529      * rule", and is used to handle optional text: a rule with optional
    530      * text is represented internally as two rules, and the rollback rule
    531      * selects appropriate between them.  This avoids things like "two
    532      * hundred zero".)
    533      * @param number The number being formatted
    534      * @return The rule to use to format this number
    535      */
    536     private NFRule findNormalRule(long number) {
    537         // if this is a fraction rule set, use findFractionRuleSetRule()
    538         // to find the rule (we should only go into this clause if the
    539         // value is 0)
    540         if (isFractionRuleSet) {
    541             return findFractionRuleSetRule(number);
    542         }
    543 
    544         // if the number is negative, return the negative-number rule
    545         // (if there isn't one, pretend the number is positive)
    546         if (number < 0) {
    547             if (nonNumericalRules[NEGATIVE_RULE_INDEX] != null) {
    548                 return nonNumericalRules[NEGATIVE_RULE_INDEX];
    549             } else {
    550                 number = -number;
    551             }
    552         }
    553 
    554         // we have to repeat the preceding two checks, even though we
    555         // do them in findRule(), because the version of format() that
    556         // takes a long bypasses findRule() and goes straight to this
    557         // function.  This function does skip the fraction rules since
    558         // we know the value is an integer (it also skips the master
    559         // rule, since it's considered a fraction rule.  Skipping the
    560         // master rule in this function is also how we avoid infinite
    561         // recursion)
    562 
    563         // binary-search the rule list for the applicable rule
    564         // (a rule is used for all values from its base value to
    565         // the next rule's base value)
    566         int lo = 0;
    567         int hi = rules.length;
    568         if (hi > 0) {
    569             while (lo < hi) {
    570                 int mid = (lo + hi) >>> 1;
    571                 long ruleBaseValue = rules[mid].getBaseValue();
    572                 if (ruleBaseValue == number) {
    573                     return rules[mid];
    574                 }
    575                 else if (ruleBaseValue > number) {
    576                     hi = mid;
    577                 }
    578                 else {
    579                     lo = mid + 1;
    580                 }
    581             }
    582             if (hi == 0) { // bad rule set
    583                 throw new IllegalStateException("The rule set " + name + " cannot format the value " + number);
    584             }
    585             NFRule result = rules[hi - 1];
    586 
    587             // use shouldRollBack() to see whether we need to invoke the
    588             // rollback rule (see shouldRollBack()'s documentation for
    589             // an explanation of the rollback rule).  If we do, roll back
    590             // one rule and return that one instead of the one we'd normally
    591             // return
    592             if (result.shouldRollBack(number)) {
    593                 if (hi == 1) { // bad rule set
    594                     throw new IllegalStateException("The rule set " + name + " cannot roll back from the rule '" +
    595                             result + "'");
    596                 }
    597                 result = rules[hi - 2];
    598             }
    599             return result;
    600         }
    601         // else use the master rule
    602         return nonNumericalRules[MASTER_RULE_INDEX];
    603     }
    604 
    605     /**
    606      * If this rule is a fraction rule set, this function is used by
    607      * findRule() to select the most appropriate rule for formatting
    608      * the number.  Basically, the base value of each rule in the rule
    609      * set is treated as the denominator of a fraction.  Whichever
    610      * denominator can produce the fraction closest in value to the
    611      * number passed in is the result.  If there's a tie, the earlier
    612      * one in the list wins.  (If there are two rules in a row with the
    613      * same base value, the first one is used when the numerator of the
    614      * fraction would be 1, and the second rule is used the rest of the
    615      * time.
    616      * @param number The number being formatted (which will always be
    617      * a number between 0 and 1)
    618      * @return The rule to use to format this number
    619      */
    620     private NFRule findFractionRuleSetRule(double number) {
    621         // the obvious way to do this (multiply the value being formatted
    622         // by each rule's base value until you get an integral result)
    623         // doesn't work because of rounding error.  This method is more
    624         // accurate
    625 
    626         // find the least common multiple of the rules' base values
    627         // and multiply this by the number being formatted.  This is
    628         // all the precision we need, and we can do all of the rest
    629         // of the math using integer arithmetic
    630         long leastCommonMultiple = rules[0].getBaseValue();
    631         for (int i = 1; i < rules.length; i++) {
    632             leastCommonMultiple = lcm(leastCommonMultiple, rules[i].getBaseValue());
    633         }
    634         long numerator = Math.round(number * leastCommonMultiple);
    635 
    636         // for each rule, do the following...
    637         long tempDifference;
    638         long difference = Long.MAX_VALUE;
    639         int winner = 0;
    640         for (int i = 0; i < rules.length; i++) {
    641             // "numerator" is the numerator of the fraction is the
    642             // denominator is the LCD.  The numerator if the the rule's
    643             // base value is the denominator is "numerator" times the
    644             // base value divided by the LCD.  Here we check to see if
    645             // that's an integer, and if not, how close it is to being
    646             // an integer.
    647             tempDifference = numerator * rules[i].getBaseValue() % leastCommonMultiple;
    648 
    649             // normalize the result of the above calculation: we want
    650             // the numerator's distance from the CLOSEST multiple
    651             // of the LCD
    652             if (leastCommonMultiple - tempDifference < tempDifference) {
    653                 tempDifference = leastCommonMultiple - tempDifference;
    654             }
    655 
    656             // if this is as close as we've come, keep track of how close
    657             // that is, and the line number of the rule that did it.  If
    658             // we've scored a direct hit, we don't have to look at any more
    659             // rules
    660             if (tempDifference < difference) {
    661                 difference = tempDifference;
    662                 winner = i;
    663                 if (difference == 0) {
    664                     break;
    665                 }
    666             }
    667         }
    668 
    669         // if we have two successive rules that both have the winning base
    670         // value, then the first one (the one we found above) is used if
    671         // the numerator of the fraction is 1 and the second one is used if
    672         // the numerator of the fraction is anything else (this lets us
    673         // do things like "one third"/"two thirds" without having to define
    674         // a whole bunch of extra rule sets)
    675         if (winner + 1 < rules.length
    676                 && rules[winner + 1].getBaseValue() == rules[winner].getBaseValue()) {
    677             if (Math.round(number * rules[winner].getBaseValue()) < 1
    678                     || Math.round(number * rules[winner].getBaseValue()) >= 2) {
    679                 ++winner;
    680             }
    681         }
    682 
    683         // finally, return the winning rule
    684         return rules[winner];
    685     }
    686 
    687     /**
    688      * Calculates the least common multiple of x and y.
    689      */
    690     private static long lcm(long x, long y) {
    691         // binary gcd algorithm from Knuth, "The Art of Computer Programming,"
    692         // vol. 2, 1st ed., pp. 298-299
    693         long x1 = x;
    694         long y1 = y;
    695 
    696         int p2 = 0;
    697         while ((x1 & 1) == 0 && (y1 & 1) == 0) {
    698             ++p2;
    699             x1 >>= 1;
    700             y1 >>= 1;
    701         }
    702 
    703         long t;
    704         if ((x1 & 1) == 1) {
    705             t = -y1;
    706         } else {
    707             t = x1;
    708         }
    709 
    710         while (t != 0) {
    711             while ((t & 1) == 0) {
    712                 t >>= 1;
    713             }
    714             if (t > 0) {
    715                 x1 = t;
    716             } else {
    717                 y1 = -t;
    718             }
    719             t = x1 - y1;
    720         }
    721         long gcd = x1 << p2;
    722 
    723         // x * y == gcd(x, y) * lcm(x, y)
    724         return x / gcd * y;
    725     }
    726 
    727     //-----------------------------------------------------------------------
    728     // parsing
    729     //-----------------------------------------------------------------------
    730 
    731     /**
    732      * Parses a string.  Matches the string to be parsed against each
    733      * of its rules (with a base value less than upperBound) and returns
    734      * the value produced by the rule that matched the most characters
    735      * in the source string.
    736      * @param text The string to parse
    737      * @param parsePosition The initial position is ignored and assumed
    738      * to be 0.  On exit, this object has been updated to point to the
    739      * first character position this rule set didn't consume.
    740      * @param upperBound Limits the rules that can be allowed to match.
    741      * Only rules whose base values are strictly less than upperBound
    742      * are considered.
    743      * @return The numerical result of parsing this string.  This will
    744      * be the matching rule's base value, composed appropriately with
    745      * the results of matching any of its substitutions.  The object
    746      * will be an instance of Long if it's an integral value; otherwise,
    747      * it will be an instance of Double.  This function always returns
    748      * a valid object: If nothing matched the input string at all,
    749      * this function returns new Long(0), and the parse position is
    750      * left unchanged.
    751      */
    752     public Number parse(String text, ParsePosition parsePosition, double upperBound) {
    753         // try matching each rule in the rule set against the text being
    754         // parsed.  Whichever one matches the most characters is the one
    755         // that determines the value we return.
    756 
    757         ParsePosition highWaterMark = new ParsePosition(0);
    758         Number result = NFRule.ZERO;
    759         Number tempResult;
    760 
    761         // dump out if there's no text to parse
    762         if (text.length() == 0) {
    763             return result;
    764         }
    765 
    766         // Try each of the negative rules, fraction rules, infinity rules and NaN rules
    767         for (NFRule fractionRule : nonNumericalRules) {
    768             if (fractionRule != null) {
    769                 tempResult = fractionRule.doParse(text, parsePosition, false, upperBound);
    770                 if (parsePosition.getIndex() > highWaterMark.getIndex()) {
    771                     result = tempResult;
    772                     highWaterMark.setIndex(parsePosition.getIndex());
    773                 }
    774 // commented out because the error-index API on ParsePosition isn't there in 1.1.x
    775 //            if (parsePosition.getErrorIndex() > highWaterMark.getErrorIndex()) {
    776 //                highWaterMark.setErrorIndex(parsePosition.getErrorIndex());
    777 //            }
    778                 parsePosition.setIndex(0);
    779             }
    780         }
    781 
    782         // finally, go through the regular rules one at a time.  We start
    783         // at the end of the list because we want to try matching the most
    784         // significant rule first (this helps ensure that we parse
    785         // "five thousand three hundred six" as
    786         // "(five thousand) (three hundred) (six)" rather than
    787         // "((five thousand three) hundred) (six)").  Skip rules whose
    788         // base values are higher than the upper bound (again, this helps
    789         // limit ambiguity by making sure the rules that match a rule's
    790         // are less significant than the rule containing the substitutions)/
    791         for (int i = rules.length - 1; i >= 0 && highWaterMark.getIndex() < text.length(); i--) {
    792             if (!isFractionRuleSet && rules[i].getBaseValue() >= upperBound) {
    793                 continue;
    794             }
    795 
    796             tempResult = rules[i].doParse(text, parsePosition, isFractionRuleSet, upperBound);
    797             if (parsePosition.getIndex() > highWaterMark.getIndex()) {
    798                 result = tempResult;
    799                 highWaterMark.setIndex(parsePosition.getIndex());
    800             }
    801 // commented out because the error-index API on ParsePosition isn't there in 1.1.x
    802 //            if (parsePosition.getErrorIndex() > highWaterMark.getErrorIndex()) {
    803 //                highWaterMark.setErrorIndex(parsePosition.getErrorIndex());
    804 //            }
    805             parsePosition.setIndex(0);
    806         }
    807 
    808         // finally, update the parse position we were passed to point to the
    809         // first character we didn't use, and return the result that
    810         // corresponds to that string of characters
    811         parsePosition.setIndex(highWaterMark.getIndex());
    812 // commented out because the error-index API on ParsePosition isn't there in 1.1.x
    813 //        if (parsePosition.getIndex() == 0) {
    814 //            parsePosition.setErrorIndex(highWaterMark.getErrorIndex());
    815 //        }
    816 
    817         return result;
    818     }
    819 
    820     public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) {
    821         for (NFRule rule : rules) {
    822             rule.setDecimalFormatSymbols(newSymbols);
    823         }
    824         // Switch the fraction rules to mirror the DecimalFormatSymbols.
    825         if (fractionRules != null) {
    826             for (int nonNumericalIdx = IMPROPER_FRACTION_RULE_INDEX; nonNumericalIdx <= MASTER_RULE_INDEX; nonNumericalIdx++) {
    827                 if (nonNumericalRules[nonNumericalIdx] != null) {
    828                     for (NFRule rule : fractionRules) {
    829                         if (nonNumericalRules[nonNumericalIdx].getBaseValue() == rule.getBaseValue()) {
    830                             setBestFractionRule(nonNumericalIdx, rule, false);
    831                         }
    832                     }
    833                 }
    834             }
    835         }
    836 
    837         for (NFRule rule : nonNumericalRules) {
    838             if (rule != null) {
    839                 rule.setDecimalFormatSymbols(newSymbols);
    840             }
    841         }
    842     }
    843 }
    844