KickJava   Java API By Example, From Geeks To Geeks.

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


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 java.text.*;
10
11 //===================================================================
12
// NFSubstitution (abstract base class)
13
//===================================================================
14

15 /**
16  * An abstract class defining protocol for substitutions. A substitution
17  * is a section of a rule that inserts text into the rule's rule text
18  * based on some part of the number being formatted.
19  * @author Richard Gillam
20  */

21 abstract class NFSubstitution {
22     //-----------------------------------------------------------------------
23
// constants
24
//-----------------------------------------------------------------------
25

26     /**
27      * Puts a copyright in the .class file
28      */

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

36     /**
37      * The substitution's position in the rule text of the rule that owns it
38      */

39     int pos;
40
41     /**
42      * The rule set this substitution uses to format its result, or null.
43      * (Either this or numberFormat has to be non-null.)
44      */

45     NFRuleSet ruleSet = null;
46
47     /**
48      * The DecimalFormat this substitution uses to format its result,
49      * or null. (Either this or ruleSet has to be non-null.)
50      */

51     DecimalFormat numberFormat = null;
52
53     //-----------------------------------------------------------------------
54
// construction
55
//-----------------------------------------------------------------------
56

57     /**
58      * Parses the description, creates the right kind of substitution,
59      * and initializes it based on the description.
60      * @param pos The substitution's position in the rule text of the
61      * rule that owns it.
62      * @param rule The rule containing this substitution
63      * @param rulePredecessor The rule preceding the one that contains
64      * this substitution in the rule set's rule list (this is used
65      * only for >>> substitutions).
66      * @param ruleSet The rule set containing the rule containing this
67      * substitution
68      * @param formatter The RuleBasedNumberFormat that ultimately owns
69      * this substitution
70      * @param description The description to parse to build the substitution
71      * (this is just the substring of the rule's description containing
72      * the substitution token itself)
73      * @return A new substitution constructed according to the description
74      */

75     public static NFSubstitution makeSubstitution(int pos,
76                                                   NFRule rule,
77                                                   NFRule rulePredecessor,
78                                                   NFRuleSet ruleSet,
79                                                   RuleBasedNumberFormat formatter,
80                                                   String JavaDoc description) {
81         // if the description is empty, return a NummSubstitution
82
if (description.length() == 0) {
83             return new NullSubstitution(pos, ruleSet, formatter, description);
84         }
85
86         switch (description.charAt(0)) {
87             // if the description begins with '<'...
88
case '<':
89             // throw an exception if the rule is a negative number
90
// rule
91
if (rule.getBaseValue() == NFRule.NEGATIVE_NUMBER_RULE) {
92                 throw new IllegalArgumentException JavaDoc("<< not allowed in negative-number rule");
93             }
94
95             // if the rule is a fraction rule, return an
96
// IntegralPartSubstitution
97
else if (rule.getBaseValue() == NFRule.IMPROPER_FRACTION_RULE
98                      || rule.getBaseValue() == NFRule.PROPER_FRACTION_RULE
99                      || rule.getBaseValue() == NFRule.MASTER_RULE) {
100                 return new IntegralPartSubstitution(pos, ruleSet, formatter, description);
101             }
102
103             // if the rule set containing the rule is a fraction
104
// rule set, return a NumeratorSubstitution
105
else if (ruleSet.isFractionSet()) {
106                 return new NumeratorSubstitution(pos, rule.getBaseValue(),
107                                                  formatter.getDefaultRuleSet(), formatter, description);
108             }
109
110             // otherwise, return a MultiplierSubstitution
111
else {
112                 return new MultiplierSubstitution(pos, rule.getDivisor(), ruleSet,
113                                                   formatter, description);
114             }
115
116             // if the description begins with '>'...
117
case '>':
118             // if the rule is a negative-number rule, return
119
// an AbsoluteValueSubstitution
120
if (rule.getBaseValue() == NFRule.NEGATIVE_NUMBER_RULE) {
121                 return new AbsoluteValueSubstitution(pos, ruleSet, formatter, description);
122             }
123
124             // if the rule is a fraction rule, return a
125
// FractionalPartSubstitution
126
else if (rule.getBaseValue() == NFRule.IMPROPER_FRACTION_RULE
127                      || rule.getBaseValue() == NFRule.PROPER_FRACTION_RULE
128                      || rule.getBaseValue() == NFRule.MASTER_RULE) {
129                 return new FractionalPartSubstitution(pos, ruleSet, formatter, description);
130             }
131
132             // if the rule set owning the rule is a fraction rule set,
133
// throw an exception
134
else if (ruleSet.isFractionSet()) {
135                 throw new IllegalArgumentException JavaDoc(">> not allowed in fraction rule set");
136             }
137
138             // otherwise, return a ModulusSubstitution
139
else {
140                 return new ModulusSubstitution(pos, rule.getDivisor(), rulePredecessor,
141                                                ruleSet, formatter, description);
142             }
143
144             // if the description begins with '=', always return a
145
// SameValueSubstitution
146
case '=':
147             return new SameValueSubstitution(pos, ruleSet, formatter, description);
148
149             // and if it's anything else, throw an exception
150
default:
151             throw new IllegalArgumentException JavaDoc("Illegal substitution character");
152         }
153     }
154
155     /**
156      * Base constructor for substitutions. This constructor sets up the
157      * fields which are common to all substitutions.
158      * @param pos The substitution's position in the owning rule's rule
159      * text
160      * @param ruleSet The rule set that owns this substitution
161      * @param formatter The RuleBasedNumberFormat that owns this substitution
162      * @param description The substitution descriptor (i.e., the text
163      * inside the token characters)
164      */

165     NFSubstitution(int pos,
166                    NFRuleSet ruleSet,
167                    RuleBasedNumberFormat formatter,
168                    String JavaDoc description) {
169         // initialize the substitution's position in its parent rule
170
this.pos = pos;
171
172         // the description should begin and end with the same character.
173
// If it doesn't that's a syntax error. Otherwise,
174
// makeSubstitution() was the only thing that needed to know
175
// about these characters, so strip them off
176
if (description.length() >= 2 && description.charAt(0) == description.charAt(
177                                                                                      description.length() - 1)) {
178             description = description.substring(1, description.length() - 1);
179         }
180         else if (description.length() != 0) {
181             throw new IllegalArgumentException JavaDoc("Illegal substitution syntax");
182         }
183
184         // if the description was just two paired token characters
185
// (i.e., "<<" or ">>"), it uses the rule set it belongs to to
186
// format its result
187
if (description.length() == 0) {
188             this.ruleSet = ruleSet;
189         }
190
191         // if the description contains a rule set name, that's the rule
192
// set we use to format the result: get a reference to the
193
// names rule set
194
else if (description.charAt(0) == '%') {
195             this.ruleSet = formatter.findRuleSet(description);
196         }
197
198         // if the description begins with 0 or #, treat it as a
199
// DecimalFormat pattern, and initialize a DecimalFormat with
200
// that pattern (then set it to use the DecimalFormatSymbols
201
// belonging to our formatter)
202
else if (description.charAt(0) == '#' || description.charAt(0) == '0') {
203             this.numberFormat = new DecimalFormat(description);
204             this.numberFormat.setDecimalFormatSymbols(formatter.getDecimalFormatSymbols());
205         }
206
207         // if the description is ">>>", this substitution bypasses the
208
// usual rule-search process and always uses the rule that precedes
209
// it in its own rule set's rule list (this is used for place-value
210
// notations: formats where you want to see a particular part of
211
// a number even when it's 0)
212
else if (description.charAt(0) == '>') {
213             this.ruleSet = ruleSet; // was null, thai rules added to control space
214
this.numberFormat = null;
215         }
216
217         // and of the description is none of these things, it's a syntax error
218
else {
219             throw new IllegalArgumentException JavaDoc("Illegal substitution syntax");
220         }
221     }
222
223     /**
224      * Set's the substitution's divisor. Used by NFRule.setBaseValue().
225      * A no-op for all substitutions except multiplier and modulus
226      * substitutions.
227      * @param radix The radix of the divisor
228      * @param exponent The exponent of the divisor
229      */

230     public void setDivisor(int radix, int exponent) {
231         // a no-op for all substitutions except multiplier and modulus substitutions
232
}
233
234     //-----------------------------------------------------------------------
235
// boilerplate
236
//-----------------------------------------------------------------------
237

238     /**
239      * Compares two substitutions for equality
240      * @param The substitution to compare this one to
241      * @return true if the two substitutions are functionally equivalent
242      */

243     public boolean equals(Object JavaDoc that) {
244         // compare class and all of the fields all substitutions have
245
// in common
246
if (this.getClass() == that.getClass()) {
247             NFSubstitution that2 = (NFSubstitution)that;
248
249             return pos == that2.pos
250                 && (ruleSet == null ? that2.ruleSet == null : true) // can't compare tree structure, no .equals or recurse
251
&& (numberFormat == null ? (that2.numberFormat == null) : numberFormat.equals(that2.numberFormat));
252         }
253         return false;
254     }
255
256     /**
257      * Returns a textual description of the substitution
258      * @return A textual description of the substitution. This might
259      * not be identical to the description it was created from, but
260      * it'll produce the same result.
261      */

262     public String JavaDoc toString() {
263         // use tokenChar() to get the character at the beginning and
264
// end of the substitution token. In between them will go
265
// either the name of the rule set it uses, or the pattern of
266
// the DecimalFormat it uses
267
if (ruleSet != null) {
268             return tokenChar() + ruleSet.getName() + tokenChar();
269         } else {
270             return tokenChar() + numberFormat.toPattern() + tokenChar();
271         }
272     }
273
274     //-----------------------------------------------------------------------
275
// formatting
276
//-----------------------------------------------------------------------
277

278     /**
279      * Performs a mathematical operation on the number, formats it using
280      * either ruleSet or decimalFormat, and inserts the result into
281      * toInsertInto.
282      * @param number The number being formatted.
283      * @param toInsertInto The string we insert the result into
284      * @param pos The position in toInsertInto where the owning rule's
285      * rule text begins (this value is added to this substitution's
286      * position to determine exactly where to insert the new text)
287      */

288     public void doSubstitution(long number, StringBuffer JavaDoc toInsertInto, int pos) {
289         if (ruleSet != null) {
290             // perform a transformation on the number that is dependent
291
// on the type of substitution this is, then just call its
292
// rule set's format() method to format the result
293
long numberToFormat = transformNumber(number);
294
295             ruleSet.format(numberToFormat, toInsertInto, pos + this.pos);
296         } else {
297             // or perform the transformation on the number (preserving
298
// the result's fractional part if the formatter it set
299
// to show it), then use that formatter's format() method
300
// to format the result
301
double numberToFormat = transformNumber((double)number);
302             if (numberFormat.getMaximumFractionDigits() == 0) {
303                 numberToFormat = Math.floor(numberToFormat);
304             }
305
306             toInsertInto.insert(pos + this.pos, numberFormat.format(numberToFormat));
307         }
308     }
309
310     /**
311      * Performs a mathematical operation on the number, formats it using
312      * either ruleSet or decimalFormat, and inserts the result into
313      * toInsertInto.
314      * @param number The number being formatted.
315      * @param toInsertInto The string we insert the result into
316      * @param pos The position in toInsertInto where the owning rule's
317      * rule text begins (this value is added to this substitution's
318      * position to determine exactly where to insert the new text)
319      */

320     public void doSubstitution(double number, StringBuffer JavaDoc toInsertInto, int pos) {
321         // perform a transformation on the number being formatted that
322
// is dependent on the type of substitution this is
323
double numberToFormat = transformNumber(number);
324
325         // if the result is an integer, from here on out we work in integer
326
// space (saving time and memory and preserving accuracy)
327
if (numberToFormat == Math.floor(numberToFormat) && ruleSet != null) {
328             ruleSet.format((long)numberToFormat, toInsertInto, pos + this.pos);
329
330             // if the result isn't an integer, then call either our rule set's
331
// format() method or our DecimalFormat's format() method to
332
// format the result
333
} else {
334             if (ruleSet != null) {
335                 ruleSet.format(numberToFormat, toInsertInto, pos + this.pos);
336             } else {
337                 toInsertInto.insert(pos + this.pos, numberFormat.format(numberToFormat));
338             }
339         }
340     }
341
342     /**
343      * Subclasses override this function to perform some kind of
344      * mathematical operation on the number. The result of this operation
345      * is formatted using the rule set or DecimalFormat that this
346      * substitution refers to, and the result is inserted into the result
347      * string.
348      * @param The number being formatted
349      * @return The result of performing the opreration on the number
350      */

351     public abstract long transformNumber(long number);
352
353     /**
354      * Subclasses override this function to perform some kind of
355      * mathematical operation on the number. The result of this operation
356      * is formatted using the rule set or DecimalFormat that this
357      * substitution refers to, and the result is inserted into the result
358      * string.
359      * @param The number being formatted
360      * @return The result of performing the opreration on the number
361      */

362     public abstract double transformNumber(double number);
363
364     //-----------------------------------------------------------------------
365
// parsing
366
//-----------------------------------------------------------------------
367

368     /**
369      * Parses a string using the rule set or DecimalFormat belonging
370      * to this substitution. If there's a match, a mathematical
371      * operation (the inverse of the one used in formatting) is
372      * performed on the result of the parse and the value passed in
373      * and returned as the result. The parse position is updated to
374      * point to the first unmatched character in the string.
375      * @param text The string to parse
376      * @param parsePosition On entry, ignored, but assumed to be 0.
377      * On exit, this is updated to point to the first unmatched
378      * character (or 0 if the substitution didn't match)
379      * @param baseValue A partial parse result that should be
380      * combined with the result of this parse
381      * @param upperBound When searching the rule set for a rule
382      * matching the string passed in, only rules with base values
383      * lower than this are considered
384      * @param lenientParse If true and matching against rules fails,
385      * the substitution will also try matching the text against
386      * numerals using a default-costructed NumberFormat. If false,
387      * no extra work is done. (This value is false whenever the
388      * formatter isn't in lenient-parse mode, but is also false
389      * under some conditions even when the formatter _is_ in
390      * lenient-parse mode.)
391      * @return If there's a match, this is the result of composing
392      * baseValue with whatever was returned from matching the
393      * characters. This will be either a Long or a Double. If there's
394      * no match this is new Long(0) (not null), and parsePosition
395      * is left unchanged.
396      */

397     public Number JavaDoc doParse(String JavaDoc text, ParsePosition parsePosition, double baseValue,
398                           double upperBound, boolean lenientParse) {
399         Number JavaDoc tempResult;
400
401         // figure out the highest base value a rule can have and match
402
// the text being parsed (this varies according to the type of
403
// substitutions: multiplier, modulus, and numerator substitutions
404
// restrict the search to rules with base values lower than their
405
// own; same-value substitutions leave the upper bound wherever
406
// it was, and the others allow any rule to match
407
upperBound = calcUpperBound(upperBound);
408
409         // use our rule set to parse the text. If that fails and
410
// lenient parsing is enabled (this is always false if the
411
// formatter's lenient-parsing mode is off, but it may also
412
// be false even when the formatter's lenient-parse mode is
413
// on), then also try parsing the text using a default-
414
// constructed NumberFormat
415
if (ruleSet != null) {
416             tempResult = ruleSet.parse(text, parsePosition, upperBound);
417             if (lenientParse && !ruleSet.isFractionSet() && parsePosition.getIndex() == 0) {
418                 tempResult = NumberFormat.getInstance().parse(text, parsePosition);
419             }
420
421             // ...or use our DecimalFormat to parse the text
422
} else {
423             tempResult = numberFormat.parse(text, parsePosition);
424         }
425
426         // if the parse was successful, we've already advanced the caller's
427
// parse position (this is the one function that doesn't have one
428
// of its own). Derive a parse result and return it as a Long,
429
// if possible, or a Double
430
if (parsePosition.getIndex() != 0) {
431             double result = tempResult.doubleValue();
432
433             // composeRuleValue() produces a full parse result from
434
// the partial parse result passed to this function from
435
// the caller (this is either the owning rule's base value
436
// or the partial result obtained from composing the
437
// owning rule's base value with its other substitution's
438
// parse result) and the partial parse result obtained by
439
// matching the substitution (which will be the same value
440
// the caller would get by parsing just this part of the
441
// text with RuleBasedNumberFormat.parse() ). How the two
442
// values are used to derive the full parse result depends
443
// on the types of substitutions: For a regular rule, the
444
// ultimate result is its multiplier substitution's result
445
// times the rule's divisor (or the rule's base value) plus
446
// the modulus substitution's result (which will actually
447
// supersede part of the rule's base value). For a negative-
448
// number rule, the result is the negative of its substitution's
449
// result. For a fraction rule, it's the sum of its two
450
// substitution results. For a rule in a fraction rule set,
451
// it's the numerator substitution's result divided by
452
// the rule's base value. Results from same-value substitutions
453
// propagate back upard, and null substitutions don't affect
454
// the result.
455
result = composeRuleValue(result, baseValue);
456             if (result == (long)result) {
457                 return new Long JavaDoc((long)result);
458             } else {
459                 return new Double JavaDoc(result);
460             }
461
462             // if the parse was UNsuccessful, return 0
463
} else {
464             return tempResult;
465         }
466     }
467
468     /**
469      * Derives a new value from the two values passed in. The two values
470      * are typically either the base values of two rules (the one containing
471      * the substitution and the one matching the substitution) or partial
472      * parse results derived in some other way. The operation is generally
473      * the inverse of the operation performed by transformNumber().
474      * @param newRuleValue The value produced by matching this substitution
475      * @param oldRuleValue The value that was passed to the substitution
476      * by the rule that owns it
477      * @return A third value derived from the other two, representing a
478      * partial parse result
479      */

480     public abstract double composeRuleValue(double newRuleValue, double oldRuleValue);
481
482     /**
483      * Calculates an upper bound when searching for a rule that matches
484      * this substitution. Rules with base values greater than or equal
485      * to upperBound are not considered.
486      * @param oldUpperBound The current upper-bound setting. The new
487      * upper bound can't be any higher.
488      */

489     public abstract double calcUpperBound(double oldUpperBound);
490
491     //-----------------------------------------------------------------------
492
// simple accessors
493
//-----------------------------------------------------------------------
494

495     /**
496      * Returns the substitution's position in the rule that owns it.
497      * @return The substitution's position in the rule that owns it.
498      */

499     public final int getPos() {
500         return pos;
501     }
502
503     /**
504      * Returns the character used in the textual representation of
505      * substitutions of this type. Used by toString().
506      * @return This substitution's token character.
507      */

508     abstract char tokenChar();
509
510     /**
511      * Returns true if this is a null substitution. (We didn't do this
512      * with instanceof partially because it causes source files to
513      * proliferate and partially because we have to port this to C++.)
514      * @return true if this object is an instance of NullSubstitution
515      */

516     public boolean isNullSubstitution() {
517         return false;
518     }
519
520     /**
521      * Returns true if this is a modulus substitution. (We didn't do this
522      * with instanceof partially because it causes source files to
523      * proliferate and partially because we have to port this to C++.)
524      * @return true if this object is an instance of ModulusSubstitution
525      */

526     public boolean isModulusSubstitution() {
527         return false;
528     }
529 }
530
531 //===================================================================
532
// SameValueSubstitution
533
//===================================================================
534

