1/*
2******************************************************************************
3*   Copyright (C) 1997-2010, International Business Machines
4*   Corporation and others.  All Rights Reserved.
5******************************************************************************
6*   file name:  nfsubs.cpp
7*   encoding:   US-ASCII
8*   tab size:   8 (not used)
9*   indentation:4
10*
11* Modification history
12* Date        Name      Comments
13* 10/11/2001  Doug      Ported from ICU4J
14*/
15
16#include <stdio.h>
17#include "unicode/utypeinfo.h"  // for 'typeid' to work
18
19#include "nfsubs.h"
20#include "digitlst.h"
21
22#if U_HAVE_RBNF
23
24static const UChar gLessThan = 0x003c;
25static const UChar gEquals = 0x003d;
26static const UChar gGreaterThan = 0x003e;
27static const UChar gPercent = 0x0025;
28static const UChar gPound = 0x0023;
29static const UChar gZero = 0x0030;
30static const UChar gSpace = 0x0020;
31
32static const UChar gEqualsEquals[] =
33{
34    0x3D, 0x3D, 0
35}; /* "==" */
36static const UChar gGreaterGreaterGreaterThan[] =
37{
38    0x3E, 0x3E, 0x3E, 0
39}; /* ">>>" */
40static const UChar gGreaterGreaterThan[] =
41{
42    0x3E, 0x3E, 0
43}; /* ">>" */
44
45U_NAMESPACE_BEGIN
46
47class SameValueSubstitution : public NFSubstitution {
48public:
49    SameValueSubstitution(int32_t pos,
50        const NFRuleSet* ruleset,
51        const RuleBasedNumberFormat* formatter,
52        const UnicodeString& description,
53        UErrorCode& status);
54
55    virtual int64_t transformNumber(int64_t number) const { return number; }
56    virtual double transformNumber(double number) const { return number; }
57    virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const { return newRuleValue; }
58    virtual double calcUpperBound(double oldUpperBound) const { return oldUpperBound; }
59    virtual UChar tokenChar() const { return (UChar)0x003d; } // '='
60
61public:
62    static UClassID getStaticClassID(void);
63    virtual UClassID getDynamicClassID(void) const;
64};
65
66class MultiplierSubstitution : public NFSubstitution {
67    double divisor;
68    int64_t ldivisor;
69
70public:
71    MultiplierSubstitution(int32_t _pos,
72        double _divisor,
73        const NFRuleSet* _ruleSet,
74        const RuleBasedNumberFormat* formatter,
75        const UnicodeString& description,
76        UErrorCode& status)
77        : NFSubstitution(_pos, _ruleSet, formatter, description, status), divisor(_divisor)
78    {
79        ldivisor = util64_fromDouble(divisor);
80        if (divisor == 0) {
81            status = U_PARSE_ERROR;
82        }
83    }
84
85    virtual void setDivisor(int32_t radix, int32_t exponent, UErrorCode& status) {
86        divisor = uprv_pow(radix, exponent);
87        ldivisor = util64_fromDouble(divisor);
88
89        if(divisor == 0) {
90            status = U_PARSE_ERROR;
91        }
92    }
93
94    virtual UBool operator==(const NFSubstitution& rhs) const;
95
96    virtual int64_t transformNumber(int64_t number) const {
97        return number / ldivisor;
98    }
99
100    virtual double transformNumber(double number) const {
101        if (getRuleSet()) {
102            return uprv_floor(number / divisor);
103        } else {
104            return number/divisor;
105        }
106    }
107
108    virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const {
109        return newRuleValue * divisor;
110    }
111
112    virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor; }
113
114    virtual UChar tokenChar() const { return (UChar)0x003c; } // '<'
115
116public:
117    static UClassID getStaticClassID(void);
118    virtual UClassID getDynamicClassID(void) const;
119};
120
121class ModulusSubstitution : public NFSubstitution {
122    double divisor;
123    int64_t  ldivisor;
124    const NFRule* ruleToUse;
125public:
126    ModulusSubstitution(int32_t pos,
127        double _divisor,
128        const NFRule* rulePredecessor,
129        const NFRuleSet* ruleSet,
130        const RuleBasedNumberFormat* formatter,
131        const UnicodeString& description,
132        UErrorCode& status);
133
134    virtual void setDivisor(int32_t radix, int32_t exponent, UErrorCode& status) {
135        divisor = uprv_pow(radix, exponent);
136        ldivisor = util64_fromDouble(divisor);
137
138        if (divisor == 0) {
139            status = U_PARSE_ERROR;
140        }
141    }
142
143    virtual UBool operator==(const NFSubstitution& rhs) const;
144
145    virtual void doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t pos) const;
146    virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos) const;
147
148    virtual int64_t transformNumber(int64_t number) const { return number % ldivisor; }
149    virtual double transformNumber(double number) const { return uprv_fmod(number, divisor); }
150
151    virtual UBool doParse(const UnicodeString& text,
152        ParsePosition& parsePosition,
153        double baseValue,
154        double upperBound,
155        UBool lenientParse,
156        Formattable& result) const;
157
158    virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const {
159        return oldRuleValue - uprv_fmod(oldRuleValue, divisor) + newRuleValue;
160    }
161
162    virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor; }
163
164    virtual UBool isModulusSubstitution() const { return TRUE; }
165
166    virtual UChar tokenChar() const { return (UChar)0x003e; } // '>'
167
168public:
169    static UClassID getStaticClassID(void);
170    virtual UClassID getDynamicClassID(void) const;
171};
172
173class IntegralPartSubstitution : public NFSubstitution {
174public:
175    IntegralPartSubstitution(int32_t _pos,
176        const NFRuleSet* _ruleSet,
177        const RuleBasedNumberFormat* formatter,
178        const UnicodeString& description,
179        UErrorCode& status)
180        : NFSubstitution(_pos, _ruleSet, formatter, description, status) {}
181
182    virtual int64_t transformNumber(int64_t number) const { return number; }
183    virtual double transformNumber(double number) const { return uprv_floor(number); }
184    virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue + oldRuleValue; }
185    virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX; }
186    virtual UChar tokenChar() const { return (UChar)0x003c; } // '<'
187
188public:
189    static UClassID getStaticClassID(void);
190    virtual UClassID getDynamicClassID(void) const;
191};
192
193class FractionalPartSubstitution : public NFSubstitution {
194    UBool byDigits;
195    UBool useSpaces;
196    enum { kMaxDecimalDigits = 8 };
197public:
198    FractionalPartSubstitution(int32_t pos,
199        const NFRuleSet* ruleSet,
200        const RuleBasedNumberFormat* formatter,
201        const UnicodeString& description,
202        UErrorCode& status);
203
204    virtual UBool operator==(const NFSubstitution& rhs) const;
205
206    virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos) const;
207    virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/) const {}
208    virtual int64_t transformNumber(int64_t /*number*/) const { return 0; }
209    virtual double transformNumber(double number) const { return number - uprv_floor(number); }
210
211    virtual UBool doParse(const UnicodeString& text,
212        ParsePosition& parsePosition,
213        double baseValue,
214        double upperBound,
215        UBool lenientParse,
216        Formattable& result) const;
217
218    virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue + oldRuleValue; }
219    virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0.0; }
220    virtual UChar tokenChar() const { return (UChar)0x003e; } // '>'
221
222public:
223    static UClassID getStaticClassID(void);
224    virtual UClassID getDynamicClassID(void) const;
225};
226
227class AbsoluteValueSubstitution : public NFSubstitution {
228public:
229    AbsoluteValueSubstitution(int32_t _pos,
230        const NFRuleSet* _ruleSet,
231        const RuleBasedNumberFormat* formatter,
232        const UnicodeString& description,
233        UErrorCode& status)
234        : NFSubstitution(_pos, _ruleSet, formatter, description, status) {}
235
236    virtual int64_t transformNumber(int64_t number) const { return number >= 0 ? number : -number; }
237    virtual double transformNumber(double number) const { return uprv_fabs(number); }
238    virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const { return -newRuleValue; }
239    virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX; }
240    virtual UChar tokenChar() const { return (UChar)0x003e; } // '>'
241
242public:
243    static UClassID getStaticClassID(void);
244    virtual UClassID getDynamicClassID(void) const;
245};
246
247class NumeratorSubstitution : public NFSubstitution {
248    double denominator;
249    int64_t ldenominator;
250    UBool withZeros;
251public:
252    static inline UnicodeString fixdesc(const UnicodeString& desc) {
253        if (desc.endsWith(LTLT, 2)) {
254            UnicodeString result(desc, 0, desc.length()-1);
255            return result;
256        }
257        return desc;
258    }
259    NumeratorSubstitution(int32_t _pos,
260        double _denominator,
261        const NFRuleSet* _ruleSet,
262        const RuleBasedNumberFormat* formatter,
263        const UnicodeString& description,
264        UErrorCode& status)
265        : NFSubstitution(_pos, _ruleSet, formatter, fixdesc(description), status), denominator(_denominator)
266    {
267        ldenominator = util64_fromDouble(denominator);
268        withZeros = description.endsWith(LTLT, 2);
269    }
270
271    virtual UBool operator==(const NFSubstitution& rhs) const;
272
273    virtual int64_t transformNumber(int64_t number) const { return number * ldenominator; }
274    virtual double transformNumber(double number) const { return uprv_round(number * denominator); }
275
276    virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/) const {}
277    virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos) const;
278    virtual UBool doParse(const UnicodeString& text,
279        ParsePosition& parsePosition,
280        double baseValue,
281        double upperBound,
282        UBool /*lenientParse*/,
283        Formattable& result) const;
284
285    virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue / oldRuleValue; }
286    virtual double calcUpperBound(double /*oldUpperBound*/) const { return denominator; }
287    virtual UChar tokenChar() const { return (UChar)0x003c; } // '<'
288private:
289    static const UChar LTLT[2];
290
291public:
292    static UClassID getStaticClassID(void);
293    virtual UClassID getDynamicClassID(void) const;
294};
295
296class NullSubstitution : public NFSubstitution {
297public:
298    NullSubstitution(int32_t _pos,
299        const NFRuleSet* _ruleSet,
300        const RuleBasedNumberFormat* formatter,
301        const UnicodeString& description,
302        UErrorCode& status)
303        : NFSubstitution(_pos, _ruleSet, formatter, description, status) {}
304
305    virtual void toString(UnicodeString& /*result*/) const {}
306    virtual void doSubstitution(double /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/) const {}
307    virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/) const {}
308    virtual int64_t transformNumber(int64_t /*number*/) const { return 0; }
309    virtual double transformNumber(double /*number*/) const { return 0; }
310    virtual UBool doParse(const UnicodeString& /*text*/,
311        ParsePosition& /*parsePosition*/,
312        double baseValue,
313        double /*upperBound*/,
314        UBool /*lenientParse*/,
315        Formattable& result) const
316    { result.setDouble(baseValue); return TRUE; }
317    virtual double composeRuleValue(double /*newRuleValue*/, double /*oldRuleValue*/) const { return 0.0; } // never called
318    virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0; } // never called
319    virtual UBool isNullSubstitution() const { return TRUE; }
320    virtual UChar tokenChar() const { return (UChar)0x0020; } // ' ' never called
321
322public:
323    static UClassID getStaticClassID(void);
324    virtual UClassID getDynamicClassID(void) const;
325};
326
327NFSubstitution*
328NFSubstitution::makeSubstitution(int32_t pos,
329                                 const NFRule* rule,
330                                 const NFRule* predecessor,
331                                 const NFRuleSet* ruleSet,
332                                 const RuleBasedNumberFormat* formatter,
333                                 const UnicodeString& description,
334                                 UErrorCode& status)
335{
336    // if the description is empty, return a NullSubstitution
337    if (description.length() == 0) {
338        return new NullSubstitution(pos, ruleSet, formatter, description, status);
339    }
340
341    switch (description.charAt(0)) {
342        // if the description begins with '<'...
343    case gLessThan:
344        // throw an exception if the rule is a negative number
345        // rule
346        if (rule->getBaseValue() == NFRule::kNegativeNumberRule) {
347            // throw new IllegalArgumentException("<< not allowed in negative-number rule");
348            status = U_PARSE_ERROR;
349            return NULL;
350        }
351
352        // if the rule is a fraction rule, return an
353        // IntegralPartSubstitution
354        else if (rule->getBaseValue() == NFRule::kImproperFractionRule
355            || rule->getBaseValue() == NFRule::kProperFractionRule
356            || rule->getBaseValue() == NFRule::kMasterRule) {
357            return new IntegralPartSubstitution(pos, ruleSet, formatter, description, status);
358        }
359
360        // if the rule set containing the rule is a fraction
361        // rule set, return a NumeratorSubstitution
362        else if (ruleSet->isFractionRuleSet()) {
363            return new NumeratorSubstitution(pos, (double)rule->getBaseValue(),
364                formatter->getDefaultRuleSet(), formatter, description, status);
365        }
366
367        // otherwise, return a MultiplierSubstitution
368        else {
369            return new MultiplierSubstitution(pos, rule->getDivisor(), ruleSet,
370                formatter, description, status);
371        }
372
373        // if the description begins with '>'...
374    case gGreaterThan:
375        // if the rule is a negative-number rule, return
376        // an AbsoluteValueSubstitution
377        if (rule->getBaseValue() == NFRule::kNegativeNumberRule) {
378            return new AbsoluteValueSubstitution(pos, ruleSet, formatter, description, status);
379        }
380
381        // if the rule is a fraction rule, return a
382        // FractionalPartSubstitution
383        else if (rule->getBaseValue() == NFRule::kImproperFractionRule
384            || rule->getBaseValue() == NFRule::kProperFractionRule
385            || rule->getBaseValue() == NFRule::kMasterRule) {
386            return new FractionalPartSubstitution(pos, ruleSet, formatter, description, status);
387        }
388
389        // if the rule set owning the rule is a fraction rule set,
390        // throw an exception
391        else if (ruleSet->isFractionRuleSet()) {
392            // throw new IllegalArgumentException(">> not allowed in fraction rule set");
393            status = U_PARSE_ERROR;
394            return NULL;
395        }
396
397        // otherwise, return a ModulusSubstitution
398        else {
399            return new ModulusSubstitution(pos, rule->getDivisor(), predecessor,
400                ruleSet, formatter, description, status);
401        }
402
403        // if the description begins with '=', always return a
404        // SameValueSubstitution
405    case gEquals:
406        return new SameValueSubstitution(pos, ruleSet, formatter, description, status);
407
408        // and if it's anything else, throw an exception
409    default:
410        // throw new IllegalArgumentException("Illegal substitution character");
411        status = U_PARSE_ERROR;
412    }
413    return NULL;
414}
415
416NFSubstitution::NFSubstitution(int32_t _pos,
417                               const NFRuleSet* _ruleSet,
418                               const RuleBasedNumberFormat* formatter,
419                               const UnicodeString& description,
420                               UErrorCode& status)
421                               : pos(_pos), ruleSet(NULL), numberFormat(NULL)
422{
423    // the description should begin and end with the same character.
424    // If it doesn't that's a syntax error.  Otherwise,
425    // makeSubstitution() was the only thing that needed to know
426    // about these characters, so strip them off
427    UnicodeString workingDescription(description);
428    if (description.length() >= 2
429        && description.charAt(0) == description.charAt(description.length() - 1))
430    {
431        workingDescription.remove(description.length() - 1, 1);
432        workingDescription.remove(0, 1);
433    }
434    else if (description.length() != 0) {
435        // throw new IllegalArgumentException("Illegal substitution syntax");
436        status = U_PARSE_ERROR;
437        return;
438    }
439
440    // if the description was just two paired token characters
441    // (i.e., "<<" or ">>"), it uses the rule set it belongs to to
442    // format its result
443    if (workingDescription.length() == 0) {
444        this->ruleSet = _ruleSet;
445    }
446    // if the description contains a rule set name, that's the rule
447    // set we use to format the result: get a reference to the
448    // names rule set
449    else if (workingDescription.charAt(0) == gPercent) {
450        this->ruleSet = formatter->findRuleSet(workingDescription, status);
451    }
452    // if the description begins with 0 or #, treat it as a
453    // DecimalFormat pattern, and initialize a DecimalFormat with
454    // that pattern (then set it to use the DecimalFormatSymbols
455    // belonging to our formatter)
456    else if (workingDescription.charAt(0) == gPound || workingDescription.charAt(0) ==gZero) {
457        DecimalFormatSymbols* sym = formatter->getDecimalFormatSymbols();
458        if (!sym) {
459            status = U_MISSING_RESOURCE_ERROR;
460            return;
461        }
462        this->numberFormat = new DecimalFormat(workingDescription, *sym, status);
463        /* test for NULL */
464        if (this->numberFormat == 0) {
465            status = U_MEMORY_ALLOCATION_ERROR;
466            return;
467        }
468        if (U_FAILURE(status)) {
469            delete (DecimalFormat*)this->numberFormat;
470            this->numberFormat = NULL;
471            return;
472        }
473        // this->numberFormat->setDecimalFormatSymbols(formatter->getDecimalFormatSymbols());
474    }
475    // if the description is ">>>", this substitution bypasses the
476    // usual rule-search process and always uses the rule that precedes
477    // it in its own rule set's rule list (this is used for place-value
478    // notations: formats where you want to see a particular part of
479    // a number even when it's 0)
480    else if (workingDescription.charAt(0) == gGreaterThan) {
481        // this causes problems when >>> is used in a frationalPartSubstitution
482        // this->ruleSet = NULL;
483        this->ruleSet = _ruleSet;
484        this->numberFormat = NULL;
485    }
486    // and of the description is none of these things, it's a syntax error
487    else {
488        // throw new IllegalArgumentException("Illegal substitution syntax");
489        status = U_PARSE_ERROR;
490    }
491}
492
493NFSubstitution::~NFSubstitution()
494{
495  // cast away const
496  delete (NumberFormat*)numberFormat; numberFormat = NULL;
497}
498
499/**
500 * Set's the substitution's divisor.  Used by NFRule.setBaseValue().
501 * A no-op for all substitutions except multiplier and modulus
502 * substitutions.
503 * @param radix The radix of the divisor
504 * @param exponent The exponent of the divisor
505 */
506void
507NFSubstitution::setDivisor(int32_t /*radix*/, int32_t /*exponent*/, UErrorCode& /*status*/) {
508  // a no-op for all substitutions except multiplier and modulus substitutions
509}
510
511
512//-----------------------------------------------------------------------
513// boilerplate
514//-----------------------------------------------------------------------
515
516UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NFSubstitution)
517
518/**
519 * Compares two substitutions for equality
520 * @param The substitution to compare this one to
521 * @return true if the two substitutions are functionally equivalent
522 */
523UBool
524NFSubstitution::operator==(const NFSubstitution& rhs) const
525{
526  // compare class and all of the fields all substitutions have
527  // in common
528  // this should be called by subclasses before their own equality tests
529  return typeid(*this) == typeid(rhs)
530  && pos == rhs.pos
531  && (ruleSet == NULL) == (rhs.ruleSet == NULL)
532  // && ruleSet == rhs.ruleSet causes circularity, other checks to make instead?
533  && (numberFormat == NULL
534      ? (rhs.numberFormat == NULL)
535      : (*numberFormat == *rhs.numberFormat));
536}
537
538/**
539 * Returns a textual description of the substitution
540 * @return A textual description of the substitution.  This might
541 * not be identical to the description it was created from, but
542 * it'll produce the same result.
543 */
544void
545NFSubstitution::toString(UnicodeString& text) const
546{
547  // use tokenChar() to get the character at the beginning and
548  // end of the substitutin token.  In between them will go
549  // either the name of the rule set it uses, or the pattern of
550  // the DecimalFormat it uses
551  text.remove();
552  text.append(tokenChar());
553
554  UnicodeString temp;
555  if (ruleSet != NULL) {
556    ruleSet->getName(temp);
557  } else if (numberFormat != NULL) {
558    numberFormat->toPattern(temp);
559  }
560  text.append(temp);
561  text.append(tokenChar());
562}
563
564//-----------------------------------------------------------------------
565// formatting
566//-----------------------------------------------------------------------
567
568/**
569 * Performs a mathematical operation on the number, formats it using
570 * either ruleSet or decimalFormat, and inserts the result into
571 * toInsertInto.
572 * @param number The number being formatted.
573 * @param toInsertInto The string we insert the result into
574 * @param pos The position in toInsertInto where the owning rule's
575 * rule text begins (this value is added to this substitution's
576 * position to determine exactly where to insert the new text)
577 */
578void
579NFSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos) const
580{
581    if (ruleSet != NULL) {
582        // perform a transformation on the number that is dependent
583        // on the type of substitution this is, then just call its
584        // rule set's format() method to format the result
585        ruleSet->format(transformNumber(number), toInsertInto, _pos + this->pos);
586    } else if (numberFormat != NULL) {
587        // or perform the transformation on the number (preserving
588        // the result's fractional part if the formatter it set
589        // to show it), then use that formatter's format() method
590        // to format the result
591        double numberToFormat = transformNumber((double)number);
592        if (numberFormat->getMaximumFractionDigits() == 0) {
593            numberToFormat = uprv_floor(numberToFormat);
594        }
595
596        UnicodeString temp;
597        numberFormat->format(numberToFormat, temp);
598        toInsertInto.insert(_pos + this->pos, temp);
599    }
600}
601
602/**
603 * Performs a mathematical operation on the number, formats it using
604 * either ruleSet or decimalFormat, and inserts the result into
605 * toInsertInto.
606 * @param number The number being formatted.
607 * @param toInsertInto The string we insert the result into
608 * @param pos The position in toInsertInto where the owning rule's
609 * rule text begins (this value is added to this substitution's
610 * position to determine exactly where to insert the new text)
611 */
612void
613NFSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) const {
614    // perform a transformation on the number being formatted that
615    // is dependent on the type of substitution this is
616    double numberToFormat = transformNumber(number);
617
618    // if the result is an integer, from here on out we work in integer
619    // space (saving time and memory and preserving accuracy)
620    if (numberToFormat == uprv_floor(numberToFormat) && ruleSet != NULL) {
621        ruleSet->format(util64_fromDouble(numberToFormat), toInsertInto, _pos + this->pos);
622
623        // if the result isn't an integer, then call either our rule set's
624        // format() method or our DecimalFormat's format() method to
625        // format the result
626    } else {
627        if (ruleSet != NULL) {
628            ruleSet->format(numberToFormat, toInsertInto, _pos + this->pos);
629        } else if (numberFormat != NULL) {
630            UnicodeString temp;
631            numberFormat->format(numberToFormat, temp);
632            toInsertInto.insert(_pos + this->pos, temp);
633        }
634    }
635}
636
637
638    //-----------------------------------------------------------------------
639    // parsing
640    //-----------------------------------------------------------------------
641
642#ifdef RBNF_DEBUG
643#include <stdio.h>
644#endif
645
646/**
647 * Parses a string using the rule set or DecimalFormat belonging
648 * to this substitution.  If there's a match, a mathematical
649 * operation (the inverse of the one used in formatting) is
650 * performed on the result of the parse and the value passed in
651 * and returned as the result.  The parse position is updated to
652 * point to the first unmatched character in the string.
653 * @param text The string to parse
654 * @param parsePosition On entry, ignored, but assumed to be 0.
655 * On exit, this is updated to point to the first unmatched
656 * character (or 0 if the substitution didn't match)
657 * @param baseValue A partial parse result that should be
658 * combined with the result of this parse
659 * @param upperBound When searching the rule set for a rule
660 * matching the string passed in, only rules with base values
661 * lower than this are considered
662 * @param lenientParse If true and matching against rules fails,
663 * the substitution will also try matching the text against
664 * numerals using a default-costructed NumberFormat.  If false,
665 * no extra work is done.  (This value is false whenever the
666 * formatter isn't in lenient-parse mode, but is also false
667 * under some conditions even when the formatter _is_ in
668 * lenient-parse mode.)
669 * @return If there's a match, this is the result of composing
670 * baseValue with whatever was returned from matching the
671 * characters.  This will be either a Long or a Double.  If there's
672 * no match this is new Long(0) (not null), and parsePosition
673 * is left unchanged.
674 */
675UBool
676NFSubstitution::doParse(const UnicodeString& text,
677                        ParsePosition& parsePosition,
678                        double baseValue,
679                        double upperBound,
680                        UBool lenientParse,
681                        Formattable& result) const
682{
683#ifdef RBNF_DEBUG
684    fprintf(stderr, "<nfsubs> %x bv: %g ub: %g\n", this, baseValue, upperBound);
685#endif
686    // figure out the highest base value a rule can have and match
687    // the text being parsed (this varies according to the type of
688    // substitutions: multiplier, modulus, and numerator substitutions
689    // restrict the search to rules with base values lower than their
690    // own; same-value substitutions leave the upper bound wherever
691    // it was, and the others allow any rule to match
692    upperBound = calcUpperBound(upperBound);
693
694    // use our rule set to parse the text.  If that fails and
695    // lenient parsing is enabled (this is always false if the
696    // formatter's lenient-parsing mode is off, but it may also
697    // be false even when the formatter's lenient-parse mode is
698    // on), then also try parsing the text using a default-
699    // constructed NumberFormat
700    if (ruleSet != NULL) {
701        ruleSet->parse(text, parsePosition, upperBound, result);
702        if (lenientParse && !ruleSet->isFractionRuleSet() && parsePosition.getIndex() == 0) {
703            UErrorCode status = U_ZERO_ERROR;
704            NumberFormat* fmt = NumberFormat::createInstance(status);
705            if (U_SUCCESS(status)) {
706                fmt->parse(text, result, parsePosition);
707            }
708            delete fmt;
709        }
710
711        // ...or use our DecimalFormat to parse the text
712    } else if (numberFormat != NULL) {
713        numberFormat->parse(text, result, parsePosition);
714    }
715
716    // if the parse was successful, we've already advanced the caller's
717    // parse position (this is the one function that doesn't have one
718    // of its own).  Derive a parse result and return it as a Long,
719    // if possible, or a Double
720    if (parsePosition.getIndex() != 0) {
721        UErrorCode status = U_ZERO_ERROR;
722        double tempResult = result.getDouble(status);
723
724        // composeRuleValue() produces a full parse result from
725        // the partial parse result passed to this function from
726        // the caller (this is either the owning rule's base value
727        // or the partial result obtained from composing the
728        // owning rule's base value with its other substitution's
729        // parse result) and the partial parse result obtained by
730        // matching the substitution (which will be the same value
731        // the caller would get by parsing just this part of the
732        // text with RuleBasedNumberFormat.parse() ).  How the two
733        // values are used to derive the full parse result depends
734        // on the types of substitutions: For a regular rule, the
735        // ultimate result is its multiplier substitution's result
736        // times the rule's divisor (or the rule's base value) plus
737        // the modulus substitution's result (which will actually
738        // supersede part of the rule's base value).  For a negative-
739        // number rule, the result is the negative of its substitution's
740        // result.  For a fraction rule, it's the sum of its two
741        // substitution results.  For a rule in a fraction rule set,
742        // it's the numerator substitution's result divided by
743        // the rule's base value.  Results from same-value substitutions
744        // propagate back upard, and null substitutions don't affect
745        // the result.
746        tempResult = composeRuleValue(tempResult, baseValue);
747        result.setDouble(tempResult);
748        return TRUE;
749        // if the parse was UNsuccessful, return 0
750    } else {
751        result.setLong(0);
752        return FALSE;
753    }
754}
755
756UBool
757NFSubstitution::isNullSubstitution() const {
758    return FALSE;
759}
760
761    /**
762     * Returns true if this is a modulus substitution.  (We didn't do this
763     * with instanceof partially because it causes source files to
764     * proliferate and partially because we have to port this to C++.)
765     * @return true if this object is an instance of ModulusSubstitution
766     */
767UBool
768NFSubstitution::isModulusSubstitution() const {
769    return FALSE;
770}
771
772//===================================================================
773// SameValueSubstitution
774//===================================================================
775
776/**
777 * A substitution that passes the value passed to it through unchanged.
778 * Represented by == in rule descriptions.
779 */
780SameValueSubstitution::SameValueSubstitution(int32_t _pos,
781                        const NFRuleSet* _ruleSet,
782                        const RuleBasedNumberFormat* formatter,
783                        const UnicodeString& description,
784                        UErrorCode& status)
785: NFSubstitution(_pos, _ruleSet, formatter, description, status)
786{
787    if (description == gEqualsEquals) {
788        // throw new IllegalArgumentException("== is not a legal token");
789        status = U_PARSE_ERROR;
790    }
791}
792
793UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SameValueSubstitution)
794
795//===================================================================
796// MultiplierSubstitution
797//===================================================================
798
799UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MultiplierSubstitution)
800
801UBool MultiplierSubstitution::operator==(const NFSubstitution& rhs) const
802{
803    return NFSubstitution::operator==(rhs) &&
804        divisor == ((const MultiplierSubstitution*)&rhs)->divisor;
805}
806
807
808//===================================================================
809// ModulusSubstitution
810//===================================================================
811
812/**
813 * A substitution that divides the number being formatted by the its rule's
814 * divisor and formats the remainder.  Represented by "&gt;&gt;" in a
815 * regular rule.
816 */
817ModulusSubstitution::ModulusSubstitution(int32_t _pos,
818                                         double _divisor,
819                                         const NFRule* predecessor,
820                                         const NFRuleSet* _ruleSet,
821                                         const RuleBasedNumberFormat* formatter,
822                                         const UnicodeString& description,
823                                         UErrorCode& status)
824 : NFSubstitution(_pos, _ruleSet, formatter, description, status)
825 , divisor(_divisor)
826 , ruleToUse(NULL)
827{
828  ldivisor = util64_fromDouble(_divisor);
829
830  // the owning rule's divisor controls the behavior of this
831  // substitution: rather than keeping a backpointer to the rule,
832  // we keep a copy of the divisor
833
834  if (ldivisor == 0) {
835      status = U_PARSE_ERROR;
836  }
837
838  if (description == gGreaterGreaterGreaterThan) {
839    // the >>> token doesn't alter how this substituion calculates the
840    // values it uses for formatting and parsing, but it changes
841    // what's done with that value after it's obtained: >>> short-
842    // circuits the rule-search process and goes straight to the
843    // specified rule to format the substitution value
844    ruleToUse = predecessor;
845  }
846}
847
848UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ModulusSubstitution)
849
850UBool ModulusSubstitution::operator==(const NFSubstitution& rhs) const
851{
852  return NFSubstitution::operator==(rhs) &&
853  divisor == ((const ModulusSubstitution*)&rhs)->divisor &&
854  ruleToUse == ((const ModulusSubstitution*)&rhs)->ruleToUse;
855}
856
857//-----------------------------------------------------------------------
858// formatting
859//-----------------------------------------------------------------------
860
861
862/**
863 * If this is a &gt;&gt;&gt; substitution, use ruleToUse to fill in
864 * the substitution.  Otherwise, just use the superclass function.
865 * @param number The number being formatted
866 * @toInsertInto The string to insert the result of this substitution
867 * into
868 * @param pos The position of the rule text in toInsertInto
869 */
870void
871ModulusSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos) const
872{
873    // if this isn't a >>> substitution, just use the inherited version
874    // of this function (which uses either a rule set or a DecimalFormat
875    // to format its substitution value)
876    if (ruleToUse == NULL) {
877        NFSubstitution::doSubstitution(number, toInsertInto, _pos);
878
879        // a >>> substitution goes straight to a particular rule to
880        // format the substitution value
881    } else {
882        int64_t numberToFormat = transformNumber(number);
883        ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos());
884    }
885}
886
887/**
888* If this is a &gt;&gt;&gt; substitution, use ruleToUse to fill in
889* the substitution.  Otherwise, just use the superclass function.
890* @param number The number being formatted
891* @toInsertInto The string to insert the result of this substitution
892* into
893* @param pos The position of the rule text in toInsertInto
894*/
895void
896ModulusSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) const
897{
898    // if this isn't a >>> substitution, just use the inherited version
899    // of this function (which uses either a rule set or a DecimalFormat
900    // to format its substitution value)
901    if (ruleToUse == NULL) {
902        NFSubstitution::doSubstitution(number, toInsertInto, _pos);
903
904        // a >>> substitution goes straight to a particular rule to
905        // format the substitution value
906    } else {
907        double numberToFormat = transformNumber(number);
908
909        ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos());
910    }
911}
912
913//-----------------------------------------------------------------------
914// parsing
915//-----------------------------------------------------------------------
916
917/**
918 * If this is a &gt;&gt;&gt; substitution, match only against ruleToUse.
919 * Otherwise, use the superclass function.
920 * @param text The string to parse
921 * @param parsePosition Ignored on entry, updated on exit to point to
922 * the first unmatched character.
923 * @param baseValue The partial parse result prior to calling this
924 * routine.
925 */
926UBool
927ModulusSubstitution::doParse(const UnicodeString& text,
928                             ParsePosition& parsePosition,
929                             double baseValue,
930                             double upperBound,
931                             UBool lenientParse,
932                             Formattable& result) const
933{
934    // if this isn't a >>> substitution, we can just use the
935    // inherited parse() routine to do the parsing
936    if (ruleToUse == NULL) {
937        return NFSubstitution::doParse(text, parsePosition, baseValue, upperBound, lenientParse, result);
938
939        // but if it IS a >>> substitution, we have to do it here: we
940        // use the specific rule's doParse() method, and then we have to
941        // do some of the other work of NFRuleSet.parse()
942    } else {
943        ruleToUse->doParse(text, parsePosition, FALSE, upperBound, result);
944
945        if (parsePosition.getIndex() != 0) {
946            UErrorCode status = U_ZERO_ERROR;
947            double tempResult = result.getDouble(status);
948            tempResult = composeRuleValue(tempResult, baseValue);
949            result.setDouble(tempResult);
950        }
951
952        return TRUE;
953    }
954}
955
956
957//===================================================================
958// IntegralPartSubstitution
959//===================================================================
960
961UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IntegralPartSubstitution)
962
963
964//===================================================================
965// FractionalPartSubstitution
966//===================================================================
967
968
969    /**
970     * Constructs a FractionalPartSubstitution.  This object keeps a flag
971     * telling whether it should format by digits or not.  In addition,
972     * it marks the rule set it calls (if any) as a fraction rule set.
973     */
974FractionalPartSubstitution::FractionalPartSubstitution(int32_t _pos,
975                             const NFRuleSet* _ruleSet,
976                             const RuleBasedNumberFormat* formatter,
977                             const UnicodeString& description,
978                             UErrorCode& status)
979 : NFSubstitution(_pos, _ruleSet, formatter, description, status)
980 , byDigits(FALSE)
981 , useSpaces(TRUE)
982
983{
984    // akk, ruleSet can change in superclass constructor
985    if (description == gGreaterGreaterThan ||
986        description == gGreaterGreaterGreaterThan ||
987        _ruleSet == getRuleSet()) {
988        byDigits = TRUE;
989        if (description == gGreaterGreaterGreaterThan) {
990            useSpaces = FALSE;
991        }
992    } else {
993        // cast away const
994        ((NFRuleSet*)getRuleSet())->makeIntoFractionRuleSet();
995    }
996}
997
998//-----------------------------------------------------------------------
999// formatting
1000//-----------------------------------------------------------------------
1001
1002/**
1003 * If in "by digits" mode, fills in the substitution one decimal digit
1004 * at a time using the rule set containing this substitution.
1005 * Otherwise, uses the superclass function.
1006 * @param number The number being formatted
1007 * @param toInsertInto The string to insert the result of formatting
1008 * the substitution into
1009 * @param pos The position of the owning rule's rule text in
1010 * toInsertInto
1011 */
1012void
1013FractionalPartSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) const
1014{
1015  // if we're not in "byDigits" mode, just use the inherited
1016  // doSubstitution() routine
1017  if (!byDigits) {
1018    NFSubstitution::doSubstitution(number, toInsertInto, _pos);
1019
1020    // if we're in "byDigits" mode, transform the value into an integer
1021    // by moving the decimal point eight places to the right and
1022    // pulling digits off the right one at a time, formatting each digit
1023    // as an integer using this substitution's owning rule set
1024    // (this is slower, but more accurate, than doing it from the
1025    // other end)
1026  } else {
1027    //          int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits));
1028    //          // this flag keeps us from formatting trailing zeros.  It starts
1029    //          // out false because we're pulling from the right, and switches
1030    //          // to true the first time we encounter a non-zero digit
1031    //          UBool doZeros = FALSE;
1032    //          for (int32_t i = 0; i < kMaxDecimalDigits; i++) {
1033    //              int64_t digit = numberToFormat % 10;
1034    //              if (digit != 0 || doZeros) {
1035    //                  if (doZeros && useSpaces) {
1036    //                      toInsertInto.insert(_pos + getPos(), gSpace);
1037    //                  }
1038    //                  doZeros = TRUE;
1039    //                  getRuleSet()->format(digit, toInsertInto, _pos + getPos());
1040    //              }
1041    //              numberToFormat /= 10;
1042    //          }
1043
1044    DigitList dl;
1045    dl.set(number);
1046    dl.roundFixedPoint(20);     // round to 20 fraction digits.
1047    dl.reduce();                // Removes any trailing zeros.
1048
1049    UBool pad = FALSE;
1050    for (int32_t didx = dl.getCount()-1; didx>=dl.getDecimalAt(); didx--) {
1051      // Loop iterates over fraction digits, starting with the LSD.
1052      //   include both real digits from the number, and zeros
1053      //   to the left of the MSD but to the right of the decimal point.
1054      if (pad && useSpaces) {
1055        toInsertInto.insert(_pos + getPos(), gSpace);
1056      } else {
1057        pad = TRUE;
1058      }
1059      int64_t digit = didx>=0 ? dl.getDigit(didx) - '0' : 0;
1060      getRuleSet()->format(digit, toInsertInto, _pos + getPos());
1061    }
1062
1063    if (!pad) {
1064      // hack around lack of precision in digitlist. if we would end up with
1065      // "foo point" make sure we add a " zero" to the end.
1066      getRuleSet()->format((int64_t)0, toInsertInto, _pos + getPos());
1067    }
1068  }
1069}
1070
1071//-----------------------------------------------------------------------
1072// parsing
1073//-----------------------------------------------------------------------
1074
1075/**
1076 * If in "by digits" mode, parses the string as if it were a string
1077 * of individual digits; otherwise, uses the superclass function.
1078 * @param text The string to parse
1079 * @param parsePosition Ignored on entry, but updated on exit to point
1080 * to the first unmatched character
1081 * @param baseValue The partial parse result prior to entering this
1082 * function
1083 * @param upperBound Only consider rules with base values lower than
1084 * this when filling in the substitution
1085 * @param lenientParse If true, try matching the text as numerals if
1086 * matching as words doesn't work
1087 * @return If the match was successful, the current partial parse
1088 * result; otherwise new Long(0).  The result is either a Long or
1089 * a Double.
1090 */
1091
1092UBool
1093FractionalPartSubstitution::doParse(const UnicodeString& text,
1094                ParsePosition& parsePosition,
1095                double baseValue,
1096                double /*upperBound*/,
1097                UBool lenientParse,
1098                Formattable& resVal) const
1099{
1100    // if we're not in byDigits mode, we can just use the inherited
1101    // doParse()
1102    if (!byDigits) {
1103        return NFSubstitution::doParse(text, parsePosition, baseValue, 0, lenientParse, resVal);
1104
1105        // if we ARE in byDigits mode, parse the text one digit at a time
1106        // using this substitution's owning rule set (we do this by setting
1107        // upperBound to 10 when calling doParse() ) until we reach
1108        // nonmatching text
1109    } else {
1110        UnicodeString workText(text);
1111        ParsePosition workPos(1);
1112        double result = 0;
1113        int32_t digit;
1114//          double p10 = 0.1;
1115
1116        DigitList dl;
1117        NumberFormat* fmt = NULL;
1118        while (workText.length() > 0 && workPos.getIndex() != 0) {
1119            workPos.setIndex(0);
1120            Formattable temp;
1121            getRuleSet()->parse(workText, workPos, 10, temp);
1122            UErrorCode status = U_ZERO_ERROR;
1123            digit = temp.getLong(status);
1124//            digit = temp.getType() == Formattable::kLong ?
1125//               temp.getLong() :
1126//            (int32_t)temp.getDouble();
1127
1128            if (lenientParse && workPos.getIndex() == 0) {
1129                if (!fmt) {
1130                    status = U_ZERO_ERROR;
1131                    fmt = NumberFormat::createInstance(status);
1132                    if (U_FAILURE(status)) {
1133                        delete fmt;
1134                        fmt = NULL;
1135                    }
1136                }
1137                if (fmt) {
1138                    fmt->parse(workText, temp, workPos);
1139                    digit = temp.getLong(status);
1140                }
1141            }
1142
1143            if (workPos.getIndex() != 0) {
1144                dl.append((char)('0' + digit));
1145//                  result += digit * p10;
1146//                  p10 /= 10;
1147                parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex());
1148                workText.removeBetween(0, workPos.getIndex());
1149                while (workText.length() > 0 && workText.charAt(0) == gSpace) {
1150                    workText.removeBetween(0, 1);
1151                    parsePosition.setIndex(parsePosition.getIndex() + 1);
1152                }
1153            }
1154        }
1155        delete fmt;
1156
1157        result = dl.getCount() == 0 ? 0 : dl.getDouble();
1158        result = composeRuleValue(result, baseValue);
1159        resVal.setDouble(result);
1160        return TRUE;
1161    }
1162}
1163
1164UBool
1165FractionalPartSubstitution::operator==(const NFSubstitution& rhs) const
1166{
1167  return NFSubstitution::operator==(rhs) &&
1168  ((const FractionalPartSubstitution*)&rhs)->byDigits == byDigits;
1169}
1170
1171UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FractionalPartSubstitution)
1172
1173
1174//===================================================================
1175// AbsoluteValueSubstitution
1176//===================================================================
1177
1178UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AbsoluteValueSubstitution)
1179
1180//===================================================================
1181// NumeratorSubstitution
1182//===================================================================
1183
1184void
1185NumeratorSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t apos) const {
1186    // perform a transformation on the number being formatted that
1187    // is dependent on the type of substitution this is
1188
1189    double numberToFormat = transformNumber(number);
1190    int64_t longNF = util64_fromDouble(numberToFormat);
1191
1192    const NFRuleSet* aruleSet = getRuleSet();
1193    if (withZeros && aruleSet != NULL) {
1194        // if there are leading zeros in the decimal expansion then emit them
1195        int64_t nf =longNF;
1196        int32_t len = toInsertInto.length();
1197        while ((nf *= 10) < denominator) {
1198            toInsertInto.insert(apos + getPos(), gSpace);
1199            aruleSet->format((int64_t)0, toInsertInto, apos + getPos());
1200        }
1201        apos += toInsertInto.length() - len;
1202    }
1203
1204    // if the result is an integer, from here on out we work in integer
1205    // space (saving time and memory and preserving accuracy)
1206    if (numberToFormat == longNF && aruleSet != NULL) {
1207        aruleSet->format(longNF, toInsertInto, apos + getPos());
1208
1209        // if the result isn't an integer, then call either our rule set's
1210        // format() method or our DecimalFormat's format() method to
1211        // format the result
1212    } else {
1213        if (aruleSet != NULL) {
1214            aruleSet->format(numberToFormat, toInsertInto, apos + getPos());
1215        } else {
1216            UErrorCode status = U_ZERO_ERROR;
1217            UnicodeString temp;
1218            getNumberFormat()->format(numberToFormat, temp, status);
1219            toInsertInto.insert(apos + getPos(), temp);
1220        }
1221    }
1222}
1223
1224UBool
1225NumeratorSubstitution::doParse(const UnicodeString& text,
1226                               ParsePosition& parsePosition,
1227                               double baseValue,
1228                               double upperBound,
1229                               UBool /*lenientParse*/,
1230                               Formattable& result) const
1231{
1232    // we don't have to do anything special to do the parsing here,
1233    // but we have to turn lenient parsing off-- if we leave it on,
1234    // it SERIOUSLY messes up the algorithm
1235
1236    // if withZeros is true, we need to count the zeros
1237    // and use that to adjust the parse result
1238    UErrorCode status = U_ZERO_ERROR;
1239    int32_t zeroCount = 0;
1240    UnicodeString workText(text);
1241
1242    if (withZeros) {
1243        ParsePosition workPos(1);
1244        Formattable temp;
1245
1246        while (workText.length() > 0 && workPos.getIndex() != 0) {
1247            workPos.setIndex(0);
1248            getRuleSet()->parse(workText, workPos, 1, temp); // parse zero or nothing at all
1249            if (workPos.getIndex() == 0) {
1250                // we failed, either there were no more zeros, or the number was formatted with digits
1251                // either way, we're done
1252                break;
1253            }
1254
1255            ++zeroCount;
1256            parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex());
1257            workText.remove(0, workPos.getIndex());
1258            while (workText.length() > 0 && workText.charAt(0) == gSpace) {
1259                workText.remove(0, 1);
1260                parsePosition.setIndex(parsePosition.getIndex() + 1);
1261            }
1262        }
1263
1264        workText = text;
1265        workText.remove(0, (int32_t)parsePosition.getIndex());
1266        parsePosition.setIndex(0);
1267    }
1268
1269    // we've parsed off the zeros, now let's parse the rest from our current position
1270    NFSubstitution::doParse(workText, parsePosition, withZeros ? 1 : baseValue, upperBound, FALSE, result);
1271
1272    if (withZeros) {
1273        // any base value will do in this case.  is there a way to
1274        // force this to not bother trying all the base values?
1275
1276        // compute the 'effective' base and prescale the value down
1277        int64_t n = result.getLong(status); // force conversion!
1278        int64_t d = 1;
1279        int32_t pow = 0;
1280        while (d <= n) {
1281            d *= 10;
1282            ++pow;
1283        }
1284        // now add the zeros
1285        while (zeroCount > 0) {
1286            d *= 10;
1287            --zeroCount;
1288        }
1289        // d is now our true denominator
1290        result.setDouble((double)n/(double)d);
1291    }
1292
1293    return TRUE;
1294}
1295
1296UBool
1297NumeratorSubstitution::operator==(const NFSubstitution& rhs) const
1298{
1299    return NFSubstitution::operator==(rhs) &&
1300        denominator == ((const NumeratorSubstitution*)&rhs)->denominator;
1301}
1302
1303UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumeratorSubstitution)
1304
1305const UChar NumeratorSubstitution::LTLT[] = { 0x003c, 0x003c };
1306
1307//===================================================================
1308// NullSubstitution
1309//===================================================================
1310
1311UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NullSubstitution)
1312
1313U_NAMESPACE_END
1314
1315/* U_HAVE_RBNF */
1316#endif
1317
1318