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