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