535 /**
536  * A substitution that passes the value passed to it through unchanged.
537  * Represented by == in rule descriptions.
538  */

539 class SameValueSubstitution extends NFSubstitution {
540     //-----------------------------------------------------------------------
541
// constants
542
//-----------------------------------------------------------------------
543

544     /**
545      * Puts a copyright in the .class file
546      */

547     private static final String JavaDoc copyrightNotice
548         = "Copyright \u00a91997-1998 IBM Corp. All rights reserved.";
549
550     //-----------------------------------------------------------------------
551
// construction
552
//-----------------------------------------------------------------------
553

554     /**
555      * Constructs a SameValueSubstution. This function just uses the
556      * superclass constructor, but it performs a check that this
557      * substitution doesn't call the rule set that owns it, since that
558      * would lead to infinite recursion.
559      */

560     SameValueSubstitution(int pos,
561                           NFRuleSet ruleSet,
562                           RuleBasedNumberFormat formatter,
563                           String JavaDoc description) {
564         super(pos, ruleSet, formatter, description);
565         if (description.equals("==")) {
566             throw new IllegalArgumentException JavaDoc("== is not a legal token");
567         }
568     }
569
570     //-----------------------------------------------------------------------
571
// formatting
572
//-----------------------------------------------------------------------
573

574     /**
575      * Returns "number" unchanged.
576      * @return "number"
577      */

578     public long transformNumber(long number) {
579         return number;
580     }
581
582     /**
583      * Returns "number" unchanged.
584      * @return "number"
585      */

586     public double transformNumber(double number) {
587         return number;
588     }
589
590     //-----------------------------------------------------------------------
591
// parsing
592
//-----------------------------------------------------------------------
593

594     /**
595      * Returns newRuleValue and ignores oldRuleValue. (The value we got
596      * matching the substitution supersedes the value of the rule
597      * that owns the substitution.)
598      * @param newRuleValue The value resulting from matching the substituion
599      * @param oldRuleValue The value of the rule containing the
600      * substitution.
601      * @return newRuleValue
602      */

603     public double composeRuleValue(double newRuleValue, double oldRuleValue) {
604         return newRuleValue;
605     }
606
607     /**
608      * SameValueSubstitution doesn't change the upper bound.
609      * @param oldUpperBound The current upper bound.
610      * @return oldUpperBound
611      */

612     public double calcUpperBound(double oldUpperBound) {
613         return oldUpperBound;
614     }
615
616     //-----------------------------------------------------------------------
617
// simple accessor
618
//-----------------------------------------------------------------------
619

620     /**
621      * The token character for a SameValueSubstitution is =.
622      * @return '='
623      */

624     char tokenChar() {
625         return '=';
626     }
627 }
628
629 //===================================================================
630
// MultiplierSubstitution
631
//===================================================================
632

