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