2 ******************************************************************************
3 * Copyright (C) 1997-2015, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 ******************************************************************************
6 * file name: nfsubs.cpp
8 * tab size: 8 (not used)
11 * Modification history
13 * 10/11/2001 Doug Ported from ICU4J
17 #include "utypeinfo.h" // for 'typeid' to work
24 static const UChar gLessThan
= 0x003c;
25 static const UChar gEquals
= 0x003d;
26 static const UChar gGreaterThan
= 0x003e;
27 static const UChar gPercent
= 0x0025;
28 static const UChar gPound
= 0x0023;
29 static const UChar gZero
= 0x0030;
30 static const UChar gSpace
= 0x0020;
32 static const UChar gEqualsEquals
[] =
36 static const UChar gGreaterGreaterGreaterThan
[] =
40 static const UChar gGreaterGreaterThan
[] =
47 class SameValueSubstitution
: public NFSubstitution
{
49 SameValueSubstitution(int32_t pos
,
50 const NFRuleSet
* ruleset
,
51 const UnicodeString
& description
,
53 virtual ~SameValueSubstitution();
55 virtual int64_t transformNumber(int64_t number
) const { return number
; }
56 virtual double transformNumber(double number
) const { return number
; }
57 virtual double composeRuleValue(double newRuleValue
, double /*oldRuleValue*/) const { return newRuleValue
; }
58 virtual double calcUpperBound(double oldUpperBound
) const { return oldUpperBound
; }
59 virtual UChar
tokenChar() const { return (UChar
)0x003d; } // '='
62 static UClassID
getStaticClassID(void);
63 virtual UClassID
getDynamicClassID(void) const;
66 SameValueSubstitution::~SameValueSubstitution() {}
68 class MultiplierSubstitution
: public NFSubstitution
{
73 MultiplierSubstitution(int32_t _pos
,
75 const NFRuleSet
* _ruleSet
,
76 const UnicodeString
& description
,
78 : NFSubstitution(_pos
, _ruleSet
, description
, status
), divisor(_divisor
)
80 ldivisor
= util64_fromDouble(divisor
);
82 status
= U_PARSE_ERROR
;
85 virtual ~MultiplierSubstitution();
87 virtual void setDivisor(int32_t radix
, int32_t exponent
, UErrorCode
& status
) {
88 divisor
= uprv_pow(radix
, exponent
);
89 ldivisor
= util64_fromDouble(divisor
);
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
/ ldivisor
;
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
{
128 const NFRule
* ruleToUse
;
130 ModulusSubstitution(int32_t pos
,
132 const NFRule
* rulePredecessor
,
133 const NFRuleSet
* ruleSet
,
134 const UnicodeString
& description
,
136 virtual ~ModulusSubstitution();
138 virtual void setDivisor(int32_t radix
, int32_t exponent
, UErrorCode
& status
) {
139 divisor
= uprv_pow(radix
, exponent
);
140 ldivisor
= util64_fromDouble(divisor
);
143 status
= U_PARSE_ERROR
;
147 virtual UBool
operator==(const NFSubstitution
& rhs
) const;
149 virtual void doSubstitution(int64_t number
, UnicodeString
& toInsertInto
, int32_t pos
, int32_t recursionCount
, UErrorCode
& status
) const;
150 virtual void doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t pos
, int32_t recursionCount
, UErrorCode
& status
) const;
152 virtual int64_t transformNumber(int64_t number
) const { return number
% ldivisor
; }
153 virtual double transformNumber(double number
) const { return uprv_fmod(number
, divisor
); }
155 virtual UBool
doParse(const UnicodeString
& text
,
156 ParsePosition
& parsePosition
,
160 Formattable
& result
) const;
162 virtual double composeRuleValue(double newRuleValue
, double oldRuleValue
) const {
163 return oldRuleValue
- uprv_fmod(oldRuleValue
, divisor
) + newRuleValue
;
166 virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor
; }
168 virtual UBool
isModulusSubstitution() const { return TRUE
; }
170 virtual UChar
tokenChar() const { return (UChar
)0x003e; } // '>'
172 virtual void toString(UnicodeString
& result
) const;
175 static UClassID
getStaticClassID(void);
176 virtual UClassID
getDynamicClassID(void) const;
179 ModulusSubstitution::~ModulusSubstitution() {}
181 class IntegralPartSubstitution
: public NFSubstitution
{
183 IntegralPartSubstitution(int32_t _pos
,
184 const NFRuleSet
* _ruleSet
,
185 const UnicodeString
& description
,
187 : NFSubstitution(_pos
, _ruleSet
, description
, status
) {}
188 virtual ~IntegralPartSubstitution();
190 virtual int64_t transformNumber(int64_t number
) const { return number
; }
191 virtual double transformNumber(double number
) const { return uprv_floor(number
); }
192 virtual double composeRuleValue(double newRuleValue
, double oldRuleValue
) const { return newRuleValue
+ oldRuleValue
; }
193 virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX
; }
194 virtual UChar
tokenChar() const { return (UChar
)0x003c; } // '<'
197 static UClassID
getStaticClassID(void);
198 virtual UClassID
getDynamicClassID(void) const;
201 IntegralPartSubstitution::~IntegralPartSubstitution() {}
203 class FractionalPartSubstitution
: public NFSubstitution
{
206 enum { kMaxDecimalDigits
= 8 };
208 FractionalPartSubstitution(int32_t pos
,
209 const NFRuleSet
* ruleSet
,
210 const UnicodeString
& description
,
212 virtual ~FractionalPartSubstitution();
214 virtual UBool
operator==(const NFSubstitution
& rhs
) const;
216 virtual void doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t pos
, int32_t recursionCount
, UErrorCode
& status
) const;
217 virtual void doSubstitution(int64_t /*number*/, UnicodeString
& /*toInsertInto*/, int32_t /*_pos*/, int32_t /*recursionCount*/, UErrorCode
& /*status*/) const {}
218 virtual int64_t transformNumber(int64_t /*number*/) const { return 0; }
219 virtual double transformNumber(double number
) const { return number
- uprv_floor(number
); }
221 virtual UBool
doParse(const UnicodeString
& text
,
222 ParsePosition
& parsePosition
,
226 Formattable
& result
) const;
228 virtual double composeRuleValue(double newRuleValue
, double oldRuleValue
) const { return newRuleValue
+ oldRuleValue
; }
229 virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0.0; }
230 virtual UChar
tokenChar() const { return (UChar
)0x003e; } // '>'
233 static UClassID
getStaticClassID(void);
234 virtual UClassID
getDynamicClassID(void) const;
237 FractionalPartSubstitution::~FractionalPartSubstitution() {}
239 class AbsoluteValueSubstitution
: public NFSubstitution
{
241 AbsoluteValueSubstitution(int32_t _pos
,
242 const NFRuleSet
* _ruleSet
,
243 const UnicodeString
& description
,
245 : NFSubstitution(_pos
, _ruleSet
, description
, status
) {}
246 virtual ~AbsoluteValueSubstitution();
248 virtual int64_t transformNumber(int64_t number
) const { return number
>= 0 ? number
: -number
; }
249 virtual double transformNumber(double number
) const { return uprv_fabs(number
); }
250 virtual double composeRuleValue(double newRuleValue
, double /*oldRuleValue*/) const { return -newRuleValue
; }
251 virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX
; }
252 virtual UChar
tokenChar() const { return (UChar
)0x003e; } // '>'
255 static UClassID
getStaticClassID(void);
256 virtual UClassID
getDynamicClassID(void) const;
259 AbsoluteValueSubstitution::~AbsoluteValueSubstitution() {}
261 class NumeratorSubstitution
: public NFSubstitution
{
263 int64_t ldenominator
;
266 static inline UnicodeString
fixdesc(const UnicodeString
& desc
) {
267 if (desc
.endsWith(LTLT
, 2)) {
268 UnicodeString
result(desc
, 0, desc
.length()-1);
273 NumeratorSubstitution(int32_t _pos
,
276 const UnicodeString
& description
,
278 : NFSubstitution(_pos
, _ruleSet
, fixdesc(description
), status
), denominator(_denominator
)
280 ldenominator
= util64_fromDouble(denominator
);
281 withZeros
= description
.endsWith(LTLT
, 2);
283 virtual ~NumeratorSubstitution();
285 virtual UBool
operator==(const NFSubstitution
& rhs
) const;
287 virtual int64_t transformNumber(int64_t number
) const { return number
* ldenominator
; }
288 virtual double transformNumber(double number
) const { return uprv_round(number
* denominator
); }
290 virtual void doSubstitution(int64_t /*number*/, UnicodeString
& /*toInsertInto*/, int32_t /*_pos*/, int32_t /*recursionCount*/, UErrorCode
& /*status*/) const {}
291 virtual void doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t pos
, int32_t recursionCount
, UErrorCode
& status
) const;
292 virtual UBool
doParse(const UnicodeString
& text
,
293 ParsePosition
& parsePosition
,
296 UBool
/*lenientParse*/,
297 Formattable
& result
) const;
299 virtual double composeRuleValue(double newRuleValue
, double oldRuleValue
) const { return newRuleValue
/ oldRuleValue
; }
300 virtual double calcUpperBound(double /*oldUpperBound*/) const { return denominator
; }
301 virtual UChar
tokenChar() const { return (UChar
)0x003c; } // '<'
303 static const UChar LTLT
[2];
306 static UClassID
getStaticClassID(void);
307 virtual UClassID
getDynamicClassID(void) const;
310 NumeratorSubstitution::~NumeratorSubstitution() {}
313 NFSubstitution::makeSubstitution(int32_t pos
,
315 const NFRule
* predecessor
,
316 const NFRuleSet
* ruleSet
,
317 const RuleBasedNumberFormat
* formatter
,
318 const UnicodeString
& description
,
321 // if the description is empty, return a NullSubstitution
322 if (description
.length() == 0) {
326 switch (description
.charAt(0)) {
327 // if the description begins with '<'...
329 // throw an exception if the rule is a negative number
331 if (rule
->getBaseValue() == NFRule::kNegativeNumberRule
) {
332 // throw new IllegalArgumentException("<< not allowed in negative-number rule");
333 status
= U_PARSE_ERROR
;
337 // if the rule is a fraction rule, return an
338 // IntegralPartSubstitution
339 else if (rule
->getBaseValue() == NFRule::kImproperFractionRule
340 || rule
->getBaseValue() == NFRule::kProperFractionRule
341 || rule
->getBaseValue() == NFRule::kMasterRule
) {
342 return new IntegralPartSubstitution(pos
, ruleSet
, description
, status
);
345 // if the rule set containing the rule is a fraction
346 // rule set, return a NumeratorSubstitution
347 else if (ruleSet
->isFractionRuleSet()) {
348 return new NumeratorSubstitution(pos
, (double)rule
->getBaseValue(),
349 formatter
->getDefaultRuleSet(), description
, status
);
352 // otherwise, return a MultiplierSubstitution
354 return new MultiplierSubstitution(pos
, rule
->getDivisor(), ruleSet
,
355 description
, status
);
358 // if the description begins with '>'...
360 // if the rule is a negative-number rule, return
361 // an AbsoluteValueSubstitution
362 if (rule
->getBaseValue() == NFRule::kNegativeNumberRule
) {
363 return new AbsoluteValueSubstitution(pos
, ruleSet
, description
, status
);
366 // if the rule is a fraction rule, return a
367 // FractionalPartSubstitution
368 else if (rule
->getBaseValue() == NFRule::kImproperFractionRule
369 || rule
->getBaseValue() == NFRule::kProperFractionRule
370 || rule
->getBaseValue() == NFRule::kMasterRule
) {
371 return new FractionalPartSubstitution(pos
, ruleSet
, description
, status
);
374 // if the rule set owning the rule is a fraction rule set,
375 // throw an exception
376 else if (ruleSet
->isFractionRuleSet()) {
377 // throw new IllegalArgumentException(">> not allowed in fraction rule set");
378 status
= U_PARSE_ERROR
;
382 // otherwise, return a ModulusSubstitution
384 return new ModulusSubstitution(pos
, rule
->getDivisor(), predecessor
,
385 ruleSet
, description
, status
);
388 // if the description begins with '=', always return a
389 // SameValueSubstitution
391 return new SameValueSubstitution(pos
, ruleSet
, description
, status
);
393 // and if it's anything else, throw an exception
395 // throw new IllegalArgumentException("Illegal substitution character");
396 status
= U_PARSE_ERROR
;
401 NFSubstitution::NFSubstitution(int32_t _pos
,
402 const NFRuleSet
* _ruleSet
,
403 const UnicodeString
& description
,
405 : pos(_pos
), ruleSet(NULL
), numberFormat(NULL
)
407 // the description should begin and end with the same character.
408 // If it doesn't that's a syntax error. Otherwise,
409 // makeSubstitution() was the only thing that needed to know
410 // about these characters, so strip them off
411 UnicodeString
workingDescription(description
);
412 if (description
.length() >= 2
413 && description
.charAt(0) == description
.charAt(description
.length() - 1))
415 workingDescription
.remove(description
.length() - 1, 1);
416 workingDescription
.remove(0, 1);
418 else if (description
.length() != 0) {
419 // throw new IllegalArgumentException("Illegal substitution syntax");
420 status
= U_PARSE_ERROR
;
424 if (workingDescription
.length() == 0) {
425 // if the description was just two paired token characters
426 // (i.e., "<<" or ">>"), it uses the rule set it belongs to to
428 this->ruleSet
= _ruleSet
;
430 else if (workingDescription
.charAt(0) == gPercent
) {
431 // if the description contains a rule set name, that's the rule
432 // set we use to format the result: get a reference to the
434 this->ruleSet
= _ruleSet
->getOwner()->findRuleSet(workingDescription
, status
);
436 else if (workingDescription
.charAt(0) == gPound
|| workingDescription
.charAt(0) ==gZero
) {
437 // if the description begins with 0 or #, treat it as a
438 // DecimalFormat pattern, and initialize a DecimalFormat with
439 // that pattern (then set it to use the DecimalFormatSymbols
440 // belonging to our formatter)
441 const DecimalFormatSymbols
* sym
= _ruleSet
->getOwner()->getDecimalFormatSymbols();
443 status
= U_MISSING_RESOURCE_ERROR
;
446 DecimalFormat
*tempNumberFormat
= new DecimalFormat(workingDescription
, *sym
, status
);
448 if (!tempNumberFormat
) {
449 status
= U_MEMORY_ALLOCATION_ERROR
;
452 if (U_FAILURE(status
)) {
453 delete tempNumberFormat
;
456 this->numberFormat
= tempNumberFormat
;
458 else if (workingDescription
.charAt(0) == gGreaterThan
) {
459 // if the description is ">>>", this substitution bypasses the
460 // usual rule-search process and always uses the rule that precedes
461 // it in its own rule set's rule list (this is used for place-value
462 // notations: formats where you want to see a particular part of
463 // a number even when it's 0)
465 // this causes problems when >>> is used in a frationalPartSubstitution
466 // this->ruleSet = NULL;
467 this->ruleSet
= _ruleSet
;
468 this->numberFormat
= NULL
;
471 // and of the description is none of these things, it's a syntax error
473 // throw new IllegalArgumentException("Illegal substitution syntax");
474 status
= U_PARSE_ERROR
;
478 NFSubstitution::~NFSubstitution()
485 * Set's the substitution's divisor. Used by NFRule.setBaseValue().
486 * A no-op for all substitutions except multiplier and modulus
488 * @param radix The radix of the divisor
489 * @param exponent The exponent of the divisor
492 NFSubstitution::setDivisor(int32_t /*radix*/, int32_t /*exponent*/, UErrorCode
& /*status*/) {
493 // a no-op for all substitutions except multiplier and modulus substitutions
497 NFSubstitution::setDecimalFormatSymbols(const DecimalFormatSymbols
&newSymbols
, UErrorCode
& /*status*/) {
498 if (numberFormat
!= NULL
) {
499 numberFormat
->setDecimalFormatSymbols(newSymbols
);
503 //-----------------------------------------------------------------------
505 //-----------------------------------------------------------------------
507 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NFSubstitution
)
510 * Compares two substitutions for equality
511 * @param The substitution to compare this one to
512 * @return true if the two substitutions are functionally equivalent
515 NFSubstitution::operator==(const NFSubstitution
& rhs
) const
517 // compare class and all of the fields all substitutions have
519 // this should be called by subclasses before their own equality tests
520 return typeid(*this) == typeid(rhs
)
522 && (ruleSet
== NULL
) == (rhs
.ruleSet
== NULL
)
523 // && ruleSet == rhs.ruleSet causes circularity, other checks to make instead?
524 && (numberFormat
== NULL
525 ? (rhs
.numberFormat
== NULL
)
526 : (*numberFormat
== *rhs
.numberFormat
));
530 * Returns a textual description of the substitution
531 * @return A textual description of the substitution. This might
532 * not be identical to the description it was created from, but
533 * it'll produce the same result.
536 NFSubstitution::toString(UnicodeString
& text
) const
538 // use tokenChar() to get the character at the beginning and
539 // end of the substitutin token. In between them will go
540 // either the name of the rule set it uses, or the pattern of
541 // the DecimalFormat it uses
543 text
.append(tokenChar());
546 if (ruleSet
!= NULL
) {
547 ruleSet
->getName(temp
);
548 } else if (numberFormat
!= NULL
) {
549 numberFormat
->toPattern(temp
);
552 text
.append(tokenChar());
555 //-----------------------------------------------------------------------
557 //-----------------------------------------------------------------------
560 * Performs a mathematical operation on the number, formats it using
561 * either ruleSet or decimalFormat, and inserts the result into
563 * @param number The number being formatted.
564 * @param toInsertInto The string we insert the result into
565 * @param pos The position in toInsertInto where the owning rule's
566 * rule text begins (this value is added to this substitution's
567 * position to determine exactly where to insert the new text)
570 NFSubstitution::doSubstitution(int64_t number
, UnicodeString
& toInsertInto
, int32_t _pos
, int32_t recursionCount
, UErrorCode
& status
) const
572 if (ruleSet
!= NULL
) {
573 // perform a transformation on the number that is dependent
574 // on the type of substitution this is, then just call its
575 // rule set's format() method to format the result
576 ruleSet
->format(transformNumber(number
), toInsertInto
, _pos
+ this->pos
, recursionCount
, status
);
577 } else if (numberFormat
!= NULL
) {
578 // or perform the transformation on the number (preserving
579 // the result's fractional part if the formatter it set
580 // to show it), then use that formatter's format() method
581 // to format the result
582 double numberToFormat
= transformNumber((double)number
);
583 if (numberFormat
->getMaximumFractionDigits() == 0) {
584 numberToFormat
= uprv_floor(numberToFormat
);
588 numberFormat
->format(numberToFormat
, temp
, status
);
589 toInsertInto
.insert(_pos
+ this->pos
, temp
);
594 * Performs a mathematical operation on the number, formats it using
595 * either ruleSet or decimalFormat, and inserts the result into
597 * @param number The number being formatted.
598 * @param toInsertInto The string we insert the result into
599 * @param pos The position in toInsertInto where the owning rule's
600 * rule text begins (this value is added to this substitution's
601 * position to determine exactly where to insert the new text)
604 NFSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t _pos
, int32_t recursionCount
, UErrorCode
& status
) const {
605 // perform a transformation on the number being formatted that
606 // is dependent on the type of substitution this is
607 double numberToFormat
= transformNumber(number
);
609 if (uprv_isInfinite(numberToFormat
)) {
610 // This is probably a minus rule. Combine it with an infinite rule.
611 const NFRule
*infiniteRule
= ruleSet
->findDoubleRule(uprv_getInfinity());
612 infiniteRule
->doFormat(numberToFormat
, toInsertInto
, _pos
+ this->pos
, recursionCount
, status
);
616 // if the result is an integer, from here on out we work in integer
617 // space (saving time and memory and preserving accuracy)
618 if (numberToFormat
== uprv_floor(numberToFormat
) && ruleSet
!= NULL
) {
619 ruleSet
->format(util64_fromDouble(numberToFormat
), toInsertInto
, _pos
+ this->pos
, recursionCount
, status
);
621 // if the result isn't an integer, then call either our rule set's
622 // format() method or our DecimalFormat's format() method to
625 if (ruleSet
!= NULL
) {
626 ruleSet
->format(numberToFormat
, toInsertInto
, _pos
+ this->pos
, recursionCount
, status
);
627 } else if (numberFormat
!= NULL
) {
629 numberFormat
->format(numberToFormat
, temp
);
630 toInsertInto
.insert(_pos
+ this->pos
, temp
);
636 //-----------------------------------------------------------------------
638 //-----------------------------------------------------------------------
645 * Parses a string using the rule set or DecimalFormat belonging
646 * to this substitution. If there's a match, a mathematical
647 * operation (the inverse of the one used in formatting) is
648 * performed on the result of the parse and the value passed in
649 * and returned as the result. The parse position is updated to
650 * point to the first unmatched character in the string.
651 * @param text The string to parse
652 * @param parsePosition On entry, ignored, but assumed to be 0.
653 * On exit, this is updated to point to the first unmatched
654 * character (or 0 if the substitution didn't match)
655 * @param baseValue A partial parse result that should be
656 * combined with the result of this parse
657 * @param upperBound When searching the rule set for a rule
658 * matching the string passed in, only rules with base values
659 * lower than this are considered
660 * @param lenientParse If true and matching against rules fails,
661 * the substitution will also try matching the text against
662 * numerals using a default-costructed NumberFormat. If false,
663 * no extra work is done. (This value is false whenever the
664 * formatter isn't in lenient-parse mode, but is also false
665 * under some conditions even when the formatter _is_ in
666 * lenient-parse mode.)
667 * @return If there's a match, this is the result of composing
668 * baseValue with whatever was returned from matching the
669 * characters. This will be either a Long or a Double. If there's
670 * no match this is new Long(0) (not null), and parsePosition
674 NFSubstitution::doParse(const UnicodeString
& text
,
675 ParsePosition
& parsePosition
,
679 Formattable
& result
) const
682 fprintf(stderr
, "<nfsubs> %x bv: %g ub: %g\n", this, baseValue
, upperBound
);
684 // figure out the highest base value a rule can have and match
685 // the text being parsed (this varies according to the type of
686 // substitutions: multiplier, modulus, and numerator substitutions
687 // restrict the search to rules with base values lower than their
688 // own; same-value substitutions leave the upper bound wherever
689 // it was, and the others allow any rule to match
690 upperBound
= calcUpperBound(upperBound
);
692 // use our rule set to parse the text. If that fails and
693 // lenient parsing is enabled (this is always false if the
694 // formatter's lenient-parsing mode is off, but it may also
695 // be false even when the formatter's lenient-parse mode is
696 // on), then also try parsing the text using a default-
697 // constructed NumberFormat
698 if (ruleSet
!= NULL
) {
699 ruleSet
->parse(text
, parsePosition
, upperBound
, result
);
700 if (lenientParse
&& !ruleSet
->isFractionRuleSet() && parsePosition
.getIndex() == 0) {
701 UErrorCode status
= U_ZERO_ERROR
;
702 NumberFormat
* fmt
= NumberFormat::createInstance(status
);
703 if (U_SUCCESS(status
)) {
704 fmt
->parse(text
, result
, parsePosition
);
709 // ...or use our DecimalFormat to parse the text
710 } else if (numberFormat
!= NULL
) {
711 numberFormat
->parse(text
, result
, parsePosition
);
714 // if the parse was successful, we've already advanced the caller's
715 // parse position (this is the one function that doesn't have one
716 // of its own). Derive a parse result and return it as a Long,
717 // if possible, or a Double
718 if (parsePosition
.getIndex() != 0) {
719 UErrorCode status
= U_ZERO_ERROR
;
720 double tempResult
= result
.getDouble(status
);
722 // composeRuleValue() produces a full parse result from
723 // the partial parse result passed to this function from
724 // the caller (this is either the owning rule's base value
725 // or the partial result obtained from composing the
726 // owning rule's base value with its other substitution's
727 // parse result) and the partial parse result obtained by
728 // matching the substitution (which will be the same value
729 // the caller would get by parsing just this part of the
730 // text with RuleBasedNumberFormat.parse() ). How the two
731 // values are used to derive the full parse result depends
732 // on the types of substitutions: For a regular rule, the
733 // ultimate result is its multiplier substitution's result
734 // times the rule's divisor (or the rule's base value) plus
735 // the modulus substitution's result (which will actually
736 // supersede part of the rule's base value). For a negative-
737 // number rule, the result is the negative of its substitution's
738 // result. For a fraction rule, it's the sum of its two
739 // substitution results. For a rule in a fraction rule set,
740 // it's the numerator substitution's result divided by
741 // the rule's base value. Results from same-value substitutions
742 // propagate back upard, and null substitutions don't affect
744 tempResult
= composeRuleValue(tempResult
, baseValue
);
745 result
.setDouble(tempResult
);
747 // if the parse was UNsuccessful, return 0
755 * Returns true if this is a modulus substitution. (We didn't do this
756 * with instanceof partially because it causes source files to
757 * proliferate and partially because we have to port this to C++.)
758 * @return true if this object is an instance of ModulusSubstitution
761 NFSubstitution::isModulusSubstitution() const {
767 * @return true if this is a decimal format-only substitution
770 NFSubstitution::isDecimalFormatSubstitutionOnly() const {
771 return (ruleSet
== NULL
&& getNumberFormat() != NULL
);
775 * Apple addition, not currently used
776 * @return true if this substitution uses another ruleSet
779 //NFSubstitution::isRuleSetSubstitutionOnly() const {
780 // return (getNumberFormat() == NULL && ruleSet != NULL);
783 //===================================================================
784 // SameValueSubstitution
785 //===================================================================
788 * A substitution that passes the value passed to it through unchanged.
789 * Represented by == in rule descriptions.
791 SameValueSubstitution::SameValueSubstitution(int32_t _pos
,
792 const NFRuleSet
* _ruleSet
,
793 const UnicodeString
& description
,
795 : NFSubstitution(_pos
, _ruleSet
, description
, status
)
797 if (0 == description
.compare(gEqualsEquals
, 2)) {
798 // throw new IllegalArgumentException("== is not a legal token");
799 status
= U_PARSE_ERROR
;
803 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SameValueSubstitution
)
805 //===================================================================
806 // MultiplierSubstitution
807 //===================================================================
809 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MultiplierSubstitution
)
811 UBool
MultiplierSubstitution::operator==(const NFSubstitution
& rhs
) const
813 return NFSubstitution::operator==(rhs
) &&
814 divisor
== ((const MultiplierSubstitution
*)&rhs
)->divisor
;
818 //===================================================================
819 // ModulusSubstitution
820 //===================================================================
823 * A substitution that divides the number being formatted by the its rule's
824 * divisor and formats the remainder. Represented by ">>" in a
827 ModulusSubstitution::ModulusSubstitution(int32_t _pos
,
829 const NFRule
* predecessor
,
830 const NFRuleSet
* _ruleSet
,
831 const UnicodeString
& description
,
833 : NFSubstitution(_pos
, _ruleSet
, description
, status
)
837 ldivisor
= util64_fromDouble(_divisor
);
839 // the owning rule's divisor controls the behavior of this
840 // substitution: rather than keeping a backpointer to the rule,
841 // we keep a copy of the divisor
844 status
= U_PARSE_ERROR
;
847 if (0 == description
.compare(gGreaterGreaterGreaterThan
, 3)) {
848 // the >>> token doesn't alter how this substituion calculates the
849 // values it uses for formatting and parsing, but it changes
850 // what's done with that value after it's obtained: >>> short-
851 // circuits the rule-search process and goes straight to the
852 // specified rule to format the substitution value
853 ruleToUse
= predecessor
;
857 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ModulusSubstitution
)
859 UBool
ModulusSubstitution::operator==(const NFSubstitution
& rhs
) const
861 return NFSubstitution::operator==(rhs
) &&
862 divisor
== ((const ModulusSubstitution
*)&rhs
)->divisor
&&
863 ruleToUse
== ((const ModulusSubstitution
*)&rhs
)->ruleToUse
;
866 //-----------------------------------------------------------------------
868 //-----------------------------------------------------------------------
872 * If this is a >>> substitution, use ruleToUse to fill in
873 * the substitution. Otherwise, just use the superclass function.
874 * @param number The number being formatted
875 * @toInsertInto The string to insert the result of this substitution
877 * @param pos The position of the rule text in toInsertInto
880 ModulusSubstitution::doSubstitution(int64_t number
, UnicodeString
& toInsertInto
, int32_t _pos
, int32_t recursionCount
, UErrorCode
& status
) const
882 // if this isn't a >>> substitution, just use the inherited version
883 // of this function (which uses either a rule set or a DecimalFormat
884 // to format its substitution value)
885 if (ruleToUse
== NULL
) {
886 NFSubstitution::doSubstitution(number
, toInsertInto
, _pos
, recursionCount
, status
);
888 // a >>> substitution goes straight to a particular rule to
889 // format the substitution value
891 int64_t numberToFormat
= transformNumber(number
);
892 ruleToUse
->doFormat(numberToFormat
, toInsertInto
, _pos
+ getPos(), recursionCount
, status
);
897 * If this is a >>> substitution, use ruleToUse to fill in
898 * the substitution. Otherwise, just use the superclass function.
899 * @param number The number being formatted
900 * @toInsertInto The string to insert the result of this substitution
902 * @param pos The position of the rule text in toInsertInto
905 ModulusSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t _pos
, int32_t recursionCount
, UErrorCode
& status
) const
907 // if this isn't a >>> substitution, just use the inherited version
908 // of this function (which uses either a rule set or a DecimalFormat
909 // to format its substitution value)
910 if (ruleToUse
== NULL
) {
911 NFSubstitution::doSubstitution(number
, toInsertInto
, _pos
, recursionCount
, status
);
913 // a >>> substitution goes straight to a particular rule to
914 // format the substitution value
916 double numberToFormat
= transformNumber(number
);
918 ruleToUse
->doFormat(numberToFormat
, toInsertInto
, _pos
+ getPos(), recursionCount
, status
);
922 //-----------------------------------------------------------------------
924 //-----------------------------------------------------------------------
927 * If this is a >>> substitution, match only against ruleToUse.
928 * Otherwise, use the superclass function.
929 * @param text The string to parse
930 * @param parsePosition Ignored on entry, updated on exit to point to
931 * the first unmatched character.
932 * @param baseValue The partial parse result prior to calling this
936 ModulusSubstitution::doParse(const UnicodeString
& text
,
937 ParsePosition
& parsePosition
,
941 Formattable
& result
) const
943 // if this isn't a >>> substitution, we can just use the
944 // inherited parse() routine to do the parsing
945 if (ruleToUse
== NULL
) {
946 return NFSubstitution::doParse(text
, parsePosition
, baseValue
, upperBound
, lenientParse
, result
);
948 // but if it IS a >>> substitution, we have to do it here: we
949 // use the specific rule's doParse() method, and then we have to
950 // do some of the other work of NFRuleSet.parse()
952 ruleToUse
->doParse(text
, parsePosition
, FALSE
, upperBound
, result
);
954 if (parsePosition
.getIndex() != 0) {
955 UErrorCode status
= U_ZERO_ERROR
;
956 double tempResult
= result
.getDouble(status
);
957 tempResult
= composeRuleValue(tempResult
, baseValue
);
958 result
.setDouble(tempResult
);
965 * Returns a textual description of the substitution
966 * @return A textual description of the substitution. This might
967 * not be identical to the description it was created from, but
968 * it'll produce the same result.
971 ModulusSubstitution::toString(UnicodeString
& text
) const
973 // use tokenChar() to get the character at the beginning and
974 // end of the substitutin token. In between them will go
975 // either the name of the rule set it uses, or the pattern of
976 // the DecimalFormat it uses
978 if ( ruleToUse
!= NULL
) { // Must have been a >>> substitution.
980 text
.append(tokenChar());
981 text
.append(tokenChar());
982 text
.append(tokenChar());
983 } else { // Otherwise just use the super-class function.
984 NFSubstitution::toString(text
);
987 //===================================================================
988 // IntegralPartSubstitution
989 //===================================================================
991 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IntegralPartSubstitution
)
994 //===================================================================
995 // FractionalPartSubstitution
996 //===================================================================
1000 * Constructs a FractionalPartSubstitution. This object keeps a flag
1001 * telling whether it should format by digits or not. In addition,
1002 * it marks the rule set it calls (if any) as a fraction rule set.
1004 FractionalPartSubstitution::FractionalPartSubstitution(int32_t _pos
,
1005 const NFRuleSet
* _ruleSet
,
1006 const UnicodeString
& description
,
1008 : NFSubstitution(_pos
, _ruleSet
, description
, status
)
1013 // akk, ruleSet can change in superclass constructor
1014 if (0 == description
.compare(gGreaterGreaterThan
, 2) ||
1015 0 == description
.compare(gGreaterGreaterGreaterThan
, 3) ||
1016 _ruleSet
== getRuleSet()) {
1018 if (0 == description
.compare(gGreaterGreaterGreaterThan
, 3)) {
1023 ((NFRuleSet
*)getRuleSet())->makeIntoFractionRuleSet();
1027 //-----------------------------------------------------------------------
1029 //-----------------------------------------------------------------------
1032 * If in "by digits" mode, fills in the substitution one decimal digit
1033 * at a time using the rule set containing this substitution.
1034 * Otherwise, uses the superclass function.
1035 * @param number The number being formatted
1036 * @param toInsertInto The string to insert the result of formatting
1037 * the substitution into
1038 * @param pos The position of the owning rule's rule text in
1042 FractionalPartSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
,
1043 int32_t _pos
, int32_t recursionCount
, UErrorCode
& status
) const
1045 // if we're not in "byDigits" mode, just use the inherited
1046 // doSubstitution() routine
1048 NFSubstitution::doSubstitution(number
, toInsertInto
, _pos
, recursionCount
, status
);
1050 // if we're in "byDigits" mode, transform the value into an integer
1051 // by moving the decimal point eight places to the right and
1052 // pulling digits off the right one at a time, formatting each digit
1053 // as an integer using this substitution's owning rule set
1054 // (this is slower, but more accurate, than doing it from the
1057 // int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits));
1058 // // this flag keeps us from formatting trailing zeros. It starts
1059 // // out false because we're pulling from the right, and switches
1060 // // to true the first time we encounter a non-zero digit
1061 // UBool doZeros = FALSE;
1062 // for (int32_t i = 0; i < kMaxDecimalDigits; i++) {
1063 // int64_t digit = numberToFormat % 10;
1064 // if (digit != 0 || doZeros) {
1065 // if (doZeros && useSpaces) {
1066 // toInsertInto.insert(_pos + getPos(), gSpace);
1069 // getRuleSet()->format(digit, toInsertInto, _pos + getPos());
1071 // numberToFormat /= 10;
1076 dl
.roundFixedPoint(20); // round to 20 fraction digits.
1077 dl
.reduce(); // Removes any trailing zeros.
1080 for (int32_t didx
= dl
.getCount()-1; didx
>=dl
.getDecimalAt(); didx
--) {
1081 // Loop iterates over fraction digits, starting with the LSD.
1082 // include both real digits from the number, and zeros
1083 // to the left of the MSD but to the right of the decimal point.
1084 if (pad
&& useSpaces
) {
1085 toInsertInto
.insert(_pos
+ getPos(), gSpace
);
1089 int64_t digit
= didx
>=0 ? dl
.getDigit(didx
) - '0' : 0;
1090 getRuleSet()->format(digit
, toInsertInto
, _pos
+ getPos(), recursionCount
, status
);
1094 // hack around lack of precision in digitlist. if we would end up with
1095 // "foo point" make sure we add a " zero" to the end.
1096 getRuleSet()->format((int64_t)0, toInsertInto
, _pos
+ getPos(), recursionCount
, status
);
1101 //-----------------------------------------------------------------------
1103 //-----------------------------------------------------------------------
1106 * If in "by digits" mode, parses the string as if it were a string
1107 * of individual digits; otherwise, uses the superclass function.
1108 * @param text The string to parse
1109 * @param parsePosition Ignored on entry, but updated on exit to point
1110 * to the first unmatched character
1111 * @param baseValue The partial parse result prior to entering this
1113 * @param upperBound Only consider rules with base values lower than
1114 * this when filling in the substitution
1115 * @param lenientParse If true, try matching the text as numerals if
1116 * matching as words doesn't work
1117 * @return If the match was successful, the current partial parse
1118 * result; otherwise new Long(0). The result is either a Long or
1123 FractionalPartSubstitution::doParse(const UnicodeString
& text
,
1124 ParsePosition
& parsePosition
,
1126 double /*upperBound*/,
1128 Formattable
& resVal
) const
1130 // if we're not in byDigits mode, we can just use the inherited
1133 return NFSubstitution::doParse(text
, parsePosition
, baseValue
, 0, lenientParse
, resVal
);
1135 // if we ARE in byDigits mode, parse the text one digit at a time
1136 // using this substitution's owning rule set (we do this by setting
1137 // upperBound to 10 when calling doParse() ) until we reach
1140 UnicodeString
workText(text
);
1141 ParsePosition
workPos(1);
1144 // double p10 = 0.1;
1147 NumberFormat
* fmt
= NULL
;
1148 while (workText
.length() > 0 && workPos
.getIndex() != 0) {
1149 workPos
.setIndex(0);
1151 getRuleSet()->parse(workText
, workPos
, 10, temp
);
1152 UErrorCode status
= U_ZERO_ERROR
;
1153 digit
= temp
.getLong(status
);
1154 // digit = temp.getType() == Formattable::kLong ?
1156 // (int32_t)temp.getDouble();
1158 if (lenientParse
&& workPos
.getIndex() == 0) {
1160 status
= U_ZERO_ERROR
;
1161 fmt
= NumberFormat::createInstance(status
);
1162 if (U_FAILURE(status
)) {
1168 fmt
->parse(workText
, temp
, workPos
);
1169 digit
= temp
.getLong(status
);
1173 if (workPos
.getIndex() != 0) {
1174 dl
.append((char)('0' + digit
));
1175 // result += digit * p10;
1177 parsePosition
.setIndex(parsePosition
.getIndex() + workPos
.getIndex());
1178 workText
.removeBetween(0, workPos
.getIndex());
1179 while (workText
.length() > 0 && workText
.charAt(0) == gSpace
) {
1180 workText
.removeBetween(0, 1);
1181 parsePosition
.setIndex(parsePosition
.getIndex() + 1);
1187 result
= dl
.getCount() == 0 ? 0 : dl
.getDouble();
1188 result
= composeRuleValue(result
, baseValue
);
1189 resVal
.setDouble(result
);
1195 FractionalPartSubstitution::operator==(const NFSubstitution
& rhs
) const
1197 return NFSubstitution::operator==(rhs
) &&
1198 ((const FractionalPartSubstitution
*)&rhs
)->byDigits
== byDigits
;
1201 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FractionalPartSubstitution
)
1204 //===================================================================
1205 // AbsoluteValueSubstitution
1206 //===================================================================
1208 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AbsoluteValueSubstitution
)
1210 //===================================================================
1211 // NumeratorSubstitution
1212 //===================================================================
1215 NumeratorSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t apos
, int32_t recursionCount
, UErrorCode
& status
) const {
1216 // perform a transformation on the number being formatted that
1217 // is dependent on the type of substitution this is
1219 double numberToFormat
= transformNumber(number
);
1220 int64_t longNF
= util64_fromDouble(numberToFormat
);
1222 const NFRuleSet
* aruleSet
= getRuleSet();
1223 if (withZeros
&& aruleSet
!= NULL
) {
1224 // if there are leading zeros in the decimal expansion then emit them
1226 int32_t len
= toInsertInto
.length();
1227 while ((nf
*= 10) < denominator
) {
1228 toInsertInto
.insert(apos
+ getPos(), gSpace
);
1229 aruleSet
->format((int64_t)0, toInsertInto
, apos
+ getPos(), recursionCount
, status
);
1231 apos
+= toInsertInto
.length() - len
;
1234 // if the result is an integer, from here on out we work in integer
1235 // space (saving time and memory and preserving accuracy)
1236 if (numberToFormat
== longNF
&& aruleSet
!= NULL
) {
1237 aruleSet
->format(longNF
, toInsertInto
, apos
+ getPos(), recursionCount
, status
);
1239 // if the result isn't an integer, then call either our rule set's
1240 // format() method or our DecimalFormat's format() method to
1241 // format the result
1243 if (aruleSet
!= NULL
) {
1244 aruleSet
->format(numberToFormat
, toInsertInto
, apos
+ getPos(), recursionCount
, status
);
1247 getNumberFormat()->format(numberToFormat
, temp
, status
);
1248 toInsertInto
.insert(apos
+ getPos(), temp
);
1254 NumeratorSubstitution::doParse(const UnicodeString
& text
,
1255 ParsePosition
& parsePosition
,
1258 UBool
/*lenientParse*/,
1259 Formattable
& result
) const
1261 // we don't have to do anything special to do the parsing here,
1262 // but we have to turn lenient parsing off-- if we leave it on,
1263 // it SERIOUSLY messes up the algorithm
1265 // if withZeros is true, we need to count the zeros
1266 // and use that to adjust the parse result
1267 UErrorCode status
= U_ZERO_ERROR
;
1268 int32_t zeroCount
= 0;
1269 UnicodeString
workText(text
);
1272 ParsePosition
workPos(1);
1275 while (workText
.length() > 0 && workPos
.getIndex() != 0) {
1276 workPos
.setIndex(0);
1277 getRuleSet()->parse(workText
, workPos
, 1, temp
); // parse zero or nothing at all
1278 if (workPos
.getIndex() == 0) {
1279 // we failed, either there were no more zeros, or the number was formatted with digits
1280 // either way, we're done
1285 parsePosition
.setIndex(parsePosition
.getIndex() + workPos
.getIndex());
1286 workText
.remove(0, workPos
.getIndex());
1287 while (workText
.length() > 0 && workText
.charAt(0) == gSpace
) {
1288 workText
.remove(0, 1);
1289 parsePosition
.setIndex(parsePosition
.getIndex() + 1);
1294 workText
.remove(0, (int32_t)parsePosition
.getIndex());
1295 parsePosition
.setIndex(0);
1298 // we've parsed off the zeros, now let's parse the rest from our current position
1299 NFSubstitution::doParse(workText
, parsePosition
, withZeros
? 1 : baseValue
, upperBound
, FALSE
, result
);
1302 // any base value will do in this case. is there a way to
1303 // force this to not bother trying all the base values?
1305 // compute the 'effective' base and prescale the value down
1306 int64_t n
= result
.getLong(status
); // force conversion!
1313 // now add the zeros
1314 while (zeroCount
> 0) {
1318 // d is now our true denominator
1319 result
.setDouble((double)n
/(double)d
);
1326 NumeratorSubstitution::operator==(const NFSubstitution
& rhs
) const
1328 return NFSubstitution::operator==(rhs
) &&
1329 denominator
== ((const NumeratorSubstitution
*)&rhs
)->denominator
;
1332 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumeratorSubstitution
)
1334 const UChar
NumeratorSubstitution::LTLT
[] = { 0x003c, 0x003c };