633 /**
634  * A substitution that divides the number being formatted by the rule's
635  * divisor and formats the quotient. Represented by &lt;&lt; in normal
636  * rules.
637  */

638 class MultiplierSubstitution extends NFSubstitution {
639     //-----------------------------------------------------------------------
640
// constants
641
//-----------------------------------------------------------------------
642

643     /**
644      * Puts a copyright in the .class file
645      */

646     private static final String JavaDoc copyrightNotice
647         = "Copyright \u00a91997-1998 IBM Corp. All rights reserved.";
648
649     //-----------------------------------------------------------------------
650
// data members
651
//-----------------------------------------------------------------------
652

653     /**
654      * The divisor of the rule that owns this substitution.
655      */

656     double divisor;
657
658     //-----------------------------------------------------------------------
659
// construction
660
//-----------------------------------------------------------------------
661

662     /**
663      * Constructs a MultiplierSubstitution. This uses the superclass
664      * constructor to initialize most members, but this substitution
665      * also maintains its own copy of its rule's divisor.
666      * @param pos The substitution's position in its rule's rule text
667      * @param divisor The owning rule's divisor
668      * @ruleSet The ruleSet this substitution uses to format its result
669      * @formatter The formatter that owns this substitution
670      * @description The description describing this substitution
671      */

672     MultiplierSubstitution(int pos,
673                            double divisor,
674                            NFRuleSet ruleSet,
675                            RuleBasedNumberFormat formatter,
676                            String JavaDoc description) {
677         super(pos, ruleSet, formatter, description);
678
679         // the owning rule's divisor affects the behavior of this
680
// substitution. Rather than keeping a back-pointer to the
681
// rule, we keep a copy of the divisor
682
this.divisor = divisor;
683
684     if (divisor == 0) { // this will cause recursion
685
throw new IllegalStateException JavaDoc("Substitution with bad divisor (" + divisor + ") " + description.substring(0, pos) +
686                      " | " + description.substring(pos));
687     }
688     }
689
690     /**
691      * Sets the substitution's divisor based on the values passed in.
692      * @param radix The radix of the divisor.
693      * @param exponent The exponent of the divisor.
694      */

695     public void setDivisor(int radix, int exponent) {
696         divisor = Math.pow(radix, exponent);
697
698     if (divisor == 0) {
699         throw new IllegalStateException JavaDoc("Substitution with divisor 0");
700     }
701     }
702
703     //-----------------------------------------------------------------------
704
// boilerplate
705
//-----------------------------------------------------------------------
706

707     /**
708      * Augments the superclass's equals() function by comparing divisors.
709      * @param that The other substitution
710      * @return true if the two substitutions are functionally equal
711      */

712     public boolean equals(Object JavaDoc that) {
713         if (super.equals(that)) {
714             MultiplierSubstitution that2 = (MultiplierSubstitution)that;
715
716             return divisor == that2.divisor;
717         } else {
718             return false;
719         }
720     }
721
722     //-----------------------------------------------------------------------
723
// formatting
724
//-----------------------------------------------------------------------
725

726     /**
727      * Divides the number by the rule's divisor and returns the quotient.
728      * @param number The number being formatted.
729      * @return "number" divided by the rule's divisor
730      */

731     public long transformNumber(long number) {
732         return (long)Math.floor(number / divisor);
733     }
734
735     /**
736      * Divides the number by the rule's divisor and returns the quotient.
737      * This is an integral quotient if we're filling in the substitution
738      * using another rule set, but it's the full quotient (integral and
739      * fractional parts) if we're filling in the substitution using
740      * a DecimalFormat. (This allows things such as "1.2 million".)
741      * @param number The number being formatted
742      * @return "number" divided by the rule's divisor
743      */

744     public double transformNumber(double number) {
745         if (ruleSet == null) {
746             return number / divisor;
747         } else {
748             return Math.floor(number / divisor);
749         }
750     }
751
752     //-----------------------------------------------------------------------
753
// parsing
754
//-----------------------------------------------------------------------
755

756     /**
757      * Returns newRuleValue times the divisor. Ignores oldRuleValue.
758      * (The result of matching a << substitution supersedes the base
759      * value of the rule that contains it.)
760      * @param newRuleValue The result of matching the substitution
761      * @param oldRuleValue The base value of the rule containing the
762      * substitution
763      * @return newRuleValue * divisor
764      */

765     public double composeRuleValue(double newRuleValue, double oldRuleValue) {
766         return newRuleValue * divisor;
767     }
768
769     /**
770      * Sets the upper bound down to the rule's divisor.
771      * @param oldUpperBound Ignored.
772      * @return The rule's divisor.
773      */

774     public double calcUpperBound(double oldUpperBound) {
775         return divisor;
776     }
777
778     //-----------------------------------------------------------------------
779
// simple accessor
780
//-----------------------------------------------------------------------
781

782     /**
783      * The token character for a multiplier substitution is &lt;.
784      * @return '&lt;'
785      */

786     char tokenChar() {
787         return '<';
788     }
789 }
790
791 //===================================================================
792
// ModulusSubstitution
793
//===================================================================
794

