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 ">>" 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 >>> 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 >>> 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 >>> 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