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