795 /**
796  * A substitution that divides the number being formatted by the its rule's
797  * divisor and formats the remainder. Represented by "&gt;&gt;" in a
798  * regular rule.
799  */

800 class ModulusSubstitution extends NFSubstitution {
801     //-----------------------------------------------------------------------
802
// constants
803
//-----------------------------------------------------------------------
804

805     /**
806      * Puts a copyright in the .class file
807      */

808     private static final String JavaDoc copyrightNotice
809         = "Copyright \u00a91997-1998 IBM Corp. All rights reserved.";
810
811     //-----------------------------------------------------------------------
812
// data members
813
//-----------------------------------------------------------------------
814

815     /**
816      * The divisor of the rule owning this substitution
817      */

818     double divisor;
819
820     /**
821      * If this is a &gt;&gt;&gt; substitution, the rule to use to format
822      * the substitution value. Otherwise, null.
823      */

824     NFRule ruleToUse;
825
826     //-----------------------------------------------------------------------
827
// construction
828
//-----------------------------------------------------------------------
829

830     /**
831      * Constructs a ModulusSubstution. In addition to the inherited
832      * members, a ModulusSubstitution keeps track of the divisor of the
833      * rule that owns it, and may also keep a reference to the rule
834      * that precedes the rule containing this substitution in the rule
835      * set's rule list.
836      * @param pos The substitution's position in its rule's rule text
837      * @param divisor The divisor of the rule that owns this substitution
838      * @param rulePredecessor The rule that precedes this substitution's
839      * rule in its rule set's rule list
840      * @param formatter The RuleBasedNumberFormat owning this substitution
841      * @param description The description for this substitution
842      */

843     ModulusSubstitution(int pos,
844                         double divisor,
845                         NFRule rulePredecessor,
846                         NFRuleSet ruleSet,
847                         RuleBasedNumberFormat formatter,
848                         String JavaDoc description) {
849         super(pos, ruleSet, formatter, description);
850
851         // the owning rule's divisor controls the behavior of this
852
// substitution: rather than keeping a backpointer to the rule,
853
// we keep a copy of the divisor
854
this.divisor = divisor;
855
856     if (divisor == 0) { // this will cause recursion
857
throw new IllegalStateException JavaDoc("Substitution with bad divisor (" + divisor + ") "+ description.substring(0, pos) +
858                      " | " + description.substring(pos));
859     }
860
861         // the >>> token doesn't alter how this substituion calculates the
862
// values it uses for formatting and parsing, but it changes
863
// what's done with that value after it's obtained: >>> short-
864
// circuits the rule-search process and goes straight to the
865
// specified rule to format the substitution value
866
if (description.equals(">>>")) {
867             ruleToUse = rulePredecessor;
868         } else {
869             ruleToUse = null;
870         }
871     }
872
873     /**
874      * Makes the substitution's divisor conform to that of the rule
875      * that owns it. Used when the divisor is determined after creation.
876      * @param radix The radix of the divsor.
877      * @param exponent The exponent of the divisor.
878      */

879     public void setDivisor(int radix, int exponent) {
880         divisor = Math.pow(radix, exponent);
881
882     if (divisor == 0) { // this will cause recursion
883
throw new IllegalStateException JavaDoc("Substitution with bad divisor");
884     }
885     }
886
887     //-----------------------------------------------------------------------
888
// boilerplate
889
//-----------------------------------------------------------------------
890

891     /**
892      * Augments the inherited equals() function by comparing divisors and
893      * ruleToUse.
894      * @param that The other substitution
895      * @return true if the two substitutions are functionally equivalent
896      */

897     public boolean equals(Object JavaDoc that) {
898         if (super.equals(that)) {
899             ModulusSubstitution that2 = (ModulusSubstitution)that;
900
901             return divisor == that2.divisor;
902         } else {
903             return false;
904         }
905     }
906
907     //-----------------------------------------------------------------------
908
// formatting
909
//-----------------------------------------------------------------------
910

911     /**
912      * If this is a &gt;&gt;&gt; substitution, use ruleToUse to fill in
913      * the substitution. Otherwise, just use the superclass function.
914      * @param number The number being formatted
915      * @toInsertInto The string to insert the result of this substitution
916      * into
917      * @param pos The position of the rule text in toInsertInto
918      */

919     public void doSubstitution(long number, StringBuffer JavaDoc toInsertInto, int pos) {
920         // if this isn't a >>> substitution, just use the inherited version
921
// of this function (which uses either a rule set or a DecimalFormat
922
// to format its substitution value)
923
if (ruleToUse == null) {
924             super.doSubstitution(number, toInsertInto, pos);
925
926         // a >>> substitution goes straight to a particular rule to
927
// format the substitution value
928
} else {
929             long numberToFormat = transformNumber(number);
930             ruleToUse.doFormat(numberToFormat, toInsertInto, pos + this.pos);
931         }
932     }
933
934     /**
935      * If this is a &gt;&gt;&gt; substitution, use ruleToUse to fill in
936      * the substitution. Otherwise, just use the superclass function.
937      * @param number The number being formatted
938      * @toInsertInto The string to insert the result of this substitution
939      * into
940      * @param pos The position of the rule text in toInsertInto
941      */

942     public void doSubstitution(double number, StringBuffer JavaDoc toInsertInto, int pos) {
943         // if this isn't a >>> substitution, just use the inherited version
944
// of this function (which uses either a rule set or a DecimalFormat
945
// to format its substitution value)
946
if (ruleToUse == null) {
947             super.doSubstitution(number, toInsertInto, pos);
948
949         // a >>> substitution goes straight to a particular rule to
950
// format the substitution value
951
} else {
952             double numberToFormat = transformNumber(number);
953
954             ruleToUse.doFormat(numberToFormat, toInsertInto, pos + this.pos);
955         }
956     }
957
958     /**
959      * Divides the number being formatted by the rule's divisor and
960      * returns the remainder.
961      * @param number The number being formatted
962      * @return "number" mod divisor
963      */

964     public long transformNumber(long number) {
965         return (long)Math.floor(number % divisor);
966     }
967
968     /**
969      * Divides the number being formatted by the rule's divisor and
970      * returns the remainder.
971      * @param number The number being formatted
972      * @return "number" mod divisor
973      */

974     public double transformNumber(double number) {
975         return Math.floor(number % divisor);
976     }
977
978     //-----------------------------------------------------------------------
979
// parsing
980
//-----------------------------------------------------------------------
981

982     /**
983      * If this is a &gt;&gt;&gt; substitution, match only against ruleToUse.
984      * Otherwise, use the superclass function.
985      * @param text The string to parse
986      * @param parsePosition Ignored on entry, updated on exit to point to
987      * the first unmatched character.
988      * @param baseValue The partial parse result prior to calling this
989      * routine.
990      */

991     public Number JavaDoc doParse(String JavaDoc text, ParsePosition parsePosition, double baseValue,
992                         double upperBound, boolean lenientParse) {
993         // if this isn't a >>> substitution, we can just use the
994
// inherited parse() routine to do the parsing
995
if (ruleToUse == null) {
996             return super.doParse(text, parsePosition, baseValue, upperBound, lenientParse);
997
998         // but if it IS a >>> substitution, we have to do it here: we
999
// use the specific rule's doParse() method, and then we have to
1000
// do some of the other work of NFRuleSet.parse()
1001
} else {
1002            Number JavaDoc tempResult = ruleToUse.doParse(text, parsePosition, false, upperBound);
1003
1004            if (parsePosition.getIndex() != 0) {
1005                double result = tempResult.doubleValue();
1006
1007                result = composeRuleValue(result, baseValue);
1008                if (result == (long)result) {
1009                    return new Long JavaDoc((long)result);
1010                } else {
1011                    return new Double JavaDoc(result);
1012                }
1013            } else {
1014                return tempResult;
1015            }
1016        }
1017    }
1018
1019    /**
1020     * Returns the highest multiple of the rule's divisor that its less
1021     * than or equal to oldRuleValue, plus newRuleValue. (The result
1022     * is the sum of the result of parsing the substitution plus the
1023     * base valueof the rule containing the substitution, but if the
1024     * owning rule's base value isn't an even multiple of its divisor,
1025     * we have to round it down to a multiple of the divisor, or we
1026     * get unwanted digits in the result.)
1027     * @param newRuleValue The result of parsing the substitution
1028     * @param oldRuleValue The base value of the rule containing the
1029     * substitution
1030     * @return (oldRuleValue - (oldRuleValue % divisor)) + newRuleValue
1031     */

1032    public double composeRuleValue(double newRuleValue, double oldRuleValue) {
1033        return (oldRuleValue - (oldRuleValue % divisor)) + newRuleValue;
1034    }
1035
1036    /**
1037     * Sets the upper bound down to the owning rule's divisor
1038     * @param oldUpperBound Ignored
1039     * @return The owning rule's dvisor
1040     */

1041    public double calcUpperBound(double oldUpperBound) {
1042        return divisor;
1043    }
1044
1045    //-----------------------------------------------------------------------
1046
// simple accessors
1047
//-----------------------------------------------------------------------
1048

1049    /**
1050     * Returns true. This _is_ a ModulusSubstitution.
1051     * @return true
1052     */

1053    public boolean isModulusSubstitution() {
1054        return true;
1055    }
1056
1057    /**
1058     * The token character of a ModulusSubstitution is &gt;.
1059     * @return '&gt;'
1060     */

1061    char tokenChar() {
1062        return '>';
1063    }
1064}
1065
1066//===================================================================
1067
// IntegralPartSubstitution
1068
//===================================================================
1069

