]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/main/classes/core/src/com/ibm/icu/text/NFSubstitution.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / main / classes / core / src / com / ibm / icu / text / NFSubstitution.java
1 /*\r
2  *******************************************************************************\r
3  * Copyright (C) 1996-2010, International Business Machines Corporation and    *\r
4  * others. All Rights Reserved.                                                *\r
5  *******************************************************************************\r
6  */\r
7 package com.ibm.icu.text;\r
8 \r
9 import java.text.ParsePosition;\r
10 \r
11 //===================================================================\r
12 // NFSubstitution (abstract base class)\r
13 //===================================================================\r
14 \r
15 /**\r
16  * An abstract class defining protocol for substitutions.  A substitution\r
17  * is a section of a rule that inserts text into the rule's rule text\r
18  * based on some part of the number being formatted.\r
19  * @author Richard Gillam\r
20  */\r
21 abstract class NFSubstitution {\r
22     //-----------------------------------------------------------------------\r
23     // data members\r
24     //-----------------------------------------------------------------------\r
25 \r
26     /**\r
27      * The substitution's position in the rule text of the rule that owns it\r
28      */\r
29     int pos;\r
30 \r
31     /**\r
32      * The rule set this substitution uses to format its result, or null.\r
33      * (Either this or numberFormat has to be non-null.)\r
34      */\r
35     NFRuleSet ruleSet = null;\r
36 \r
37     /**\r
38      * The DecimalFormat this substitution uses to format its result,\r
39      * or null.  (Either this or ruleSet has to be non-null.)\r
40      */\r
41     DecimalFormat numberFormat = null;\r
42     \r
43     /**\r
44      * Link to the RBNF so that we can access its decimalFormat if need be.\r
45      */\r
46     RuleBasedNumberFormat rbnf = null;\r
47 \r
48     //-----------------------------------------------------------------------\r
49     // construction\r
50     //-----------------------------------------------------------------------\r
51 \r
52     /**\r
53      * Parses the description, creates the right kind of substitution,\r
54      * and initializes it based on the description.\r
55      * @param pos The substitution's position in the rule text of the\r
56      * rule that owns it.\r
57      * @param rule The rule containing this substitution\r
58      * @param rulePredecessor The rule preceding the one that contains\r
59      * this substitution in the rule set's rule list (this is used\r
60      * only for >>> substitutions).\r
61      * @param ruleSet The rule set containing the rule containing this\r
62      * substitution\r
63      * @param formatter The RuleBasedNumberFormat that ultimately owns\r
64      * this substitution\r
65      * @param description The description to parse to build the substitution\r
66      * (this is just the substring of the rule's description containing\r
67      * the substitution token itself)\r
68      * @return A new substitution constructed according to the description\r
69      */\r
70     public static NFSubstitution makeSubstitution(int pos,\r
71                                                   NFRule rule,\r
72                                                   NFRule rulePredecessor,\r
73                                                   NFRuleSet ruleSet,\r
74                                                   RuleBasedNumberFormat formatter,\r
75                                                   String description) {\r
76         // if the description is empty, return a NummSubstitution\r
77         if (description.length() == 0) {\r
78             return new NullSubstitution(pos, ruleSet, formatter, description);\r
79         }\r
80 \r
81         switch (description.charAt(0)) {\r
82             // if the description begins with '<'...\r
83         case '<':\r
84             // throw an exception if the rule is a negative number\r
85             // rule\r
86             ///CLOVER:OFF\r
87             // If you look at the call hierarchy of this method, the rule would\r
88             // never be directly modified by the user and therefore makes the\r
89             // following pointless unless the user changes the ruleset.\r
90             if (rule.getBaseValue() == NFRule.NEGATIVE_NUMBER_RULE) {\r
91                 throw new IllegalArgumentException("<< not allowed in negative-number rule");\r
92             }\r
93             ///CLOVER:ON\r
94 \r
95             // if the rule is a fraction rule, return an\r
96             // IntegralPartSubstitution\r
97             else if (rule.getBaseValue() == NFRule.IMPROPER_FRACTION_RULE\r
98                      || rule.getBaseValue() == NFRule.PROPER_FRACTION_RULE\r
99                      || rule.getBaseValue() == NFRule.MASTER_RULE) {\r
100                 return new IntegralPartSubstitution(pos, ruleSet, formatter, description);\r
101             }\r
102 \r
103             // if the rule set containing the rule is a fraction\r
104             // rule set, return a NumeratorSubstitution\r
105             else if (ruleSet.isFractionSet()) {\r
106                 return new NumeratorSubstitution(pos, rule.getBaseValue(),\r
107                                                  formatter.getDefaultRuleSet(), formatter, description);\r
108             }\r
109 \r
110             // otherwise, return a MultiplierSubstitution\r
111             else {\r
112                 return new MultiplierSubstitution(pos, rule.getDivisor(), ruleSet,\r
113                                                   formatter, description);\r
114             }\r
115 \r
116             // if the description begins with '>'...\r
117         case '>':\r
118             // if the rule is a negative-number rule, return\r
119             // an AbsoluteValueSubstitution\r
120             if (rule.getBaseValue() == NFRule.NEGATIVE_NUMBER_RULE) {\r
121                 return new AbsoluteValueSubstitution(pos, ruleSet, formatter, description);\r
122             }\r
123 \r
124             // if the rule is a fraction rule, return a\r
125             // FractionalPartSubstitution\r
126             else if (rule.getBaseValue() == NFRule.IMPROPER_FRACTION_RULE\r
127                      || rule.getBaseValue() == NFRule.PROPER_FRACTION_RULE\r
128                      || rule.getBaseValue() == NFRule.MASTER_RULE) {\r
129                 return new FractionalPartSubstitution(pos, ruleSet, formatter, description);\r
130             }\r
131 \r
132             // if the rule set owning the rule is a fraction rule set,\r
133             // throw an exception\r
134             ///CLOVER:OFF\r
135             // If you look at the call hierarchy of this method, the rule would\r
136             // never be directly modified by the user and therefore makes the\r
137             // following pointless unless the user changes the ruleset.\r
138             else if (ruleSet.isFractionSet()) {\r
139                 throw new IllegalArgumentException(">> not allowed in fraction rule set");\r
140             }\r
141             ///CLOVER:ON\r
142 \r
143             // otherwise, return a ModulusSubstitution\r
144             else {\r
145                 return new ModulusSubstitution(pos, rule.getDivisor(), rulePredecessor,\r
146                                                ruleSet, formatter, description);\r
147             }\r
148 \r
149             // if the description begins with '=', always return a\r
150             // SameValueSubstitution\r
151         case '=':\r
152             return new SameValueSubstitution(pos, ruleSet, formatter, description);\r
153 \r
154             // and if it's anything else, throw an exception\r
155             ///CLOVER:OFF\r
156             // If you look at the call hierarchy of this method, the rule would\r
157             // never be directly modified by the user and therefore makes the\r
158             // following pointless unless the user changes the ruleset.\r
159         default:\r
160             throw new IllegalArgumentException("Illegal substitution character");\r
161             ///CLOVER:ON\r
162         }\r
163     }\r
164 \r
165     /**\r
166      * Base constructor for substitutions.  This constructor sets up the\r
167      * fields which are common to all substitutions.\r
168      * @param pos The substitution's position in the owning rule's rule\r
169      * text\r
170      * @param ruleSet The rule set that owns this substitution\r
171      * @param formatter The RuleBasedNumberFormat that owns this substitution\r
172      * @param description The substitution descriptor (i.e., the text\r
173      * inside the token characters)\r
174      */\r
175     NFSubstitution(int pos,\r
176                    NFRuleSet ruleSet,\r
177                    RuleBasedNumberFormat formatter,\r
178                    String description) {\r
179         // initialize the substitution's position in its parent rule\r
180         this.pos = pos;\r
181         this.rbnf = formatter;\r
182 \r
183         // the description should begin and end with the same character.\r
184         // If it doesn't that's a syntax error.  Otherwise,\r
185         // makeSubstitution() was the only thing that needed to know\r
186         // about these characters, so strip them off\r
187         if (description.length() >= 2 && description.charAt(0) == description.charAt(\r
188                                                                                      description.length() - 1)) {\r
189             description = description.substring(1, description.length() - 1);\r
190         }\r
191         else if (description.length() != 0) {\r
192             throw new IllegalArgumentException("Illegal substitution syntax");\r
193         }\r
194 \r
195         // if the description was just two paired token characters\r
196         // (i.e., "<<" or ">>"), it uses the rule set it belongs to to\r
197         // format its result\r
198         if (description.length() == 0) {\r
199             this.ruleSet = ruleSet;\r
200         }\r
201 \r
202         // if the description contains a rule set name, that's the rule\r
203         // set we use to format the result: get a reference to the\r
204         // names rule set\r
205         else if (description.charAt(0) == '%') {\r
206             this.ruleSet = formatter.findRuleSet(description);\r
207         }\r
208 \r
209         // if the description begins with 0 or #, treat it as a\r
210         // DecimalFormat pattern, and initialize a DecimalFormat with\r
211         // that pattern (then set it to use the DecimalFormatSymbols\r
212         // belonging to our formatter)\r
213         else if (description.charAt(0) == '#' || description.charAt(0) == '0') {\r
214             this.numberFormat = new DecimalFormat(description);\r
215             this.numberFormat.setDecimalFormatSymbols(formatter.getDecimalFormatSymbols());\r
216         }\r
217 \r
218         // if the description is ">>>", this substitution bypasses the\r
219         // usual rule-search process and always uses the rule that precedes\r
220         // it in its own rule set's rule list (this is used for place-value\r
221         // notations: formats where you want to see a particular part of\r
222         // a number even when it's 0)\r
223         else if (description.charAt(0) == '>') {\r
224             this.ruleSet = ruleSet; // was null, thai rules added to control space\r
225             this.numberFormat = null;\r
226         }\r
227 \r
228         // and of the description is none of these things, it's a syntax error\r
229         else {\r
230             throw new IllegalArgumentException("Illegal substitution syntax");\r
231         }\r
232     }\r
233 \r
234     /**\r
235      * Set's the substitution's divisor.  Used by NFRule.setBaseValue().\r
236      * A no-op for all substitutions except multiplier and modulus\r
237      * substitutions.\r
238      * @param radix The radix of the divisor\r
239      * @param exponent The exponent of the divisor\r
240      */\r
241     public void setDivisor(int radix, int exponent) {\r
242         // a no-op for all substitutions except multiplier and modulus substitutions\r
243     }\r
244 \r
245     //-----------------------------------------------------------------------\r
246     // boilerplate\r
247     //-----------------------------------------------------------------------\r
248 \r
249     /**\r
250      * Compares two substitutions for equality\r
251      * @param that The substitution to compare this one to\r
252      * @return true if the two substitutions are functionally equivalent\r
253      */\r
254     public boolean equals(Object that) {\r
255         // compare class and all of the fields all substitutions have\r
256         // in common\r
257         if (this.getClass() == that.getClass()) {\r
258             NFSubstitution that2 = (NFSubstitution)that;\r
259 \r
260             return pos == that2.pos\r
261                 && (ruleSet == null ? that2.ruleSet == null : true) // can't compare tree structure, no .equals or recurse\r
262                 && (numberFormat == null ? (that2.numberFormat == null) : numberFormat.equals(that2.numberFormat));\r
263         }\r
264         return false;\r
265     }\r
266 \r
267     /**\r
268      * Returns a textual description of the substitution\r
269      * @return A textual description of the substitution.  This might\r
270      * not be identical to the description it was created from, but\r
271      * it'll produce the same result.\r
272      */\r
273     public String toString() {\r
274         // use tokenChar() to get the character at the beginning and\r
275         // end of the substitution token.  In between them will go\r
276         // either the name of the rule set it uses, or the pattern of\r
277         // the DecimalFormat it uses\r
278         if (ruleSet != null) {\r
279             return tokenChar() + ruleSet.getName() + tokenChar();\r
280         } else {\r
281             return tokenChar() + numberFormat.toPattern() + tokenChar();\r
282         }\r
283     }\r
284 \r
285     //-----------------------------------------------------------------------\r
286     // formatting\r
287     //-----------------------------------------------------------------------\r
288 \r
289     /**\r
290      * Performs a mathematical operation on the number, formats it using\r
291      * either ruleSet or decimalFormat, and inserts the result into\r
292      * toInsertInto.\r
293      * @param number The number being formatted.\r
294      * @param toInsertInto The string we insert the result into\r
295      * @param position The position in toInsertInto where the owning rule's\r
296      * rule text begins (this value is added to this substitution's\r
297      * position to determine exactly where to insert the new text)\r
298      */\r
299     public void doSubstitution(long number, StringBuffer toInsertInto, int position) {\r
300         if (ruleSet != null) {\r
301             // perform a transformation on the number that is dependent\r
302             // on the type of substitution this is, then just call its\r
303             // rule set's format() method to format the result\r
304             long numberToFormat = transformNumber(number);\r
305 \r
306             ruleSet.format(numberToFormat, toInsertInto, position + pos);\r
307         } else {\r
308             // or perform the transformation on the number (preserving\r
309             // the result's fractional part if the formatter it set\r
310             // to show it), then use that formatter's format() method\r
311             // to format the result\r
312             double numberToFormat = transformNumber((double)number);\r
313             if (numberFormat.getMaximumFractionDigits() == 0) {\r
314                 numberToFormat = Math.floor(numberToFormat);\r
315             }\r
316 \r
317             toInsertInto.insert(position + pos, numberFormat.format(numberToFormat));\r
318         }\r
319     }\r
320 \r
321     /**\r
322      * Performs a mathematical operation on the number, formats it using\r
323      * either ruleSet or decimalFormat, and inserts the result into\r
324      * toInsertInto.\r
325      * @param number The number being formatted.\r
326      * @param toInsertInto The string we insert the result into\r
327      * @param position The position in toInsertInto where the owning rule's\r
328      * rule text begins (this value is added to this substitution's\r
329      * position to determine exactly where to insert the new text)\r
330      */\r
331     public void doSubstitution(double number, StringBuffer toInsertInto, int position) {\r
332         // perform a transformation on the number being formatted that\r
333         // is dependent on the type of substitution this is\r
334         double numberToFormat = transformNumber(number);\r
335 \r
336         // if the result is an integer, from here on out we work in integer\r
337         // space (saving time and memory and preserving accuracy)\r
338         if (numberToFormat == Math.floor(numberToFormat) && ruleSet != null) {\r
339             ruleSet.format((long)numberToFormat, toInsertInto, position + pos);\r
340 \r
341             // if the result isn't an integer, then call either our rule set's\r
342             // format() method or our DecimalFormat's format() method to\r
343             // format the result\r
344         } else {\r
345             if (ruleSet != null) {\r
346                 ruleSet.format(numberToFormat, toInsertInto, position + pos);\r
347             } else {\r
348                 toInsertInto.insert(position + this.pos, numberFormat.format(numberToFormat));\r
349             }\r
350         }\r
351     }\r
352 \r
353     /**\r
354      * Subclasses override this function to perform some kind of\r
355      * mathematical operation on the number.  The result of this operation\r
356      * is formatted using the rule set or DecimalFormat that this\r
357      * substitution refers to, and the result is inserted into the result\r
358      * string.\r
359      * @param number The number being formatted\r
360      * @return The result of performing the opreration on the number\r
361      */\r
362     public abstract long transformNumber(long number);\r
363 \r
364     /**\r
365      * Subclasses override this function to perform some kind of\r
366      * mathematical operation on the number.  The result of this operation\r
367      * is formatted using the rule set or DecimalFormat that this\r
368      * substitution refers to, and the result is inserted into the result\r
369      * string.\r
370      * @param number The number being formatted\r
371      * @return The result of performing the opreration on the number\r
372      */\r
373     public abstract double transformNumber(double number);\r
374 \r
375     //-----------------------------------------------------------------------\r
376     // parsing\r
377     //-----------------------------------------------------------------------\r
378 \r
379     /**\r
380      * Parses a string using the rule set or DecimalFormat belonging\r
381      * to this substitution.  If there's a match, a mathematical\r
382      * operation (the inverse of the one used in formatting) is\r
383      * performed on the result of the parse and the value passed in\r
384      * and returned as the result.  The parse position is updated to\r
385      * point to the first unmatched character in the string.\r
386      * @param text The string to parse\r
387      * @param parsePosition On entry, ignored, but assumed to be 0.\r
388      * On exit, this is updated to point to the first unmatched\r
389      * character (or 0 if the substitution didn't match)\r
390      * @param baseValue A partial parse result that should be\r
391      * combined with the result of this parse\r
392      * @param upperBound When searching the rule set for a rule\r
393      * matching the string passed in, only rules with base values\r
394      * lower than this are considered\r
395      * @param lenientParse If true and matching against rules fails,\r
396      * the substitution will also try matching the text against\r
397      * numerals using a default-costructed NumberFormat.  If false,\r
398      * no extra work is done.  (This value is false whenever the\r
399      * formatter isn't in lenient-parse mode, but is also false\r
400      * under some conditions even when the formatter _is_ in\r
401      * lenient-parse mode.)\r
402      * @return If there's a match, this is the result of composing\r
403      * baseValue with whatever was returned from matching the\r
404      * characters.  This will be either a Long or a Double.  If there's\r
405      * no match this is new Long(0) (not null), and parsePosition\r
406      * is left unchanged.\r
407      */\r
408     public Number doParse(String text, ParsePosition parsePosition, double baseValue,\r
409                           double upperBound, boolean lenientParse) {\r
410         Number tempResult;\r
411 \r
412         // figure out the highest base value a rule can have and match\r
413         // the text being parsed (this varies according to the type of\r
414         // substitutions: multiplier, modulus, and numerator substitutions\r
415         // restrict the search to rules with base values lower than their\r
416         // own; same-value substitutions leave the upper bound wherever\r
417         // it was, and the others allow any rule to match\r
418         upperBound = calcUpperBound(upperBound);\r
419 \r
420         // use our rule set to parse the text.  If that fails and\r
421         // lenient parsing is enabled (this is always false if the\r
422         // formatter's lenient-parsing mode is off, but it may also\r
423         // be false even when the formatter's lenient-parse mode is\r
424         // on), then also try parsing the text using a default-\r
425         // constructed NumberFormat\r
426         if (ruleSet != null) {\r
427             tempResult = ruleSet.parse(text, parsePosition, upperBound);\r
428             if (lenientParse && !ruleSet.isFractionSet() && parsePosition.getIndex() == 0) {\r
429                 tempResult = rbnf.getDecimalFormat().parse(text, parsePosition);\r
430             }\r
431 \r
432             // ...or use our DecimalFormat to parse the text\r
433         } else {\r
434             tempResult = numberFormat.parse(text, parsePosition);\r
435         }\r
436 \r
437         // if the parse was successful, we've already advanced the caller's\r
438         // parse position (this is the one function that doesn't have one\r
439         // of its own).  Derive a parse result and return it as a Long,\r
440         // if possible, or a Double\r
441         if (parsePosition.getIndex() != 0) {\r
442             double result = tempResult.doubleValue();\r
443 \r
444             // composeRuleValue() produces a full parse result from\r
445             // the partial parse result passed to this function from\r
446             // the caller (this is either the owning rule's base value\r
447             // or the partial result obtained from composing the\r
448             // owning rule's base value with its other substitution's\r
449             // parse result) and the partial parse result obtained by\r
450             // matching the substitution (which will be the same value\r
451             // the caller would get by parsing just this part of the\r
452             // text with RuleBasedNumberFormat.parse() ).  How the two\r
453             // values are used to derive the full parse result depends\r
454             // on the types of substitutions: For a regular rule, the\r
455             // ultimate result is its multiplier substitution's result\r
456             // times the rule's divisor (or the rule's base value) plus\r
457             // the modulus substitution's result (which will actually\r
458             // supersede part of the rule's base value).  For a negative-\r
459             // number rule, the result is the negative of its substitution's\r
460             // result.  For a fraction rule, it's the sum of its two\r
461             // substitution results.  For a rule in a fraction rule set,\r
462             // it's the numerator substitution's result divided by\r
463             // the rule's base value.  Results from same-value substitutions\r
464             // propagate back upard, and null substitutions don't affect\r
465             // the result.\r
466             result = composeRuleValue(result, baseValue);\r
467             if (result == (long)result) {\r
468                 return new Long((long)result);\r
469             } else {\r
470                 return new Double(result);\r
471             }\r
472 \r
473             // if the parse was UNsuccessful, return 0\r
474         } else {\r
475             return tempResult;\r
476         }\r
477     }\r
478 \r
479     /**\r
480      * Derives a new value from the two values passed in.  The two values\r
481      * are typically either the base values of two rules (the one containing\r
482      * the substitution and the one matching the substitution) or partial\r
483      * parse results derived in some other way.  The operation is generally\r
484      * the inverse of the operation performed by transformNumber().\r
485      * @param newRuleValue The value produced by matching this substitution\r
486      * @param oldRuleValue The value that was passed to the substitution\r
487      * by the rule that owns it\r
488      * @return A third value derived from the other two, representing a\r
489      * partial parse result\r
490      */\r
491     public abstract double composeRuleValue(double newRuleValue, double oldRuleValue);\r
492 \r
493     /**\r
494      * Calculates an upper bound when searching for a rule that matches\r
495      * this substitution.  Rules with base values greater than or equal\r
496      * to upperBound are not considered.\r
497      * @param oldUpperBound The current upper-bound setting.  The new\r
498      * upper bound can't be any higher.\r
499      */\r
500     public abstract double calcUpperBound(double oldUpperBound);\r
501 \r
502     //-----------------------------------------------------------------------\r
503     // simple accessors\r
504     //-----------------------------------------------------------------------\r
505 \r
506     /**\r
507      * Returns the substitution's position in the rule that owns it.\r
508      * @return The substitution's position in the rule that owns it.\r
509      */\r
510     public final int getPos() {\r
511         return pos;\r
512     }\r
513 \r
514     /**\r
515      * Returns the character used in the textual representation of\r
516      * substitutions of this type.  Used by toString().\r
517      * @return This substitution's token character.\r
518      */\r
519     abstract char tokenChar();\r
520 \r
521     /**\r
522      * Returns true if this is a null substitution.  (We didn't do this\r
523      * with instanceof partially because it causes source files to\r
524      * proliferate and partially because we have to port this to C++.)\r
525      * @return true if this object is an instance of NullSubstitution\r
526      */\r
527     public boolean isNullSubstitution() {\r
528         return false;\r
529     }\r
530 \r
531     /**\r
532      * Returns true if this is a modulus substitution.  (We didn't do this\r
533      * with instanceof partially because it causes source files to\r
534      * proliferate and partially because we have to port this to C++.)\r
535      * @return true if this object is an instance of ModulusSubstitution\r
536      */\r
537     public boolean isModulusSubstitution() {\r
538         return false;\r
539     }\r
540 }\r
541 \r
542 //===================================================================\r
543 // SameValueSubstitution\r
544 //===================================================================\r
545 \r
546 /**\r
547  * A substitution that passes the value passed to it through unchanged.\r
548  * Represented by == in rule descriptions.\r
549  */\r
550 class SameValueSubstitution extends NFSubstitution {\r
551     //-----------------------------------------------------------------------\r
552     // construction\r
553     //-----------------------------------------------------------------------\r
554 \r
555     /**\r
556      * Constructs a SameValueSubstution.  This function just uses the\r
557      * superclass constructor, but it performs a check that this\r
558      * substitution doesn't call the rule set that owns it, since that\r
559      * would lead to infinite recursion.\r
560      */\r
561     SameValueSubstitution(int pos,\r
562                           NFRuleSet ruleSet,\r
563                           RuleBasedNumberFormat formatter,\r
564                           String description) {\r
565         super(pos, ruleSet, formatter, description);\r
566         if (description.equals("==")) {\r
567             throw new IllegalArgumentException("== is not a legal token");\r
568         }\r
569     }\r
570 \r
571     //-----------------------------------------------------------------------\r
572     // formatting\r
573     //-----------------------------------------------------------------------\r
574 \r
575     /**\r
576      * Returns "number" unchanged.\r
577      * @return "number"\r
578      */\r
579     public long transformNumber(long number) {\r
580         return number;\r
581     }\r
582 \r
583     /**\r
584      * Returns "number" unchanged.\r
585      * @return "number"\r
586      */\r
587     public double transformNumber(double number) {\r
588         return number;\r
589     }\r
590 \r
591     //-----------------------------------------------------------------------\r
592     // parsing\r
593     //-----------------------------------------------------------------------\r
594 \r
595     /**\r
596      * Returns newRuleValue and ignores oldRuleValue. (The value we got\r
597      * matching the substitution supersedes the value of the rule\r
598      * that owns the substitution.)\r
599      * @param newRuleValue The value resulting from matching the substituion\r
600      * @param oldRuleValue The value of the rule containing the\r
601      * substitution.\r
602      * @return newRuleValue\r
603      */\r
604     public double composeRuleValue(double newRuleValue, double oldRuleValue) {\r
605         return newRuleValue;\r
606     }\r
607 \r
608     /**\r
609      * SameValueSubstitution doesn't change the upper bound.\r
610      * @param oldUpperBound The current upper bound.\r
611      * @return oldUpperBound\r
612      */\r
613     public double calcUpperBound(double oldUpperBound) {\r
614         return oldUpperBound;\r
615     }\r
616 \r
617     //-----------------------------------------------------------------------\r
618     // simple accessor\r
619     //-----------------------------------------------------------------------\r
620 \r
621     /**\r
622      * The token character for a SameValueSubstitution is =.\r
623      * @return '='\r
624      */\r
625     char tokenChar() {\r
626         return '=';\r
627     }\r
628 }\r
629 \r
630 //===================================================================\r
631 // MultiplierSubstitution\r
632 //===================================================================\r
633 \r
634 /**\r
635  * A substitution that divides the number being formatted by the rule's\r
636  * divisor and formats the quotient.  Represented by &lt;&lt; in normal\r
637  * rules.\r
638  */\r
639 class MultiplierSubstitution extends NFSubstitution {\r
640     //-----------------------------------------------------------------------\r
641     // data members\r
642     //-----------------------------------------------------------------------\r
643 \r
644     /**\r
645      * The divisor of the rule that owns this substitution.\r
646      */\r
647     double divisor;\r
648 \r
649     //-----------------------------------------------------------------------\r
650     // construction\r
651     //-----------------------------------------------------------------------\r
652 \r
653     /**\r
654      * Constructs a MultiplierSubstitution.  This uses the superclass\r
655      * constructor to initialize most members, but this substitution\r
656      * also maintains its own copy of its rule's divisor.\r
657      * @param pos The substitution's position in its rule's rule text\r
658      * @param divisor The owning rule's divisor\r
659      * @ruleSet The ruleSet this substitution uses to format its result\r
660      * @formatter The formatter that owns this substitution\r
661      * @description The description describing this substitution\r
662      */\r
663     MultiplierSubstitution(int pos,\r
664                            double divisor,\r
665                            NFRuleSet ruleSet,\r
666                            RuleBasedNumberFormat formatter,\r
667                            String description) {\r
668         super(pos, ruleSet, formatter, description);\r
669 \r
670         // the owning rule's divisor affects the behavior of this\r
671         // substitution.  Rather than keeping a back-pointer to the\r
672         // rule, we keep a copy of the divisor\r
673         this.divisor = divisor;\r
674 \r
675     if (divisor == 0) { // this will cause recursion\r
676         throw new IllegalStateException("Substitution with bad divisor (" + divisor + ") " + description.substring(0, pos) + \r
677                      " | " + description.substring(pos));\r
678     }\r
679     }\r
680 \r
681     /**\r
682      * Sets the substitution's divisor based on the values passed in.\r
683      * @param radix The radix of the divisor.\r
684      * @param exponent The exponent of the divisor.\r
685      */\r
686     public void setDivisor(int radix, int exponent) {\r
687         divisor = Math.pow(radix, exponent);\r
688 \r
689     if (divisor == 0) {\r
690         throw new IllegalStateException("Substitution with divisor 0");\r
691     }\r
692     }\r
693 \r
694     //-----------------------------------------------------------------------\r
695     // boilerplate\r
696     //-----------------------------------------------------------------------\r
697 \r
698     /**\r
699      * Augments the superclass's equals() function by comparing divisors.\r
700      * @param that The other substitution\r
701      * @return true if the two substitutions are functionally equal\r
702      */\r
703     public boolean equals(Object that) {\r
704         if (super.equals(that)) {\r
705             MultiplierSubstitution that2 = (MultiplierSubstitution)that;\r
706 \r
707             return divisor == that2.divisor;\r
708         } else {\r
709             return false;\r
710         }\r
711     }\r
712 \r
713     //-----------------------------------------------------------------------\r
714     // formatting\r
715     //-----------------------------------------------------------------------\r
716 \r
717     /**\r
718      * Divides the number by the rule's divisor and returns the quotient.\r
719      * @param number The number being formatted.\r
720      * @return "number" divided by the rule's divisor\r
721      */\r
722     public long transformNumber(long number) {\r
723         return (long)Math.floor(number / divisor);\r
724     }\r
725 \r
726     /**\r
727      * Divides the number by the rule's divisor and returns the quotient.\r
728      * This is an integral quotient if we're filling in the substitution\r
729      * using another rule set, but it's the full quotient (integral and\r
730      * fractional parts) if we're filling in the substitution using\r
731      * a DecimalFormat.  (This allows things such as "1.2 million".)\r
732      * @param number The number being formatted\r
733      * @return "number" divided by the rule's divisor\r
734      */\r
735     public double transformNumber(double number) {\r
736         if (ruleSet == null) {\r
737             return number / divisor;\r
738         } else {\r
739             return Math.floor(number / divisor);\r
740         }\r
741     }\r
742 \r
743     //-----------------------------------------------------------------------\r
744     // parsing\r
745     //-----------------------------------------------------------------------\r
746 \r
747     /**\r
748      * Returns newRuleValue times the divisor.  Ignores oldRuleValue.\r
749      * (The result of matching a << substitution supersedes the base\r
750      * value of the rule that contains it.)\r
751      * @param newRuleValue The result of matching the substitution\r
752      * @param oldRuleValue The base value of the rule containing the\r
753      * substitution\r
754      * @return newRuleValue * divisor\r
755      */\r
756     public double composeRuleValue(double newRuleValue, double oldRuleValue) {\r
757         return newRuleValue * divisor;\r
758     }\r
759 \r
760     /**\r
761      * Sets the upper bound down to the rule's divisor.\r
762      * @param oldUpperBound Ignored.\r
763      * @return The rule's divisor.\r
764      */\r
765     public double calcUpperBound(double oldUpperBound) {\r
766         return divisor;\r
767     }\r
768 \r
769     //-----------------------------------------------------------------------\r
770     // simple accessor\r
771     //-----------------------------------------------------------------------\r
772 \r
773     /**\r
774      * The token character for a multiplier substitution is &lt;.\r
775      * @return '&lt;'\r
776      */\r
777     char tokenChar() {\r
778         return '<';\r
779     }\r
780 }\r
781 \r
782 //===================================================================\r
783 // ModulusSubstitution\r
784 //===================================================================\r
785 \r
786 /**\r
787  * A substitution that divides the number being formatted by the its rule's\r
788  * divisor and formats the remainder.  Represented by "&gt;&gt;" in a\r
789  * regular rule.\r
790  */\r
791 class ModulusSubstitution extends NFSubstitution {\r
792     //-----------------------------------------------------------------------\r
793     // data members\r
794     //-----------------------------------------------------------------------\r
795 \r
796     /**\r
797      * The divisor of the rule owning this substitution\r
798      */\r
799     double divisor;\r
800 \r
801     /**\r
802      * If this is a &gt;&gt;&gt; substitution, the rule to use to format\r
803      * the substitution value.  Otherwise, null.\r
804      */\r
805     NFRule ruleToUse;\r
806 \r
807     //-----------------------------------------------------------------------\r
808     // construction\r
809     //-----------------------------------------------------------------------\r
810 \r
811     /**\r
812      * Constructs a ModulusSubstution.  In addition to the inherited\r
813      * members, a ModulusSubstitution keeps track of the divisor of the\r
814      * rule that owns it, and may also keep a reference to the rule\r
815      * that precedes the rule containing this substitution in the rule\r
816      * set's rule list.\r
817      * @param pos The substitution's position in its rule's rule text\r
818      * @param divisor The divisor of the rule that owns this substitution\r
819      * @param rulePredecessor The rule that precedes this substitution's\r
820      * rule in its rule set's rule list\r
821      * @param formatter The RuleBasedNumberFormat owning this substitution\r
822      * @param description The description for this substitution\r
823      */\r
824     ModulusSubstitution(int pos,\r
825                         double divisor,\r
826                         NFRule rulePredecessor,\r
827                         NFRuleSet ruleSet,\r
828                         RuleBasedNumberFormat formatter,\r
829                         String description) {\r
830         super(pos, ruleSet, formatter, description);\r
831 \r
832         // the owning rule's divisor controls the behavior of this\r
833         // substitution: rather than keeping a backpointer to the rule,\r
834         // we keep a copy of the divisor\r
835         this.divisor = divisor;\r
836 \r
837     if (divisor == 0) { // this will cause recursion\r
838         throw new IllegalStateException("Substitution with bad divisor (" + divisor + ") "+ description.substring(0, pos) + \r
839                      " | " + description.substring(pos));\r
840     }\r
841 \r
842         // the >>> token doesn't alter how this substituion calculates the\r
843         // values it uses for formatting and parsing, but it changes\r
844         // what's done with that value after it's obtained: >>> short-\r
845         // circuits the rule-search process and goes straight to the\r
846         // specified rule to format the substitution value\r
847         if (description.equals(">>>")) {\r
848             ruleToUse = rulePredecessor;\r
849         } else {\r
850             ruleToUse = null;\r
851         }\r
852     }\r
853 \r
854     /**\r
855      * Makes the substitution's divisor conform to that of the rule\r
856      * that owns it.  Used when the divisor is determined after creation.\r
857      * @param radix The radix of the divsor.\r
858      * @param exponent The exponent of the divisor.\r
859      */\r
860     public void setDivisor(int radix, int exponent) {\r
861         divisor = Math.pow(radix, exponent);\r
862 \r
863     if (divisor == 0) { // this will cause recursion\r
864         throw new IllegalStateException("Substitution with bad divisor");\r
865     }\r
866     }\r
867 \r
868     //-----------------------------------------------------------------------\r
869     // boilerplate\r
870     //-----------------------------------------------------------------------\r
871 \r
872     /**\r
873      * Augments the inherited equals() function by comparing divisors and\r
874      * ruleToUse.\r
875      * @param that The other substitution\r
876      * @return true if the two substitutions are functionally equivalent\r
877      */\r
878     public boolean equals(Object that) {\r
879         if (super.equals(that)) {\r
880             ModulusSubstitution that2 = (ModulusSubstitution)that;\r
881 \r
882             return divisor == that2.divisor;\r
883         } else {\r
884             return false;\r
885         }\r
886     }\r
887 \r
888     //-----------------------------------------------------------------------\r
889     // formatting\r
890     //-----------------------------------------------------------------------\r
891 \r
892     /**\r
893      * If this is a &gt;&gt;&gt; substitution, use ruleToUse to fill in\r
894      * the substitution.  Otherwise, just use the superclass function.\r
895      * @param number The number being formatted\r
896      * @toInsertInto The string to insert the result of this substitution\r
897      * into\r
898      * @param position The position of the rule text in toInsertInto\r
899      */\r
900     public void doSubstitution(long number, StringBuffer toInsertInto, int position) {\r
901         // if this isn't a >>> substitution, just use the inherited version\r
902         // of this function (which uses either a rule set or a DecimalFormat\r
903         // to format its substitution value)\r
904         if (ruleToUse == null) {\r
905             super.doSubstitution(number, toInsertInto, position);\r
906 \r
907         // a >>> substitution goes straight to a particular rule to\r
908         // format the substitution value\r
909         } else {\r
910             long numberToFormat = transformNumber(number);\r
911             ruleToUse.doFormat(numberToFormat, toInsertInto, position + pos);\r
912         }\r
913     }\r
914 \r
915     /**\r
916      * If this is a &gt;&gt;&gt; substitution, use ruleToUse to fill in\r
917      * the substitution.  Otherwise, just use the superclass function.\r
918      * @param number The number being formatted\r
919      * @toInsertInto The string to insert the result of this substitution\r
920      * into\r
921      * @param position The position of the rule text in toInsertInto\r
922      */\r
923     public void doSubstitution(double number, StringBuffer toInsertInto, int position) {\r
924         // if this isn't a >>> substitution, just use the inherited version\r
925         // of this function (which uses either a rule set or a DecimalFormat\r
926         // to format its substitution value)\r
927         if (ruleToUse == null) {\r
928             super.doSubstitution(number, toInsertInto, position);\r
929 \r
930         // a >>> substitution goes straight to a particular rule to\r
931         // format the substitution value\r
932         } else {\r
933             double numberToFormat = transformNumber(number);\r
934 \r
935             ruleToUse.doFormat(numberToFormat, toInsertInto, position + pos);\r
936         }\r
937     }\r
938 \r
939     /**\r
940      * Divides the number being formatted by the rule's divisor and\r
941      * returns the remainder.\r
942      * @param number The number being formatted\r
943      * @return "number" mod divisor\r
944      */\r
945     public long transformNumber(long number) {\r
946         return (long)Math.floor(number % divisor);\r
947     }\r
948 \r
949     /**\r
950      * Divides the number being formatted by the rule's divisor and\r
951      * returns the remainder.\r
952      * @param number The number being formatted\r
953      * @return "number" mod divisor\r
954      */\r
955     public double transformNumber(double number) {\r
956         return Math.floor(number % divisor);\r
957     }\r
958 \r
959     //-----------------------------------------------------------------------\r
960     // parsing\r
961     //-----------------------------------------------------------------------\r
962 \r
963     /**\r
964      * If this is a &gt;&gt;&gt; substitution, match only against ruleToUse.\r
965      * Otherwise, use the superclass function.\r
966      * @param text The string to parse\r
967      * @param parsePosition Ignored on entry, updated on exit to point to\r
968      * the first unmatched character.\r
969      * @param baseValue The partial parse result prior to calling this\r
970      * routine.\r
971      */\r
972     public Number doParse(String text, ParsePosition parsePosition, double baseValue,\r
973                         double upperBound, boolean lenientParse) {\r
974         // if this isn't a >>> substitution, we can just use the\r
975         // inherited parse() routine to do the parsing\r
976         if (ruleToUse == null) {\r
977             return super.doParse(text, parsePosition, baseValue, upperBound, lenientParse);\r
978 \r
979         // but if it IS a >>> substitution, we have to do it here: we\r
980         // use the specific rule's doParse() method, and then we have to\r
981         // do some of the other work of NFRuleSet.parse()\r
982         } else {\r
983             Number tempResult = ruleToUse.doParse(text, parsePosition, false, upperBound);\r
984 \r
985             if (parsePosition.getIndex() != 0) {\r
986                 double result = tempResult.doubleValue();\r
987 \r
988                 result = composeRuleValue(result, baseValue);\r
989                 if (result == (long)result) {\r
990                     return new Long((long)result);\r
991                 } else {\r
992                     return new Double(result);\r
993                 }\r
994             } else {\r
995                 return tempResult;\r
996             }\r
997         }\r
998     }\r
999 \r
1000     /**\r
1001      * Returns the highest multiple of the rule's divisor that its less\r
1002      * than or equal to oldRuleValue, plus newRuleValue.  (The result\r
1003      * is the sum of the result of parsing the substitution plus the\r
1004      * base valueof the rule containing the substitution, but if the\r
1005      * owning rule's base value isn't an even multiple of its divisor,\r
1006      * we have to round it down to a multiple of the divisor, or we\r
1007      * get unwanted digits in the result.)\r
1008      * @param newRuleValue The result of parsing the substitution\r
1009      * @param oldRuleValue The base value of the rule containing the\r
1010      * substitution\r
1011      * @return (oldRuleValue - (oldRuleValue % divisor)) + newRuleValue\r
1012      */\r
1013     public double composeRuleValue(double newRuleValue, double oldRuleValue) {\r
1014         return (oldRuleValue - (oldRuleValue % divisor)) + newRuleValue;\r
1015     }\r
1016 \r
1017     /**\r
1018      * Sets the upper bound down to the owning rule's divisor\r
1019      * @param oldUpperBound Ignored\r
1020      * @return The owning rule's dvisor\r
1021      */\r
1022     public double calcUpperBound(double oldUpperBound) {\r
1023         return divisor;\r
1024     }\r
1025 \r
1026     //-----------------------------------------------------------------------\r
1027     // simple accessors\r
1028     //-----------------------------------------------------------------------\r
1029 \r
1030     /**\r
1031      * Returns true.  This _is_ a ModulusSubstitution.\r
1032      * @return true\r
1033      */\r
1034     public boolean isModulusSubstitution() {\r
1035         return true;\r
1036     }\r
1037 \r
1038     /**\r
1039      * The token character of a ModulusSubstitution is &gt;.\r
1040      * @return '&gt;'\r
1041      */\r
1042     char tokenChar() {\r
1043         return '>';\r
1044     }\r
1045 }\r
1046 \r
1047 //===================================================================\r
1048 // IntegralPartSubstitution\r
1049 //===================================================================\r
1050 \r
1051 /**\r
1052  * A substitution that formats the number's integral part.  This is\r
1053  * represented by &lt;&lt; in a fraction rule.\r
1054  */\r
1055 class IntegralPartSubstitution extends NFSubstitution {\r
1056     //-----------------------------------------------------------------------\r
1057     // construction\r
1058     //-----------------------------------------------------------------------\r
1059 \r
1060     /**\r
1061      * Constructs an IntegralPartSubstitution.  This just calls\r
1062      * the superclass constructor.\r
1063      */\r
1064     IntegralPartSubstitution(int pos,\r
1065                              NFRuleSet ruleSet,\r
1066                              RuleBasedNumberFormat formatter,\r
1067                              String description) {\r
1068         super(pos, ruleSet, formatter, description);\r
1069     }\r
1070 \r
1071     //-----------------------------------------------------------------------\r
1072     // formatting\r
1073     //-----------------------------------------------------------------------\r
1074 \r
1075     /**\r
1076      * Returns the number's integral part. (For a long, that's just the\r
1077      * number unchanged.)\r
1078      * @param number The number being formatted\r
1079      * @return "number" unchanged\r
1080      */\r
1081     public long transformNumber(long number) {\r
1082         return number;\r
1083     }\r
1084 \r
1085     /**\r
1086      * Returns the number's integral part.\r
1087      * @param number The integral part of the number being formatted\r
1088      * @return floor(number)\r
1089      */\r
1090     public double transformNumber(double number) {\r
1091         return Math.floor(number);\r
1092     }\r
1093 \r
1094     //-----------------------------------------------------------------------\r
1095     // parsing\r
1096     //-----------------------------------------------------------------------\r
1097 \r
1098     /**\r
1099      * Returns the sum of the result of parsing the substitution and the\r
1100      * owning rule's base value.  (The owning rule, at best, has an\r
1101      * integral-part substitution and a fractional-part substitution,\r
1102      * so we can safely just add them.)\r
1103      * @param newRuleValue The result of matching the substitution\r
1104      * @param oldRuleValue The partial result of the parse prior to\r
1105      * calling this function\r
1106      * @return oldRuleValue + newRuleValue\r
1107      */\r
1108     public double composeRuleValue(double newRuleValue, double oldRuleValue) {\r
1109         return newRuleValue + oldRuleValue;\r
1110     }\r
1111 \r
1112     /**\r
1113      * An IntegralPartSubstitution sets the upper bound back up so all\r
1114      * potentially matching rules are considered.\r
1115      * @param oldUpperBound Ignored\r
1116      * @return Double.MAX_VALUE\r
1117      */\r
1118     public double calcUpperBound(double oldUpperBound) {\r
1119         return Double.MAX_VALUE;\r
1120     }\r
1121 \r
1122     //-----------------------------------------------------------------------\r
1123     // simple accessor\r
1124     //-----------------------------------------------------------------------\r
1125 \r
1126     /**\r
1127      * An IntegralPartSubstitution's token character is &lt;\r
1128      * @return '&lt;'\r
1129      */\r
1130     char tokenChar() {\r
1131         return '<';\r
1132     }\r
1133 }\r
1134 \r
1135 //===================================================================\r
1136 // FractionalPartSubstitution\r
1137 //===================================================================\r
1138 \r
1139 /**\r
1140  * A substitution that formats the fractional part of a number.  This is\r
1141  * represented by &gt;&gt; in a fraction rule.\r
1142  */\r
1143 class FractionalPartSubstitution extends NFSubstitution {\r
1144     //-----------------------------------------------------------------------\r
1145     // data members\r
1146     //-----------------------------------------------------------------------\r
1147 \r
1148     /**\r
1149      * true if this substitution should have the default "by digits"\r
1150      * behavior, false otherwise\r
1151      */\r
1152     private boolean byDigits = false;\r
1153 \r
1154     /**\r
1155      * true if we automatically insert spaces to separate names of digits\r
1156      * set to false by '>>>' in fraction rules, used by Thai.\r
1157      */\r
1158     private boolean useSpaces = true;\r
1159 \r
1160     /*\r
1161      * The largest number of digits after the decimal point that this\r
1162      * object will show in "by digits" mode\r
1163      */\r
1164     //private static final int MAXDECIMALDIGITS = 18; // 8\r
1165 \r
1166     //-----------------------------------------------------------------------\r
1167     // construction\r
1168     //-----------------------------------------------------------------------\r
1169 \r
1170     /**\r
1171      * Constructs a FractionalPartSubstitution.  This object keeps a flag\r
1172      * telling whether it should format by digits or not.  In addition,\r
1173      * it marks the rule set it calls (if any) as a fraction rule set.\r
1174      */\r
1175     FractionalPartSubstitution(int pos,\r
1176                                NFRuleSet ruleSet,\r
1177                                RuleBasedNumberFormat formatter,\r
1178                                String description) {\r
1179         super(pos, ruleSet, formatter, description);\r
1180 //    boolean chevron = description.startsWith(">>") || ruleSet == this.ruleSet;\r
1181 //      if (chevron || ruleSet == this.ruleSet) {\r
1182 \r
1183         if (description.equals(">>") || description.equals(">>>") || ruleSet == this.ruleSet) {\r
1184             byDigits = true;\r
1185             if (description.equals(">>>")) {\r
1186               useSpaces = false;\r
1187             }\r
1188         } else {\r
1189             this.ruleSet.makeIntoFractionRuleSet();\r
1190         }\r
1191     }\r
1192 \r
1193     //-----------------------------------------------------------------------\r
1194     // formatting\r
1195     //-----------------------------------------------------------------------\r
1196 \r
1197     /**\r
1198      * If in "by digits" mode, fills in the substitution one decimal digit\r
1199      * at a time using the rule set containing this substitution.\r
1200      * Otherwise, uses the superclass function.\r
1201      * @param number The number being formatted\r
1202      * @param toInsertInto The string to insert the result of formatting\r
1203      * the substitution into\r
1204      * @param position The position of the owning rule's rule text in\r
1205      * toInsertInto\r
1206      */\r
1207     public void doSubstitution(double number, StringBuffer toInsertInto, int position) {\r
1208         // if we're not in "byDigits" mode, just use the inherited\r
1209         // doSubstitution() routine\r
1210         if (!byDigits) {\r
1211             super.doSubstitution(number, toInsertInto, position);\r
1212 \r
1213         // if we're in "byDigits" mode, transform the value into an integer\r
1214         // by moving the decimal point eight places to the right and\r
1215         // pulling digits off the right one at a time, formatting each digit\r
1216         // as an integer using this substitution's owning rule set\r
1217         // (this is slower, but more accurate, than doing it from the\r
1218         // other end)\r
1219         } else {\r
1220 //              int numberToFormat = (int)Math.round(transformNumber(number) * Math.pow(\r
1221 //                              10, MAXDECIMALDIGITS));\r
1222 //        long numberToFormat = (long)Math.round(transformNumber(number) * Math.pow(10, MAXDECIMALDIGITS));\r
1223 \r
1224         // just print to string and then use that\r
1225         DigitList dl = new DigitList();\r
1226         dl.set(number, 20, true);\r
1227 \r
1228             // this flag keeps us from formatting trailing zeros.  It starts\r
1229             // out false because we're pulling from the right, and switches\r
1230             // to true the first time we encounter a non-zero digit\r
1231 //              boolean doZeros = false;\r
1232 //          System.out.println("class: " + getClass().getName());\r
1233 //          System.out.println("number: " + number + " transformed: " + transformNumber(number));\r
1234 //          System.out.println("formatting " + numberToFormat);\r
1235 //              for (int i = 0; i < MAXDECIMALDIGITS; i++) {\r
1236 //                  int digit = (int)(numberToFormat % 10);\r
1237 //          System.out.println("   #: '" + numberToFormat + "'" + " digit '" + digit + "'");\r
1238 //                  if (digit != 0 || doZeros) {\r
1239 //                      if (doZeros && useSpaces) {\r
1240 //                          toInsertInto.insert(pos + this.pos, ' ');\r
1241 //                      }\r
1242 //                      doZeros = true;\r
1243 //                      ruleSet.format(digit, toInsertInto, pos + this.pos);\r
1244 //                  }\r
1245 //                  numberToFormat /= 10;\r
1246 //              }\r
1247 \r
1248         boolean pad = false;\r
1249         while (dl.count > Math.max(0, dl.decimalAt)) {\r
1250         if (pad && useSpaces) {\r
1251             toInsertInto.insert(position + pos, ' ');\r
1252         } else {\r
1253             pad = true;\r
1254         }\r
1255         ruleSet.format(dl.digits[--dl.count] - '0', toInsertInto, position + pos);\r
1256         }\r
1257         while (dl.decimalAt < 0) {\r
1258         if (pad && useSpaces) {\r
1259             toInsertInto.insert(position + pos, ' ');\r
1260         } else {\r
1261             pad = true;\r
1262         }\r
1263         ruleSet.format(0, toInsertInto, position + pos);\r
1264         ++dl.decimalAt;\r
1265         }\r
1266     }\r
1267     }\r
1268 \r
1269     /**\r
1270      * Returns the fractional part of the number, which will always be\r
1271      * zero if it's a long.\r
1272      * @param number The number being formatted\r
1273      * @return 0\r
1274      */\r
1275     public long transformNumber(long number) {\r
1276         return 0;\r
1277     }\r
1278 \r
1279     /**\r
1280      * Returns the fractional part of the number.\r
1281      * @param number The number being formatted.\r
1282      * @return number - floor(number)\r
1283      */\r
1284     public double transformNumber(double number) {\r
1285         return number - Math.floor(number);\r
1286     }\r
1287 \r
1288     //-----------------------------------------------------------------------\r
1289     // parsing\r
1290     //-----------------------------------------------------------------------\r
1291 \r
1292     /**\r
1293      * If in "by digits" mode, parses the string as if it were a string\r
1294      * of individual digits; otherwise, uses the superclass function.\r
1295      * @param text The string to parse\r
1296      * @param parsePosition Ignored on entry, but updated on exit to point\r
1297      * to the first unmatched character\r
1298      * @param baseValue The partial parse result prior to entering this\r
1299      * function\r
1300      * @param upperBound Only consider rules with base values lower than\r
1301      * this when filling in the substitution\r
1302      * @param lenientParse If true, try matching the text as numerals if\r
1303      * matching as words doesn't work\r
1304      * @return If the match was successful, the current partial parse\r
1305      * result; otherwise new Long(0).  The result is either a Long or\r
1306      * a Double.\r
1307      */\r
1308     public Number doParse(String text, ParsePosition parsePosition, double baseValue,\r
1309                         double upperBound, boolean lenientParse) {\r
1310         // if we're not in byDigits mode, we can just use the inherited\r
1311         // doParse()\r
1312         if (!byDigits) {\r
1313             return super.doParse(text, parsePosition, baseValue, 0, lenientParse);\r
1314 \r
1315         // if we ARE in byDigits mode, parse the text one digit at a time\r
1316         // using this substitution's owning rule set (we do this by setting\r
1317         // upperBound to 10 when calling doParse() ) until we reach\r
1318         // nonmatching text\r
1319         } else {\r
1320             String workText = text;\r
1321             ParsePosition workPos = new ParsePosition(1);\r
1322             double result = 0;\r
1323         int digit;\r
1324 //              double p10 = 0.1;\r
1325 \r
1326 //              while (workText.length() > 0 && workPos.getIndex() != 0) {\r
1327 //                  workPos.setIndex(0);\r
1328 //                  digit = ruleSet.parse(workText, workPos, 10).intValue();\r
1329 //                  if (lenientParse && workPos.getIndex() == 0) {\r
1330 //                      digit = NumberFormat.getInstance().parse(workText, workPos).intValue();\r
1331 //                  }\r
1332 \r
1333 //                  if (workPos.getIndex() != 0) {\r
1334 //                      result += digit * p10;\r
1335 //                      p10 /= 10;\r
1336 //                      parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex());\r
1337 //                      workText = workText.substring(workPos.getIndex());\r
1338 //                      while (workText.length() > 0 && workText.charAt(0) == ' ') {\r
1339 //                          workText = workText.substring(1);\r
1340 //                          parsePosition.setIndex(parsePosition.getIndex() + 1);\r
1341 //                      }\r
1342 //                  }\r
1343 //              }\r
1344 \r
1345 \r
1346         DigitList dl = new DigitList();\r
1347             while (workText.length() > 0 && workPos.getIndex() != 0) {\r
1348                 workPos.setIndex(0);\r
1349                 digit = ruleSet.parse(workText, workPos, 10).intValue();\r
1350                 if (lenientParse && workPos.getIndex() == 0) {\r
1351                     Number n = rbnf.getDecimalFormat().parse(workText, workPos);\r
1352                     if (n != null) {\r
1353                         digit = n.intValue();\r
1354                     }\r
1355                 }\r
1356 \r
1357                 if (workPos.getIndex() != 0) {\r
1358             dl.append('0'+digit);\r
1359 \r
1360                     parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex());\r
1361                     workText = workText.substring(workPos.getIndex());\r
1362                     while (workText.length() > 0 && workText.charAt(0) == ' ') {\r
1363                         workText = workText.substring(1);\r
1364                         parsePosition.setIndex(parsePosition.getIndex() + 1);\r
1365                     }\r
1366                 }\r
1367             }\r
1368         result = dl.count == 0 ? 0 : dl.getDouble();\r
1369 \r
1370             result = composeRuleValue(result, baseValue);\r
1371             return new Double(result);\r
1372         }\r
1373     }\r
1374 \r
1375     /**\r
1376      * Returns the sum of the two partial parse results.\r
1377      * @param newRuleValue The result of parsing the substitution\r
1378      * @param oldRuleValue The partial parse result prior to calling\r
1379      * this function\r
1380      * @return newRuleValue + oldRuleValue\r
1381      */\r
1382     public double composeRuleValue(double newRuleValue, double oldRuleValue) {\r
1383         return newRuleValue + oldRuleValue;\r
1384     }\r
1385 \r
1386     /**\r
1387      * Not used.\r
1388      */\r
1389     public double calcUpperBound(double oldUpperBound) {\r
1390         return 0;   // this value is ignored\r
1391     }\r
1392 \r
1393     //-----------------------------------------------------------------------\r
1394     // simple accessor\r
1395     //-----------------------------------------------------------------------\r
1396 \r
1397     /**\r
1398      * The token character for a FractionalPartSubstitution is &gt;.\r
1399      * @return '&gt;'\r
1400      */\r
1401     char tokenChar() {\r
1402         return '>';\r
1403     }\r
1404 }\r
1405 \r
1406 //===================================================================\r
1407 // AbsoluteValueSubstitution\r
1408 //===================================================================\r
1409 \r
1410  /**\r
1411   * A substitution that formats the absolute value of the number.\r
1412   * This substition is represented by &gt;&gt; in a negative-number rule.\r
1413   */\r
1414 class AbsoluteValueSubstitution extends NFSubstitution {\r
1415     //-----------------------------------------------------------------------\r
1416     // construction\r
1417     //-----------------------------------------------------------------------\r
1418 \r
1419     /**\r
1420      * Constructs an AbsoluteValueSubstitution.  This just uses the\r
1421      * superclass constructor.\r
1422      */\r
1423     AbsoluteValueSubstitution(int pos,\r
1424                               NFRuleSet ruleSet,\r
1425                               RuleBasedNumberFormat formatter,\r
1426                               String description) {\r
1427         super(pos, ruleSet, formatter, description);\r
1428     }\r
1429 \r
1430     //-----------------------------------------------------------------------\r
1431     // formatting\r
1432     //-----------------------------------------------------------------------\r
1433 \r
1434     /**\r
1435      * Returns the absolute value of the number.\r
1436      * @param number The number being formatted.\r
1437      * @return abs(number)\r
1438      */\r
1439     public long transformNumber(long number) {\r
1440         return Math.abs(number);\r
1441     }\r
1442 \r
1443     /**\r
1444      * Returns the absolute value of the number.\r
1445      * @param number The number being formatted.\r
1446      * @return abs(number)\r
1447      */\r
1448     public double transformNumber(double number) {\r
1449         return Math.abs(number);\r
1450     }\r
1451 \r
1452     //-----------------------------------------------------------------------\r
1453     // parsing\r
1454     //-----------------------------------------------------------------------\r
1455 \r
1456     /**\r
1457      * Returns the addtive inverse of the result of parsing the\r
1458      * substitution (this supersedes the earlier partial result)\r
1459      * @param newRuleValue The result of parsing the substitution\r
1460      * @param oldRuleValue The partial parse result prior to calling\r
1461      * this function\r
1462      * @return -newRuleValue\r
1463      */\r
1464     public double composeRuleValue(double newRuleValue, double oldRuleValue) {\r
1465         return -newRuleValue;\r
1466     }\r
1467 \r
1468     /**\r
1469      * Sets the upper bound beck up to consider all rules\r
1470      * @param oldUpperBound Ignored.\r
1471      * @return Double.MAX_VALUE\r
1472      */\r
1473     public double calcUpperBound(double oldUpperBound) {\r
1474         return Double.MAX_VALUE;\r
1475     }\r
1476 \r
1477     //-----------------------------------------------------------------------\r
1478     // simple accessor\r
1479     //-----------------------------------------------------------------------\r
1480 \r
1481     /**\r
1482      * The token character for an AbsoluteValueSubstitution is &gt;\r
1483      * @return '&gt;'\r
1484      */\r
1485     char tokenChar() {\r
1486         return '>';\r
1487     }\r
1488 }\r
1489 \r
1490 //===================================================================\r
1491 // NumeratorSubstitution\r
1492 //===================================================================\r
1493 \r
1494 /**\r
1495  * A substitution that multiplies the number being formatted (which is\r
1496  * between 0 and 1) by the base value of the rule that owns it and\r
1497  * formats the result.  It is represented by &lt;&lt; in the rules\r
1498  * in a fraction rule set.\r
1499  */\r
1500 class NumeratorSubstitution extends NFSubstitution {\r
1501     //-----------------------------------------------------------------------\r
1502     // data members\r
1503     //-----------------------------------------------------------------------\r
1504 \r
1505     /**\r
1506      * The denominator of the fraction we're finding the numerator for.\r
1507      * (The base value of the rule that owns this substitution.)\r
1508      */\r
1509     double denominator;\r
1510 \r
1511     /**\r
1512      * True if we format leading zeros (this is a hack for Hebrew spellout)\r
1513      */\r
1514     boolean withZeros;\r
1515 \r
1516     //-----------------------------------------------------------------------\r
1517     // construction\r
1518     //-----------------------------------------------------------------------\r
1519 \r
1520     /**\r
1521      * Constructs a NumberatorSubstitution.  In addition to the inherited\r
1522      * fields, a NumeratorSubstitution keeps track of a denominator, which\r
1523      * is merely the base value of the rule that owns it.\r
1524      */\r
1525     NumeratorSubstitution(int pos,\r
1526                           double denominator,\r
1527                           NFRuleSet ruleSet,\r
1528                           RuleBasedNumberFormat formatter,\r
1529                           String description) {\r
1530         super(pos, ruleSet, formatter, fixdesc(description));\r
1531 \r
1532         // this substitution's behavior depends on the rule's base value\r
1533         // Rather than keeping a backpointer to the rule, we copy its\r
1534         // base value here\r
1535         this.denominator = denominator;\r
1536         \r
1537         this.withZeros = description.endsWith("<<");\r
1538     }\r
1539 \r
1540     static String fixdesc(String description) {\r
1541         return description.endsWith("<<") \r
1542             ? description.substring(0,description.length()-1) \r
1543             : description;\r
1544     }\r
1545 \r
1546     //-----------------------------------------------------------------------\r
1547     // boilerplate\r
1548     //-----------------------------------------------------------------------\r
1549 \r
1550     /**\r
1551      * Tests two NumeratorSubstitutions for equality\r
1552      * @param that The other NumeratorSubstitution\r
1553      * @return true if the two objects are functionally equivalent\r
1554      */\r
1555     public boolean equals(Object that) {\r
1556         if (super.equals(that)) {\r
1557             NumeratorSubstitution that2 = (NumeratorSubstitution)that;\r
1558             return denominator == that2.denominator;\r
1559         } else {\r
1560             return false;\r
1561         }\r
1562     }\r
1563 \r
1564     //-----------------------------------------------------------------------\r
1565     // formatting\r
1566     //-----------------------------------------------------------------------\r
1567 \r
1568     /**\r
1569      * Performs a mathematical operation on the number, formats it using\r
1570      * either ruleSet or decimalFormat, and inserts the result into\r
1571      * toInsertInto.\r
1572      * @param number The number being formatted.\r
1573      * @param toInsertInto The string we insert the result into\r
1574      * @param position The position in toInsertInto where the owning rule's\r
1575      * rule text begins (this value is added to this substitution's\r
1576      * position to determine exactly where to insert the new text)\r
1577      */\r
1578     public void doSubstitution(double number, StringBuffer toInsertInto, int position) {\r
1579         // perform a transformation on the number being formatted that\r
1580         // is dependent on the type of substitution this is\r
1581         //String s = toInsertInto.toString();\r
1582         double numberToFormat = transformNumber(number);\r
1583 \r
1584         if (withZeros && ruleSet != null) {\r
1585             // if there are leading zeros in the decimal expansion then emit them\r
1586             long nf = (long)numberToFormat;\r
1587             int len = toInsertInto.length();\r
1588             while ((nf *= 10) < denominator) {\r
1589                 toInsertInto.insert(position + pos, ' ');\r
1590                 ruleSet.format(0, toInsertInto, position + pos);\r
1591             }\r
1592             position += toInsertInto.length() - len;\r
1593         }\r
1594 \r
1595         // if the result is an integer, from here on out we work in integer\r
1596         // space (saving time and memory and preserving accuracy)\r
1597         if (numberToFormat == Math.floor(numberToFormat) && ruleSet != null) {\r
1598             ruleSet.format((long)numberToFormat, toInsertInto, position + pos);\r
1599 \r
1600             // if the result isn't an integer, then call either our rule set's\r
1601             // format() method or our DecimalFormat's format() method to\r
1602             // format the result\r
1603         } else {\r
1604             if (ruleSet != null) {\r
1605                 ruleSet.format(numberToFormat, toInsertInto, position + pos);\r
1606             } else {\r
1607                 toInsertInto.insert(position + pos, numberFormat.format(numberToFormat));\r
1608             }\r
1609         }\r
1610     }\r
1611 \r
1612     /**\r
1613      * Returns the number being formatted times the denominator.\r
1614      * @param number The number being formatted\r
1615      * @return number * denominator\r
1616      */\r
1617     public long transformNumber(long number) {\r
1618         return Math.round(number * denominator);\r
1619     }\r
1620 \r
1621     /**\r
1622      * Returns the number being formatted times the denominator.\r
1623      * @param number The number being formatted\r
1624      * @return number * denominator\r
1625      */\r
1626     public double transformNumber(double number) {\r
1627         return Math.round(number * denominator);\r
1628     }\r
1629 \r
1630     //-----------------------------------------------------------------------\r
1631     // parsing\r
1632     //-----------------------------------------------------------------------\r
1633 \r
1634     /**\r
1635      * Dispatches to the inherited version of this function, but makes\r
1636      * sure that lenientParse is off.\r
1637      */\r
1638     public Number doParse(String text, ParsePosition parsePosition, double baseValue,\r
1639                         double upperBound, boolean lenientParse) {\r
1640         // we don't have to do anything special to do the parsing here,\r
1641         // but we have to turn lenient parsing off-- if we leave it on,\r
1642         // it SERIOUSLY messes up the algorithm\r
1643 \r
1644         // if withZeros is true, we need to count the zeros\r
1645         // and use that to adjust the parse result\r
1646         int zeroCount = 0;\r
1647         if (withZeros) {\r
1648             String workText = text;\r
1649             ParsePosition workPos = new ParsePosition(1);\r
1650             //int digit;\r
1651 \r
1652             while (workText.length() > 0 && workPos.getIndex() != 0) {\r
1653                 workPos.setIndex(0);\r
1654                 /*digit = */ruleSet.parse(workText, workPos, 1).intValue(); // parse zero or nothing at all\r
1655                 if (workPos.getIndex() == 0) {\r
1656                     // we failed, either there were no more zeros, or the number was formatted with digits\r
1657                     // either way, we're done\r
1658                     break;\r
1659                 }\r
1660 \r
1661                 ++zeroCount;\r
1662                 parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex());\r
1663                 workText = workText.substring(workPos.getIndex());\r
1664                 while (workText.length() > 0 && workText.charAt(0) == ' ') {\r
1665                     workText = workText.substring(1);\r
1666                     parsePosition.setIndex(parsePosition.getIndex() + 1);\r
1667                 }\r
1668             }\r
1669 \r
1670             text = text.substring(parsePosition.getIndex()); // arrgh!\r
1671             parsePosition.setIndex(0);\r
1672         }\r
1673 \r
1674         // we've parsed off the zeros, now let's parse the rest from our current position\r
1675         Number result =  super.doParse(text, parsePosition, withZeros ? 1 : baseValue, upperBound, false);\r
1676 \r
1677         if (withZeros) {\r
1678             // any base value will do in this case.  is there a way to\r
1679             // force this to not bother trying all the base values?\r
1680             \r
1681             // compute the 'effective' base and prescale the value down\r
1682             long n = result.longValue();\r
1683             long d = 1;\r
1684             int pow = 0;\r
1685             while (d <= n) {\r
1686                 d *= 10;\r
1687                 ++pow;\r
1688             }\r
1689             // now add the zeros\r
1690             while (zeroCount > 0) {\r
1691                 d *= 10;\r
1692                 --zeroCount;\r
1693             }\r
1694             // d is now our true denominator\r
1695             result = new Double(n/(double)d);\r
1696         }\r
1697 \r
1698         return result;\r
1699     }\r
1700 \r
1701     /**\r
1702      * Divides the result of parsing the substitution by the partial\r
1703      * parse result.\r
1704      * @param newRuleValue The result of parsing the substitution\r
1705      * @param oldRuleValue The owning rule's base value\r
1706      * @return newRuleValue / oldRuleValue\r
1707      */\r
1708     public double composeRuleValue(double newRuleValue, double oldRuleValue) {\r
1709         return newRuleValue / oldRuleValue;\r
1710     }\r
1711 \r
1712     /**\r
1713      * Sets the uper bound down to this rule's base value\r
1714      * @param oldUpperBound Ignored\r
1715      * @return The base value of the rule owning this substitution\r
1716      */\r
1717     public double calcUpperBound(double oldUpperBound) {\r
1718         return denominator;\r
1719     }\r
1720 \r
1721     //-----------------------------------------------------------------------\r
1722     // simple accessor\r
1723     //-----------------------------------------------------------------------\r
1724 \r
1725     /**\r
1726      * The token character for a NumeratorSubstitution is &lt;\r
1727      * @return '&lt;'\r
1728      */\r
1729     char tokenChar() {\r
1730         return '<';\r
1731     }\r
1732 }\r
1733 \r
1734 //===================================================================\r
1735 // NullSubstitution\r
1736 //===================================================================\r
1737 \r
1738 /**\r
1739  * A substitution which does nothing.  This class exists just to simplify\r
1740  * the logic in some other routines so that they don't have to worry\r
1741  * about how many substitutions a rule has.\r
1742  */\r
1743 class NullSubstitution extends NFSubstitution {\r
1744     //-----------------------------------------------------------------------\r
1745     // construction\r
1746     //-----------------------------------------------------------------------\r
1747 \r
1748     /**\r
1749      * Constructs a NullSubstitution.  This just delegates to the superclass\r
1750      * constructor, but the only value we really care about is the position.\r
1751      */\r
1752     NullSubstitution(int pos,\r
1753                      NFRuleSet ruleSet,\r
1754                      RuleBasedNumberFormat formatter,\r
1755                      String description) {\r
1756         super(pos, ruleSet, formatter, description);\r
1757     }\r
1758 \r
1759     //-----------------------------------------------------------------------\r
1760     // boilerplate\r
1761     //-----------------------------------------------------------------------\r
1762 \r
1763     /**\r
1764      * Only checks for class equality\r
1765      */\r
1766     public boolean equals(Object that) {\r
1767         return super.equals(that);\r
1768     }\r
1769 \r
1770     /**\r
1771      * NullSubstitutions don't show up in the textual representation\r
1772      * of a RuleBasedNumberFormat\r
1773      */\r
1774     public String toString() {\r
1775         return "";\r
1776     }\r
1777 \r
1778     //-----------------------------------------------------------------------\r
1779     // formatting\r
1780     //-----------------------------------------------------------------------\r
1781 \r
1782     /**\r
1783      * Does nothing.\r
1784      */\r
1785     public void doSubstitution(long number, StringBuffer toInsertInto, int position) {\r
1786     }\r
1787 \r
1788     /**\r
1789      * Does nothing.\r
1790      */\r
1791     public void doSubstitution(double number, StringBuffer toInsertInto, int position) {\r
1792     }\r
1793 \r
1794     /**\r
1795      * Never called.\r
1796      */\r
1797     ///CLOVER:OFF\r
1798     public long transformNumber(long number) {\r
1799         return 0;\r
1800     }\r
1801     ///CLOVER:ON\r
1802 \r
1803     /**\r
1804      * Never called.\r
1805      */\r
1806     ///CLOVER:OFF\r
1807     public double transformNumber(double number) {\r
1808         return 0;\r
1809     }\r
1810     ///CLOVER:ON\r
1811 \r
1812     //-----------------------------------------------------------------------\r
1813     // parsing\r
1814     //-----------------------------------------------------------------------\r
1815 \r
1816     /**\r
1817      * Returns the partial parse result unchanged\r
1818      */\r
1819     public Number doParse(String text, ParsePosition parsePosition, double baseValue,\r
1820                         double upperBound, boolean lenientParse) {\r
1821         if (baseValue == (long)baseValue) {\r
1822             return new Long((long)baseValue);\r
1823         } else {\r
1824             return new Double(baseValue);\r
1825         }\r
1826     }\r
1827 \r
1828     /**\r
1829      * Never called.\r
1830      */\r
1831     ///CLOVER:OFF\r
1832     public double composeRuleValue(double newRuleValue, double oldRuleValue) {\r
1833         return 0;\r
1834     }\r
1835     ///CLOVER:ON\r
1836 \r
1837     /**\r
1838      * Never called.\r
1839      */\r
1840     ///CLOVER:OFF\r
1841     public double calcUpperBound(double oldUpperBound) {\r
1842         return 0;\r
1843     }\r
1844     ///CLOVER:ON\r
1845 \r
1846     //-----------------------------------------------------------------------\r
1847     // simple accessors\r
1848     //-----------------------------------------------------------------------\r
1849 \r
1850     /**\r
1851      * Returns true (this _is_ a NullSubstitution).\r
1852      * @return true\r
1853      */\r
1854     public boolean isNullSubstitution() {\r
1855         return true;\r
1856     }\r
1857 \r
1858     /**\r
1859      * Never called.\r
1860      */\r
1861     ///CLOVER:OFF\r
1862     char tokenChar() {\r
1863         return ' ';\r
1864     }\r
1865     ///CLOVER:ON\r
1866 }\r
1867 \r