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