1070/**
1071 * A substitution that formats the number's integral part. This is
1072 * represented by &lt;&lt; in a fraction rule.
1073 */

1074class IntegralPartSubstitution extends NFSubstitution {
1075    //-----------------------------------------------------------------------
1076
// constants
1077
//-----------------------------------------------------------------------
1078

1079    /**
1080     * Puts a copyright in the .class file
1081     */

1082    private static final String JavaDoc copyrightNotice
1083        = "Copyright \u00a91997-1998 IBM Corp. All rights reserved.";
1084
1085    //-----------------------------------------------------------------------
1086
// construction
1087
//-----------------------------------------------------------------------
1088

1089    /**
1090     * Constructs an IntegralPartSubstitution. This just calls
1091     * the superclass constructor.
1092     */

1093    IntegralPartSubstitution(int pos,
1094                             NFRuleSet ruleSet,
1095                             RuleBasedNumberFormat formatter,
1096                             String JavaDoc description) {
1097        super(pos, ruleSet, formatter, description);
1098    }
1099
1100    //-----------------------------------------------------------------------
1101
// formatting
1102
//-----------------------------------------------------------------------
1103

1104    /**
1105     * Returns the number's integral part. (For a long, that's just the
1106     * number unchanged.)
1107     * @param number The number being formatted
1108     * @return "number" unchanged
1109     */

1110    public long transformNumber(long number) {
1111        return number;
1112    }
1113
1114    /**
1115     * Returns the number's integral part.
1116     * @param number The integral part of the number being formatted
1117     * @return floor(number)
1118     */

1119    public double transformNumber(double number) {
1120        return Math.floor(number);
1121    }
1122
1123    //-----------------------------------------------------------------------
1124
// parsing
1125
//-----------------------------------------------------------------------
1126

1127    /**
1128     * Returns the sum of the result of parsing the substitution and the
1129     * owning rule's base value. (The owning rule, at best, has an
1130     * integral-part substitution and a fractional-part substitution,
1131     * so we can safely just add them.)
1132     * @param newRuleValue The result of matching the substitution
1133     * @param oldRuleValue The partial result of the parse prior to
1134     * calling this function
1135     * @return oldRuleValue + newRuleValue
1136     */

1137    public double composeRuleValue(double newRuleValue, double oldRuleValue) {
1138        return newRuleValue + oldRuleValue;
1139    }
1140
1141    /**
1142     * An IntegralPartSubstitution sets the upper bound back up so all
1143     * potentially matching rules are considered.
1144     * @param oldUpperBound Ignored
1145     * @return Double.MAX_VALUE
1146     */

1147    public double calcUpperBound(double oldUpperBound) {
1148        return Double.MAX_VALUE;
1149    }
1150
1151    //-----------------------------------------------------------------------
1152
// simple accessor
1153
//-----------------------------------------------------------------------
1154

1155    /**
1156     * An IntegralPartSubstitution's token character is &lt;
1157     * @return '&lt;'
1158     */

1159    char tokenChar() {
1160        return '<';
1161    }
1162}
1163
1164//===================================================================
1165
// FractionalPartSubstitution
1166
//===================================================================
1167

1168/**
1169 * A substitution that formats the fractional part of a number. This is
1170 * represented by &gt;&gt; in a fraction rule.
1171 */

