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