2 ******************************************************************************
3 * Copyright (C) 1997-2010, 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 <typeinfo> // 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 RuleBasedNumberFormat
* formatter
,
52 const UnicodeString
& description
,
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 class MultiplierSubstitution
: public NFSubstitution
{
71 MultiplierSubstitution(int32_t _pos
,
73 const NFRuleSet
* _ruleSet
,
74 const RuleBasedNumberFormat
* formatter
,
75 const UnicodeString
& description
,
77 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
), divisor(_divisor
)
79 ldivisor
= util64_fromDouble(divisor
);
81 status
= U_PARSE_ERROR
;
85 virtual void setDivisor(int32_t radix
, int32_t exponent
, UErrorCode
& status
) {
86 divisor
= uprv_pow(radix
, exponent
);
87 ldivisor
= util64_fromDouble(divisor
);
90 status
= U_PARSE_ERROR
;
94 virtual UBool
operator==(const NFSubstitution
& rhs
) const;
96 virtual int64_t transformNumber(int64_t number
) const {
97 return number
/ ldivisor
;
100 virtual double transformNumber(double number
) const {
102 return uprv_floor(number
/ divisor
);
104 return number
/divisor
;
108 virtual double composeRuleValue(double newRuleValue
, double /*oldRuleValue*/) const {
109 return newRuleValue
* divisor
;
112 virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor
; }
114 virtual UChar
tokenChar() const { return (UChar
)0x003c; } // '<'
117 static UClassID
getStaticClassID(void);
118 virtual UClassID
getDynamicClassID(void) const;
121 class ModulusSubstitution
: public NFSubstitution
{
124 const NFRule
* ruleToUse
;
126 ModulusSubstitution(int32_t pos
,
128 const NFRule
* rulePredecessor
,
129 const NFRuleSet
* ruleSet
,
130 const RuleBasedNumberFormat
* formatter
,
131 const UnicodeString
& description
,
134 virtual void setDivisor(int32_t radix
, int32_t exponent
, UErrorCode
& status
) {
135 divisor
= uprv_pow(radix
, exponent
);
136 ldivisor
= util64_fromDouble(divisor
);
139 status
= U_PARSE_ERROR
;
143 virtual UBool
operator==(const NFSubstitution
& rhs
) const;
145 virtual void doSubstitution(int64_t number
, UnicodeString
& toInsertInto
, int32_t pos
) const;
146 virtual void doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t pos
) const;
148 virtual int64_t transformNumber(int64_t number
) const { return number
% ldivisor
; }
149 virtual double transformNumber(double number
) const { return uprv_fmod(number
, divisor
); }
151 virtual UBool
doParse(const UnicodeString
& text
,
152 ParsePosition
& parsePosition
,
156 Formattable
& result
) const;
158 virtual double composeRuleValue(double newRuleValue
, double oldRuleValue
) const {
159 return oldRuleValue
- uprv_fmod(oldRuleValue
, divisor
) + newRuleValue
;
162 virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor
; }
164 virtual UBool
isModulusSubstitution() const { return TRUE
; }
166 virtual UChar
tokenChar() const { return (UChar
)0x003e; } // '>'
169 static UClassID
getStaticClassID(void);
170 virtual UClassID
getDynamicClassID(void) const;
173 class IntegralPartSubstitution
: public NFSubstitution
{
175 IntegralPartSubstitution(int32_t _pos
,
176 const NFRuleSet
* _ruleSet
,
177 const RuleBasedNumberFormat
* formatter
,
178 const UnicodeString
& description
,
180 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
) {}
182 virtual int64_t transformNumber(int64_t number
) const { return number
; }
183 virtual double transformNumber(double number
) const { return uprv_floor(number
); }
184 virtual double composeRuleValue(double newRuleValue
, double oldRuleValue
) const { return newRuleValue
+ oldRuleValue
; }
185 virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX
; }
186 virtual UChar
tokenChar() const { return (UChar
)0x003c; } // '<'
189 static UClassID
getStaticClassID(void);
190 virtual UClassID
getDynamicClassID(void) const;
193 class FractionalPartSubstitution
: public NFSubstitution
{
196 enum { kMaxDecimalDigits
= 8 };
198 FractionalPartSubstitution(int32_t pos
,
199 const NFRuleSet
* ruleSet
,
200 const RuleBasedNumberFormat
* formatter
,
201 const UnicodeString
& description
,
204 virtual UBool
operator==(const NFSubstitution
& rhs
) const;
206 virtual void doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t pos
) const;
207 virtual void doSubstitution(int64_t /*number*/, UnicodeString
& /*toInsertInto*/, int32_t /*_pos*/) const {}
208 virtual int64_t transformNumber(int64_t /*number*/) const { return 0; }
209 virtual double transformNumber(double number
) const { return number
- uprv_floor(number
); }
211 virtual UBool
doParse(const UnicodeString
& text
,
212 ParsePosition
& parsePosition
,
216 Formattable
& result
) const;
218 virtual double composeRuleValue(double newRuleValue
, double oldRuleValue
) const { return newRuleValue
+ oldRuleValue
; }
219 virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0.0; }
220 virtual UChar
tokenChar() const { return (UChar
)0x003e; } // '>'
223 static UClassID
getStaticClassID(void);
224 virtual UClassID
getDynamicClassID(void) const;
227 class AbsoluteValueSubstitution
: public NFSubstitution
{
229 AbsoluteValueSubstitution(int32_t _pos
,
230 const NFRuleSet
* _ruleSet
,
231 const RuleBasedNumberFormat
* formatter
,
232 const UnicodeString
& description
,
234 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
) {}
236 virtual int64_t transformNumber(int64_t number
) const { return number
>= 0 ? number
: -number
; }
237 virtual double transformNumber(double number
) const { return uprv_fabs(number
); }
238 virtual double composeRuleValue(double newRuleValue
, double /*oldRuleValue*/) const { return -newRuleValue
; }
239 virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX
; }
240 virtual UChar
tokenChar() const { return (UChar
)0x003e; } // '>'
243 static UClassID
getStaticClassID(void);
244 virtual UClassID
getDynamicClassID(void) const;
247 class NumeratorSubstitution
: public NFSubstitution
{
249 int64_t ldenominator
;
252 static inline UnicodeString
fixdesc(const UnicodeString
& desc
) {
253 if (desc
.endsWith(LTLT
, 2)) {
254 UnicodeString
result(desc
, 0, desc
.length()-1);
259 NumeratorSubstitution(int32_t _pos
,
261 const NFRuleSet
* _ruleSet
,
262 const RuleBasedNumberFormat
* formatter
,
263 const UnicodeString
& description
,
265 : NFSubstitution(_pos
, _ruleSet
, formatter
, fixdesc(description
), status
), denominator(_denominator
)
267 ldenominator
= util64_fromDouble(denominator
);
268 withZeros
= description
.endsWith(LTLT
, 2);
271 virtual UBool
operator==(const NFSubstitution
& rhs
) const;
273 virtual int64_t transformNumber(int64_t number
) const { return number
* ldenominator
; }
274 virtual double transformNumber(double number
) const { return uprv_round(number
* denominator
); }
276 virtual void doSubstitution(int64_t /*number*/, UnicodeString
& /*toInsertInto*/, int32_t /*_pos*/) const {}
277 virtual void doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t pos
) const;
278 virtual UBool
doParse(const UnicodeString
& text
,
279 ParsePosition
& parsePosition
,
282 UBool
/*lenientParse*/,
283 Formattable
& result
) const;
285 virtual double composeRuleValue(double newRuleValue
, double oldRuleValue
) const { return newRuleValue
/ oldRuleValue
; }
286 virtual double calcUpperBound(double /*oldUpperBound*/) const { return denominator
; }
287 virtual UChar
tokenChar() const { return (UChar
)0x003c; } // '<'
289 static const UChar LTLT
[2];
292 static UClassID
getStaticClassID(void);
293 virtual UClassID
getDynamicClassID(void) const;
296 class NullSubstitution
: public NFSubstitution
{
298 NullSubstitution(int32_t _pos
,
299 const NFRuleSet
* _ruleSet
,
300 const RuleBasedNumberFormat
* formatter
,
301 const UnicodeString
& description
,
303 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
) {}
305 virtual void toString(UnicodeString
& /*result*/) const {}
306 virtual void doSubstitution(double /*number*/, UnicodeString
& /*toInsertInto*/, int32_t /*_pos*/) const {}
307 virtual void doSubstitution(int64_t /*number*/, UnicodeString
& /*toInsertInto*/, int32_t /*_pos*/) const {}
308 virtual int64_t transformNumber(int64_t /*number*/) const { return 0; }
309 virtual double transformNumber(double /*number*/) const { return 0; }
310 virtual UBool
doParse(const UnicodeString
& /*text*/,
311 ParsePosition
& /*parsePosition*/,
313 double /*upperBound*/,
314 UBool
/*lenientParse*/,
315 Formattable
& result
) const
316 { result
.setDouble(baseValue
); return TRUE
; }
317 virtual double composeRuleValue(double /*newRuleValue*/, double /*oldRuleValue*/) const { return 0.0; } // never called
318 virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0; } // never called
319 virtual UBool
isNullSubstitution() const { return TRUE
; }
320 virtual UChar
tokenChar() const { return (UChar
)0x0020; } // ' ' never called
323 static UClassID
getStaticClassID(void);
324 virtual UClassID
getDynamicClassID(void) const;
328 NFSubstitution::makeSubstitution(int32_t pos
,
330 const NFRule
* predecessor
,
331 const NFRuleSet
* ruleSet
,
332 const RuleBasedNumberFormat
* formatter
,
333 const UnicodeString
& description
,
336 // if the description is empty, return a NullSubstitution
337 if (description
.length() == 0) {
338 return new NullSubstitution(pos
, ruleSet
, formatter
, description
, status
);
341 switch (description
.charAt(0)) {
342 // if the description begins with '<'...
344 // throw an exception if the rule is a negative number
346 if (rule
->getBaseValue() == NFRule::kNegativeNumberRule
) {
347 // throw new IllegalArgumentException("<< not allowed in negative-number rule");
348 status
= U_PARSE_ERROR
;
352 // if the rule is a fraction rule, return an
353 // IntegralPartSubstitution
354 else if (rule
->getBaseValue() == NFRule::kImproperFractionRule
355 || rule
->getBaseValue() == NFRule::kProperFractionRule
356 || rule
->getBaseValue() == NFRule::kMasterRule
) {
357 return new IntegralPartSubstitution(pos
, ruleSet
, formatter
, description
, status
);
360 // if the rule set containing the rule is a fraction
361 // rule set, return a NumeratorSubstitution
362 else if (ruleSet
->isFractionRuleSet()) {
363 return new NumeratorSubstitution(pos
, (double)rule
->getBaseValue(),
364 formatter
->getDefaultRuleSet(), formatter
, description
, status
);
367 // otherwise, return a MultiplierSubstitution
369 return new MultiplierSubstitution(pos
, rule
->getDivisor(), ruleSet
,
370 formatter
, description
, status
);
373 // if the description begins with '>'...
375 // if the rule is a negative-number rule, return
376 // an AbsoluteValueSubstitution
377 if (rule
->getBaseValue() == NFRule::kNegativeNumberRule
) {
378 return new AbsoluteValueSubstitution(pos
, ruleSet
, formatter
, description
, status
);
381 // if the rule is a fraction rule, return a
382 // FractionalPartSubstitution
383 else if (rule
->getBaseValue() == NFRule::kImproperFractionRule
384 || rule
->getBaseValue() == NFRule::kProperFractionRule
385 || rule
->getBaseValue() == NFRule::kMasterRule
) {
386 return new FractionalPartSubstitution(pos
, ruleSet
, formatter
, description
, status
);
389 // if the rule set owning the rule is a fraction rule set,
390 // throw an exception
391 else if (ruleSet
->isFractionRuleSet()) {
392 // throw new IllegalArgumentException(">> not allowed in fraction rule set");
393 status
= U_PARSE_ERROR
;
397 // otherwise, return a ModulusSubstitution
399 return new ModulusSubstitution(pos
, rule
->getDivisor(), predecessor
,
400 ruleSet
, formatter
, description
, status
);
403 // if the description begins with '=', always return a
404 // SameValueSubstitution
406 return new SameValueSubstitution(pos
, ruleSet
, formatter
, description
, status
);
408 // and if it's anything else, throw an exception
410 // throw new IllegalArgumentException("Illegal substitution character");
411 status
= U_PARSE_ERROR
;
416 NFSubstitution::NFSubstitution(int32_t _pos
,
417 const NFRuleSet
* _ruleSet
,
418 const RuleBasedNumberFormat
* formatter
,
419 const UnicodeString
& description
,
421 : pos(_pos
), ruleSet(NULL
), numberFormat(NULL
)
423 // the description should begin and end with the same character.
424 // If it doesn't that's a syntax error. Otherwise,
425 // makeSubstitution() was the only thing that needed to know
426 // about these characters, so strip them off
427 UnicodeString
workingDescription(description
);
428 if (description
.length() >= 2
429 && description
.charAt(0) == description
.charAt(description
.length() - 1))
431 workingDescription
.remove(description
.length() - 1, 1);
432 workingDescription
.remove(0, 1);
434 else if (description
.length() != 0) {
435 // throw new IllegalArgumentException("Illegal substitution syntax");
436 status
= U_PARSE_ERROR
;
440 // if the description was just two paired token characters
441 // (i.e., "<<" or ">>"), it uses the rule set it belongs to to
443 if (workingDescription
.length() == 0) {
444 this->ruleSet
= _ruleSet
;
446 // if the description contains a rule set name, that's the rule
447 // set we use to format the result: get a reference to the
449 else if (workingDescription
.charAt(0) == gPercent
) {
450 this->ruleSet
= formatter
->findRuleSet(workingDescription
, status
);
452 // if the description begins with 0 or #, treat it as a
453 // DecimalFormat pattern, and initialize a DecimalFormat with
454 // that pattern (then set it to use the DecimalFormatSymbols
455 // belonging to our formatter)
456 else if (workingDescription
.charAt(0) == gPound
|| workingDescription
.charAt(0) ==gZero
) {
457 DecimalFormatSymbols
* sym
= formatter
->getDecimalFormatSymbols();
459 status
= U_MISSING_RESOURCE_ERROR
;
462 this->numberFormat
= new DecimalFormat(workingDescription
, *sym
, status
);
464 if (this->numberFormat
== 0) {
465 status
= U_MEMORY_ALLOCATION_ERROR
;
468 if (U_FAILURE(status
)) {
469 delete (DecimalFormat
*)this->numberFormat
;
470 this->numberFormat
= NULL
;
473 // this->numberFormat->setDecimalFormatSymbols(formatter->getDecimalFormatSymbols());
475 // if the description is ">>>", this substitution bypasses the
476 // usual rule-search process and always uses the rule that precedes
477 // it in its own rule set's rule list (this is used for place-value
478 // notations: formats where you want to see a particular part of
479 // a number even when it's 0)
480 else if (workingDescription
.charAt(0) == gGreaterThan
) {
481 // this causes problems when >>> is used in a frationalPartSubstitution
482 // this->ruleSet = NULL;
483 this->ruleSet
= _ruleSet
;
484 this->numberFormat
= NULL
;
486 // and of the description is none of these things, it's a syntax error
488 // throw new IllegalArgumentException("Illegal substitution syntax");
489 status
= U_PARSE_ERROR
;
493 NFSubstitution::~NFSubstitution()
496 delete (NumberFormat
*)numberFormat
; numberFormat
= NULL
;
500 * Set's the substitution's divisor. Used by NFRule.setBaseValue().
501 * A no-op for all substitutions except multiplier and modulus
503 * @param radix The radix of the divisor
504 * @param exponent The exponent of the divisor
507 NFSubstitution::setDivisor(int32_t /*radix*/, int32_t /*exponent*/, UErrorCode
& /*status*/) {
508 // a no-op for all substitutions except multiplier and modulus substitutions
512 //-----------------------------------------------------------------------
514 //-----------------------------------------------------------------------
516 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NFSubstitution
)
519 * Compares two substitutions for equality
520 * @param The substitution to compare this one to
521 * @return true if the two substitutions are functionally equivalent
524 NFSubstitution::operator==(const NFSubstitution
& rhs
) const
526 // compare class and all of the fields all substitutions have
528 // this should be called by subclasses before their own equality tests
529 return typeid(*this) == typeid(rhs
)
531 && (ruleSet
== NULL
) == (rhs
.ruleSet
== NULL
)
532 // && ruleSet == rhs.ruleSet causes circularity, other checks to make instead?
533 && (numberFormat
== NULL
534 ? (rhs
.numberFormat
== NULL
)
535 : (*numberFormat
== *rhs
.numberFormat
));
539 * Returns a textual description of the substitution
540 * @return A textual description of the substitution. This might
541 * not be identical to the description it was created from, but
542 * it'll produce the same result.
545 NFSubstitution::toString(UnicodeString
& text
) const
547 // use tokenChar() to get the character at the beginning and
548 // end of the substitutin token. In between them will go
549 // either the name of the rule set it uses, or the pattern of
550 // the DecimalFormat it uses
552 text
.append(tokenChar());
555 if (ruleSet
!= NULL
) {
556 ruleSet
->getName(temp
);
557 } else if (numberFormat
!= NULL
) {
558 numberFormat
->toPattern(temp
);
561 text
.append(tokenChar());
564 //-----------------------------------------------------------------------
566 //-----------------------------------------------------------------------
569 * Performs a mathematical operation on the number, formats it using
570 * either ruleSet or decimalFormat, and inserts the result into
572 * @param number The number being formatted.
573 * @param toInsertInto The string we insert the result into
574 * @param pos The position in toInsertInto where the owning rule's
575 * rule text begins (this value is added to this substitution's
576 * position to determine exactly where to insert the new text)
579 NFSubstitution::doSubstitution(int64_t number
, UnicodeString
& toInsertInto
, int32_t _pos
) const
581 if (ruleSet
!= NULL
) {
582 // perform a transformation on the number that is dependent
583 // on the type of substitution this is, then just call its
584 // rule set's format() method to format the result
585 ruleSet
->format(transformNumber(number
), toInsertInto
, _pos
+ this->pos
);
586 } else if (numberFormat
!= NULL
) {
587 // or perform the transformation on the number (preserving
588 // the result's fractional part if the formatter it set
589 // to show it), then use that formatter's format() method
590 // to format the result
591 double numberToFormat
= transformNumber((double)number
);
592 if (numberFormat
->getMaximumFractionDigits() == 0) {
593 numberToFormat
= uprv_floor(numberToFormat
);
597 numberFormat
->format(numberToFormat
, temp
);
598 toInsertInto
.insert(_pos
+ this->pos
, temp
);
603 * Performs a mathematical operation on the number, formats it using
604 * either ruleSet or decimalFormat, and inserts the result into
606 * @param number The number being formatted.
607 * @param toInsertInto The string we insert the result into
608 * @param pos The position in toInsertInto where the owning rule's
609 * rule text begins (this value is added to this substitution's
610 * position to determine exactly where to insert the new text)
613 NFSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t _pos
) const {
614 // perform a transformation on the number being formatted that
615 // is dependent on the type of substitution this is
616 double numberToFormat
= transformNumber(number
);
618 digits
.set(numberToFormat
);
620 // if the result is an integer, from here on out we work in integer
621 // space (saving time and memory and preserving accuracy)
622 if (numberToFormat
== uprv_floor(numberToFormat
) && ruleSet
!= NULL
&& (!digits
.isInfinite())) {
623 ruleSet
->format(util64_fromDouble(numberToFormat
), toInsertInto
, _pos
+ this->pos
);
625 // if the result isn't an integer, then call either our rule set's
626 // format() method or our DecimalFormat's format() method to
629 if (ruleSet
!= NULL
) {
630 ruleSet
->format(numberToFormat
, toInsertInto
, _pos
+ this->pos
);
631 } else if (numberFormat
!= NULL
) {
633 numberFormat
->format(numberToFormat
, temp
);
634 toInsertInto
.insert(_pos
+ this->pos
, temp
);
640 //-----------------------------------------------------------------------
642 //-----------------------------------------------------------------------
649 * Parses a string using the rule set or DecimalFormat belonging
650 * to this substitution. If there's a match, a mathematical
651 * operation (the inverse of the one used in formatting) is
652 * performed on the result of the parse and the value passed in
653 * and returned as the result. The parse position is updated to
654 * point to the first unmatched character in the string.
655 * @param text The string to parse
656 * @param parsePosition On entry, ignored, but assumed to be 0.
657 * On exit, this is updated to point to the first unmatched
658 * character (or 0 if the substitution didn't match)
659 * @param baseValue A partial parse result that should be
660 * combined with the result of this parse
661 * @param upperBound When searching the rule set for a rule
662 * matching the string passed in, only rules with base values
663 * lower than this are considered
664 * @param lenientParse If true and matching against rules fails,
665 * the substitution will also try matching the text against
666 * numerals using a default-costructed NumberFormat. If false,
667 * no extra work is done. (This value is false whenever the
668 * formatter isn't in lenient-parse mode, but is also false
669 * under some conditions even when the formatter _is_ in
670 * lenient-parse mode.)
671 * @return If there's a match, this is the result of composing
672 * baseValue with whatever was returned from matching the
673 * characters. This will be either a Long or a Double. If there's
674 * no match this is new Long(0) (not null), and parsePosition
678 NFSubstitution::doParse(const UnicodeString
& text
,
679 ParsePosition
& parsePosition
,
683 Formattable
& result
) const
686 fprintf(stderr
, "<nfsubs> %x bv: %g ub: %g\n", this, baseValue
, upperBound
);
688 // figure out the highest base value a rule can have and match
689 // the text being parsed (this varies according to the type of
690 // substitutions: multiplier, modulus, and numerator substitutions
691 // restrict the search to rules with base values lower than their
692 // own; same-value substitutions leave the upper bound wherever
693 // it was, and the others allow any rule to match
694 upperBound
= calcUpperBound(upperBound
);
696 // use our rule set to parse the text. If that fails and
697 // lenient parsing is enabled (this is always false if the
698 // formatter's lenient-parsing mode is off, but it may also
699 // be false even when the formatter's lenient-parse mode is
700 // on), then also try parsing the text using a default-
701 // constructed NumberFormat
702 if (ruleSet
!= NULL
) {
703 ruleSet
->parse(text
, parsePosition
, upperBound
, result
);
704 if (lenientParse
&& !ruleSet
->isFractionRuleSet() && parsePosition
.getIndex() == 0) {
705 UErrorCode status
= U_ZERO_ERROR
;
706 NumberFormat
* fmt
= NumberFormat::createInstance(status
);
707 if (U_SUCCESS(status
)) {
708 fmt
->parse(text
, result
, parsePosition
);
713 // ...or use our DecimalFormat to parse the text
714 } else if (numberFormat
!= NULL
) {
715 numberFormat
->parse(text
, result
, parsePosition
);
718 // if the parse was successful, we've already advanced the caller's
719 // parse position (this is the one function that doesn't have one
720 // of its own). Derive a parse result and return it as a Long,
721 // if possible, or a Double
722 if (parsePosition
.getIndex() != 0) {
723 UErrorCode status
= U_ZERO_ERROR
;
724 double tempResult
= result
.getDouble(status
);
726 // composeRuleValue() produces a full parse result from
727 // the partial parse result passed to this function from
728 // the caller (this is either the owning rule's base value
729 // or the partial result obtained from composing the
730 // owning rule's base value with its other substitution's
731 // parse result) and the partial parse result obtained by
732 // matching the substitution (which will be the same value
733 // the caller would get by parsing just this part of the
734 // text with RuleBasedNumberFormat.parse() ). How the two
735 // values are used to derive the full parse result depends
736 // on the types of substitutions: For a regular rule, the
737 // ultimate result is its multiplier substitution's result
738 // times the rule's divisor (or the rule's base value) plus
739 // the modulus substitution's result (which will actually
740 // supersede part of the rule's base value). For a negative-
741 // number rule, the result is the negative of its substitution's
742 // result. For a fraction rule, it's the sum of its two
743 // substitution results. For a rule in a fraction rule set,
744 // it's the numerator substitution's result divided by
745 // the rule's base value. Results from same-value substitutions
746 // propagate back upard, and null substitutions don't affect
748 tempResult
= composeRuleValue(tempResult
, baseValue
);
749 result
.setDouble(tempResult
);
751 // if the parse was UNsuccessful, return 0
759 NFSubstitution::isNullSubstitution() const {
764 * Returns true if this is a modulus substitution. (We didn't do this
765 * with instanceof partially because it causes source files to
766 * proliferate and partially because we have to port this to C++.)
767 * @return true if this object is an instance of ModulusSubstitution
770 NFSubstitution::isModulusSubstitution() const {
775 * @return true if this is a decimal format-only substitution
778 NFSubstitution::isDecimalFormatSubstitutionOnly() const {
779 return (ruleSet
== NULL
&& getNumberFormat() != NULL
);
783 * @return true if this substitution uses another ruleSet
786 NFSubstitution::isRuleSetSubstitutionOnly() const {
787 return (getNumberFormat() == NULL
&& ruleSet
!= NULL
);
790 //===================================================================
791 // SameValueSubstitution
792 //===================================================================
795 * A substitution that passes the value passed to it through unchanged.
796 * Represented by == in rule descriptions.
798 SameValueSubstitution::SameValueSubstitution(int32_t _pos
,
799 const NFRuleSet
* _ruleSet
,
800 const RuleBasedNumberFormat
* formatter
,
801 const UnicodeString
& description
,
803 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
)
805 if (description
== gEqualsEquals
) {
806 // throw new IllegalArgumentException("== is not a legal token");
807 status
= U_PARSE_ERROR
;
811 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SameValueSubstitution
)
813 //===================================================================
814 // MultiplierSubstitution
815 //===================================================================
817 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MultiplierSubstitution
)
819 UBool
MultiplierSubstitution::operator==(const NFSubstitution
& rhs
) const
821 return NFSubstitution::operator==(rhs
) &&
822 divisor
== ((const MultiplierSubstitution
*)&rhs
)->divisor
;
826 //===================================================================
827 // ModulusSubstitution
828 //===================================================================
831 * A substitution that divides the number being formatted by the its rule's
832 * divisor and formats the remainder. Represented by ">>" in a
835 ModulusSubstitution::ModulusSubstitution(int32_t _pos
,
837 const NFRule
* predecessor
,
838 const NFRuleSet
* _ruleSet
,
839 const RuleBasedNumberFormat
* formatter
,
840 const UnicodeString
& description
,
842 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
)
846 ldivisor
= util64_fromDouble(_divisor
);
848 // the owning rule's divisor controls the behavior of this
849 // substitution: rather than keeping a backpointer to the rule,
850 // we keep a copy of the divisor
853 status
= U_PARSE_ERROR
;
856 if (description
== gGreaterGreaterGreaterThan
) {
857 // the >>> token doesn't alter how this substituion calculates the
858 // values it uses for formatting and parsing, but it changes
859 // what's done with that value after it's obtained: >>> short-
860 // circuits the rule-search process and goes straight to the
861 // specified rule to format the substitution value
862 ruleToUse
= predecessor
;
866 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ModulusSubstitution
)
868 UBool
ModulusSubstitution::operator==(const NFSubstitution
& rhs
) const
870 return NFSubstitution::operator==(rhs
) &&
871 divisor
== ((const ModulusSubstitution
*)&rhs
)->divisor
&&
872 ruleToUse
== ((const ModulusSubstitution
*)&rhs
)->ruleToUse
;
875 //-----------------------------------------------------------------------
877 //-----------------------------------------------------------------------
881 * If this is a >>> substitution, use ruleToUse to fill in
882 * the substitution. Otherwise, just use the superclass function.
883 * @param number The number being formatted
884 * @toInsertInto The string to insert the result of this substitution
886 * @param pos The position of the rule text in toInsertInto
889 ModulusSubstitution::doSubstitution(int64_t number
, UnicodeString
& toInsertInto
, int32_t _pos
) const
891 // if this isn't a >>> substitution, just use the inherited version
892 // of this function (which uses either a rule set or a DecimalFormat
893 // to format its substitution value)
894 if (ruleToUse
== NULL
) {
895 NFSubstitution::doSubstitution(number
, toInsertInto
, _pos
);
897 // a >>> substitution goes straight to a particular rule to
898 // format the substitution value
900 int64_t numberToFormat
= transformNumber(number
);
901 ruleToUse
->doFormat(numberToFormat
, toInsertInto
, _pos
+ getPos());
906 * If this is a >>> substitution, use ruleToUse to fill in
907 * the substitution. Otherwise, just use the superclass function.
908 * @param number The number being formatted
909 * @toInsertInto The string to insert the result of this substitution
911 * @param pos The position of the rule text in toInsertInto
914 ModulusSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t _pos
) const
916 // if this isn't a >>> substitution, just use the inherited version
917 // of this function (which uses either a rule set or a DecimalFormat
918 // to format its substitution value)
919 if (ruleToUse
== NULL
) {
920 NFSubstitution::doSubstitution(number
, toInsertInto
, _pos
);
922 // a >>> substitution goes straight to a particular rule to
923 // format the substitution value
925 double numberToFormat
= transformNumber(number
);
927 ruleToUse
->doFormat(numberToFormat
, toInsertInto
, _pos
+ getPos());
931 //-----------------------------------------------------------------------
933 //-----------------------------------------------------------------------
936 * If this is a >>> substitution, match only against ruleToUse.
937 * Otherwise, use the superclass function.
938 * @param text The string to parse
939 * @param parsePosition Ignored on entry, updated on exit to point to
940 * the first unmatched character.
941 * @param baseValue The partial parse result prior to calling this
945 ModulusSubstitution::doParse(const UnicodeString
& text
,
946 ParsePosition
& parsePosition
,
950 Formattable
& result
) const
952 // if this isn't a >>> substitution, we can just use the
953 // inherited parse() routine to do the parsing
954 if (ruleToUse
== NULL
) {
955 return NFSubstitution::doParse(text
, parsePosition
, baseValue
, upperBound
, lenientParse
, result
);
957 // but if it IS a >>> substitution, we have to do it here: we
958 // use the specific rule's doParse() method, and then we have to
959 // do some of the other work of NFRuleSet.parse()
961 ruleToUse
->doParse(text
, parsePosition
, FALSE
, upperBound
, result
);
963 if (parsePosition
.getIndex() != 0) {
964 UErrorCode status
= U_ZERO_ERROR
;
965 double tempResult
= result
.getDouble(status
);
966 tempResult
= composeRuleValue(tempResult
, baseValue
);
967 result
.setDouble(tempResult
);
975 //===================================================================
976 // IntegralPartSubstitution
977 //===================================================================
979 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IntegralPartSubstitution
)
982 //===================================================================
983 // FractionalPartSubstitution
984 //===================================================================
988 * Constructs a FractionalPartSubstitution. This object keeps a flag
989 * telling whether it should format by digits or not. In addition,
990 * it marks the rule set it calls (if any) as a fraction rule set.
992 FractionalPartSubstitution::FractionalPartSubstitution(int32_t _pos
,
993 const NFRuleSet
* _ruleSet
,
994 const RuleBasedNumberFormat
* formatter
,
995 const UnicodeString
& description
,
997 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
)
1002 // akk, ruleSet can change in superclass constructor
1003 if (description
== gGreaterGreaterThan
||
1004 description
== gGreaterGreaterGreaterThan
||
1005 _ruleSet
== getRuleSet()) {
1007 if (description
== gGreaterGreaterGreaterThan
) {
1012 ((NFRuleSet
*)getRuleSet())->makeIntoFractionRuleSet();
1016 //-----------------------------------------------------------------------
1018 //-----------------------------------------------------------------------
1021 * If in "by digits" mode, fills in the substitution one decimal digit
1022 * at a time using the rule set containing this substitution.
1023 * Otherwise, uses the superclass function.
1024 * @param number The number being formatted
1025 * @param toInsertInto The string to insert the result of formatting
1026 * the substitution into
1027 * @param pos The position of the owning rule's rule text in
1031 FractionalPartSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t _pos
) const
1033 // if we're not in "byDigits" mode, just use the inherited
1034 // doSubstitution() routine
1036 NFSubstitution::doSubstitution(number
, toInsertInto
, _pos
);
1038 // if we're in "byDigits" mode, transform the value into an integer
1039 // by moving the decimal point eight places to the right and
1040 // pulling digits off the right one at a time, formatting each digit
1041 // as an integer using this substitution's owning rule set
1042 // (this is slower, but more accurate, than doing it from the
1045 // int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits));
1046 // // this flag keeps us from formatting trailing zeros. It starts
1047 // // out false because we're pulling from the right, and switches
1048 // // to true the first time we encounter a non-zero digit
1049 // UBool doZeros = FALSE;
1050 // for (int32_t i = 0; i < kMaxDecimalDigits; i++) {
1051 // int64_t digit = numberToFormat % 10;
1052 // if (digit != 0 || doZeros) {
1053 // if (doZeros && useSpaces) {
1054 // toInsertInto.insert(_pos + getPos(), gSpace);
1057 // getRuleSet()->format(digit, toInsertInto, _pos + getPos());
1059 // numberToFormat /= 10;
1064 dl
.roundFixedPoint(20); // round to 20 fraction digits.
1065 dl
.reduce(); // Removes any trailing zeros.
1068 for (int32_t didx
= dl
.getCount()-1; didx
>=dl
.getDecimalAt(); didx
--) {
1069 // Loop iterates over fraction digits, starting with the LSD.
1070 // include both real digits from the number, and zeros
1071 // to the left of the MSD but to the right of the decimal point.
1072 if (pad
&& useSpaces
) {
1073 toInsertInto
.insert(_pos
+ getPos(), gSpace
);
1077 int64_t digit
= didx
>=0 ? dl
.getDigit(didx
) - '0' : 0;
1078 getRuleSet()->format(digit
, toInsertInto
, _pos
+ getPos());
1082 // hack around lack of precision in digitlist. if we would end up with
1083 // "foo point" make sure we add a " zero" to the end.
1084 getRuleSet()->format((int64_t)0, toInsertInto
, _pos
+ getPos());
1089 //-----------------------------------------------------------------------
1091 //-----------------------------------------------------------------------
1094 * If in "by digits" mode, parses the string as if it were a string
1095 * of individual digits; otherwise, uses the superclass function.
1096 * @param text The string to parse
1097 * @param parsePosition Ignored on entry, but updated on exit to point
1098 * to the first unmatched character
1099 * @param baseValue The partial parse result prior to entering this
1101 * @param upperBound Only consider rules with base values lower than
1102 * this when filling in the substitution
1103 * @param lenientParse If true, try matching the text as numerals if
1104 * matching as words doesn't work
1105 * @return If the match was successful, the current partial parse
1106 * result; otherwise new Long(0). The result is either a Long or
1111 FractionalPartSubstitution::doParse(const UnicodeString
& text
,
1112 ParsePosition
& parsePosition
,
1114 double /*upperBound*/,
1116 Formattable
& resVal
) const
1118 // if we're not in byDigits mode, we can just use the inherited
1121 return NFSubstitution::doParse(text
, parsePosition
, baseValue
, 0, lenientParse
, resVal
);
1123 // if we ARE in byDigits mode, parse the text one digit at a time
1124 // using this substitution's owning rule set (we do this by setting
1125 // upperBound to 10 when calling doParse() ) until we reach
1128 UnicodeString
workText(text
);
1129 ParsePosition
workPos(1);
1132 // double p10 = 0.1;
1135 NumberFormat
* fmt
= NULL
;
1136 while (workText
.length() > 0 && workPos
.getIndex() != 0) {
1137 workPos
.setIndex(0);
1139 getRuleSet()->parse(workText
, workPos
, 10, temp
);
1140 UErrorCode status
= U_ZERO_ERROR
;
1141 digit
= temp
.getLong(status
);
1142 // digit = temp.getType() == Formattable::kLong ?
1144 // (int32_t)temp.getDouble();
1146 if (lenientParse
&& workPos
.getIndex() == 0) {
1148 status
= U_ZERO_ERROR
;
1149 fmt
= NumberFormat::createInstance(status
);
1150 if (U_FAILURE(status
)) {
1156 fmt
->parse(workText
, temp
, workPos
);
1157 digit
= temp
.getLong(status
);
1161 if (workPos
.getIndex() != 0) {
1162 dl
.append((char)('0' + digit
));
1163 // result += digit * p10;
1165 parsePosition
.setIndex(parsePosition
.getIndex() + workPos
.getIndex());
1166 workText
.removeBetween(0, workPos
.getIndex());
1167 while (workText
.length() > 0 && workText
.charAt(0) == gSpace
) {
1168 workText
.removeBetween(0, 1);
1169 parsePosition
.setIndex(parsePosition
.getIndex() + 1);
1175 result
= dl
.getCount() == 0 ? 0 : dl
.getDouble();
1176 result
= composeRuleValue(result
, baseValue
);
1177 resVal
.setDouble(result
);
1183 FractionalPartSubstitution::operator==(const NFSubstitution
& rhs
) const
1185 return NFSubstitution::operator==(rhs
) &&
1186 ((const FractionalPartSubstitution
*)&rhs
)->byDigits
== byDigits
;
1189 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FractionalPartSubstitution
)
1192 //===================================================================
1193 // AbsoluteValueSubstitution
1194 //===================================================================
1196 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AbsoluteValueSubstitution
)
1198 //===================================================================
1199 // NumeratorSubstitution
1200 //===================================================================
1203 NumeratorSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t apos
) const {
1204 // perform a transformation on the number being formatted that
1205 // is dependent on the type of substitution this is
1207 double numberToFormat
= transformNumber(number
);
1208 int64_t longNF
= util64_fromDouble(numberToFormat
);
1210 const NFRuleSet
* aruleSet
= getRuleSet();
1211 if (withZeros
&& aruleSet
!= NULL
) {
1212 // if there are leading zeros in the decimal expansion then emit them
1214 int32_t len
= toInsertInto
.length();
1215 while ((nf
*= 10) < denominator
) {
1216 toInsertInto
.insert(apos
+ getPos(), gSpace
);
1217 aruleSet
->format((int64_t)0, toInsertInto
, apos
+ getPos());
1219 apos
+= toInsertInto
.length() - len
;
1222 // if the result is an integer, from here on out we work in integer
1223 // space (saving time and memory and preserving accuracy)
1224 if (numberToFormat
== longNF
&& aruleSet
!= NULL
) {
1225 aruleSet
->format(longNF
, toInsertInto
, apos
+ getPos());
1227 // if the result isn't an integer, then call either our rule set's
1228 // format() method or our DecimalFormat's format() method to
1229 // format the result
1231 if (aruleSet
!= NULL
) {
1232 aruleSet
->format(numberToFormat
, toInsertInto
, apos
+ getPos());
1234 UErrorCode status
= U_ZERO_ERROR
;
1236 getNumberFormat()->format(numberToFormat
, temp
, status
);
1237 toInsertInto
.insert(apos
+ getPos(), temp
);
1243 NumeratorSubstitution::doParse(const UnicodeString
& text
,
1244 ParsePosition
& parsePosition
,
1247 UBool
/*lenientParse*/,
1248 Formattable
& result
) const
1250 // we don't have to do anything special to do the parsing here,
1251 // but we have to turn lenient parsing off-- if we leave it on,
1252 // it SERIOUSLY messes up the algorithm
1254 // if withZeros is true, we need to count the zeros
1255 // and use that to adjust the parse result
1256 UErrorCode status
= U_ZERO_ERROR
;
1257 int32_t zeroCount
= 0;
1258 UnicodeString
workText(text
);
1261 ParsePosition
workPos(1);
1264 while (workText
.length() > 0 && workPos
.getIndex() != 0) {
1265 workPos
.setIndex(0);
1266 getRuleSet()->parse(workText
, workPos
, 1, temp
); // parse zero or nothing at all
1267 if (workPos
.getIndex() == 0) {
1268 // we failed, either there were no more zeros, or the number was formatted with digits
1269 // either way, we're done
1274 parsePosition
.setIndex(parsePosition
.getIndex() + workPos
.getIndex());
1275 workText
.remove(0, workPos
.getIndex());
1276 while (workText
.length() > 0 && workText
.charAt(0) == gSpace
) {
1277 workText
.remove(0, 1);
1278 parsePosition
.setIndex(parsePosition
.getIndex() + 1);
1283 workText
.remove(0, (int32_t)parsePosition
.getIndex());
1284 parsePosition
.setIndex(0);
1287 // we've parsed off the zeros, now let's parse the rest from our current position
1288 NFSubstitution::doParse(workText
, parsePosition
, withZeros
? 1 : baseValue
, upperBound
, FALSE
, result
);
1291 // any base value will do in this case. is there a way to
1292 // force this to not bother trying all the base values?
1294 // compute the 'effective' base and prescale the value down
1295 int64_t n
= result
.getLong(status
); // force conversion!
1302 // now add the zeros
1303 while (zeroCount
> 0) {
1307 // d is now our true denominator
1308 result
.setDouble((double)n
/(double)d
);
1315 NumeratorSubstitution::operator==(const NFSubstitution
& rhs
) const
1317 return NFSubstitution::operator==(rhs
) &&
1318 denominator
== ((const NumeratorSubstitution
*)&rhs
)->denominator
;
1321 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumeratorSubstitution
)
1323 const UChar
NumeratorSubstitution::LTLT
[] = { 0x003c, 0x003c };
1325 //===================================================================
1327 //===================================================================
1329 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NullSubstitution
)