1172class FractionalPartSubstitution extends NFSubstitution {
1173    //-----------------------------------------------------------------------
1174
// constants
1175
//-----------------------------------------------------------------------
1176

1177    /**
1178     * Puts a copyright in the .class file
1179     */

1180    private static final String JavaDoc copyrightNotice
1181        = "Copyright \u00a91997-1998 IBM Corp. All rights reserved.";
1182
1183    //-----------------------------------------------------------------------
1184
// data members
1185
//-----------------------------------------------------------------------
1186

1187    /**
1188     * true if this substitution should have the default "by digits"
1189     * behavior, false otherwise
1190     */

1191    private boolean byDigits = false;
1192
1193    /**
1194     * true if we automatically insert spaces to separate names of digits
1195     * set to false by '>>>' in fraction rules, used by Thai.
1196     */

1197    private boolean useSpaces = true;
1198
1199    /**
1200     * The largest number of digits after the decimal point that this
1201     * object will show in "by digits" mode
1202     */

1203    private static final int MAXDECIMALDIGITS = 18; // 8
1204

1205    //-----------------------------------------------------------------------
1206
// construction
1207
//-----------------------------------------------------------------------
1208

1209    /**
1210     * Constructs a FractionalPartSubstitution. This object keeps a flag
1211     * telling whether it should format by digits or not. In addition,
1212     * it marks the rule set it calls (if any) as a fraction rule set.
1213     */

1214    FractionalPartSubstitution(int pos,
1215                               NFRuleSet ruleSet,
1216                               RuleBasedNumberFormat formatter,
1217                               String JavaDoc description) {
1218        super(pos, ruleSet, formatter, description);
1219// boolean chevron = description.startsWith(">>") || ruleSet == this.ruleSet;
1220
// if (chevron || ruleSet == this.ruleSet) {
1221

1222        if (description.equals(">>") || description.equals(">>>") || ruleSet == this.ruleSet) {
1223            byDigits = true;
1224            if (description.equals(">>>")) {
1225              useSpaces = false;
1226            }
1227        } else {
1228            this.ruleSet.makeIntoFractionRuleSet();
1229        }
1230    }
1231
1232    //-----------------------------------------------------------------------
1233
// formatting
1234
//-----------------------------------------------------------------------
1235

1236    /**
1237     * If in "by digits" mode, fills in the substitution one decimal digit
1238     * at a time using the rule set containing this substitution.
1239     * Otherwise, uses the superclass function.
1240     * @param number The number being formatted
1241     * @param toInsertInto The string to insert the result of formatting
1242     * the substitution into
1243     * @param pos The position of the owning rule's rule text in
1244     * toInsertInto
1245     */

1246    public void doSubstitution(double number, StringBuffer JavaDoc toInsertInto, int pos) {
1247        // if we're not in "byDigits" mode, just use the inherited
1248
// doSubstitution() routine
1249
if (!byDigits) {
1250            super.doSubstitution(number, toInsertInto, pos);
1251
1252        // if we're in "byDigits" mode, transform the value into an integer
1253
// by moving the decimal point eight places to the right and
1254
// pulling digits off the right one at a time, formatting each digit
1255
// as an integer using this substitution's owning rule set
1256
// (this is slower, but more accurate, than doing it from the
1257
// other end)
1258
} else {
1259// int numberToFormat = (int)Math.round(transformNumber(number) * Math.pow(
1260
// 10, MAXDECIMALDIGITS));
1261
// long numberToFormat = (long)Math.round(transformNumber(number) * Math.pow(10, MAXDECIMALDIGITS));
1262

1263        // just print to string and then use that
1264
DigitList dl = new DigitList();
1265        dl.set(number, 20, true);
1266
1267            // this flag keeps us from formatting trailing zeros. It starts
1268
// out false because we're pulling from the right, and switches
1269
// to true the first time we encounter a non-zero digit
1270
// boolean doZeros = false;
1271
// System.out.println("class: " + getClass().getName());
1272
// System.out.println("number: " + number + " transformed: " + transformNumber(number));
1273
// System.out.println("formatting " + numberToFormat);
1274
// for (int i = 0; i < MAXDECIMALDIGITS; i++) {
1275
// int digit = (int)(numberToFormat % 10);
1276
// System.out.println(" #: '" + numberToFormat + "'" + " digit '" + digit + "'");
1277
// if (digit != 0 || doZeros) {
1278
// if (doZeros && useSpaces) {
1279
// toInsertInto.insert(pos + this.pos, ' ');
1280
// }
1281
// doZeros = true;
1282
// ruleSet.format(digit, toInsertInto, pos + this.pos);
1283
// }
1284
// numberToFormat /= 10;
1285
// }
1286

1287        boolean pad = false;
1288        while (dl.count > Math.max(0, dl.decimalAt)) {
1289        if (pad && useSpaces) {
1290            toInsertInto.insert(pos + this.pos, ' ');
1291        } else {
1292            pad = true;
1293        }
1294        ruleSet.format(dl.digits[--dl.count] - '0', toInsertInto, pos + this.pos);
1295        }
1296        while (dl.decimalAt < 0) {
1297        if (pad && useSpaces) {
1298            toInsertInto.insert(pos + this.pos, ' ');
1299        } else {
1300            pad = true;
1301        }
1302        ruleSet.format(0, toInsertInto, pos + this.pos);
1303        ++dl.decimalAt;
1304        }
1305    }
1306    }
1307
1308    /**
1309     * Returns the fractional part of the number, which will always be
1310     * zero if it's a long.
1311     * @param number The number being formatted
1312     * @return 0
1313     */

1314    public long transformNumber(long number) {
1315        return 0;
1316    }
1317
1318    /**
1319     * Returns the fractional part of the number.
1320     * @param number The number being formatted.
1321     * @return number - floor(number)
1322     */

1323    public double transformNumber(double number) {
1324        return number - Math.floor(number);
1325    }
1326
1327    //-----------------------------------------------------------------------
1328
// parsing
1329
//-----------------------------------------------------------------------
1330

1331    /**
1332     * If in "by digits" mode, parses the string as if it were a string
1333     * of individual digits; otherwise, uses the superclass function.
1334     * @param text The string to parse
1335     * @param parsePosition Ignored on entry, but updated on exit to point
1336     * to the first unmatched character
1337     * @param baseValue The partial parse result prior to entering this
1338     * function
1339     * @param upperBound Only consider rules with base values lower than
1340     * this when filling in the substitution
1341     * @param lenientParse If true, try matching the text as numerals if
1342     * matching as words doesn't work
1343     * @return If the match was successful, the current partial parse
1344     * result; otherwise new Long(0). The result is either a Long or
1345     * a Double.
1346     */

1347    public Number JavaDoc doParse(String JavaDoc text, ParsePosition parsePosition, double baseValue,
1348                        double upperBound, boolean lenientParse) {
1349        // if we're not in byDigits mode, we can just use the inherited
1350
// doParse()
1351
if (!byDigits) {
1352            return super.doParse(text, parsePosition, baseValue, 0, lenientParse);
1353
1354        // if we ARE in byDigits mode, parse the text one digit at a time
1355
// using this substitution's owning rule set (we do this by setting
1356
// upperBound to 10 when calling doParse() ) until we reach
1357
// nonmatching text
1358
} else {
1359            String JavaDoc workText = new String JavaDoc(text);
1360            ParsePosition workPos = new ParsePosition(1);
1361            double result = 0;
1362        int digit;
1363// double p10 = 0.1;
1364

1365// while (workText.length() > 0 && workPos.getIndex() != 0) {
1366
// workPos.setIndex(0);
1367
// digit = ruleSet.parse(workText, workPos, 10).intValue();
1368
// if (lenientParse && workPos.getIndex() == 0) {
1369
// digit = NumberFormat.getInstance().parse(workText, workPos).intValue();
1370
// }
1371

1372// if (workPos.getIndex() != 0) {
1373
// result += digit * p10;
1374
// p10 /= 10;
1375
// parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex());
1376
// workText = workText.substring(workPos.getIndex());
1377
// while (workText.length() > 0 && workText.charAt(0) == ' ') {
1378
// workText = workText.substring(1);
1379
// parsePosition.setIndex(parsePosition.getIndex() + 1);
1380
// }
1381
// }
1382
// }
1383

1384
1385        DigitList dl = new DigitList();
1386            while (workText.length() > 0 && workPos.getIndex() != 0) {
1387                workPos.setIndex(0);
1388                digit = ruleSet.parse(workText, workPos, 10).intValue();
1389                if (lenientParse && workPos.getIndex() == 0) {
1390                    digit = NumberFormat.getInstance().parse(workText, workPos).intValue();
1391                }
1392
1393                if (workPos.getIndex() != 0) {
1394            dl.append('0'+digit);
1395
1396                    parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex());
1397                    workText = workText.substring(workPos.getIndex());
1398                    while (workText.length() > 0 && workText.charAt(0) == ' ') {
1399                        workText = workText.substring(1);
1400                        parsePosition.setIndex(parsePosition.getIndex() + 1);
1401                    }
1402                }
1403            }
1404        result = dl.count == 0 ? 0 : dl.getDouble();
1405
1406            result = composeRuleValue(result, baseValue);
1407            return new Double JavaDoc(result);
1408        }
1409    }
1410
1411    /**
1412     * Returns the sum of the two partial parse results.
1413     * @param newRuleValue The result of parsing the substitution
1414     * @param oldRuleValue The partial parse result prior to calling
1415     * this function
1416     * @return newRuleValue + oldRuleValue
1417     */

