KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ibm > icu > text > NFRuleSet


1 /*
2  *******************************************************************************
3  * Copyright (C) 1996-2004, International Business Machines Corporation and *
4  * others. All Rights Reserved. *
5  *******************************************************************************
6  */

7 package com.ibm.icu.text;
8
9 import com.ibm.icu.impl.UCharacterProperty;
10 import com.ibm.icu.impl.Utility;
11
12 import java.text.*;
13 import java.util.Vector JavaDoc;
14
15 /**
16  * A collection of rules used by a RuleBasedNumberFormat to format and
17  * parse numbers. It is the responsibility of a RuleSet to select an
18  * appropriate rule for formatting a particular number and dispatch
19  * control to it, and to arbitrate between different rules when parsing
20  * a number.
21  */

22
23 final class NFRuleSet {
24     //-----------------------------------------------------------------------
25
// constants
26
//-----------------------------------------------------------------------
27

28     /**
29      * Puts a copyright in the .class file
30      */

31     private static final String JavaDoc copyrightNotice
32         = "Copyright \u00a91997-1998 IBM Corp. All rights reserved.";
33
34     //-----------------------------------------------------------------------
35
// data members
36
//-----------------------------------------------------------------------
37

38     /**
39      * The rule set's name
40      */

41     private String JavaDoc name;
42
43     /**
44      * The rule set's regular rules
45      */

46     private NFRule[] rules;
47
48     /**
49      * The rule set's negative-number rule
50      */

51     private NFRule negativeNumberRule = null;
52
53     /**
54      * The rule set's fraction rules: element 0 is the proper fraction
55      * (0.x) rule, element 1 is the improper fraction (x.x) rule, and
56      * element 2 is the master (x.0) rule.
57      */

58     private NFRule[] fractionRules = new NFRule[3];
59
60     /**
61      * True if the rule set is a fraction rule set. A fraction rule set
62      * is a rule set that is used to format the fractional part of a
63      * number. It is called from a >> substitution in another rule set's
64      * fraction rule, and is only called upon to format values between
65      * 0 and 1. A fraction rule set has different rule-selection
66      * behavior than a regular rule set.
67      */

68     private boolean isFractionRuleSet = false;
69     
70     /**
71      * Used to limit recursion for bad rule sets.
72      */

73     private int recursionCount = 0;
74
75     /**
76       * Limit of recursion.
77       */

78     private static final int RECURSION_LIMIT = 50;
79
80     //-----------------------------------------------------------------------
81
// construction
82
//-----------------------------------------------------------------------
83

84     /*
85      * Constructs a rule set.
86      * @param descriptions An array of Strings representing rule set
87      * descriptions. On exit, this rule set's entry in the array will
88      * have been stripped of its rule set name and any trailing whitespace.
89      * @param index The index into "descriptions" of the description
90      * for the rule to be constructed
91      */

92     public NFRuleSet(String JavaDoc[] descriptions, int index) throws IllegalArgumentException JavaDoc {
93         String JavaDoc description = descriptions[index];
94
95     if (description.length() == 0) {
96       throw new IllegalArgumentException JavaDoc("Empty rule set description");
97     }
98
99         // if the description begins with a rule set name (the rule set
100
// name can be omitted in formatter descriptions that consist
101
// of only one rule set), copy it out into our "name" member
102
// and delete it from the description
103
if (description.charAt(0) == '%') {
104             int pos = description.indexOf(':');
105             if (pos == -1) {
106                 throw new IllegalArgumentException JavaDoc("Rule set name doesn't end in colon");
107             } else {
108                 name = description.substring(0, pos);
109                 while (pos < description.length() && UCharacterProperty.isRuleWhiteSpace(description.
110                                 charAt(++pos))) {
111                 }
112                 description = description.substring(pos);
113                 descriptions[index] = description;
114             }
115
116         // if the description doesn't begin with a rule set name, its
117
// name is "%default"
118
} else {
119             name = "%default";
120         }
121
122         if (description.length() == 0) {
123             throw new IllegalArgumentException JavaDoc("Empty rule set description");
124         }
125
126         // all of the other members of NFRuleSet are initialized
127
// by parseRules()
128
}
129
130     /**
131      * Construct the subordinate data structures used by this object.
132      * This function is called by the RuleBasedNumberFormat constructor
133      * after all the rule sets have been created to actually parse
134      * the description and build rules from it. Since any rule set
135      * can refer to any other rule set, we have to have created all of
136      * them before we can create anything else.
137      * @param description The textual description of this rule set
138      * @param owner The formatter that owns this rule set
139      */

140     public void parseRules(String JavaDoc description,
141                            RuleBasedNumberFormat owner) {
142         // start by creating a Vector whose elements are Strings containing
143
// the descriptions of the rules (one rule per element). The rules
144
// are separated by semicolons (there's no escape facility: ALL
145
// semicolons are rule delimiters)
146
Vector JavaDoc ruleDescriptions = new Vector JavaDoc();
147
148         int oldP = 0;
149         int p = description.indexOf(';');
150         while (oldP != -1) {
151             if (p != -1) {
152                 ruleDescriptions.addElement(description.substring(oldP, p));
153                 oldP = p + 1;
154             } else {
155                 if (oldP < description.length()) {
156                     ruleDescriptions.addElement(description.substring(oldP));
157                 }
158                 oldP = p;
159             }
160             p = description.indexOf(';', p + 1);
161         }
162
163         // now go back through and build a vector of the rules themselves
164
// (the number of elements in the description list isn't necessarily
165
// the number of rules-- some descriptions may expend into two rules)
166
Vector JavaDoc tempRules = new Vector JavaDoc();
167
168         // we keep track of the rule before the one we're currently working
169
// on solely to support >>> substitutions
170
NFRule predecessor = null;
171         for (int i = 0; i < ruleDescriptions.size(); i++) {
172             // makeRules (a factory method on NFRule) will return either
173
// a single rule or an array of rules. Either way, add them
174
// to our rule vector
175
Object JavaDoc temp = NFRule.makeRules((String JavaDoc)ruleDescriptions.elementAt(i),
176                             this, predecessor, owner);
177
178             if (temp instanceof NFRule) {
179                 tempRules.addElement(temp);
180                 predecessor = (NFRule)temp;
181             }
182             else if (temp instanceof NFRule[]) {
183                 NFRule[] rulesToAdd = (NFRule[])temp;
184
185                 for (int j = 0; j < rulesToAdd.length; j++) {
186                     tempRules.addElement(rulesToAdd[j]);
187                     predecessor = rulesToAdd[j];
188                 }
189             }
190         }
191         // now we can bag the description list
192
ruleDescriptions = null;
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         // (this isn't a for loop because we might be deleting items from
201
// the vector-- we want to make sure we only increment i when
202
// we _didn't_ delete aything from the vector)
203
int i = 0;
204         while (i < tempRules.size()) {
205             NFRule rule = (NFRule)tempRules.elementAt(i);
206
207             switch ((int)rule.getBaseValue()) {
208                 // if the rule's base value is 0, fill in a default
209
// base value (this will be 1 plus the preceding
210
// rule's base value for regular rule sets, and the
211
// same as the preceding rule's base value in fraction
212
// rule sets)
213
case 0:
214                     rule.setBaseValue(defaultBaseValue);
215                     if (!isFractionRuleSet) {
216                         ++defaultBaseValue;
217                     }
218                     ++i;
219                     break;
220
221                 // if it's the negative-number rule, copy it into its own
222
// data member and delete it from the list
223
case NFRule.NEGATIVE_NUMBER_RULE:
224                     negativeNumberRule = rule;
225                     tempRules.removeElementAt(i);
226                     break;
227
228                 // if it's the improper fraction rule, copy it into the
229
// correct element of fractionRules
230
case NFRule.IMPROPER_FRACTION_RULE:
231                     fractionRules[0] = rule;
232                     tempRules.removeElementAt(i);
233                     break;
234
235                 // if it's the proper fraction rule, copy it into the
236
// correct element of fractionRules
237
case NFRule.PROPER_FRACTION_RULE:
238                     fractionRules[1] = rule;
239                     tempRules.removeElementAt(i);
240                     break;
241
242                 // if it's the master rule, copy it into the
243
// correct element of fractionRules
244
case NFRule.MASTER_RULE:
245                     fractionRules[2] = rule;
246                     tempRules.removeElementAt(i);
247                     break;
248
249                 // if it's a regular rule that already knows its base value,
250
// check to make sure the rules are in order, and update
251
// the default base value for the next rule
252
default:
253                     if (rule.getBaseValue() < defaultBaseValue) {
254                         throw new IllegalArgumentException JavaDoc("Rules are not in order, base: " +
255                                rule.getBaseValue() + " < " + defaultBaseValue);
256                     }
257                     defaultBaseValue = rule.getBaseValue();
258                     if (!isFractionRuleSet) {
259                         ++defaultBaseValue;
260                     }
261                     ++i;
262                     break;
263             }
264         }
265
266         // finally, we can copy the rules from the vector into a
267
// fixed-length array
268
rules = new NFRule[tempRules.size()];
269         tempRules.copyInto((Object JavaDoc[])rules);
270     }
271
272     /**
273      * Flags this rule set as a fraction rule set. This function is
274      * called during the construction process once we know this rule
275      * set is a fraction rule set. We don't know a rule set is a
276      * fraction rule set until we see it used somewhere. This function
277      * is not ad must not be called at any time other than during
278      * construction of a RuleBasedNumberFormat.
279      */

280     public void makeIntoFractionRuleSet() {
281         isFractionRuleSet = true;
282     }
283
284     //-----------------------------------------------------------------------
285
// boilerplate
286
//-----------------------------------------------------------------------
287

288     /**
289      * Compares two rule sets for equality.
290      * @param that The other rule set
291      * @return true if the two rule sets are functionally equivalent.
292      */

293     public boolean equals(Object JavaDoc that) {
294         // if different classes, they're not equal
295
if (!(that instanceof NFRuleSet)) {
296             return false;
297         } else {
298             // otherwise, compare the members one by one...
299
NFRuleSet that2 = (NFRuleSet)that;
300
301             if (!name.equals(that2.name)
302         || !Utility.objectEquals(negativeNumberRule, that2.negativeNumberRule)
303         || !Utility.objectEquals(fractionRules[0], that2.fractionRules[0])
304         || !Utility.objectEquals(fractionRules[1], that2.fractionRules[1])
305         || !Utility.objectEquals(fractionRules[2], that2.fractionRules[2])
306         || rules.length != that2.rules.length
307                 || isFractionRuleSet != that2.isFractionRuleSet) {
308
309                 return false;
310             }
311
312             // ...then compare the rule lists...
313
for (int i = 0; i < rules.length; i++) {
314                 if (!rules[i].equals(that2.rules[i])) {
315                     return false;
316                 }
317             }
318
319             // ...and if we make it here, tney're equal
320
return true;
321         }
322     }
323
324
325     /**
326      * Builds a textual representation of a rule set.
327      * @return A textual representation of a rule set. This won't
328      * necessarily be the same description that the rule set was
329      * constructed with, but it will produce the same results.
330      */

331     public String JavaDoc toString() {
332         StringBuffer JavaDoc result = new StringBuffer JavaDoc();
333
334         // the rule set name goes first...
335
result.append(name + ":\n");
336
337         // followed by the regular rules...
338
for (int i = 0; i < rules.length; i++) {
339             result.append(" " + rules[i].toString() + "\n");
340         }
341
342         // followed by the special rules (if they exist)
343
if (negativeNumberRule != null) {
344             result.append(" " + negativeNumberRule.toString() + "\n");
345         }
346         if (fractionRules[0] != null) {
347             result.append(" " + fractionRules[0].toString() + "\n");
348         }
349         if (fractionRules[1] != null) {
350             result.append(" " + fractionRules[1].toString() + "\n");
351         }
352         if (fractionRules[2] != null) {
353             result.append(" " + fractionRules[2].toString() + "\n");
354         }
355
356         return result.toString();
357     }
358
359     //-----------------------------------------------------------------------
360
// simple accessors
361
//-----------------------------------------------------------------------
362

363     /**
364      * Says whether this rule set is a fraction rule set.
365      * @return true if this rule is a fraction rule set; false if it isn't
366      */

367     public boolean isFractionSet() {
368         return isFractionRuleSet;
369     }
370
371     /**
372      * Returns the rule set's name
373      * @return The rule set's name
374      */

375     public String JavaDoc getName() {
376         return name;
377     }
378
379     /**
380      * Return true if the rule set is public.
381      * @return true if the rule set is public
382      */

383     public boolean isPublic() {
384     return !name.startsWith("%%");
385     }
386
387     //-----------------------------------------------------------------------
388
// formatting
389
//-----------------------------------------------------------------------
390

391     /**
392      * Formats a long. Selects an appropriate rule and dispatches
393      * control to it.
394      * @param number The number being formatted
395      * @param toInsertInto The string where the result is to be placed
396      * @param pos The position in toInsertInto where the result of
397      * this operation is to be inserted
398      */

399     public void format(long number, StringBuffer JavaDoc toInsertInto, int pos) {
400         NFRule applicableRule = findNormalRule(number);
401
402         if (++recursionCount >= RECURSION_LIMIT) {
403             recursionCount = 0;
404             throw new IllegalStateException JavaDoc("Recursion limit exceeded when applying ruleSet " + name);
405         }
406         applicableRule.doFormat(number, toInsertInto, pos);
407         --recursionCount;
408     }
409
410     /**
411      * Formats a double. Selects an appropriate rule and dispatches
412      * control to it.
413      * @param number The number being formatted
414      * @param toInsertInto The string where the result is to be placed
415      * @param pos The position in toInsertInto where the result of
416      * this operation is to be inserted
417      */

418     public void format(double number, StringBuffer JavaDoc toInsertInto, int pos) {
419         NFRule applicableRule = findRule(number);
420
421         if (++recursionCount >= RECURSION_LIMIT) {
422             recursionCount = 0;
423             throw new IllegalStateException JavaDoc("Recursion limit exceeded when applying ruleSet " + name);
424         }
425         applicableRule.doFormat(number, toInsertInto, pos);
426         --recursionCount;
427     }
428
429     /**
430      * Selects an apropriate rule for formatting the number.
431      * @param number The number being formatted.
432      * @return The rule that should be used to format it
433      */

434     private NFRule findRule(double number) {
435         // if this is a fraction rule set, use findFractionRuleSetRule()
436
if (isFractionRuleSet) {
437             return findFractionRuleSetRule(number);
438         }
439
440         // if the number is negative, return the negative number rule
441
// (if there isn't a negative-number rule, we pretend it's a
442
// positive number)
443
if (number < 0) {
444             if (negativeNumberRule != null) {
445                 return negativeNumberRule;
446             } else {
447                 number = -number;
448             }
449         }
450
451         // if the number isn't an integer, we use one f the fraction rules...
452
if (number != Math.floor(number)) {
453             // if the number is between 0 and 1, return the proper
454
// fraction rule
455
if (number < 1 && fractionRules[1] != null) {
456                 return fractionRules[1];
457             }
458
459             // otherwise, return the improper fraction rule
460
else if (fractionRules[0] != null) {
461                 return fractionRules[0];
462             }
463         }
464
465         // if there's a master rule, use it to format the number
466
if (fractionRules[2] != null) {
467             return fractionRules[2];
468
469         // and if we haven't yet returned a rule, use findNormalRule()
470
// to find the applicable rule
471
} else {
472             return findNormalRule((long)Math.round(number));
473         }
474     }
475
476     /**
477      * If the value passed to findRule() is a positive integer, findRule()
478      * uses this function to select the appropriate rule. The result will
479      * generally be the rule with the highest base value less than or equal
480      * to the number. There is one exception to this: If that rule has
481      * two substitutions and a base value that is not an even multiple of
482      * its divisor, and the number itself IS an even multiple of the rule's
483      * divisor, then the result will be the rule that preceded the original
484      * result in the rule list. (This behavior is known as the "rollback
485      * rule", and is used to handle optional text: a rule with optional
486      * text is represented internally as two rules, and the rollback rule
487      * selects appropriate between them. This avoids things like "two
488      * hundred zero".)
489      * @param number The number being formatted
490      * @return The rule to use to format this number
491      */

492     private NFRule findNormalRule(long number) {
493         // if this is a fraction rule set, use findFractionRuleSetRule()
494
// to find the rule (we should only go into this clause if the
495
// value is 0)
496
if (isFractionRuleSet) {
497             return findFractionRuleSetRule(number);
498         }
499
500         // if the number is negative, return the negative-number rule
501
// (if there isn't one, pretend the number is positive)
502
if (number < 0) {
503             if (negativeNumberRule != null) {
504                 return negativeNumberRule;
505             } else {
506                 number = -number;
507             }
508         }
509
510         // we have to repeat the preceding two checks, even though we
511
// do them in findRule(), because the version of format() that
512
// takes a long bypasses findRule() and goes straight to this
513
// function. This function does skip the fraction rules since
514
// we know the value is an integer (it also skips the master
515
// rule, since it's considered a fraction rule. Skipping the
516
// master rule in this function is also how we avoid infinite
517
// recursion)
518

519         // binary-search the rule list for the applicable rule
520
// (a rule is used for all values from its base value to
521
// the next rule's base value)
522
int lo = 0;
523         int hi = rules.length;
524     if (hi > 0) {
525         while (lo < hi) {
526         int mid = (lo + hi) / 2;
527         if (rules[mid].getBaseValue() == number) {
528             return rules[mid];
529         }
530         else if (rules[mid].getBaseValue() > number) {
531             hi = mid;
532         }
533         else {
534             lo = mid + 1;
535         }
536         }
537         if (hi == 0) { // bad rule set
538
throw new IllegalStateException JavaDoc("The rule set " + name + " cannot format the value " + number);
539         }
540         NFRule result = rules[hi - 1];
541
542         // use shouldRollBack() to see whether we need to invoke the
543
// rollback rule (see shouldRollBack()'s documentation for
544
// an explanation of the rollback rule). If we do, roll back
545
// one rule and return that one instead of the one we'd normally
546
// return
547
if (result.shouldRollBack(number)) {
548         if (hi == 1) { // bad rule set
549
throw new IllegalStateException JavaDoc("The rule set " + name + " cannot roll back from the rule '" +
550                             result + "'");
551         }
552         result = rules[hi - 2];
553         }
554         return result;
555     }
556     // else use the master rule
557
return fractionRules[2];
558     }
559
560     /**
561      * If this rule is a fraction rule set, this function is used by
562      * findRule() to select the most appropriate rule for formatting
563      * the number. Basically, the base value of each rule in the rule
564      * set is treated as the denominator of a fraction. Whichever
565      * denominator can produce the fraction closest in value to the
566      * number passed in is the result. If there's a tie, the earlier
567      * one in the list wins. (If there are two rules in a row with the
568      * same base value, the first one is used when the numerator of the
569      * fraction would be 1, and the second rule is used the rest of the
570      * time.
571      * @param number The number being formatted (which will always be
572      * a number between 0 and 1)
573      * @return The rule to use to format this number
574      */

575     private NFRule findFractionRuleSetRule(double number) {
576         // the obvious way to do this (multiply the value being formatted
577
// by each rule's base value until you get an integral result)
578
// doesn't work because of rounding error. This method is more
579
// accurate
580

581         // find the least common multiple of the rules' base values
582
// and multiply this by the number being formatted. This is
583
// all the precision we need, and we can do all of the rest
584
// of the math using integer arithmetic
585
long leastCommonMultiple = rules[0].getBaseValue();
586         for (int i = 1; i < rules.length; i++) {
587             leastCommonMultiple = lcm(leastCommonMultiple, rules[i].getBaseValue());
588         }
589         long numerator = (long)(Math.round(number * leastCommonMultiple));
590
591         // for each rule, do the following...
592
long tempDifference;
593         long difference = Long.MAX_VALUE;
594         int winner = 0;
595         for (int i = 0; i < rules.length; i++) {
596             // "numerator" is the numerator of the fraction is the
597
// denominator is the LCD. The numerator if the the rule's
598
// base value is the denomiator is "numerator" times the
599
// base value divided bythe LCD. Here we check to see if
600
// that's an integer, and if not, how close it is to being
601
// an integer.
602
tempDifference = numerator * rules[i].getBaseValue() % leastCommonMultiple;
603
604             // normalize the result of the above calculation: we want
605
// the numerator's distance from the CLOSEST multiple
606
// of the LCD
607
if (leastCommonMultiple - tempDifference < tempDifference) {
608                 tempDifference = leastCommonMultiple - tempDifference;
609             }
610
611             // if this is as close as we've come, keep track of how close
612
// that is, and the line number of the rule that did it. If
613
// we've scored a direct hit, we don't have to look at any more
614
// rules
615
if (tempDifference < difference) {
616                 difference = tempDifference;
617                 winner = i;
618                 if (difference == 0) {
619                     break;
620                 }
621             }
622         }
623
624         // if we have two successive rules that both have the winning base
625
// value, then the first one (the one we found above) is used if
626
// the numerator of the fraction is 1 and the second one is used if
627
// the numerator of the fraction is anything else (this lets us
628
// do things like "one third"/"two thirds" without haveing to define
629
// a whole bunch of extra rule sets)
630
if (winner + 1 < rules.length
631                 && rules[winner + 1].getBaseValue() == rules[winner].getBaseValue()) {
632             if (Math.round(number * rules[winner].getBaseValue()) < 1
633                     || Math.round(number * rules[winner].getBaseValue()) >= 2) {
634                 ++winner;
635             }
636         }
637
638         // finally, return the winning rule
639
return rules[winner];
640     }
641
642     /**
643      * Calculates the least common multiple of x and y.
644      */

645     private static long lcm(long x, long y) {
646         // binary gcd algorithm from Knuth, "The Art of Computer Programming,"
647
// vol. 2, 1st ed., pp. 298-299
648
long x1 = x;
649         long y1 = y;
650
651         int p2 = 0;
652         while ((x1 & 1) == 0 && (y1 & 1) == 0) {
653             ++p2;
654             x1 >>= 1;
655             y1 >>= 1;
656         }
657
658         long t;
659         if ((x1 & 1) == 1) {
660             t = -y1;
661         } else {
662             t = x1;
663         }
664
665         while (t != 0) {
666             while ((t & 1) == 0) {
667                 t >>= 1;
668             }
669             if (t > 0) {
670                 x1 = t;
671             } else {
672                 y1 = -t;
673             }
674             t = x1 - y1;
675         }
676         long gcd = x1 << p2;
677
678         // x * y == gcd(x, y) * lcm(x, y)
679
return x / gcd * y;
680     }
681
682     //-----------------------------------------------------------------------
683
// parsing
684
//-----------------------------------------------------------------------
685

686     /**
687      * Parses a string. Matches the string to be parsed against each
688      * of its rules (with a base value less than upperBound) and returns
689      * the value produced by the rule that matched the most charcters
690      * in the source string.
691      * @param text The string to parse
692      * @param parsePosition The initial position is ignored and assumed
693      * to be 0. On exit, this object has been updated to point to the
694      * first character position this rule set didn't consume.
695      * @param upperBound Limits the rules that can be allowed to match.
696      * Only rules whose base values are strictly less than upperBound
697      * are considered.
698      * @return The numerical result of parsing this string. This will
699      * be the matching rule's base value, composed appropriately with
700      * the results of matching any of its substitutions. The object
701      * will be an instance of Long if it's an integral value; otherwise,
702      * it will be an instance of Double. This function always returns
703      * a valid object: If nothing matched the input string at all,
704      * this function returns new Long(0), and the parse position is
705      * left unchanged.
706      */

707     public Number JavaDoc parse(String JavaDoc text, ParsePosition parsePosition, double upperBound) {
708         // try matching each rule in the rule set against the text being
709
// parsed. Whichever one matches the most characters is the one
710
// that determines the value we return.
711

712         ParsePosition highWaterMark = new ParsePosition(0);
713         Number JavaDoc result = new Long JavaDoc(0);
714         Number JavaDoc tempResult = null;
715
716         // dump out if there's no text to parse
717
if (text.length() == 0) {
718             return result;
719         }
720
721         // start by trying the nehative number rule (if there is one)
722
if (negativeNumberRule != null) {
723             tempResult = negativeNumberRule.doParse(text, parsePosition, false, upperBound);
724             if (parsePosition.getIndex() > highWaterMark.getIndex()) {
725                 result = tempResult;
726                 highWaterMark.setIndex(parsePosition.getIndex());
727             }
728 // commented out because the error-index API on ParsePosition isn't there in 1.1.x
729
// if (parsePosition.getErrorIndex() > highWaterMark.getErrorIndex()) {
730
// highWaterMark.setErrorIndex(parsePosition.getErrorIndex());
731
// }
732
parsePosition.setIndex(0);
733         }
734
735         // then try each of the fraction rules
736
for (int i = 0; i < 3; i++) {
737             if (fractionRules[i] != null) {
738                 tempResult = fractionRules[i].doParse(text, parsePosition, false, upperBound);
739                 if (parsePosition.getIndex() > highWaterMark.getIndex()) {
740                     result = tempResult;
741                     highWaterMark.setIndex(parsePosition.getIndex());
742                 }
743 // commented out because the error-index API on ParsePosition isn't there in 1.1.x
744
// if (parsePosition.getErrorIndex() > highWaterMark.getErrorIndex()) {
745
// highWaterMark.setErrorIndex(parsePosition.getErrorIndex());
746
// }
747
parsePosition.setIndex(0);
748             }
749         }
750
751         // finally, go through the regular rules one at a time. We start
752
// at the end of the list because we want to try matching the most
753
// sigificant rule first (this helps ensure that we parse
754
// "five thousand three hundred six" as
755
// "(five thousand) (three hundred) (six)" rather than
756
// "((five thousand three) hundred) (six)"). Skip rules whose
757
// base values are higher than the upper bound (again, this helps
758
// limit ambiguity by making sure the rules that match a rule's
759
// are less significant than the rule containing the substitutions)/
760
for (int i = rules.length - 1; i >= 0 && highWaterMark.getIndex() < text.length(); i--) {
761             if (!isFractionRuleSet && rules[i].getBaseValue() >= upperBound) {
762                 continue;
763             }
764
765             tempResult = rules[i].doParse(text, parsePosition, isFractionRuleSet, upperBound);
766             if (parsePosition.getIndex() > highWaterMark.getIndex()) {
767                 result = tempResult;
768                 highWaterMark.setIndex(parsePosition.getIndex());
769             }
770 // commented out because the error-index API on ParsePosition isn't there in 1.1.x
771
// if (parsePosition.getErrorIndex() > highWaterMark.getErrorIndex()) {
772
// highWaterMark.setErrorIndex(parsePosition.getErrorIndex());
773
// }
774
parsePosition.setIndex(0);
775         }
776
777         // finally, update the parse postion we were passed to point to the
778
// first character we didn't use, and return the result that
779
// cporresponds to that string of characters
780
parsePosition.setIndex(highWaterMark.getIndex());
781 // commented out because the error-index API on ParsePosition isn't there in 1.1.x
782
// if (parsePosition.getIndex() == 0) {
783
// parsePosition.setErrorIndex(highWaterMark.getErrorIndex());
784
// }
785

786         return result;
787     }
788 }
789
Popular Tags