1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 ******************************************************************************
5 * Copyright (C) 1997-2015, International Business Machines
6 * Corporation and others. All Rights Reserved.
7 ******************************************************************************
8 * file name: nfsubs.cpp
10 * tab size: 8 (not used)
13 * Modification history
15 * 10/11/2001 Doug Ported from ICU4J
19 #include "utypeinfo.h" // for 'typeid' to work
23 #include "fmtableimp.h"
27 static const UChar gLessThan
= 0x003c;
28 static const UChar gEquals
= 0x003d;
29 static const UChar gGreaterThan
= 0x003e;
30 static const UChar gPercent
= 0x0025;
31 static const UChar gPound
= 0x0023;
32 static const UChar gZero
= 0x0030;
33 static const UChar gSpace
= 0x0020;
35 static const UChar gEqualsEquals
[] =
39 static const UChar gGreaterGreaterGreaterThan
[] =
43 static const UChar gGreaterGreaterThan
[] =
50 class SameValueSubstitution
: public NFSubstitution
{
52 SameValueSubstitution(int32_t pos
,
53 const NFRuleSet
* ruleset
,
54 const UnicodeString
& description
,
56 virtual ~SameValueSubstitution();
58 virtual int64_t transformNumber(int64_t number
) const { return number
; }
59 virtual double transformNumber(double number
) const { return number
; }
60 virtual double composeRuleValue(double newRuleValue
, double /*oldRuleValue*/) const { return newRuleValue
; }
61 virtual double calcUpperBound(double oldUpperBound
) const { return oldUpperBound
; }
62 virtual UChar
tokenChar() const { return (UChar
)0x003d; } // '='
65 static UClassID
getStaticClassID(void);
66 virtual UClassID
getDynamicClassID(void) const;
69 SameValueSubstitution::~SameValueSubstitution() {}
71 class MultiplierSubstitution
: public NFSubstitution
{
75 MultiplierSubstitution(int32_t _pos
,
77 const NFRuleSet
* _ruleSet
,
78 const UnicodeString
& description
,
80 : NFSubstitution(_pos
, _ruleSet
, description
, status
), divisor(rule
->getDivisor())
83 status
= U_PARSE_ERROR
;
86 virtual ~MultiplierSubstitution();
88 virtual void setDivisor(int32_t radix
, int16_t exponent
, UErrorCode
& status
) {
89 divisor
= util64_pow(radix
, exponent
);
92 status
= U_PARSE_ERROR
;
96 virtual UBool
operator==(const NFSubstitution
& rhs
) const;
98 virtual int64_t transformNumber(int64_t number
) const {
99 return number
/ divisor
;
102 virtual double transformNumber(double number
) const {
104 return uprv_floor(number
/ divisor
);
106 return number
/ divisor
;
110 virtual double composeRuleValue(double newRuleValue
, double /*oldRuleValue*/) const {
111 return newRuleValue
* divisor
;
114 virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor
; }
116 virtual UChar
tokenChar() const { return (UChar
)0x003c; } // '<'
119 static UClassID
getStaticClassID(void);
120 virtual UClassID
getDynamicClassID(void) const;
123 MultiplierSubstitution::~MultiplierSubstitution() {}
125 class ModulusSubstitution
: public NFSubstitution
{
127 const NFRule
* ruleToUse
;
129 ModulusSubstitution(int32_t pos
,
131 const NFRule
* rulePredecessor
,
132 const NFRuleSet
* ruleSet
,
133 const UnicodeString
& description
,
135 virtual ~ModulusSubstitution();
137 virtual void setDivisor(int32_t radix
, int16_t exponent
, UErrorCode
& status
) {
138 divisor
= util64_pow(radix
, exponent
);
141 status
= U_PARSE_ERROR
;
145 virtual UBool
operator==(const NFSubstitution
& rhs
) const;
147 virtual void doSubstitution(int64_t number
, UnicodeString
& toInsertInto
, int32_t pos
, int32_t recursionCount
, UErrorCode
& status
) const;
148 virtual void doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t pos
, int32_t recursionCount
, UErrorCode
& status
) const;
150 virtual int64_t transformNumber(int64_t number
) const { return number
% divisor
; }
151 virtual double transformNumber(double number
) const { return uprv_fmod(number
, divisor
); }
153 virtual UBool
doParse(const UnicodeString
& text
,
154 ParsePosition
& parsePosition
,
158 Formattable
& result
) const;
160 virtual double composeRuleValue(double newRuleValue
, double oldRuleValue
) const {
161 return oldRuleValue
- uprv_fmod(oldRuleValue
, divisor
) + newRuleValue
;
164 virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor
; }
166 virtual UBool
isModulusSubstitution() const { return TRUE
; }
168 virtual UChar
tokenChar() const { return (UChar
)0x003e; } // '>'
170 virtual void toString(UnicodeString
& result
) const;
173 static UClassID
getStaticClassID(void);
174 virtual UClassID
getDynamicClassID(void) const;
177 ModulusSubstitution::~ModulusSubstitution() {}
179 class IntegralPartSubstitution
: public NFSubstitution
{
181 IntegralPartSubstitution(int32_t _pos
,
182 const NFRuleSet
* _ruleSet
,
183 const UnicodeString
& description
,
185 : NFSubstitution(_pos
, _ruleSet
, description
, status
) {}
186 virtual ~IntegralPartSubstitution();
188 virtual int64_t transformNumber(int64_t number
) const { return number
; }
189 virtual double transformNumber(double number
) const { return uprv_floor(number
); }
190 virtual double composeRuleValue(double newRuleValue
, double oldRuleValue
) const { return newRuleValue
+ oldRuleValue
; }
191 virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX
; }
192 virtual UChar
tokenChar() const { return (UChar
)0x003c; } // '<'
195 static UClassID
getStaticClassID(void);
196 virtual UClassID
getDynamicClassID(void) const;
199 IntegralPartSubstitution::~IntegralPartSubstitution() {}
201 class FractionalPartSubstitution
: public NFSubstitution
{
204 enum { kMaxDecimalDigits
= 8 };
206 FractionalPartSubstitution(int32_t pos
,
207 const NFRuleSet
* ruleSet
,
208 const UnicodeString
& description
,
210 virtual ~FractionalPartSubstitution();
212 virtual UBool
operator==(const NFSubstitution
& rhs
) const;
214 virtual void doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t pos
, int32_t recursionCount
, UErrorCode
& status
) const;
215 virtual void doSubstitution(int64_t /*number*/, UnicodeString
& /*toInsertInto*/, int32_t /*_pos*/, int32_t /*recursionCount*/, UErrorCode
& /*status*/) const {}
216 virtual int64_t transformNumber(int64_t /*number*/) const { return 0; }
217 virtual double transformNumber(double number
) const { return number
- uprv_floor(number
); }
219 virtual UBool
doParse(const UnicodeString
& text
,
220 ParsePosition
& parsePosition
,
224 Formattable
& result
) const;
226 virtual double composeRuleValue(double newRuleValue
, double oldRuleValue
) const { return newRuleValue
+ oldRuleValue
; }
227 virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0.0; }
228 virtual UChar
tokenChar() const { return (UChar
)0x003e; } // '>'
231 static UClassID
getStaticClassID(void);
232 virtual UClassID
getDynamicClassID(void) const;
235 FractionalPartSubstitution::~FractionalPartSubstitution() {}
237 class AbsoluteValueSubstitution
: public NFSubstitution
{
239 AbsoluteValueSubstitution(int32_t _pos
,
240 const NFRuleSet
* _ruleSet
,
241 const UnicodeString
& description
,
243 : NFSubstitution(_pos
, _ruleSet
, description
, status
) {}
244 virtual ~AbsoluteValueSubstitution();
246 virtual int64_t transformNumber(int64_t number
) const { return number
>= 0 ? number
: -number
; }
247 virtual double transformNumber(double number
) const { return uprv_fabs(number
); }
248 virtual double composeRuleValue(double newRuleValue
, double /*oldRuleValue*/) const { return -newRuleValue
; }
249 virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX
; }
250 virtual UChar
tokenChar() const { return (UChar
)0x003e; } // '>'
253 static UClassID
getStaticClassID(void);
254 virtual UClassID
getDynamicClassID(void) const;
257 AbsoluteValueSubstitution::~AbsoluteValueSubstitution() {}
259 class NumeratorSubstitution
: public NFSubstitution
{
261 int64_t ldenominator
;
264 static inline UnicodeString
fixdesc(const UnicodeString
& desc
) {
265 if (desc
.endsWith(LTLT
, 2)) {
266 UnicodeString
result(desc
, 0, desc
.length()-1);
271 NumeratorSubstitution(int32_t _pos
,
274 const UnicodeString
& description
,
276 : NFSubstitution(_pos
, _ruleSet
, fixdesc(description
), status
), denominator(_denominator
)
278 ldenominator
= util64_fromDouble(denominator
);
279 withZeros
= description
.endsWith(LTLT
, 2);
281 virtual ~NumeratorSubstitution();
283 virtual UBool
operator==(const NFSubstitution
& rhs
) const;
285 virtual int64_t transformNumber(int64_t number
) const { return number
* ldenominator
; }
286 virtual double transformNumber(double number
) const { return uprv_round(number
* denominator
); }
288 virtual void doSubstitution(int64_t /*number*/, UnicodeString
& /*toInsertInto*/, int32_t /*_pos*/, int32_t /*recursionCount*/, UErrorCode
& /*status*/) const {}
289 virtual void doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t pos
, int32_t recursionCount
, UErrorCode
& status
) const;
290 virtual UBool
doParse(const UnicodeString
& text
,
291 ParsePosition
& parsePosition
,
294 UBool
/*lenientParse*/,
295 Formattable
& result
) const;
297 virtual double composeRuleValue(double newRuleValue
, double oldRuleValue
) const { return newRuleValue
/ oldRuleValue
; }
298 virtual double calcUpperBound(double /*oldUpperBound*/) const { return denominator
; }
299 virtual UChar
tokenChar() const { return (UChar
)0x003c; } // '<'
301 static const UChar LTLT
[2];
304 static UClassID
getStaticClassID(void);
305 virtual UClassID
getDynamicClassID(void) const;
308 NumeratorSubstitution::~NumeratorSubstitution() {}
311 NFSubstitution::makeSubstitution(int32_t pos
,
313 const NFRule
* predecessor
,
314 const NFRuleSet
* ruleSet
,
315 const RuleBasedNumberFormat
* formatter
,
316 const UnicodeString
& description
,
319 // if the description is empty, return a NullSubstitution
320 if (description
.length() == 0) {
324 switch (description
.charAt(0)) {
325 // if the description begins with '<'...
327 // throw an exception if the rule is a negative number
329 if (rule
->getBaseValue() == NFRule::kNegativeNumberRule
) {
330 // throw new IllegalArgumentException("<< not allowed in negative-number rule");
331 status
= U_PARSE_ERROR
;
335 // if the rule is a fraction rule, return an
336 // IntegralPartSubstitution
337 else if (rule
->getBaseValue() == NFRule::kImproperFractionRule
338 || rule
->getBaseValue() == NFRule::kProperFractionRule
339 || rule
->getBaseValue() == NFRule::kMasterRule
) {
340 return new IntegralPartSubstitution(pos
, ruleSet
, description
, status
);
343 // if the rule set containing the rule is a fraction
344 // rule set, return a NumeratorSubstitution
345 else if (ruleSet
->isFractionRuleSet()) {
346 return new NumeratorSubstitution(pos
, (double)rule
->getBaseValue(),
347 formatter
->getDefaultRuleSet(), description
, status
);
350 // otherwise, return a MultiplierSubstitution
352 return new MultiplierSubstitution(pos
, rule
, ruleSet
,
353 description
, status
);
356 // if the description begins with '>'...
358 // if the rule is a negative-number rule, return
359 // an AbsoluteValueSubstitution
360 if (rule
->getBaseValue() == NFRule::kNegativeNumberRule
) {
361 return new AbsoluteValueSubstitution(pos
, ruleSet
, description
, status
);
364 // if the rule is a fraction rule, return a
365 // FractionalPartSubstitution
366 else if (rule
->getBaseValue() == NFRule::kImproperFractionRule
367 || rule
->getBaseValue() == NFRule::kProperFractionRule
368 || rule
->getBaseValue() == NFRule::kMasterRule
) {
369 return new FractionalPartSubstitution(pos
, ruleSet
, description
, status
);
372 // if the rule set owning the rule is a fraction rule set,
373 // throw an exception
374 else if (ruleSet
->isFractionRuleSet()) {
375 // throw new IllegalArgumentException(">> not allowed in fraction rule set");
376 status
= U_PARSE_ERROR
;
380 // otherwise, return a ModulusSubstitution
382 return new ModulusSubstitution(pos
, rule
, predecessor
,
383 ruleSet
, description
, status
);
386 // if the description begins with '=', always return a
387 // SameValueSubstitution
389 return new SameValueSubstitution(pos
, ruleSet
, description
, status
);
391 // and if it's anything else, throw an exception
393 // throw new IllegalArgumentException("Illegal substitution character");
394 status
= U_PARSE_ERROR
;
399 NFSubstitution::NFSubstitution(int32_t _pos
,
400 const NFRuleSet
* _ruleSet
,
401 const UnicodeString
& description
,
403 : pos(_pos
), ruleSet(NULL
), numberFormat(NULL
)
405 // the description should begin and end with the same character.
406 // If it doesn't that's a syntax error. Otherwise,
407 // makeSubstitution() was the only thing that needed to know
408 // about these characters, so strip them off
409 UnicodeString
workingDescription(description
);
410 if (description
.length() >= 2
411 && description
.charAt(0) == description
.charAt(description
.length() - 1))
413 workingDescription
.remove(description
.length() - 1, 1);
414 workingDescription
.remove(0, 1);
416 else if (description
.length() != 0) {
417 // throw new IllegalArgumentException("Illegal substitution syntax");
418 status
= U_PARSE_ERROR
;
422 if (workingDescription
.length() == 0) {
423 // if the description was just two paired token characters
424 // (i.e., "<<" or ">>"), it uses the rule set it belongs to to
426 this->ruleSet
= _ruleSet
;
428 else if (workingDescription
.charAt(0) == gPercent
) {
429 // if the description contains a rule set name, that's the rule
430 // set we use to format the result: get a reference to the
432 this->ruleSet
= _ruleSet
->getOwner()->findRuleSet(workingDescription
, status
);
434 else if (workingDescription
.charAt(0) == gPound
|| workingDescription
.charAt(0) ==gZero
) {
435 // if the description begins with 0 or #, treat it as a
436 // DecimalFormat pattern, and initialize a DecimalFormat with
437 // that pattern (then set it to use the DecimalFormatSymbols
438 // belonging to our formatter)
439 const DecimalFormatSymbols
* sym
= _ruleSet
->getOwner()->getDecimalFormatSymbols();
441 status
= U_MISSING_RESOURCE_ERROR
;
444 DecimalFormat
*tempNumberFormat
= new DecimalFormat(workingDescription
, *sym
, status
);
446 if (!tempNumberFormat
) {
447 status
= U_MEMORY_ALLOCATION_ERROR
;
450 if (U_FAILURE(status
)) {
451 delete tempNumberFormat
;
454 this->numberFormat
= tempNumberFormat
;
456 else if (workingDescription
.charAt(0) == gGreaterThan
) {
457 // if the description is ">>>", this substitution bypasses the
458 // usual rule-search process and always uses the rule that precedes
459 // it in its own rule set's rule list (this is used for place-value
460 // notations: formats where you want to see a particular part of
461 // a number even when it's 0)
463 // this causes problems when >>> is used in a frationalPartSubstitution
464 // this->ruleSet = NULL;
465 this->ruleSet
= _ruleSet
;
466 this->numberFormat
= NULL
;
469 // and of the description is none of these things, it's a syntax error
471 // throw new IllegalArgumentException("Illegal substitution syntax");
472 status
= U_PARSE_ERROR
;
476 NFSubstitution::~NFSubstitution()
483 * Set's the substitution's divisor. Used by NFRule.setBaseValue().
484 * A no-op for all substitutions except multiplier and modulus
486 * @param radix The radix of the divisor
487 * @param exponent The exponent of the divisor
490 NFSubstitution::setDivisor(int32_t /*radix*/, int16_t /*exponent*/, UErrorCode
& /*status*/) {
491 // a no-op for all substitutions except multiplier and modulus substitutions
495 NFSubstitution::setDecimalFormatSymbols(const DecimalFormatSymbols
&newSymbols
, UErrorCode
& /*status*/) {
496 if (numberFormat
!= NULL
) {
497 numberFormat
->setDecimalFormatSymbols(newSymbols
);
501 //-----------------------------------------------------------------------
503 //-----------------------------------------------------------------------
505 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NFSubstitution
)
508 * Compares two substitutions for equality
509 * @param The substitution to compare this one to
510 * @return true if the two substitutions are functionally equivalent
513 NFSubstitution::operator==(const NFSubstitution
& rhs
) const
515 // compare class and all of the fields all substitutions have
517 // this should be called by subclasses before their own equality tests
518 return typeid(*this) == typeid(rhs
)
520 && (ruleSet
== NULL
) == (rhs
.ruleSet
== NULL
)
521 // && ruleSet == rhs.ruleSet causes circularity, other checks to make instead?
522 && (numberFormat
== NULL
523 ? (rhs
.numberFormat
== NULL
)
524 : (*numberFormat
== *rhs
.numberFormat
));
528 * Returns a textual description of the substitution
529 * @return A textual description of the substitution. This might
530 * not be identical to the description it was created from, but
531 * it'll produce the same result.
534 NFSubstitution::toString(UnicodeString
& text
) const
536 // use tokenChar() to get the character at the beginning and
537 // end of the substitutin token. In between them will go
538 // either the name of the rule set it uses, or the pattern of
539 // the DecimalFormat it uses
541 text
.append(tokenChar());
544 if (ruleSet
!= NULL
) {
545 ruleSet
->getName(temp
);
546 } else if (numberFormat
!= NULL
) {
547 numberFormat
->toPattern(temp
);
550 text
.append(tokenChar());
553 //-----------------------------------------------------------------------
555 //-----------------------------------------------------------------------
558 * Performs a mathematical operation on the number, formats it using
559 * either ruleSet or decimalFormat, and inserts the result into
561 * @param number The number being formatted.
562 * @param toInsertInto The string we insert the result into
563 * @param pos The position in toInsertInto where the owning rule's
564 * rule text begins (this value is added to this substitution's
565 * position to determine exactly where to insert the new text)
568 NFSubstitution::doSubstitution(int64_t number
, UnicodeString
& toInsertInto
, int32_t _pos
, int32_t recursionCount
, UErrorCode
& status
) const
570 if (ruleSet
!= NULL
) {
571 // Perform a transformation on the number that is dependent
572 // on the type of substitution this is, then just call its
573 // rule set's format() method to format the result
574 ruleSet
->format(transformNumber(number
), toInsertInto
, _pos
+ this->pos
, recursionCount
, status
);
575 } else if (numberFormat
!= NULL
) {
576 if (number
<= MAX_INT64_IN_DOUBLE
) {
577 // or perform the transformation on the number (preserving
578 // the result's fractional part if the formatter it set
579 // to show it), then use that formatter's format() method
580 // to format the result
581 double numberToFormat
= transformNumber((double)number
);
582 if (numberFormat
->getMaximumFractionDigits() == 0) {
583 numberToFormat
= uprv_floor(numberToFormat
);
587 numberFormat
->format(numberToFormat
, temp
, status
);
588 toInsertInto
.insert(_pos
+ this->pos
, temp
);
591 // We have gone beyond double precision. Something has to give.
592 // We're favoring accuracy of the large number over potential rules
593 // that round like a CompactDecimalFormat, which is not a common use case.
595 // Perform a transformation on the number that is dependent
596 // on the type of substitution this is, then just call its
597 // rule set's format() method to format the result
598 int64_t numberToFormat
= transformNumber(number
);
600 numberFormat
->format(numberToFormat
, temp
, status
);
601 toInsertInto
.insert(_pos
+ this->pos
, temp
);
607 * Performs a mathematical operation on the number, formats it using
608 * either ruleSet or decimalFormat, and inserts the result into
610 * @param number The number being formatted.
611 * @param toInsertInto The string we insert the result into
612 * @param pos The position in toInsertInto where the owning rule's
613 * rule text begins (this value is added to this substitution's
614 * position to determine exactly where to insert the new text)
617 NFSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t _pos
, int32_t recursionCount
, UErrorCode
& status
) const {
618 // perform a transformation on the number being formatted that
619 // is dependent on the type of substitution this is
620 double numberToFormat
= transformNumber(number
);
622 if (uprv_isInfinite(numberToFormat
)) {
623 // This is probably a minus rule. Combine it with an infinite rule.
624 const NFRule
*infiniteRule
= ruleSet
->findDoubleRule(uprv_getInfinity());
625 infiniteRule
->doFormat(numberToFormat
, toInsertInto
, _pos
+ this->pos
, recursionCount
, status
);
629 // if the result is an integer, from here on out we work in integer
630 // space (saving time and memory and preserving accuracy)
631 if (numberToFormat
== uprv_floor(numberToFormat
) && ruleSet
!= NULL
) {
632 ruleSet
->format(util64_fromDouble(numberToFormat
), toInsertInto
, _pos
+ this->pos
, recursionCount
, status
);
634 // if the result isn't an integer, then call either our rule set's
635 // format() method or our DecimalFormat's format() method to
638 if (ruleSet
!= NULL
) {
639 ruleSet
->format(numberToFormat
, toInsertInto
, _pos
+ this->pos
, recursionCount
, status
);
640 } else if (numberFormat
!= NULL
) {
642 numberFormat
->format(numberToFormat
, temp
);
643 toInsertInto
.insert(_pos
+ this->pos
, temp
);
649 //-----------------------------------------------------------------------
651 //-----------------------------------------------------------------------
658 * Parses a string using the rule set or DecimalFormat belonging
659 * to this substitution. If there's a match, a mathematical
660 * operation (the inverse of the one used in formatting) is
661 * performed on the result of the parse and the value passed in
662 * and returned as the result. The parse position is updated to
663 * point to the first unmatched character in the string.
664 * @param text The string to parse
665 * @param parsePosition On entry, ignored, but assumed to be 0.
666 * On exit, this is updated to point to the first unmatched
667 * character (or 0 if the substitution didn't match)
668 * @param baseValue A partial parse result that should be
669 * combined with the result of this parse
670 * @param upperBound When searching the rule set for a rule
671 * matching the string passed in, only rules with base values
672 * lower than this are considered
673 * @param lenientParse If true and matching against rules fails,
674 * the substitution will also try matching the text against
675 * numerals using a default-costructed NumberFormat. If false,
676 * no extra work is done. (This value is false whenever the
677 * formatter isn't in lenient-parse mode, but is also false
678 * under some conditions even when the formatter _is_ in
679 * lenient-parse mode.)
680 * @return If there's a match, this is the result of composing
681 * baseValue with whatever was returned from matching the
682 * characters. This will be either a Long or a Double. If there's
683 * no match this is new Long(0) (not null), and parsePosition
687 NFSubstitution::doParse(const UnicodeString
& text
,
688 ParsePosition
& parsePosition
,
692 Formattable
& result
) const
695 fprintf(stderr
, "<nfsubs> %x bv: %g ub: %g\n", this, baseValue
, upperBound
);
697 // figure out the highest base value a rule can have and match
698 // the text being parsed (this varies according to the type of
699 // substitutions: multiplier, modulus, and numerator substitutions
700 // restrict the search to rules with base values lower than their
701 // own; same-value substitutions leave the upper bound wherever
702 // it was, and the others allow any rule to match
703 upperBound
= calcUpperBound(upperBound
);
705 // use our rule set to parse the text. If that fails and
706 // lenient parsing is enabled (this is always false if the
707 // formatter's lenient-parsing mode is off, but it may also
708 // be false even when the formatter's lenient-parse mode is
709 // on), then also try parsing the text using a default-
710 // constructed NumberFormat
711 if (ruleSet
!= NULL
) {
712 ruleSet
->parse(text
, parsePosition
, upperBound
, result
);
713 if (lenientParse
&& !ruleSet
->isFractionRuleSet() && parsePosition
.getIndex() == 0) {
714 UErrorCode status
= U_ZERO_ERROR
;
715 NumberFormat
* fmt
= NumberFormat::createInstance(status
);
716 if (U_SUCCESS(status
)) {
717 fmt
->parse(text
, result
, parsePosition
);
722 // ...or use our DecimalFormat to parse the text
723 } else if (numberFormat
!= NULL
) {
724 numberFormat
->parse(text
, result
, parsePosition
);
727 // if the parse was successful, we've already advanced the caller's
728 // parse position (this is the one function that doesn't have one
729 // of its own). Derive a parse result and return it as a Long,
730 // if possible, or a Double
731 if (parsePosition
.getIndex() != 0) {
732 UErrorCode status
= U_ZERO_ERROR
;
733 double tempResult
= result
.getDouble(status
);
735 // composeRuleValue() produces a full parse result from
736 // the partial parse result passed to this function from
737 // the caller (this is either the owning rule's base value
738 // or the partial result obtained from composing the
739 // owning rule's base value with its other substitution's
740 // parse result) and the partial parse result obtained by
741 // matching the substitution (which will be the same value
742 // the caller would get by parsing just this part of the
743 // text with RuleBasedNumberFormat.parse() ). How the two
744 // values are used to derive the full parse result depends
745 // on the types of substitutions: For a regular rule, the
746 // ultimate result is its multiplier substitution's result
747 // times the rule's divisor (or the rule's base value) plus
748 // the modulus substitution's result (which will actually
749 // supersede part of the rule's base value). For a negative-
750 // number rule, the result is the negative of its substitution's
751 // result. For a fraction rule, it's the sum of its two
752 // substitution results. For a rule in a fraction rule set,
753 // it's the numerator substitution's result divided by
754 // the rule's base value. Results from same-value substitutions
755 // propagate back upard, and null substitutions don't affect
757 tempResult
= composeRuleValue(tempResult
, baseValue
);
758 result
.setDouble(tempResult
);
760 // if the parse was UNsuccessful, return 0
768 * Returns true if this is a modulus substitution. (We didn't do this
769 * with instanceof partially because it causes source files to
770 * proliferate and partially because we have to port this to C++.)
771 * @return true if this object is an instance of ModulusSubstitution
774 NFSubstitution::isModulusSubstitution() const {
780 * @return true if this is a decimal format-only substitution
783 NFSubstitution::isDecimalFormatSubstitutionOnly() const {
784 return (ruleSet
== NULL
&& getNumberFormat() != NULL
);
788 * Apple addition, not currently used
789 * @return true if this substitution uses another ruleSet
792 //NFSubstitution::isRuleSetSubstitutionOnly() const {
793 // return (getNumberFormat() == NULL && ruleSet != NULL);
796 //===================================================================
797 // SameValueSubstitution
798 //===================================================================
801 * A substitution that passes the value passed to it through unchanged.
802 * Represented by == in rule descriptions.
804 SameValueSubstitution::SameValueSubstitution(int32_t _pos
,
805 const NFRuleSet
* _ruleSet
,
806 const UnicodeString
& description
,
808 : NFSubstitution(_pos
, _ruleSet
, description
, status
)
810 if (0 == description
.compare(gEqualsEquals
, 2)) {
811 // throw new IllegalArgumentException("== is not a legal token");
812 status
= U_PARSE_ERROR
;
816 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SameValueSubstitution
)
818 //===================================================================
819 // MultiplierSubstitution
820 //===================================================================
822 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MultiplierSubstitution
)
824 UBool
MultiplierSubstitution::operator==(const NFSubstitution
& rhs
) const
826 return NFSubstitution::operator==(rhs
) &&
827 divisor
== ((const MultiplierSubstitution
*)&rhs
)->divisor
;
831 //===================================================================
832 // ModulusSubstitution
833 //===================================================================
836 * A substitution that divides the number being formatted by the its rule's
837 * divisor and formats the remainder. Represented by ">>" in a
840 ModulusSubstitution::ModulusSubstitution(int32_t _pos
,
842 const NFRule
* predecessor
,
843 const NFRuleSet
* _ruleSet
,
844 const UnicodeString
& description
,
846 : NFSubstitution(_pos
, _ruleSet
, description
, status
)
847 , divisor(rule
->getDivisor())
850 // the owning rule's divisor controls the behavior of this
851 // substitution: rather than keeping a backpointer to the rule,
852 // we keep a copy of the divisor
855 status
= U_PARSE_ERROR
;
858 if (0 == description
.compare(gGreaterGreaterGreaterThan
, 3)) {
859 // the >>> token doesn't alter how this substituion calculates the
860 // values it uses for formatting and parsing, but it changes
861 // what's done with that value after it's obtained: >>> short-
862 // circuits the rule-search process and goes straight to the
863 // specified rule to format the substitution value
864 ruleToUse
= predecessor
;
868 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ModulusSubstitution
)
870 UBool
ModulusSubstitution::operator==(const NFSubstitution
& rhs
) const
872 return NFSubstitution::operator==(rhs
) &&
873 divisor
== ((const ModulusSubstitution
*)&rhs
)->divisor
&&
874 ruleToUse
== ((const ModulusSubstitution
*)&rhs
)->ruleToUse
;
877 //-----------------------------------------------------------------------
879 //-----------------------------------------------------------------------
883 * If this is a >>> substitution, use ruleToUse to fill in
884 * the substitution. Otherwise, just use the superclass function.
885 * @param number The number being formatted
886 * @toInsertInto The string to insert the result of this substitution
888 * @param pos The position of the rule text in toInsertInto
891 ModulusSubstitution::doSubstitution(int64_t number
, UnicodeString
& toInsertInto
, int32_t _pos
, int32_t recursionCount
, UErrorCode
& status
) const
893 // if this isn't a >>> substitution, just use the inherited version
894 // of this function (which uses either a rule set or a DecimalFormat
895 // to format its substitution value)
896 if (ruleToUse
== NULL
) {
897 NFSubstitution::doSubstitution(number
, toInsertInto
, _pos
, recursionCount
, status
);
899 // a >>> substitution goes straight to a particular rule to
900 // format the substitution value
902 int64_t numberToFormat
= transformNumber(number
);
903 ruleToUse
->doFormat(numberToFormat
, toInsertInto
, _pos
+ getPos(), recursionCount
, status
);
908 * If this is a >>> substitution, use ruleToUse to fill in
909 * the substitution. Otherwise, just use the superclass function.
910 * @param number The number being formatted
911 * @toInsertInto The string to insert the result of this substitution
913 * @param pos The position of the rule text in toInsertInto
916 ModulusSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t _pos
, int32_t recursionCount
, UErrorCode
& status
) const
918 // if this isn't a >>> substitution, just use the inherited version
919 // of this function (which uses either a rule set or a DecimalFormat
920 // to format its substitution value)
921 if (ruleToUse
== NULL
) {
922 NFSubstitution::doSubstitution(number
, toInsertInto
, _pos
, recursionCount
, status
);
924 // a >>> substitution goes straight to a particular rule to
925 // format the substitution value
927 double numberToFormat
= transformNumber(number
);
929 ruleToUse
->doFormat(numberToFormat
, toInsertInto
, _pos
+ getPos(), recursionCount
, status
);
933 //-----------------------------------------------------------------------
935 //-----------------------------------------------------------------------
938 * If this is a >>> substitution, match only against ruleToUse.
939 * Otherwise, use the superclass function.
940 * @param text The string to parse
941 * @param parsePosition Ignored on entry, updated on exit to point to
942 * the first unmatched character.
943 * @param baseValue The partial parse result prior to calling this
947 ModulusSubstitution::doParse(const UnicodeString
& text
,
948 ParsePosition
& parsePosition
,
952 Formattable
& result
) const
954 // if this isn't a >>> substitution, we can just use the
955 // inherited parse() routine to do the parsing
956 if (ruleToUse
== NULL
) {
957 return NFSubstitution::doParse(text
, parsePosition
, baseValue
, upperBound
, lenientParse
, result
);
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()
963 ruleToUse
->doParse(text
, parsePosition
, FALSE
, upperBound
, result
);
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
);
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.
982 ModulusSubstitution::toString(UnicodeString
& text
) const
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
989 if ( ruleToUse
!= NULL
) { // Must have been a >>> substitution.
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
);
998 //===================================================================
999 // IntegralPartSubstitution
1000 //===================================================================
1002 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IntegralPartSubstitution
)
1005 //===================================================================
1006 // FractionalPartSubstitution
1007 //===================================================================
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.
1015 FractionalPartSubstitution::FractionalPartSubstitution(int32_t _pos
,
1016 const NFRuleSet
* _ruleSet
,
1017 const UnicodeString
& description
,
1019 : NFSubstitution(_pos
, _ruleSet
, description
, status
)
1024 // akk, ruleSet can change in superclass constructor
1025 if (0 == description
.compare(gGreaterGreaterThan
, 2) ||
1026 0 == description
.compare(gGreaterGreaterGreaterThan
, 3) ||
1027 _ruleSet
== getRuleSet()) {
1029 if (0 == description
.compare(gGreaterGreaterGreaterThan
, 3)) {
1034 ((NFRuleSet
*)getRuleSet())->makeIntoFractionRuleSet();
1038 //-----------------------------------------------------------------------
1040 //-----------------------------------------------------------------------
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
1053 FractionalPartSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
,
1054 int32_t _pos
, int32_t recursionCount
, UErrorCode
& status
) const
1056 // if we're not in "byDigits" mode, just use the inherited
1057 // doSubstitution() routine
1059 NFSubstitution::doSubstitution(number
, toInsertInto
, _pos
, recursionCount
, status
);
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
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);
1080 // getRuleSet()->format(digit, toInsertInto, _pos + getPos());
1082 // numberToFormat /= 10;
1087 dl
.roundFixedPoint(20); // round to 20 fraction digits.
1088 dl
.reduce(); // Removes any trailing zeros.
1091 for (int32_t didx
= dl
.getCount()-1; didx
>=dl
.getDecimalAt(); didx
--) {
1092 // Loop iterates over fraction digits, starting with the LSD.
1093 // include both real digits from the number, and zeros
1094 // to the left of the MSD but to the right of the decimal point.
1095 if (pad
&& useSpaces
) {
1096 toInsertInto
.insert(_pos
+ getPos(), gSpace
);
1100 int64_t digit
= didx
>=0 ? dl
.getDigit(didx
) - '0' : 0;
1101 getRuleSet()->format(digit
, toInsertInto
, _pos
+ getPos(), recursionCount
, status
);
1105 // hack around lack of precision in digitlist. if we would end up with
1106 // "foo point" make sure we add a " zero" to the end.
1107 getRuleSet()->format((int64_t)0, toInsertInto
, _pos
+ getPos(), recursionCount
, status
);
1112 //-----------------------------------------------------------------------
1114 //-----------------------------------------------------------------------
1117 * If in "by digits" mode, parses the string as if it were a string
1118 * of individual digits; otherwise, uses the superclass function.
1119 * @param text The string to parse
1120 * @param parsePosition Ignored on entry, but updated on exit to point
1121 * to the first unmatched character
1122 * @param baseValue The partial parse result prior to entering this
1124 * @param upperBound Only consider rules with base values lower than
1125 * this when filling in the substitution
1126 * @param lenientParse If true, try matching the text as numerals if
1127 * matching as words doesn't work
1128 * @return If the match was successful, the current partial parse
1129 * result; otherwise new Long(0). The result is either a Long or
1134 FractionalPartSubstitution::doParse(const UnicodeString
& text
,
1135 ParsePosition
& parsePosition
,
1137 double /*upperBound*/,
1139 Formattable
& resVal
) const
1141 // if we're not in byDigits mode, we can just use the inherited
1144 return NFSubstitution::doParse(text
, parsePosition
, baseValue
, 0, lenientParse
, resVal
);
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
1151 UnicodeString
workText(text
);
1152 ParsePosition
workPos(1);
1155 // double p10 = 0.1;
1158 NumberFormat
* fmt
= NULL
;
1159 while (workText
.length() > 0 && workPos
.getIndex() != 0) {
1160 workPos
.setIndex(0);
1162 getRuleSet()->parse(workText
, workPos
, 10, temp
);
1163 UErrorCode status
= U_ZERO_ERROR
;
1164 digit
= temp
.getLong(status
);
1165 // digit = temp.getType() == Formattable::kLong ?
1167 // (int32_t)temp.getDouble();
1169 if (lenientParse
&& workPos
.getIndex() == 0) {
1171 status
= U_ZERO_ERROR
;
1172 fmt
= NumberFormat::createInstance(status
);
1173 if (U_FAILURE(status
)) {
1179 fmt
->parse(workText
, temp
, workPos
);
1180 digit
= temp
.getLong(status
);
1184 if (workPos
.getIndex() != 0) {
1185 dl
.append((char)('0' + digit
));
1186 // result += digit * p10;
1188 parsePosition
.setIndex(parsePosition
.getIndex() + workPos
.getIndex());
1189 workText
.removeBetween(0, workPos
.getIndex());
1190 while (workText
.length() > 0 && workText
.charAt(0) == gSpace
) {
1191 workText
.removeBetween(0, 1);
1192 parsePosition
.setIndex(parsePosition
.getIndex() + 1);
1198 result
= dl
.getCount() == 0 ? 0 : dl
.getDouble();
1199 result
= composeRuleValue(result
, baseValue
);
1200 resVal
.setDouble(result
);
1206 FractionalPartSubstitution::operator==(const NFSubstitution
& rhs
) const
1208 return NFSubstitution::operator==(rhs
) &&
1209 ((const FractionalPartSubstitution
*)&rhs
)->byDigits
== byDigits
;
1212 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FractionalPartSubstitution
)
1215 //===================================================================
1216 // AbsoluteValueSubstitution
1217 //===================================================================
1219 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AbsoluteValueSubstitution
)
1221 //===================================================================
1222 // NumeratorSubstitution
1223 //===================================================================
1226 NumeratorSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t apos
, int32_t recursionCount
, UErrorCode
& status
) const {
1227 // perform a transformation on the number being formatted that
1228 // is dependent on the type of substitution this is
1230 double numberToFormat
= transformNumber(number
);
1231 int64_t longNF
= util64_fromDouble(numberToFormat
);
1233 const NFRuleSet
* aruleSet
= getRuleSet();
1234 if (withZeros
&& aruleSet
!= NULL
) {
1235 // if there are leading zeros in the decimal expansion then emit them
1237 int32_t len
= toInsertInto
.length();
1238 while ((nf
*= 10) < denominator
) {
1239 toInsertInto
.insert(apos
+ getPos(), gSpace
);
1240 aruleSet
->format((int64_t)0, toInsertInto
, apos
+ getPos(), recursionCount
, status
);
1242 apos
+= toInsertInto
.length() - len
;
1245 // if the result is an integer, from here on out we work in integer
1246 // space (saving time and memory and preserving accuracy)
1247 if (numberToFormat
== longNF
&& aruleSet
!= NULL
) {
1248 aruleSet
->format(longNF
, toInsertInto
, apos
+ getPos(), recursionCount
, status
);
1250 // if the result isn't an integer, then call either our rule set's
1251 // format() method or our DecimalFormat's format() method to
1252 // format the result
1254 if (aruleSet
!= NULL
) {
1255 aruleSet
->format(numberToFormat
, toInsertInto
, apos
+ getPos(), recursionCount
, status
);
1258 getNumberFormat()->format(numberToFormat
, temp
, status
);
1259 toInsertInto
.insert(apos
+ getPos(), temp
);
1265 NumeratorSubstitution::doParse(const UnicodeString
& text
,
1266 ParsePosition
& parsePosition
,
1269 UBool
/*lenientParse*/,
1270 Formattable
& result
) const
1272 // we don't have to do anything special to do the parsing here,
1273 // but we have to turn lenient parsing off-- if we leave it on,
1274 // it SERIOUSLY messes up the algorithm
1276 // if withZeros is true, we need to count the zeros
1277 // and use that to adjust the parse result
1278 UErrorCode status
= U_ZERO_ERROR
;
1279 int32_t zeroCount
= 0;
1280 UnicodeString
workText(text
);
1283 ParsePosition
workPos(1);
1286 while (workText
.length() > 0 && workPos
.getIndex() != 0) {
1287 workPos
.setIndex(0);
1288 getRuleSet()->parse(workText
, workPos
, 1, temp
); // parse zero or nothing at all
1289 if (workPos
.getIndex() == 0) {
1290 // we failed, either there were no more zeros, or the number was formatted with digits
1291 // either way, we're done
1296 parsePosition
.setIndex(parsePosition
.getIndex() + workPos
.getIndex());
1297 workText
.remove(0, workPos
.getIndex());
1298 while (workText
.length() > 0 && workText
.charAt(0) == gSpace
) {
1299 workText
.remove(0, 1);
1300 parsePosition
.setIndex(parsePosition
.getIndex() + 1);
1305 workText
.remove(0, (int32_t)parsePosition
.getIndex());
1306 parsePosition
.setIndex(0);
1309 // we've parsed off the zeros, now let's parse the rest from our current position
1310 NFSubstitution::doParse(workText
, parsePosition
, withZeros
? 1 : baseValue
, upperBound
, FALSE
, result
);
1313 // any base value will do in this case. is there a way to
1314 // force this to not bother trying all the base values?
1316 // compute the 'effective' base and prescale the value down
1317 int64_t n
= result
.getLong(status
); // force conversion!
1324 // now add the zeros
1325 while (zeroCount
> 0) {
1329 // d is now our true denominator
1330 result
.setDouble((double)n
/(double)d
);
1337 NumeratorSubstitution::operator==(const NFSubstitution
& rhs
) const
1339 return NFSubstitution::operator==(rhs
) &&
1340 denominator
== ((const NumeratorSubstitution
*)&rhs
)->denominator
;
1343 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumeratorSubstitution
)
1345 const UChar
NumeratorSubstitution::LTLT
[] = { 0x003c, 0x003c };