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