1418    public double composeRuleValue(double newRuleValue, double oldRuleValue) {
1419        return newRuleValue + oldRuleValue;
1420    }
1421
1422    /**
1423     * Not used.
1424     */

1425    public double calcUpperBound(double oldUpperBound) {
1426        return 0; // this value is ignored
1427
}
1428
1429    //-----------------------------------------------------------------------
1430
// simple accessor
1431
//-----------------------------------------------------------------------
1432

1433    /**
1434     * The token character for a FractionalPartSubstitution is &gt;.
1435     * @return '&gt;'
1436     */

1437    char tokenChar() {
1438        return '>';
1439    }
1440}
1441
1442//===================================================================
1443
// AbsoluteValueSubstitution
1444
//===================================================================
1445

1446 /**
1447  * A substitution that formats the absolute value of the number.
1448  * This substition is represented by &gt;&gt; in a negative-number rule.
1449  */

1450class AbsoluteValueSubstitution extends NFSubstitution {
1451    //-----------------------------------------------------------------------
1452
// constants
1453
//-----------------------------------------------------------------------
1454

1455    /**
1456     * Puts a copyright in the .class file
1457     */

1458    private static final String JavaDoc copyrightNotice
1459        = "Copyright \u00a91997-1998 IBM Corp. All rights reserved.";
1460
1461    //-----------------------------------------------------------------------
1462
// construction
1463
//-----------------------------------------------------------------------
1464

1465    /**
1466     * Constructs an AbsoluteValueSubstitution. This just uses the
1467     * superclass constructor.
1468     */

1469    AbsoluteValueSubstitution(int pos,
1470                              NFRuleSet ruleSet,
1471                              RuleBasedNumberFormat formatter,
1472                              String JavaDoc description) {
1473        super(pos, ruleSet, formatter, description);
1474    }
1475
1476    //-----------------------------------------------------------------------
1477
// formatting
1478
//-----------------------------------------------------------------------
1479

1480    /**
1481     * Returns the absolute value of the number.
1482     * @param number The number being formatted.
1483     * @return abs(number)
1484     */

1485    public long transformNumber(long number) {
1486        return Math.abs(number);
1487    }
1488
1489    /**
1490     * Returns the absolute value of the number.
1491     * @param number The number being formatted.
1492     * @return abs(number)
1493     */

1494    public double transformNumber(double number) {
1495        return Math.abs(number);
1496    }
1497
1498    //-----------------------------------------------------------------------
1499
// parsing
1500
//-----------------------------------------------------------------------
1501

1502    /**
1503     * Returns the addtive inverse of the result of parsing the
1504     * substitution (this supersedes the earlier partial result)
1505     * @param newRuleValue The result of parsing the substitution
1506     * @param oldRuleValue The partial parse result prior to calling
1507     * this function
1508     * @return -newRuleValue
1509     */

1510    public double composeRuleValue(double newRuleValue, double oldRuleValue) {
1511        return -newRuleValue;
1512    }
1513
1514    /**
1515     * Sets the upper bound beck up to consider all rules
1516     * @param oldUpperBound Ignored.
1517     * @return Double.MAX_VALUE
1518     */

1519    public double calcUpperBound(double oldUpperBound) {
1520        return Double.MAX_VALUE;
1521    }
1522
1523    //-----------------------------------------------------------------------
1524
// simple accessor
1525
//-----------------------------------------------------------------------
1526

1527    /**
1528     * The token character for an AbsoluteValueSubstitution is &gt;
1529     * @return '&gt;'
1530     */

1531    char tokenChar() {
1532        return '>';
1533    }
1534}
1535
1536//===================================================================
1537
// NumeratorSubstitution
1538
//===================================================================
1539

1540/**
1541 * A substitution that multiplies the number being formatted (which is
1542 * between 0 and 1) by the base value of the rule that owns it and
1543 * formats the result. It is represented by &lt;&lt; in the rules
1544 * in a fraction rule set.
1545 */

1546class NumeratorSubstitution extends NFSubstitution {
1547    //-----------------------------------------------------------------------
1548
// constants
1549
//-----------------------------------------------------------------------
1550

1551    /**
1552     * Puts a copyright in the .class file
1553     */

1554    private static final String JavaDoc copyrightNotice
1555        = "Copyright \u00a91997-1998 IBM Corp. All rights reserved.";
1556
1557    //-----------------------------------------------------------------------
1558
// data members
1559
//-----------------------------------------------------------------------
1560

1561    /**
1562     * The denominator of the fraction we're finding the numerator for.
1563     * (The base value of the rule that owns this substitution.)
1564     */

1565    double denominator;
1566
1567    /**
1568     * True if we format leading zeros (this is a hack for Hebrew spellout)
1569     */

1570    boolean withZeros;
1571
1572    //-----------------------------------------------------------------------
1573
// construction
1574
//-----------------------------------------------------------------------
1575

1576    /**
1577     * Constructs a NumberatorSubstitution. In addition to the inherited
1578     * fields, a NumeratorSubstitution keeps track of a denominator, which
1579     * is merely the base value of the rule that owns it.
1580     */

1581    NumeratorSubstitution(int pos,
1582                          double denominator,
1583                          NFRuleSet ruleSet,
1584                          RuleBasedNumberFormat formatter,
1585                          String JavaDoc description) {
1586        super(pos, ruleSet, formatter, fixdesc(description));
1587
1588        // this substitution's behavior depends on the rule's base value
1589
// Rather than keeping a backpointer to the rule, we copy its
1590
// base value here
1591
this.denominator = denominator;
1592        
1593        this.withZeros = description.endsWith("<<");
1594    }
1595
1596    static String JavaDoc fixdesc(String JavaDoc description) {
1597        return description.endsWith("<<")
1598            ? description.substring(0,description.length()-1)
1599            : description;
1600    }
1601
1602    //-----------------------------------------------------------------------
1603
// boilerplate
1604
//-----------------------------------------------------------------------
1605

1606    /**
1607     * Tests two NumeratorSubstitutions for equality
1608     * @param that The other NumeratorSubstitution
1609     * @return true if the two objects are functionally equivalent
1610     */

1611    public boolean equals(Object JavaDoc that) {
1612        if (super.equals(that)) {
1613            NumeratorSubstitution that2 = (NumeratorSubstitution)that;
1614            return denominator == that2.denominator;
1615        } else {
1616            return false;
1617        }
1618    }
1619
1620    //-----------------------------------------------------------------------
1621
// formatting
1622
//-----------------------------------------------------------------------
1623

1624    /**
1625     * Performs a mathematical operation on the number, formats it using
1626     * either ruleSet or decimalFormat, and inserts the result into
1627     * toInsertInto.
1628     * @param number The number being formatted.
1629     * @param toInsertInto The string we insert the result into
1630     * @param pos The position in toInsertInto where the owning rule's
1631     * rule text begins (this value is added to this substitution's
1632     * position to determine exactly where to insert the new text)
1633     */

1634    public void doSubstitution(double number, StringBuffer JavaDoc toInsertInto, int pos) {
1635        // perform a transformation on the number being formatted that
1636
// is dependent on the type of substitution this is
1637
String JavaDoc s = toInsertInto.toString();
1638        double numberToFormat = transformNumber(number);
1639
1640        if (withZeros && ruleSet != null) {
1641            // if there are leading zeros in the decimal expansion then emit them
1642
long nf = (long)numberToFormat;
1643            int len = toInsertInto.length();
1644            while ((nf *= 10) < denominator) {
1645                toInsertInto.insert(pos + this.pos, ' ');
1646                ruleSet.format(0, toInsertInto, pos + this.pos);
1647            }
1648            pos += toInsertInto.length() - len;
1649        }
1650
1651        // if the result is an integer, from here on out we work in integer
1652
// space (saving time and memory and preserving accuracy)
1653
if (numberToFormat == Math.floor(numberToFormat) && ruleSet != null) {
1654            ruleSet.format((long)numberToFormat, toInsertInto, pos + this.pos);
1655
1656            // if the result isn't an integer, then call either our rule set's
1657
// format() method or our DecimalFormat's format() method to
1658
// format the result
1659
} else {
1660            if (ruleSet != null) {
1661                ruleSet.format(numberToFormat, toInsertInto, pos + this.pos);
1662            } else {
1663                toInsertInto.insert(pos + this.pos, numberFormat.format(numberToFormat));
1664            }
1665        }
1666    }
1667
1668    /**
1669     * Returns the number being formatted times the denominator.
1670     * @param number The number being formatted
1671     * @return number * denominator
1672     */

1673    public long transformNumber(long number) {
1674        return Math.round(number * denominator);
1675    }
1676
1677    /**
1678     * Returns the number being formatted times the denominator.
1679     * @param number The number being formatted
1680     * @return number * denominator
1681     */

1682    public double transformNumber(double number) {
1683        return Math.round(number * denominator);
1684    }
1685
1686    //-----------------------------------------------------------------------
1687
// parsing
1688
//-----------------------------------------------------------------------
1689

1690    /**
1691     * Dispatches to the inherited version of this function, but makes
1692     * sure that lenientParse is off.
1693     */

1694    public Number JavaDoc doParse(String JavaDoc text, ParsePosition parsePosition, double baseValue,
1695                        double upperBound, boolean lenientParse) {
1696        // we don't have to do anything special to do the parsing here,
1697
// but we have to turn lenient parsing off-- if we leave it on,
1698
// it SERIOUSLY messes up the algorithm
1699

1700        // if withZeros is true, we need to count the zeros
1701
// and use that to adjust the parse result
1702
int zeroCount = 0;
1703        if (withZeros) {
1704            String JavaDoc workText = new String JavaDoc(text);
1705            ParsePosition workPos = new ParsePosition(1);
1706            int digit;
1707
1708            while (workText.length() > 0 && workPos.getIndex() != 0) {
1709                workPos.setIndex(0);
1710                digit = ruleSet.parse(workText, workPos, 1).intValue(); // parse zero or nothing at all
1711
if (workPos.getIndex() == 0) {
1712                    // we failed, either there were no more zeros, or the number was formatted with digits
1713
// either way, we're done
1714
break;
1715                }
1716
1717                ++zeroCount;
1718                parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex());
1719                workText = workText.substring(workPos.getIndex());
1720                while (workText.length() > 0 && workText.charAt(0) == ' ') {
1721                    workText = workText.substring(1);
1722                    parsePosition.setIndex(parsePosition.getIndex() + 1);
1723                }
1724            }
1725
1726            text = text.substring(parsePosition.getIndex()); // arrgh!
1727
parsePosition.setIndex(0);
1728        }
1729
1730        // we've parsed off the zeros, now let's parse the rest from our current position
1731
Number JavaDoc result = super.doParse(text, parsePosition, withZeros ? 1 : baseValue, upperBound, false);
1732
1733        if (withZeros) {
1734            // any base value will do in this case. is there a way to
1735
// force this to not bother trying all the base values?
1736

1737            // compute the 'effective' base and prescale the value down
1738
long n = result.longValue();
1739            long d = 1;
1740            int pow = 0;
1741            while (d <= n) {
1742                d *= 10;
1743                ++pow;
1744            }
1745            // now add the zeros
1746
while (zeroCount > 0) {
1747                d *= 10;
1748                --zeroCount;
1749            }
1750            // d is now our true denominator
1751
result = new Double JavaDoc(n/(double)d);
1752        }
1753
1754        return result;
1755    }
1756
1757    /**
1758     * Divides the result of parsing the substitution by the partial
1759     * parse result.
1760     * @param newRuleValue The result of parsing the substitution
1761     * @param oldRuleValue The owning rule's base value
1762     * @return newRuleValue / oldRuleValue
1763     */

