2 *******************************************************************************
\r
3 * Copyright (C) 1996-2010, International Business Machines Corporation and *
\r
4 * others. All Rights Reserved. *
\r
5 *******************************************************************************
\r
7 package com.ibm.icu.text;
\r
9 import java.text.ParsePosition;
\r
11 //===================================================================
\r
12 // NFSubstitution (abstract base class)
\r
13 //===================================================================
\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
21 abstract class NFSubstitution {
\r
22 //-----------------------------------------------------------------------
\r
24 //-----------------------------------------------------------------------
\r
27 * The substitution's position in the rule text of the rule that owns it
\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
35 NFRuleSet ruleSet = null;
\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
41 DecimalFormat numberFormat = null;
\r
44 * Link to the RBNF so that we can access its decimalFormat if need be.
\r
46 RuleBasedNumberFormat rbnf = null;
\r
48 //-----------------------------------------------------------------------
\r
50 //-----------------------------------------------------------------------
\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
63 * @param formatter The RuleBasedNumberFormat that ultimately owns
\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
70 public static NFSubstitution makeSubstitution(int pos,
\r
72 NFRule rulePredecessor,
\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
81 switch (description.charAt(0)) {
\r
82 // if the description begins with '<'...
\r
84 // throw an exception if the rule is a negative number
\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
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
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
110 // otherwise, return a MultiplierSubstitution
\r
112 return new MultiplierSubstitution(pos, rule.getDivisor(), ruleSet,
\r
113 formatter, description);
\r
116 // if the description begins with '>'...
\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
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
132 // if the rule set owning the rule is a fraction rule set,
\r
133 // throw an exception
\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
143 // otherwise, return a ModulusSubstitution
\r
145 return new ModulusSubstitution(pos, rule.getDivisor(), rulePredecessor,
\r
146 ruleSet, formatter, description);
\r
149 // if the description begins with '=', always return a
\r
150 // SameValueSubstitution
\r
152 return new SameValueSubstitution(pos, ruleSet, formatter, description);
\r
154 // and if it's anything else, throw an exception
\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
160 throw new IllegalArgumentException("Illegal substitution character");
\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
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
175 NFSubstitution(int pos,
\r
177 RuleBasedNumberFormat formatter,
\r
178 String description) {
\r
179 // initialize the substitution's position in its parent rule
\r
181 this.rbnf = formatter;
\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
191 else if (description.length() != 0) {
\r
192 throw new IllegalArgumentException("Illegal substitution syntax");
\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
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
205 else if (description.charAt(0) == '%') {
\r
206 this.ruleSet = formatter.findRuleSet(description);
\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
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
228 // and of the description is none of these things, it's a syntax error
\r
230 throw new IllegalArgumentException("Illegal substitution syntax");
\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
238 * @param radix The radix of the divisor
\r
239 * @param exponent The exponent of the divisor
\r
241 public void setDivisor(int radix, int exponent) {
\r
242 // a no-op for all substitutions except multiplier and modulus substitutions
\r
245 //-----------------------------------------------------------------------
\r
247 //-----------------------------------------------------------------------
\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
254 public boolean equals(Object that) {
\r
255 // compare class and all of the fields all substitutions have
\r
257 if (this.getClass() == that.getClass()) {
\r
258 NFSubstitution that2 = (NFSubstitution)that;
\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
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
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
281 return tokenChar() + numberFormat.toPattern() + tokenChar();
\r
285 //-----------------------------------------------------------------------
\r
287 //-----------------------------------------------------------------------
\r
290 * Performs a mathematical operation on the number, formats it using
\r
291 * either ruleSet or decimalFormat, and inserts the result into
\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
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
306 ruleSet.format(numberToFormat, toInsertInto, position + pos);
\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
317 toInsertInto.insert(position + pos, numberFormat.format(numberToFormat));
\r
322 * Performs a mathematical operation on the number, formats it using
\r
323 * either ruleSet or decimalFormat, and inserts the result into
\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
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
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
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
345 if (ruleSet != null) {
\r
346 ruleSet.format(numberToFormat, toInsertInto, position + pos);
\r
348 toInsertInto.insert(position + this.pos, numberFormat.format(numberToFormat));
\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
359 * @param number The number being formatted
\r
360 * @return The result of performing the opreration on the number
\r
362 public abstract long transformNumber(long number);
\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
370 * @param number The number being formatted
\r
371 * @return The result of performing the opreration on the number
\r
373 public abstract double transformNumber(double number);
\r
375 //-----------------------------------------------------------------------
\r
377 //-----------------------------------------------------------------------
\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
408 public Number doParse(String text, ParsePosition parsePosition, double baseValue,
\r
409 double upperBound, boolean lenientParse) {
\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
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
432 // ...or use our DecimalFormat to parse the text
\r
434 tempResult = numberFormat.parse(text, parsePosition);
\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
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
466 result = composeRuleValue(result, baseValue);
\r
467 if (result == (long)result) {
\r
468 return new Long((long)result);
\r
470 return new Double(result);
\r
473 // if the parse was UNsuccessful, return 0
\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
491 public abstract double composeRuleValue(double newRuleValue, double oldRuleValue);
\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
500 public abstract double calcUpperBound(double oldUpperBound);
\r
502 //-----------------------------------------------------------------------
\r
503 // simple accessors
\r
504 //-----------------------------------------------------------------------
\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
510 public final int getPos() {
\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
519 abstract char tokenChar();
\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
527 public boolean isNullSubstitution() {
\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
537 public boolean isModulusSubstitution() {
\r
542 //===================================================================
\r
543 // SameValueSubstitution
\r
544 //===================================================================
\r
547 * A substitution that passes the value passed to it through unchanged.
\r
548 * Represented by == in rule descriptions.
\r
550 class SameValueSubstitution extends NFSubstitution {
\r
551 //-----------------------------------------------------------------------
\r
553 //-----------------------------------------------------------------------
\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
561 SameValueSubstitution(int pos,
\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
571 //-----------------------------------------------------------------------
\r
573 //-----------------------------------------------------------------------
\r
576 * Returns "number" unchanged.
\r
579 public long transformNumber(long number) {
\r
584 * Returns "number" unchanged.
\r
587 public double transformNumber(double number) {
\r
591 //-----------------------------------------------------------------------
\r
593 //-----------------------------------------------------------------------
\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
602 * @return newRuleValue
\r
604 public double composeRuleValue(double newRuleValue, double oldRuleValue) {
\r
605 return newRuleValue;
\r
609 * SameValueSubstitution doesn't change the upper bound.
\r
610 * @param oldUpperBound The current upper bound.
\r
611 * @return oldUpperBound
\r
613 public double calcUpperBound(double oldUpperBound) {
\r
614 return oldUpperBound;
\r
617 //-----------------------------------------------------------------------
\r
619 //-----------------------------------------------------------------------
\r
622 * The token character for a SameValueSubstitution is =.
\r
630 //===================================================================
\r
631 // MultiplierSubstitution
\r
632 //===================================================================
\r
635 * A substitution that divides the number being formatted by the rule's
\r
636 * divisor and formats the quotient. Represented by << in normal
\r
639 class MultiplierSubstitution extends NFSubstitution {
\r
640 //-----------------------------------------------------------------------
\r
642 //-----------------------------------------------------------------------
\r
645 * The divisor of the rule that owns this substitution.
\r
649 //-----------------------------------------------------------------------
\r
651 //-----------------------------------------------------------------------
\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
663 MultiplierSubstitution(int pos,
\r
666 RuleBasedNumberFormat formatter,
\r
667 String description) {
\r
668 super(pos, ruleSet, formatter, description);
\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
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
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
686 public void setDivisor(int radix, int exponent) {
\r
687 divisor = Math.pow(radix, exponent);
\r
689 if (divisor == 0) {
\r
690 throw new IllegalStateException("Substitution with divisor 0");
\r
694 //-----------------------------------------------------------------------
\r
696 //-----------------------------------------------------------------------
\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
703 public boolean equals(Object that) {
\r
704 if (super.equals(that)) {
\r
705 MultiplierSubstitution that2 = (MultiplierSubstitution)that;
\r
707 return divisor == that2.divisor;
\r
713 //-----------------------------------------------------------------------
\r
715 //-----------------------------------------------------------------------
\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
722 public long transformNumber(long number) {
\r
723 return (long)Math.floor(number / divisor);
\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
735 public double transformNumber(double number) {
\r
736 if (ruleSet == null) {
\r
737 return number / divisor;
\r
739 return Math.floor(number / divisor);
\r
743 //-----------------------------------------------------------------------
\r
745 //-----------------------------------------------------------------------
\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
754 * @return newRuleValue * divisor
\r
756 public double composeRuleValue(double newRuleValue, double oldRuleValue) {
\r
757 return newRuleValue * divisor;
\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
765 public double calcUpperBound(double oldUpperBound) {
\r
769 //-----------------------------------------------------------------------
\r
771 //-----------------------------------------------------------------------
\r
774 * The token character for a multiplier substitution is <.
\r
782 //===================================================================
\r
783 // ModulusSubstitution
\r
784 //===================================================================
\r
787 * A substitution that divides the number being formatted by the its rule's
\r
788 * divisor and formats the remainder. Represented by ">>" in a
\r
791 class ModulusSubstitution extends NFSubstitution {
\r
792 //-----------------------------------------------------------------------
\r
794 //-----------------------------------------------------------------------
\r
797 * The divisor of the rule owning this substitution
\r
802 * If this is a >>> substitution, the rule to use to format
\r
803 * the substitution value. Otherwise, null.
\r
807 //-----------------------------------------------------------------------
\r
809 //-----------------------------------------------------------------------
\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
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
824 ModulusSubstitution(int pos,
\r
826 NFRule rulePredecessor,
\r
828 RuleBasedNumberFormat formatter,
\r
829 String description) {
\r
830 super(pos, ruleSet, formatter, description);
\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
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
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
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
860 public void setDivisor(int radix, int exponent) {
\r
861 divisor = Math.pow(radix, exponent);
\r
863 if (divisor == 0) { // this will cause recursion
\r
864 throw new IllegalStateException("Substitution with bad divisor");
\r
868 //-----------------------------------------------------------------------
\r
870 //-----------------------------------------------------------------------
\r
873 * Augments the inherited equals() function by comparing divisors and
\r
875 * @param that The other substitution
\r
876 * @return true if the two substitutions are functionally equivalent
\r
878 public boolean equals(Object that) {
\r
879 if (super.equals(that)) {
\r
880 ModulusSubstitution that2 = (ModulusSubstitution)that;
\r
882 return divisor == that2.divisor;
\r
888 //-----------------------------------------------------------------------
\r
890 //-----------------------------------------------------------------------
\r
893 * If this is a >>> 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
898 * @param position The position of the rule text in toInsertInto
\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
907 // a >>> substitution goes straight to a particular rule to
\r
908 // format the substitution value
\r
910 long numberToFormat = transformNumber(number);
\r
911 ruleToUse.doFormat(numberToFormat, toInsertInto, position + pos);
\r
916 * If this is a >>> 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
921 * @param position The position of the rule text in toInsertInto
\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
930 // a >>> substitution goes straight to a particular rule to
\r
931 // format the substitution value
\r
933 double numberToFormat = transformNumber(number);
\r
935 ruleToUse.doFormat(numberToFormat, toInsertInto, position + pos);
\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
945 public long transformNumber(long number) {
\r
946 return (long)Math.floor(number % divisor);
\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
955 public double transformNumber(double number) {
\r
956 return Math.floor(number % divisor);
\r
959 //-----------------------------------------------------------------------
\r
961 //-----------------------------------------------------------------------
\r
964 * If this is a >>> 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
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
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
983 Number tempResult = ruleToUse.doParse(text, parsePosition, false, upperBound);
\r
985 if (parsePosition.getIndex() != 0) {
\r
986 double result = tempResult.doubleValue();
\r
988 result = composeRuleValue(result, baseValue);
\r
989 if (result == (long)result) {
\r
990 return new Long((long)result);
\r
992 return new Double(result);
\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
1011 * @return (oldRuleValue - (oldRuleValue % divisor)) + newRuleValue
\r
1013 public double composeRuleValue(double newRuleValue, double oldRuleValue) {
\r
1014 return (oldRuleValue - (oldRuleValue % divisor)) + newRuleValue;
\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
1022 public double calcUpperBound(double oldUpperBound) {
\r
1026 //-----------------------------------------------------------------------
\r
1027 // simple accessors
\r
1028 //-----------------------------------------------------------------------
\r
1031 * Returns true. This _is_ a ModulusSubstitution.
\r
1034 public boolean isModulusSubstitution() {
\r
1039 * The token character of a ModulusSubstitution is >.
\r
1042 char tokenChar() {
\r
1047 //===================================================================
\r
1048 // IntegralPartSubstitution
\r
1049 //===================================================================
\r
1052 * A substitution that formats the number's integral part. This is
\r
1053 * represented by << in a fraction rule.
\r
1055 class IntegralPartSubstitution extends NFSubstitution {
\r
1056 //-----------------------------------------------------------------------
\r
1058 //-----------------------------------------------------------------------
\r
1061 * Constructs an IntegralPartSubstitution. This just calls
\r
1062 * the superclass constructor.
\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
1071 //-----------------------------------------------------------------------
\r
1073 //-----------------------------------------------------------------------
\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
1081 public long transformNumber(long number) {
\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
1090 public double transformNumber(double number) {
\r
1091 return Math.floor(number);
\r
1094 //-----------------------------------------------------------------------
\r
1096 //-----------------------------------------------------------------------
\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
1108 public double composeRuleValue(double newRuleValue, double oldRuleValue) {
\r
1109 return newRuleValue + oldRuleValue;
\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
1118 public double calcUpperBound(double oldUpperBound) {
\r
1119 return Double.MAX_VALUE;
\r
1122 //-----------------------------------------------------------------------
\r
1123 // simple accessor
\r
1124 //-----------------------------------------------------------------------
\r
1127 * An IntegralPartSubstitution's token character is <
\r
1130 char tokenChar() {
\r
1135 //===================================================================
\r
1136 // FractionalPartSubstitution
\r
1137 //===================================================================
\r
1140 * A substitution that formats the fractional part of a number. This is
\r
1141 * represented by >> in a fraction rule.
\r
1143 class FractionalPartSubstitution extends NFSubstitution {
\r
1144 //-----------------------------------------------------------------------
\r
1146 //-----------------------------------------------------------------------
\r
1149 * true if this substitution should have the default "by digits"
\r
1150 * behavior, false otherwise
\r
1152 private boolean byDigits = false;
\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
1158 private boolean useSpaces = true;
\r
1161 * The largest number of digits after the decimal point that this
\r
1162 * object will show in "by digits" mode
\r
1164 //private static final int MAXDECIMALDIGITS = 18; // 8
\r
1166 //-----------------------------------------------------------------------
\r
1168 //-----------------------------------------------------------------------
\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
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
1183 if (description.equals(">>") || description.equals(">>>") || ruleSet == this.ruleSet) {
\r
1185 if (description.equals(">>>")) {
\r
1186 useSpaces = false;
\r
1189 this.ruleSet.makeIntoFractionRuleSet();
\r
1193 //-----------------------------------------------------------------------
\r
1195 //-----------------------------------------------------------------------
\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
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
1211 super.doSubstitution(number, toInsertInto, position);
\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
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
1224 // just print to string and then use that
\r
1225 DigitList dl = new DigitList();
\r
1226 dl.set(number, 20, true);
\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
1242 // doZeros = true;
\r
1243 // ruleSet.format(digit, toInsertInto, pos + this.pos);
\r
1245 // numberToFormat /= 10;
\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
1255 ruleSet.format(dl.digits[--dl.count] - '0', toInsertInto, position + pos);
\r
1257 while (dl.decimalAt < 0) {
\r
1258 if (pad && useSpaces) {
\r
1259 toInsertInto.insert(position + pos, ' ');
\r
1263 ruleSet.format(0, toInsertInto, position + pos);
\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
1275 public long transformNumber(long number) {
\r
1280 * Returns the fractional part of the number.
\r
1281 * @param number The number being formatted.
\r
1282 * @return number - floor(number)
\r
1284 public double transformNumber(double number) {
\r
1285 return number - Math.floor(number);
\r
1288 //-----------------------------------------------------------------------
\r
1290 //-----------------------------------------------------------------------
\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
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
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
1313 return super.doParse(text, parsePosition, baseValue, 0, lenientParse);
\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
1320 String workText = text;
\r
1321 ParsePosition workPos = new ParsePosition(1);
\r
1322 double result = 0;
\r
1324 // double p10 = 0.1;
\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
1333 // if (workPos.getIndex() != 0) {
\r
1334 // result += digit * p10;
\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
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
1353 digit = n.intValue();
\r
1357 if (workPos.getIndex() != 0) {
\r
1358 dl.append('0'+digit);
\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
1368 result = dl.count == 0 ? 0 : dl.getDouble();
\r
1370 result = composeRuleValue(result, baseValue);
\r
1371 return new Double(result);
\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
1380 * @return newRuleValue + oldRuleValue
\r
1382 public double composeRuleValue(double newRuleValue, double oldRuleValue) {
\r
1383 return newRuleValue + oldRuleValue;
\r
1389 public double calcUpperBound(double oldUpperBound) {
\r
1390 return 0; // this value is ignored
\r
1393 //-----------------------------------------------------------------------
\r
1394 // simple accessor
\r
1395 //-----------------------------------------------------------------------
\r
1398 * The token character for a FractionalPartSubstitution is >.
\r
1401 char tokenChar() {
\r
1406 //===================================================================
\r
1407 // AbsoluteValueSubstitution
\r
1408 //===================================================================
\r
1411 * A substitution that formats the absolute value of the number.
\r
1412 * This substition is represented by >> in a negative-number rule.
\r
1414 class AbsoluteValueSubstitution extends NFSubstitution {
\r
1415 //-----------------------------------------------------------------------
\r
1417 //-----------------------------------------------------------------------
\r
1420 * Constructs an AbsoluteValueSubstitution. This just uses the
\r
1421 * superclass constructor.
\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
1430 //-----------------------------------------------------------------------
\r
1432 //-----------------------------------------------------------------------
\r
1435 * Returns the absolute value of the number.
\r
1436 * @param number The number being formatted.
\r
1437 * @return abs(number)
\r
1439 public long transformNumber(long number) {
\r
1440 return Math.abs(number);
\r
1444 * Returns the absolute value of the number.
\r
1445 * @param number The number being formatted.
\r
1446 * @return abs(number)
\r
1448 public double transformNumber(double number) {
\r
1449 return Math.abs(number);
\r
1452 //-----------------------------------------------------------------------
\r
1454 //-----------------------------------------------------------------------
\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
1462 * @return -newRuleValue
\r
1464 public double composeRuleValue(double newRuleValue, double oldRuleValue) {
\r
1465 return -newRuleValue;
\r
1469 * Sets the upper bound beck up to consider all rules
\r
1470 * @param oldUpperBound Ignored.
\r
1471 * @return Double.MAX_VALUE
\r
1473 public double calcUpperBound(double oldUpperBound) {
\r
1474 return Double.MAX_VALUE;
\r
1477 //-----------------------------------------------------------------------
\r
1478 // simple accessor
\r
1479 //-----------------------------------------------------------------------
\r
1482 * The token character for an AbsoluteValueSubstitution is >
\r
1485 char tokenChar() {
\r
1490 //===================================================================
\r
1491 // NumeratorSubstitution
\r
1492 //===================================================================
\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 << in the rules
\r
1498 * in a fraction rule set.
\r
1500 class NumeratorSubstitution extends NFSubstitution {
\r
1501 //-----------------------------------------------------------------------
\r
1503 //-----------------------------------------------------------------------
\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
1509 double denominator;
\r
1512 * True if we format leading zeros (this is a hack for Hebrew spellout)
\r
1514 boolean withZeros;
\r
1516 //-----------------------------------------------------------------------
\r
1518 //-----------------------------------------------------------------------
\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
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
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
1537 this.withZeros = description.endsWith("<<");
\r
1540 static String fixdesc(String description) {
\r
1541 return description.endsWith("<<")
\r
1542 ? description.substring(0,description.length()-1)
\r
1546 //-----------------------------------------------------------------------
\r
1548 //-----------------------------------------------------------------------
\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
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
1564 //-----------------------------------------------------------------------
\r
1566 //-----------------------------------------------------------------------
\r
1569 * Performs a mathematical operation on the number, formats it using
\r
1570 * either ruleSet or decimalFormat, and inserts the result into
\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
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
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
1592 position += toInsertInto.length() - len;
\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
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
1604 if (ruleSet != null) {
\r
1605 ruleSet.format(numberToFormat, toInsertInto, position + pos);
\r
1607 toInsertInto.insert(position + pos, numberFormat.format(numberToFormat));
\r
1613 * Returns the number being formatted times the denominator.
\r
1614 * @param number The number being formatted
\r
1615 * @return number * denominator
\r
1617 public long transformNumber(long number) {
\r
1618 return Math.round(number * denominator);
\r
1622 * Returns the number being formatted times the denominator.
\r
1623 * @param number The number being formatted
\r
1624 * @return number * denominator
\r
1626 public double transformNumber(double number) {
\r
1627 return Math.round(number * denominator);
\r
1630 //-----------------------------------------------------------------------
\r
1632 //-----------------------------------------------------------------------
\r
1635 * Dispatches to the inherited version of this function, but makes
\r
1636 * sure that lenientParse is off.
\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
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
1648 String workText = text;
\r
1649 ParsePosition workPos = new ParsePosition(1);
\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
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
1670 text = text.substring(parsePosition.getIndex()); // arrgh!
\r
1671 parsePosition.setIndex(0);
\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
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
1681 // compute the 'effective' base and prescale the value down
\r
1682 long n = result.longValue();
\r
1689 // now add the zeros
\r
1690 while (zeroCount > 0) {
\r
1694 // d is now our true denominator
\r
1695 result = new Double(n/(double)d);
\r
1702 * Divides the result of parsing the substitution by the partial
\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
1708 public double composeRuleValue(double newRuleValue, double oldRuleValue) {
\r
1709 return newRuleValue / oldRuleValue;
\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
1717 public double calcUpperBound(double oldUpperBound) {
\r
1718 return denominator;
\r
1721 //-----------------------------------------------------------------------
\r
1722 // simple accessor
\r
1723 //-----------------------------------------------------------------------
\r
1726 * The token character for a NumeratorSubstitution is <
\r
1729 char tokenChar() {
\r
1734 //===================================================================
\r
1735 // NullSubstitution
\r
1736 //===================================================================
\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
1743 class NullSubstitution extends NFSubstitution {
\r
1744 //-----------------------------------------------------------------------
\r
1746 //-----------------------------------------------------------------------
\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
1752 NullSubstitution(int pos,
\r
1753 NFRuleSet ruleSet,
\r
1754 RuleBasedNumberFormat formatter,
\r
1755 String description) {
\r
1756 super(pos, ruleSet, formatter, description);
\r
1759 //-----------------------------------------------------------------------
\r
1761 //-----------------------------------------------------------------------
\r
1764 * Only checks for class equality
\r
1766 public boolean equals(Object that) {
\r
1767 return super.equals(that);
\r
1771 * NullSubstitutions don't show up in the textual representation
\r
1772 * of a RuleBasedNumberFormat
\r
1774 public String toString() {
\r
1778 //-----------------------------------------------------------------------
\r
1780 //-----------------------------------------------------------------------
\r
1785 public void doSubstitution(long number, StringBuffer toInsertInto, int position) {
\r
1791 public void doSubstitution(double number, StringBuffer toInsertInto, int position) {
\r
1798 public long transformNumber(long number) {
\r
1807 public double transformNumber(double number) {
\r
1812 //-----------------------------------------------------------------------
\r
1814 //-----------------------------------------------------------------------
\r
1817 * Returns the partial parse result unchanged
\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
1824 return new Double(baseValue);
\r
1832 public double composeRuleValue(double newRuleValue, double oldRuleValue) {
\r
1841 public double calcUpperBound(double oldUpperBound) {
\r
1846 //-----------------------------------------------------------------------
\r
1847 // simple accessors
\r
1848 //-----------------------------------------------------------------------
\r
1851 * Returns true (this _is_ a NullSubstitution).
\r
1854 public boolean isNullSubstitution() {
\r
1862 char tokenChar() {
\r