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