1764    public double composeRuleValue(double newRuleValue, double oldRuleValue) {
1765        return newRuleValue / oldRuleValue;
1766    }
1767
1768    /**
1769     * Sets the uper bound down to this rule's base value
1770     * @param oldUpperBound Ignored
1771     * @return The base value of the rule owning this substitution
1772     */

1773    public double calcUpperBound(double oldUpperBound) {
1774        return denominator;
1775    }
1776
1777    //-----------------------------------------------------------------------
1778
// simple accessor
1779
//-----------------------------------------------------------------------
1780

1781    /**
1782     * The token character for a NumeratorSubstitution is &lt;
1783     * @return '&lt;'
1784     */

1785    char tokenChar() {
1786        return '<';
1787    }
1788}
1789
1790//===================================================================
1791
// NullSubstitution
1792
//===================================================================
1793

1794/**
1795 * A substitution which does nothing. This class exists just to simplify
1796 * the logic in some other routines so that they don't have to worry
1797 * about how many substitutions a rule has.
1798 */

1799class NullSubstitution extends NFSubstitution {
1800    //-----------------------------------------------------------------------
1801
// constants
1802
//-----------------------------------------------------------------------
1803

1804    /**
1805     * Puts a copyright in the .class file
1806     */

1807    private static final String JavaDoc copyrightNotice
1808        = "Copyright \u00a91997-1998 IBM Corp. All rights reserved.";
1809
1810    //-----------------------------------------------------------------------
1811
// construction
1812
//-----------------------------------------------------------------------
1813

1814    /**
1815     * Constructs a NullSubstitution. This just delegates to the superclass
1816     * constructor, but the only value we really care about is the position.
1817     */

1818    NullSubstitution(int pos,
1819                     NFRuleSet ruleSet,
1820                     RuleBasedNumberFormat formatter,
1821                     String JavaDoc description) {
1822        super(pos, ruleSet, formatter, description);
1823    }
1824
1825    //-----------------------------------------------------------------------
1826
// boilerplate
1827
//-----------------------------------------------------------------------
1828

1829    /**
1830     * Only checks for class equality
1831     */

1832    public boolean equals(Object JavaDoc that) {
1833        return super.equals(that);
1834    }
1835
1836    /**
1837     * NullSubstitutions don't show up in the textual representation
1838     * of a RuleBasedNumberFormat
1839     */

1840    public String JavaDoc toString() {
1841        return "";
1842    }
1843
1844    //-----------------------------------------------------------------------
1845
// formatting
1846
//-----------------------------------------------------------------------
1847

1848    /**
1849     * Does nothing.
1850     */

1851    public void doSubstitution(long number, StringBuffer JavaDoc toInsertInto, int pos) {
1852    }
1853
1854    /**
1855     * Does nothing.
1856     */

1857    public void doSubstitution(double number, StringBuffer JavaDoc toInsertInto, int pos) {
1858    }
1859
1860    /**
1861     * Never called.
1862     */

1863    ///CLOVER:OFF
1864
public long transformNumber(long number) {
1865        return 0;
1866    }
1867    ///CLOVER:ON
1868

1869    /**
1870     * Never called.
1871     */

1872    ///CLOVER:OFF
1873
public double transformNumber(double number) {
1874        return 0;
1875    }
1876    ///CLOVER:ON
1877

1878    //-----------------------------------------------------------------------
1879
// parsing
1880
//-----------------------------------------------------------------------
1881

1882    /**
1883     * Returns the partial parse result unchanged
1884     */

1885    public Number JavaDoc doParse(String JavaDoc text, ParsePosition parsePosition, double baseValue,
1886                        double upperBound, boolean lenientParse) {
1887        if (baseValue == (long)baseValue) {
1888            return new Long JavaDoc((long)baseValue);
1889        } else {
1890            return new Double JavaDoc(baseValue);
1891        }
1892    }
1893
1894    /**
1895     * Never called.
1896     */

1897    ///CLOVER:OFF
1898
public double composeRuleValue(double newRuleValue, double oldRuleValue) {
1899        return 0;
1900    }
1901    ///CLOVER:ON
1902

1903    /**
1904     * Never called.
1905     */

1906    ///CLOVER:OFF
1907
public double calcUpperBound(double oldUpperBound) {
1908        return 0;
1909    }
1910    ///CLOVER:ON
1911

1912    //-----------------------------------------------------------------------
1913
// simple accessors
1914
//-----------------------------------------------------------------------
1915

1916    /**
1917     * Returns true (this _is_ a NullSubstitution).
1918     * @return true
1919     */

1920    public boolean isNullSubstitution() {
1921        return true;
1922    }
1923
1924    /**
1925     * Never called.
1926     */

1927    ///CLOVER:OFF
1928
char tokenChar() {
1929        return ' ';
1930    }
1931    ///CLOVER:ON
1932
}
1933
1934
Popular Tags