2 *******************************************************************************
\r
3 * Copyright (C) 1996-2010, International Business Machines Corporation and *
\r
4 * others. All Rights Reserved. *
\r
5 *******************************************************************************
\r
7 package com.ibm.icu.text;
\r
9 import java.text.ParsePosition;
\r
10 import java.util.Vector;
\r
12 import com.ibm.icu.impl.UCharacterProperty;
\r
13 import com.ibm.icu.impl.Utility;
\r
16 * A collection of rules used by a RuleBasedNumberFormat to format and
\r
17 * parse numbers. It is the responsibility of a RuleSet to select an
\r
18 * appropriate rule for formatting a particular number and dispatch
\r
19 * control to it, and to arbitrate between different rules when parsing
\r
23 final class NFRuleSet {
\r
24 //-----------------------------------------------------------------------
\r
26 //-----------------------------------------------------------------------
\r
29 * The rule set's name
\r
31 private String name;
\r
34 * The rule set's regular rules
\r
36 private NFRule[] rules;
\r
39 * The rule set's negative-number rule
\r
41 private NFRule negativeNumberRule = null;
\r
44 * The rule set's fraction rules: element 0 is the proper fraction
\r
45 * (0.x) rule, element 1 is the improper fraction (x.x) rule, and
\r
46 * element 2 is the master (x.0) rule.
\r
48 private NFRule[] fractionRules = new NFRule[3];
\r
51 * True if the rule set is a fraction rule set. A fraction rule set
\r
52 * is a rule set that is used to format the fractional part of a
\r
53 * number. It is called from a >> substitution in another rule set's
\r
54 * fraction rule, and is only called upon to format values between
\r
55 * 0 and 1. A fraction rule set has different rule-selection
\r
56 * behavior than a regular rule set.
\r
58 private boolean isFractionRuleSet = false;
\r
61 * Used to limit recursion for bad rule sets.
\r
63 private int recursionCount = 0;
\r
66 * Limit of recursion.
\r
68 private static final int RECURSION_LIMIT = 50;
\r
70 //-----------------------------------------------------------------------
\r
72 //-----------------------------------------------------------------------
\r
75 * Constructs a rule set.
\r
76 * @param descriptions An array of Strings representing rule set
\r
77 * descriptions. On exit, this rule set's entry in the array will
\r
78 * have been stripped of its rule set name and any trailing whitespace.
\r
79 * @param index The index into "descriptions" of the description
\r
80 * for the rule to be constructed
\r
82 public NFRuleSet(String[] descriptions, int index) throws IllegalArgumentException {
\r
83 String description = descriptions[index];
\r
85 if (description.length() == 0) {
\r
86 throw new IllegalArgumentException("Empty rule set description");
\r
89 // if the description begins with a rule set name (the rule set
\r
90 // name can be omitted in formatter descriptions that consist
\r
91 // of only one rule set), copy it out into our "name" member
\r
92 // and delete it from the description
\r
93 if (description.charAt(0) == '%') {
\r
94 int pos = description.indexOf(':');
\r
96 throw new IllegalArgumentException("Rule set name doesn't end in colon");
\r
98 name = description.substring(0, pos);
\r
99 while (pos < description.length() && UCharacterProperty.isRuleWhiteSpace(description.
\r
102 description = description.substring(pos);
\r
103 descriptions[index] = description;
\r
106 // if the description doesn't begin with a rule set name, its
\r
107 // name is "%default"
\r
112 if (description.length() == 0) {
\r
113 throw new IllegalArgumentException("Empty rule set description");
\r
116 // all of the other members of NFRuleSet are initialized
\r
121 * Construct the subordinate data structures used by this object.
\r
122 * This function is called by the RuleBasedNumberFormat constructor
\r
123 * after all the rule sets have been created to actually parse
\r
124 * the description and build rules from it. Since any rule set
\r
125 * can refer to any other rule set, we have to have created all of
\r
126 * them before we can create anything else.
\r
127 * @param description The textual description of this rule set
\r
128 * @param owner The formatter that owns this rule set
\r
130 public void parseRules(String description,
\r
131 RuleBasedNumberFormat owner) {
\r
132 // start by creating a Vector whose elements are Strings containing
\r
133 // the descriptions of the rules (one rule per element). The rules
\r
134 // are separated by semicolons (there's no escape facility: ALL
\r
135 // semicolons are rule delimiters)
\r
136 Vector<String> ruleDescriptions = new Vector<String>();
\r
139 int p = description.indexOf(';');
\r
140 while (oldP != -1) {
\r
142 ruleDescriptions.addElement(description.substring(oldP, p));
\r
145 if (oldP < description.length()) {
\r
146 ruleDescriptions.addElement(description.substring(oldP));
\r
150 p = description.indexOf(';', p + 1);
\r
153 // now go back through and build a vector of the rules themselves
\r
154 // (the number of elements in the description list isn't necessarily
\r
155 // the number of rules-- some descriptions may expend into two rules)
\r
156 Vector<NFRule> tempRules = new Vector<NFRule>();
\r
158 // we keep track of the rule before the one we're currently working
\r
159 // on solely to support >>> substitutions
\r
160 NFRule predecessor = null;
\r
161 for (int i = 0; i < ruleDescriptions.size(); i++) {
\r
162 // makeRules (a factory method on NFRule) will return either
\r
163 // a single rule or an array of rules. Either way, add them
\r
164 // to our rule vector
\r
165 Object temp = NFRule.makeRules(ruleDescriptions.elementAt(i),
\r
166 this, predecessor, owner);
\r
168 if (temp instanceof NFRule) {
\r
169 tempRules.addElement((NFRule)temp);
\r
170 predecessor = (NFRule)temp;
\r
172 else if (temp instanceof NFRule[]) {
\r
173 NFRule[] rulesToAdd = (NFRule[])temp;
\r
175 for (int j = 0; j < rulesToAdd.length; j++) {
\r
176 tempRules.addElement(rulesToAdd[j]);
\r
177 predecessor = rulesToAdd[j];
\r
181 // now we can bag the description list
\r
182 ruleDescriptions = null;
\r
184 // for rules that didn't specify a base value, their base values
\r
185 // were initialized to 0. Make another pass through the list and
\r
186 // set all those rules' base values. We also remove any special
\r
187 // rules from the list and put them into their own member variables
\r
188 long defaultBaseValue = 0;
\r
190 // (this isn't a for loop because we might be deleting items from
\r
191 // the vector-- we want to make sure we only increment i when
\r
192 // we _didn't_ delete aything from the vector)
\r
194 while (i < tempRules.size()) {
\r
195 NFRule rule = tempRules.elementAt(i);
\r
197 switch ((int)rule.getBaseValue()) {
\r
198 // if the rule's base value is 0, fill in a default
\r
199 // base value (this will be 1 plus the preceding
\r
200 // rule's base value for regular rule sets, and the
\r
201 // same as the preceding rule's base value in fraction
\r
204 rule.setBaseValue(defaultBaseValue);
\r
205 if (!isFractionRuleSet) {
\r
206 ++defaultBaseValue;
\r
211 // if it's the negative-number rule, copy it into its own
\r
212 // data member and delete it from the list
\r
213 case NFRule.NEGATIVE_NUMBER_RULE:
\r
214 negativeNumberRule = rule;
\r
215 tempRules.removeElementAt(i);
\r
218 // if it's the improper fraction rule, copy it into the
\r
219 // correct element of fractionRules
\r
220 case NFRule.IMPROPER_FRACTION_RULE:
\r
221 fractionRules[0] = rule;
\r
222 tempRules.removeElementAt(i);
\r
225 // if it's the proper fraction rule, copy it into the
\r
226 // correct element of fractionRules
\r
227 case NFRule.PROPER_FRACTION_RULE:
\r
228 fractionRules[1] = rule;
\r
229 tempRules.removeElementAt(i);
\r
232 // if it's the master rule, copy it into the
\r
233 // correct element of fractionRules
\r
234 case NFRule.MASTER_RULE:
\r
235 fractionRules[2] = rule;
\r
236 tempRules.removeElementAt(i);
\r
239 // if it's a regular rule that already knows its base value,
\r
240 // check to make sure the rules are in order, and update
\r
241 // the default base value for the next rule
\r
243 if (rule.getBaseValue() < defaultBaseValue) {
\r
244 throw new IllegalArgumentException("Rules are not in order, base: " +
\r
245 rule.getBaseValue() + " < " + defaultBaseValue);
\r
247 defaultBaseValue = rule.getBaseValue();
\r
248 if (!isFractionRuleSet) {
\r
249 ++defaultBaseValue;
\r
256 // finally, we can copy the rules from the vector into a
\r
257 // fixed-length array
\r
258 rules = new NFRule[tempRules.size()];
\r
259 tempRules.copyInto((Object[])rules);
\r
263 * Flags this rule set as a fraction rule set. This function is
\r
264 * called during the construction process once we know this rule
\r
265 * set is a fraction rule set. We don't know a rule set is a
\r
266 * fraction rule set until we see it used somewhere. This function
\r
267 * is not ad must not be called at any time other than during
\r
268 * construction of a RuleBasedNumberFormat.
\r
270 public void makeIntoFractionRuleSet() {
\r
271 isFractionRuleSet = true;
\r
274 //-----------------------------------------------------------------------
\r
276 //-----------------------------------------------------------------------
\r
279 * Compares two rule sets for equality.
\r
280 * @param that The other rule set
\r
281 * @return true if the two rule sets are functionally equivalent.
\r
283 public boolean equals(Object that) {
\r
284 // if different classes, they're not equal
\r
285 if (!(that instanceof NFRuleSet)) {
\r
288 // otherwise, compare the members one by one...
\r
289 NFRuleSet that2 = (NFRuleSet)that;
\r
291 if (!name.equals(that2.name)
\r
292 || !Utility.objectEquals(negativeNumberRule, that2.negativeNumberRule)
\r
293 || !Utility.objectEquals(fractionRules[0], that2.fractionRules[0])
\r
294 || !Utility.objectEquals(fractionRules[1], that2.fractionRules[1])
\r
295 || !Utility.objectEquals(fractionRules[2], that2.fractionRules[2])
\r
296 || rules.length != that2.rules.length
\r
297 || isFractionRuleSet != that2.isFractionRuleSet) {
\r
302 // ...then compare the rule lists...
\r
303 for (int i = 0; i < rules.length; i++) {
\r
304 if (!rules[i].equals(that2.rules[i])) {
\r
309 // ...and if we make it here, tney're equal
\r
316 * Builds a textual representation of a rule set.
\r
317 * @return A textual representation of a rule set. This won't
\r
318 * necessarily be the same description that the rule set was
\r
319 * constructed with, but it will produce the same results.
\r
321 public String toString() {
\r
322 StringBuilder result = new StringBuilder();
\r
324 // the rule set name goes first...
\r
325 result.append(name + ":\n");
\r
327 // followed by the regular rules...
\r
328 for (int i = 0; i < rules.length; i++) {
\r
329 result.append(" " + rules[i].toString() + "\n");
\r
332 // followed by the special rules (if they exist)
\r
333 if (negativeNumberRule != null) {
\r
334 result.append(" " + negativeNumberRule.toString() + "\n");
\r
336 if (fractionRules[0] != null) {
\r
337 result.append(" " + fractionRules[0].toString() + "\n");
\r
339 if (fractionRules[1] != null) {
\r
340 result.append(" " + fractionRules[1].toString() + "\n");
\r
342 if (fractionRules[2] != null) {
\r
343 result.append(" " + fractionRules[2].toString() + "\n");
\r
346 return result.toString();
\r
349 //-----------------------------------------------------------------------
\r
350 // simple accessors
\r
351 //-----------------------------------------------------------------------
\r
354 * Says whether this rule set is a fraction rule set.
\r
355 * @return true if this rule is a fraction rule set; false if it isn't
\r
357 public boolean isFractionSet() {
\r
358 return isFractionRuleSet;
\r
362 * Returns the rule set's name
\r
363 * @return The rule set's name
\r
365 public String getName() {
\r
370 * Return true if the rule set is public.
\r
371 * @return true if the rule set is public
\r
373 public boolean isPublic() {
\r
374 return !name.startsWith("%%");
\r
378 * Return true if the rule set can be used for parsing.
\r
379 * @return true if the rule set can be used for parsing.
\r
381 public boolean isParseable() {
\r
383 // In CLDR 1.7, we have no distinction between
\r
384 // parseable/unparseable. Rules which have one of
\r
385 // 3 suffixes below are know as unparseable for now.
\r
386 // We should add the information in CLDR data.
\r
387 return !(name.endsWith("-prefixpart")
\r
388 || name.endsWith("-postfixpart")
\r
389 || name.endsWith("-postfx"));
\r
392 //-----------------------------------------------------------------------
\r
394 //-----------------------------------------------------------------------
\r
397 * Formats a long. Selects an appropriate rule and dispatches
\r
399 * @param number The number being formatted
\r
400 * @param toInsertInto The string where the result is to be placed
\r
401 * @param pos The position in toInsertInto where the result of
\r
402 * this operation is to be inserted
\r
404 public void format(long number, StringBuffer toInsertInto, int pos) {
\r
405 NFRule applicableRule = findNormalRule(number);
\r
407 if (++recursionCount >= RECURSION_LIMIT) {
\r
408 recursionCount = 0;
\r
409 throw new IllegalStateException("Recursion limit exceeded when applying ruleSet " + name);
\r
411 applicableRule.doFormat(number, toInsertInto, pos);
\r
416 * Formats a double. Selects an appropriate rule and dispatches
\r
418 * @param number The number being formatted
\r
419 * @param toInsertInto The string where the result is to be placed
\r
420 * @param pos The position in toInsertInto where the result of
\r
421 * this operation is to be inserted
\r
423 public void format(double number, StringBuffer toInsertInto, int pos) {
\r
424 NFRule applicableRule = findRule(number);
\r
426 if (++recursionCount >= RECURSION_LIMIT) {
\r
427 recursionCount = 0;
\r
428 throw new IllegalStateException("Recursion limit exceeded when applying ruleSet " + name);
\r
430 applicableRule.doFormat(number, toInsertInto, pos);
\r
435 * Selects an apropriate rule for formatting the number.
\r
436 * @param number The number being formatted.
\r
437 * @return The rule that should be used to format it
\r
439 private NFRule findRule(double number) {
\r
440 // if this is a fraction rule set, use findFractionRuleSetRule()
\r
441 if (isFractionRuleSet) {
\r
442 return findFractionRuleSetRule(number);
\r
445 // if the number is negative, return the negative number rule
\r
446 // (if there isn't a negative-number rule, we pretend it's a
\r
447 // positive number)
\r
449 if (negativeNumberRule != null) {
\r
450 return negativeNumberRule;
\r
456 // if the number isn't an integer, we use one f the fraction rules...
\r
457 if (number != Math.floor(number)) {
\r
458 // if the number is between 0 and 1, return the proper
\r
460 if (number < 1 && fractionRules[1] != null) {
\r
461 return fractionRules[1];
\r
464 // otherwise, return the improper fraction rule
\r
465 else if (fractionRules[0] != null) {
\r
466 return fractionRules[0];
\r
470 // if there's a master rule, use it to format the number
\r
471 if (fractionRules[2] != null) {
\r
472 return fractionRules[2];
\r
474 // and if we haven't yet returned a rule, use findNormalRule()
\r
475 // to find the applicable rule
\r
477 return findNormalRule(Math.round(number));
\r
482 * If the value passed to findRule() is a positive integer, findRule()
\r
483 * uses this function to select the appropriate rule. The result will
\r
484 * generally be the rule with the highest base value less than or equal
\r
485 * to the number. There is one exception to this: If that rule has
\r
486 * two substitutions and a base value that is not an even multiple of
\r
487 * its divisor, and the number itself IS an even multiple of the rule's
\r
488 * divisor, then the result will be the rule that preceded the original
\r
489 * result in the rule list. (This behavior is known as the "rollback
\r
490 * rule", and is used to handle optional text: a rule with optional
\r
491 * text is represented internally as two rules, and the rollback rule
\r
492 * selects appropriate between them. This avoids things like "two
\r
494 * @param number The number being formatted
\r
495 * @return The rule to use to format this number
\r
497 private NFRule findNormalRule(long number) {
\r
498 // if this is a fraction rule set, use findFractionRuleSetRule()
\r
499 // to find the rule (we should only go into this clause if the
\r
501 if (isFractionRuleSet) {
\r
502 return findFractionRuleSetRule(number);
\r
505 // if the number is negative, return the negative-number rule
\r
506 // (if there isn't one, pretend the number is positive)
\r
508 if (negativeNumberRule != null) {
\r
509 return negativeNumberRule;
\r
515 // we have to repeat the preceding two checks, even though we
\r
516 // do them in findRule(), because the version of format() that
\r
517 // takes a long bypasses findRule() and goes straight to this
\r
518 // function. This function does skip the fraction rules since
\r
519 // we know the value is an integer (it also skips the master
\r
520 // rule, since it's considered a fraction rule. Skipping the
\r
521 // master rule in this function is also how we avoid infinite
\r
524 // binary-search the rule list for the applicable rule
\r
525 // (a rule is used for all values from its base value to
\r
526 // the next rule's base value)
\r
528 int hi = rules.length;
\r
531 int mid = (lo + hi) / 2;
\r
532 if (rules[mid].getBaseValue() == number) {
\r
535 else if (rules[mid].getBaseValue() > number) {
\r
542 if (hi == 0) { // bad rule set
\r
543 throw new IllegalStateException("The rule set " + name + " cannot format the value " + number);
\r
545 NFRule result = rules[hi - 1];
\r
547 // use shouldRollBack() to see whether we need to invoke the
\r
548 // rollback rule (see shouldRollBack()'s documentation for
\r
549 // an explanation of the rollback rule). If we do, roll back
\r
550 // one rule and return that one instead of the one we'd normally
\r
552 if (result.shouldRollBack(number)) {
\r
553 if (hi == 1) { // bad rule set
\r
554 throw new IllegalStateException("The rule set " + name + " cannot roll back from the rule '" +
\r
557 result = rules[hi - 2];
\r
561 // else use the master rule
\r
562 return fractionRules[2];
\r
566 * If this rule is a fraction rule set, this function is used by
\r
567 * findRule() to select the most appropriate rule for formatting
\r
568 * the number. Basically, the base value of each rule in the rule
\r
569 * set is treated as the denominator of a fraction. Whichever
\r
570 * denominator can produce the fraction closest in value to the
\r
571 * number passed in is the result. If there's a tie, the earlier
\r
572 * one in the list wins. (If there are two rules in a row with the
\r
573 * same base value, the first one is used when the numerator of the
\r
574 * fraction would be 1, and the second rule is used the rest of the
\r
576 * @param number The number being formatted (which will always be
\r
577 * a number between 0 and 1)
\r
578 * @return The rule to use to format this number
\r
580 private NFRule findFractionRuleSetRule(double number) {
\r
581 // the obvious way to do this (multiply the value being formatted
\r
582 // by each rule's base value until you get an integral result)
\r
583 // doesn't work because of rounding error. This method is more
\r
586 // find the least common multiple of the rules' base values
\r
587 // and multiply this by the number being formatted. This is
\r
588 // all the precision we need, and we can do all of the rest
\r
589 // of the math using integer arithmetic
\r
590 long leastCommonMultiple = rules[0].getBaseValue();
\r
591 for (int i = 1; i < rules.length; i++) {
\r
592 leastCommonMultiple = lcm(leastCommonMultiple, rules[i].getBaseValue());
\r
594 long numerator = Math.round(number * leastCommonMultiple);
\r
596 // for each rule, do the following...
\r
597 long tempDifference;
\r
598 long difference = Long.MAX_VALUE;
\r
600 for (int i = 0; i < rules.length; i++) {
\r
601 // "numerator" is the numerator of the fraction is the
\r
602 // denominator is the LCD. The numerator if the the rule's
\r
603 // base value is the denomiator is "numerator" times the
\r
604 // base value divided bythe LCD. Here we check to see if
\r
605 // that's an integer, and if not, how close it is to being
\r
607 tempDifference = numerator * rules[i].getBaseValue() % leastCommonMultiple;
\r
609 // normalize the result of the above calculation: we want
\r
610 // the numerator's distance from the CLOSEST multiple
\r
612 if (leastCommonMultiple - tempDifference < tempDifference) {
\r
613 tempDifference = leastCommonMultiple - tempDifference;
\r
616 // if this is as close as we've come, keep track of how close
\r
617 // that is, and the line number of the rule that did it. If
\r
618 // we've scored a direct hit, we don't have to look at any more
\r
620 if (tempDifference < difference) {
\r
621 difference = tempDifference;
\r
623 if (difference == 0) {
\r
629 // if we have two successive rules that both have the winning base
\r
630 // value, then the first one (the one we found above) is used if
\r
631 // the numerator of the fraction is 1 and the second one is used if
\r
632 // the numerator of the fraction is anything else (this lets us
\r
633 // do things like "one third"/"two thirds" without haveing to define
\r
634 // a whole bunch of extra rule sets)
\r
635 if (winner + 1 < rules.length
\r
636 && rules[winner + 1].getBaseValue() == rules[winner].getBaseValue()) {
\r
637 if (Math.round(number * rules[winner].getBaseValue()) < 1
\r
638 || Math.round(number * rules[winner].getBaseValue()) >= 2) {
\r
643 // finally, return the winning rule
\r
644 return rules[winner];
\r
648 * Calculates the least common multiple of x and y.
\r
650 private static long lcm(long x, long y) {
\r
651 // binary gcd algorithm from Knuth, "The Art of Computer Programming,"
\r
652 // vol. 2, 1st ed., pp. 298-299
\r
657 while ((x1 & 1) == 0 && (y1 & 1) == 0) {
\r
664 if ((x1 & 1) == 1) {
\r
671 while ((t & 1) == 0) {
\r
681 long gcd = x1 << p2;
\r
683 // x * y == gcd(x, y) * lcm(x, y)
\r
684 return x / gcd * y;
\r
687 //-----------------------------------------------------------------------
\r
689 //-----------------------------------------------------------------------
\r
692 * Parses a string. Matches the string to be parsed against each
\r
693 * of its rules (with a base value less than upperBound) and returns
\r
694 * the value produced by the rule that matched the most charcters
\r
695 * in the source string.
\r
696 * @param text The string to parse
\r
697 * @param parsePosition The initial position is ignored and assumed
\r
698 * to be 0. On exit, this object has been updated to point to the
\r
699 * first character position this rule set didn't consume.
\r
700 * @param upperBound Limits the rules that can be allowed to match.
\r
701 * Only rules whose base values are strictly less than upperBound
\r
703 * @return The numerical result of parsing this string. This will
\r
704 * be the matching rule's base value, composed appropriately with
\r
705 * the results of matching any of its substitutions. The object
\r
706 * will be an instance of Long if it's an integral value; otherwise,
\r
707 * it will be an instance of Double. This function always returns
\r
708 * a valid object: If nothing matched the input string at all,
\r
709 * this function returns new Long(0), and the parse position is
\r
712 public Number parse(String text, ParsePosition parsePosition, double upperBound) {
\r
713 // try matching each rule in the rule set against the text being
\r
714 // parsed. Whichever one matches the most characters is the one
\r
715 // that determines the value we return.
\r
717 ParsePosition highWaterMark = new ParsePosition(0);
\r
718 Number result = new Long(0);
\r
719 Number tempResult = null;
\r
721 // dump out if there's no text to parse
\r
722 if (text.length() == 0) {
\r
726 // start by trying the nehative number rule (if there is one)
\r
727 if (negativeNumberRule != null) {
\r
728 tempResult = negativeNumberRule.doParse(text, parsePosition, false, upperBound);
\r
729 if (parsePosition.getIndex() > highWaterMark.getIndex()) {
\r
730 result = tempResult;
\r
731 highWaterMark.setIndex(parsePosition.getIndex());
\r
733 // commented out because the error-index API on ParsePosition isn't there in 1.1.x
\r
734 // if (parsePosition.getErrorIndex() > highWaterMark.getErrorIndex()) {
\r
735 // highWaterMark.setErrorIndex(parsePosition.getErrorIndex());
\r
737 parsePosition.setIndex(0);
\r
740 // then try each of the fraction rules
\r
741 for (int i = 0; i < 3; i++) {
\r
742 if (fractionRules[i] != null) {
\r
743 tempResult = fractionRules[i].doParse(text, parsePosition, false, upperBound);
\r
744 if (parsePosition.getIndex() > highWaterMark.getIndex()) {
\r
745 result = tempResult;
\r
746 highWaterMark.setIndex(parsePosition.getIndex());
\r
748 // commented out because the error-index API on ParsePosition isn't there in 1.1.x
\r
749 // if (parsePosition.getErrorIndex() > highWaterMark.getErrorIndex()) {
\r
750 // highWaterMark.setErrorIndex(parsePosition.getErrorIndex());
\r
752 parsePosition.setIndex(0);
\r
756 // finally, go through the regular rules one at a time. We start
\r
757 // at the end of the list because we want to try matching the most
\r
758 // sigificant rule first (this helps ensure that we parse
\r
759 // "five thousand three hundred six" as
\r
760 // "(five thousand) (three hundred) (six)" rather than
\r
761 // "((five thousand three) hundred) (six)"). Skip rules whose
\r
762 // base values are higher than the upper bound (again, this helps
\r
763 // limit ambiguity by making sure the rules that match a rule's
\r
764 // are less significant than the rule containing the substitutions)/
\r
765 for (int i = rules.length - 1; i >= 0 && highWaterMark.getIndex() < text.length(); i--) {
\r
766 if (!isFractionRuleSet && rules[i].getBaseValue() >= upperBound) {
\r
770 tempResult = rules[i].doParse(text, parsePosition, isFractionRuleSet, upperBound);
\r
771 if (parsePosition.getIndex() > highWaterMark.getIndex()) {
\r
772 result = tempResult;
\r
773 highWaterMark.setIndex(parsePosition.getIndex());
\r
775 // commented out because the error-index API on ParsePosition isn't there in 1.1.x
\r
776 // if (parsePosition.getErrorIndex() > highWaterMark.getErrorIndex()) {
\r
777 // highWaterMark.setErrorIndex(parsePosition.getErrorIndex());
\r
779 parsePosition.setIndex(0);
\r
782 // finally, update the parse postion we were passed to point to the
\r
783 // first character we didn't use, and return the result that
\r
784 // cporresponds to that string of characters
\r
785 parsePosition.setIndex(highWaterMark.getIndex());
\r
786 // commented out because the error-index API on ParsePosition isn't there in 1.1.x
\r
787 // if (parsePosition.getIndex() == 0) {
\r
788 // parsePosition.setErrorIndex(highWaterMark.getErrorIndex());
\r