2 ******************************************************************************
3 * Copyright (C) 1997-2012, 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 RuleBasedNumberFormat
* formatter
,
52 const UnicodeString
& description
,
54 virtual ~SameValueSubstitution();
56 virtual int64_t transformNumber(int64_t number
) const { return number
; }
57 virtual double transformNumber(double number
) const { return number
; }
58 virtual double composeRuleValue(double newRuleValue
, double /*oldRuleValue*/) const { return newRuleValue
; }
59 virtual double calcUpperBound(double oldUpperBound
) const { return oldUpperBound
; }
60 virtual UChar
tokenChar() const { return (UChar
)0x003d; } // '='
63 static UClassID
getStaticClassID(void);
64 virtual UClassID
getDynamicClassID(void) const;
67 SameValueSubstitution::~SameValueSubstitution() {}
69 class MultiplierSubstitution
: public NFSubstitution
{
74 MultiplierSubstitution(int32_t _pos
,
76 const NFRuleSet
* _ruleSet
,
77 const RuleBasedNumberFormat
* formatter
,
78 const UnicodeString
& description
,
80 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
), divisor(_divisor
)
82 ldivisor
= util64_fromDouble(divisor
);
84 status
= U_PARSE_ERROR
;
87 virtual ~MultiplierSubstitution();
89 virtual void setDivisor(int32_t radix
, int32_t exponent
, UErrorCode
& status
) {
90 divisor
= uprv_pow(radix
, exponent
);
91 ldivisor
= util64_fromDouble(divisor
);
94 status
= U_PARSE_ERROR
;
98 virtual UBool
operator==(const NFSubstitution
& rhs
) const;
100 virtual int64_t transformNumber(int64_t number
) const {
101 return number
/ ldivisor
;
104 virtual double transformNumber(double number
) const {
106 return uprv_floor(number
/ divisor
);
108 return number
/divisor
;
112 virtual double composeRuleValue(double newRuleValue
, double /*oldRuleValue*/) const {
113 return newRuleValue
* divisor
;
116 virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor
; }
118 virtual UChar
tokenChar() const { return (UChar
)0x003c; } // '<'
121 static UClassID
getStaticClassID(void);
122 virtual UClassID
getDynamicClassID(void) const;
125 MultiplierSubstitution::~MultiplierSubstitution() {}
127 class ModulusSubstitution
: public NFSubstitution
{
130 const NFRule
* ruleToUse
;
132 ModulusSubstitution(int32_t pos
,
134 const NFRule
* rulePredecessor
,
135 const NFRuleSet
* ruleSet
,
136 const RuleBasedNumberFormat
* formatter
,
137 const UnicodeString
& description
,
139 virtual ~ModulusSubstitution();
141 virtual void setDivisor(int32_t radix
, int32_t exponent
, UErrorCode
& status
) {
142 divisor
= uprv_pow(radix
, exponent
);
143 ldivisor
= util64_fromDouble(divisor
);
146 status
= U_PARSE_ERROR
;
150 virtual UBool
operator==(const NFSubstitution
& rhs
) const;
152 virtual void doSubstitution(int64_t number
, UnicodeString
& toInsertInto
, int32_t pos
) const;
153 virtual void doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t pos
) const;
155 virtual int64_t transformNumber(int64_t number
) const { return number
% ldivisor
; }
156 virtual double transformNumber(double number
) const { return uprv_fmod(number
, divisor
); }
158 virtual UBool
doParse(const UnicodeString
& text
,
159 ParsePosition
& parsePosition
,
163 Formattable
& result
) const;
165 virtual double composeRuleValue(double newRuleValue
, double oldRuleValue
) const {
166 return oldRuleValue
- uprv_fmod(oldRuleValue
, divisor
) + newRuleValue
;
169 virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor
; }
171 virtual UBool
isModulusSubstitution() const { return TRUE
; }
173 virtual UChar
tokenChar() const { return (UChar
)0x003e; } // '>'
175 virtual void toString(UnicodeString
& result
) const;
178 static UClassID
getStaticClassID(void);
179 virtual UClassID
getDynamicClassID(void) const;
182 ModulusSubstitution::~ModulusSubstitution() {}
184 class IntegralPartSubstitution
: public NFSubstitution
{
186 IntegralPartSubstitution(int32_t _pos
,
187 const NFRuleSet
* _ruleSet
,
188 const RuleBasedNumberFormat
* formatter
,
189 const UnicodeString
& description
,
191 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
) {}
192 virtual ~IntegralPartSubstitution();
194 virtual int64_t transformNumber(int64_t number
) const { return number
; }
195 virtual double transformNumber(double number
) const { return uprv_floor(number
); }
196 virtual double composeRuleValue(double newRuleValue
, double oldRuleValue
) const { return newRuleValue
+ oldRuleValue
; }
197 virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX
; }
198 virtual UChar
tokenChar() const { return (UChar
)0x003c; } // '<'
201 static UClassID
getStaticClassID(void);
202 virtual UClassID
getDynamicClassID(void) const;
205 IntegralPartSubstitution::~IntegralPartSubstitution() {}
207 class FractionalPartSubstitution
: public NFSubstitution
{
210 enum { kMaxDecimalDigits
= 8 };
212 FractionalPartSubstitution(int32_t pos
,
213 const NFRuleSet
* ruleSet
,
214 const RuleBasedNumberFormat
* formatter
,
215 const UnicodeString
& description
,
217 virtual ~FractionalPartSubstitution();
219 virtual UBool
operator==(const NFSubstitution
& rhs
) const;
221 virtual void doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t pos
) const;
222 virtual void doSubstitution(int64_t /*number*/, UnicodeString
& /*toInsertInto*/, int32_t /*_pos*/) const {}
223 virtual int64_t transformNumber(int64_t /*number*/) const { return 0; }
224 virtual double transformNumber(double number
) const { return number
- uprv_floor(number
); }
226 virtual UBool
doParse(const UnicodeString
& text
,
227 ParsePosition
& parsePosition
,
231 Formattable
& result
) const;
233 virtual double composeRuleValue(double newRuleValue
, double oldRuleValue
) const { return newRuleValue
+ oldRuleValue
; }
234 virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0.0; }
235 virtual UChar
tokenChar() const { return (UChar
)0x003e; } // '>'
238 static UClassID
getStaticClassID(void);
239 virtual UClassID
getDynamicClassID(void) const;
242 FractionalPartSubstitution::~FractionalPartSubstitution() {}
244 class AbsoluteValueSubstitution
: public NFSubstitution
{
246 AbsoluteValueSubstitution(int32_t _pos
,
247 const NFRuleSet
* _ruleSet
,
248 const RuleBasedNumberFormat
* formatter
,
249 const UnicodeString
& description
,
251 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
) {}
252 virtual ~AbsoluteValueSubstitution();
254 virtual int64_t transformNumber(int64_t number
) const { return number
>= 0 ? number
: -number
; }
255 virtual double transformNumber(double number
) const { return uprv_fabs(number
); }
256 virtual double composeRuleValue(double newRuleValue
, double /*oldRuleValue*/) const { return -newRuleValue
; }
257 virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX
; }
258 virtual UChar
tokenChar() const { return (UChar
)0x003e; } // '>'
261 static UClassID
getStaticClassID(void);
262 virtual UClassID
getDynamicClassID(void) const;
265 AbsoluteValueSubstitution::~AbsoluteValueSubstitution() {}
267 class NumeratorSubstitution
: public NFSubstitution
{
269 int64_t ldenominator
;
272 static inline UnicodeString
fixdesc(const UnicodeString
& desc
) {
273 if (desc
.endsWith(LTLT
, 2)) {
274 UnicodeString
result(desc
, 0, desc
.length()-1);
279 NumeratorSubstitution(int32_t _pos
,
281 const NFRuleSet
* _ruleSet
,
282 const RuleBasedNumberFormat
* formatter
,
283 const UnicodeString
& description
,
285 : NFSubstitution(_pos
, _ruleSet
, formatter
, fixdesc(description
), status
), denominator(_denominator
)
287 ldenominator
= util64_fromDouble(denominator
);
288 withZeros
= description
.endsWith(LTLT
, 2);
290 virtual ~NumeratorSubstitution();
292 virtual UBool
operator==(const NFSubstitution
& rhs
) const;
294 virtual int64_t transformNumber(int64_t number
) const { return number
* ldenominator
; }
295 virtual double transformNumber(double number
) const { return uprv_round(number
* denominator
); }
297 virtual void doSubstitution(int64_t /*number*/, UnicodeString
& /*toInsertInto*/, int32_t /*_pos*/) const {}
298 virtual void doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t pos
) const;
299 virtual UBool
doParse(const UnicodeString
& text
,
300 ParsePosition
& parsePosition
,
303 UBool
/*lenientParse*/,
304 Formattable
& result
) const;
306 virtual double composeRuleValue(double newRuleValue
, double oldRuleValue
) const { return newRuleValue
/ oldRuleValue
; }
307 virtual double calcUpperBound(double /*oldUpperBound*/) const { return denominator
; }
308 virtual UChar
tokenChar() const { return (UChar
)0x003c; } // '<'
310 static const UChar LTLT
[2];
313 static UClassID
getStaticClassID(void);
314 virtual UClassID
getDynamicClassID(void) const;
317 NumeratorSubstitution::~NumeratorSubstitution() {}
319 class NullSubstitution
: public NFSubstitution
{
321 NullSubstitution(int32_t _pos
,
322 const NFRuleSet
* _ruleSet
,
323 const RuleBasedNumberFormat
* formatter
,
324 const UnicodeString
& description
,
326 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
) {}
327 virtual ~NullSubstitution();
329 virtual void toString(UnicodeString
& /*result*/) const {}
330 virtual void doSubstitution(double /*number*/, UnicodeString
& /*toInsertInto*/, int32_t /*_pos*/) const {}
331 virtual void doSubstitution(int64_t /*number*/, UnicodeString
& /*toInsertInto*/, int32_t /*_pos*/) const {}
332 virtual int64_t transformNumber(int64_t /*number*/) const { return 0; }
333 virtual double transformNumber(double /*number*/) const { return 0; }
334 virtual UBool
doParse(const UnicodeString
& /*text*/,
335 ParsePosition
& /*parsePosition*/,
337 double /*upperBound*/,
338 UBool
/*lenientParse*/,
339 Formattable
& result
) const
340 { result
.setDouble(baseValue
); return TRUE
; }
341 virtual double composeRuleValue(double /*newRuleValue*/, double /*oldRuleValue*/) const { return 0.0; } // never called
342 virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0; } // never called
343 virtual UBool
isNullSubstitution() const { return TRUE
; }
344 virtual UChar
tokenChar() const { return (UChar
)0x0020; } // ' ' never called
347 static UClassID
getStaticClassID(void);
348 virtual UClassID
getDynamicClassID(void) const;
351 NullSubstitution::~NullSubstitution() {}
354 NFSubstitution::makeSubstitution(int32_t pos
,
356 const NFRule
* predecessor
,
357 const NFRuleSet
* ruleSet
,
358 const RuleBasedNumberFormat
* formatter
,
359 const UnicodeString
& description
,
362 // if the description is empty, return a NullSubstitution
363 if (description
.length() == 0) {
364 return new NullSubstitution(pos
, ruleSet
, formatter
, description
, status
);
367 switch (description
.charAt(0)) {
368 // if the description begins with '<'...
370 // throw an exception if the rule is a negative number
372 if (rule
->getBaseValue() == NFRule::kNegativeNumberRule
) {
373 // throw new IllegalArgumentException("<< not allowed in negative-number rule");
374 status
= U_PARSE_ERROR
;
378 // if the rule is a fraction rule, return an
379 // IntegralPartSubstitution
380 else if (rule
->getBaseValue() == NFRule::kImproperFractionRule
381 || rule
->getBaseValue() == NFRule::kProperFractionRule
382 || rule
->getBaseValue() == NFRule::kMasterRule
) {
383 return new IntegralPartSubstitution(pos
, ruleSet
, formatter
, description
, status
);
386 // if the rule set containing the rule is a fraction
387 // rule set, return a NumeratorSubstitution
388 else if (ruleSet
->isFractionRuleSet()) {
389 return new NumeratorSubstitution(pos
, (double)rule
->getBaseValue(),
390 formatter
->getDefaultRuleSet(), formatter
, description
, status
);
393 // otherwise, return a MultiplierSubstitution
395 return new MultiplierSubstitution(pos
, rule
->getDivisor(), ruleSet
,
396 formatter
, description
, status
);
399 // if the description begins with '>'...
401 // if the rule is a negative-number rule, return
402 // an AbsoluteValueSubstitution
403 if (rule
->getBaseValue() == NFRule::kNegativeNumberRule
) {
404 return new AbsoluteValueSubstitution(pos
, ruleSet
, formatter
, description
, status
);
407 // if the rule is a fraction rule, return a
408 // FractionalPartSubstitution
409 else if (rule
->getBaseValue() == NFRule::kImproperFractionRule
410 || rule
->getBaseValue() == NFRule::kProperFractionRule
411 || rule
->getBaseValue() == NFRule::kMasterRule
) {
412 return new FractionalPartSubstitution(pos
, ruleSet
, formatter
, description
, status
);
415 // if the rule set owning the rule is a fraction rule set,
416 // throw an exception
417 else if (ruleSet
->isFractionRuleSet()) {
418 // throw new IllegalArgumentException(">> not allowed in fraction rule set");
419 status
= U_PARSE_ERROR
;
423 // otherwise, return a ModulusSubstitution
425 return new ModulusSubstitution(pos
, rule
->getDivisor(), predecessor
,
426 ruleSet
, formatter
, description
, status
);
429 // if the description begins with '=', always return a
430 // SameValueSubstitution
432 return new SameValueSubstitution(pos
, ruleSet
, formatter
, description
, status
);
434 // and if it's anything else, throw an exception
436 // throw new IllegalArgumentException("Illegal substitution character");
437 status
= U_PARSE_ERROR
;
442 NFSubstitution::NFSubstitution(int32_t _pos
,
443 const NFRuleSet
* _ruleSet
,
444 const RuleBasedNumberFormat
* formatter
,
445 const UnicodeString
& description
,
447 : pos(_pos
), ruleSet(NULL
), numberFormat(NULL
)
449 // the description should begin and end with the same character.
450 // If it doesn't that's a syntax error. Otherwise,
451 // makeSubstitution() was the only thing that needed to know
452 // about these characters, so strip them off
453 UnicodeString
workingDescription(description
);
454 if (description
.length() >= 2
455 && description
.charAt(0) == description
.charAt(description
.length() - 1))
457 workingDescription
.remove(description
.length() - 1, 1);
458 workingDescription
.remove(0, 1);
460 else if (description
.length() != 0) {
461 // throw new IllegalArgumentException("Illegal substitution syntax");
462 status
= U_PARSE_ERROR
;
466 // if the description was just two paired token characters
467 // (i.e., "<<" or ">>"), it uses the rule set it belongs to to
469 if (workingDescription
.length() == 0) {
470 this->ruleSet
= _ruleSet
;
472 // if the description contains a rule set name, that's the rule
473 // set we use to format the result: get a reference to the
475 else if (workingDescription
.charAt(0) == gPercent
) {
476 this->ruleSet
= formatter
->findRuleSet(workingDescription
, status
);
478 // if the description begins with 0 or #, treat it as a
479 // DecimalFormat pattern, and initialize a DecimalFormat with
480 // that pattern (then set it to use the DecimalFormatSymbols
481 // belonging to our formatter)
482 else if (workingDescription
.charAt(0) == gPound
|| workingDescription
.charAt(0) ==gZero
) {
483 DecimalFormatSymbols
* sym
= formatter
->getDecimalFormatSymbols();
485 status
= U_MISSING_RESOURCE_ERROR
;
488 this->numberFormat
= new DecimalFormat(workingDescription
, *sym
, status
);
490 if (this->numberFormat
== 0) {
491 status
= U_MEMORY_ALLOCATION_ERROR
;
494 if (U_FAILURE(status
)) {
495 delete (DecimalFormat
*)this->numberFormat
;
496 this->numberFormat
= NULL
;
499 // this->numberFormat->setDecimalFormatSymbols(formatter->getDecimalFormatSymbols());
501 // if the description is ">>>", this substitution bypasses the
502 // usual rule-search process and always uses the rule that precedes
503 // it in its own rule set's rule list (this is used for place-value
504 // notations: formats where you want to see a particular part of
505 // a number even when it's 0)
506 else if (workingDescription
.charAt(0) == gGreaterThan
) {
507 // this causes problems when >>> is used in a frationalPartSubstitution
508 // this->ruleSet = NULL;
509 this->ruleSet
= _ruleSet
;
510 this->numberFormat
= NULL
;
512 // and of the description is none of these things, it's a syntax error
514 // throw new IllegalArgumentException("Illegal substitution syntax");
515 status
= U_PARSE_ERROR
;
519 NFSubstitution::~NFSubstitution()
522 delete (NumberFormat
*)numberFormat
; numberFormat
= NULL
;
526 * Set's the substitution's divisor. Used by NFRule.setBaseValue().
527 * A no-op for all substitutions except multiplier and modulus
529 * @param radix The radix of the divisor
530 * @param exponent The exponent of the divisor
533 NFSubstitution::setDivisor(int32_t /*radix*/, int32_t /*exponent*/, UErrorCode
& /*status*/) {
534 // a no-op for all substitutions except multiplier and modulus substitutions
538 //-----------------------------------------------------------------------
540 //-----------------------------------------------------------------------
542 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NFSubstitution
)
545 * Compares two substitutions for equality
546 * @param The substitution to compare this one to
547 * @return true if the two substitutions are functionally equivalent
550 NFSubstitution::operator==(const NFSubstitution
& rhs
) const
552 // compare class and all of the fields all substitutions have
554 // this should be called by subclasses before their own equality tests
555 return typeid(*this) == typeid(rhs
)
557 && (ruleSet
== NULL
) == (rhs
.ruleSet
== NULL
)
558 // && ruleSet == rhs.ruleSet causes circularity, other checks to make instead?
559 && (numberFormat
== NULL
560 ? (rhs
.numberFormat
== NULL
)
561 : (*numberFormat
== *rhs
.numberFormat
));
565 * Returns a textual description of the substitution
566 * @return A textual description of the substitution. This might
567 * not be identical to the description it was created from, but
568 * it'll produce the same result.
571 NFSubstitution::toString(UnicodeString
& text
) const
573 // use tokenChar() to get the character at the beginning and
574 // end of the substitutin token. In between them will go
575 // either the name of the rule set it uses, or the pattern of
576 // the DecimalFormat it uses
578 text
.append(tokenChar());
581 if (ruleSet
!= NULL
) {
582 ruleSet
->getName(temp
);
583 } else if (numberFormat
!= NULL
) {
584 numberFormat
->toPattern(temp
);
587 text
.append(tokenChar());
590 //-----------------------------------------------------------------------
592 //-----------------------------------------------------------------------
595 * Performs a mathematical operation on the number, formats it using
596 * either ruleSet or decimalFormat, and inserts the result into
598 * @param number The number being formatted.
599 * @param toInsertInto The string we insert the result into
600 * @param pos The position in toInsertInto where the owning rule's
601 * rule text begins (this value is added to this substitution's
602 * position to determine exactly where to insert the new text)
605 NFSubstitution::doSubstitution(int64_t number
, UnicodeString
& toInsertInto
, int32_t _pos
) const
607 if (ruleSet
!= NULL
) {
608 // perform a transformation on the number that is dependent
609 // on the type of substitution this is, then just call its
610 // rule set's format() method to format the result
611 ruleSet
->format(transformNumber(number
), toInsertInto
, _pos
+ this->pos
);
612 } else if (numberFormat
!= NULL
) {
613 // or perform the transformation on the number (preserving
614 // the result's fractional part if the formatter it set
615 // to show it), then use that formatter's format() method
616 // to format the result
617 double numberToFormat
= transformNumber((double)number
);
618 if (numberFormat
->getMaximumFractionDigits() == 0) {
619 numberToFormat
= uprv_floor(numberToFormat
);
623 numberFormat
->format(numberToFormat
, temp
);
624 toInsertInto
.insert(_pos
+ this->pos
, temp
);
629 * Performs a mathematical operation on the number, formats it using
630 * either ruleSet or decimalFormat, and inserts the result into
632 * @param number The number being formatted.
633 * @param toInsertInto The string we insert the result into
634 * @param pos The position in toInsertInto where the owning rule's
635 * rule text begins (this value is added to this substitution's
636 * position to determine exactly where to insert the new text)
639 NFSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t _pos
) const {
640 // perform a transformation on the number being formatted that
641 // is dependent on the type of substitution this is
642 double numberToFormat
= transformNumber(number
);
644 digits
.set(numberToFormat
);
646 // if the result is an integer, from here on out we work in integer
647 // space (saving time and memory and preserving accuracy)
648 if (numberToFormat
== uprv_floor(numberToFormat
) && ruleSet
!= NULL
&& (!digits
.isInfinite())) {
649 ruleSet
->format(util64_fromDouble(numberToFormat
), toInsertInto
, _pos
+ this->pos
);
651 // if the result isn't an integer, then call either our rule set's
652 // format() method or our DecimalFormat's format() method to
655 if (ruleSet
!= NULL
) {
656 ruleSet
->format(numberToFormat
, toInsertInto
, _pos
+ this->pos
);
657 } else if (numberFormat
!= NULL
) {
659 numberFormat
->format(numberToFormat
, temp
);
660 toInsertInto
.insert(_pos
+ this->pos
, temp
);
666 //-----------------------------------------------------------------------
668 //-----------------------------------------------------------------------
675 * Parses a string using the rule set or DecimalFormat belonging
676 * to this substitution. If there's a match, a mathematical
677 * operation (the inverse of the one used in formatting) is
678 * performed on the result of the parse and the value passed in
679 * and returned as the result. The parse position is updated to
680 * point to the first unmatched character in the string.
681 * @param text The string to parse
682 * @param parsePosition On entry, ignored, but assumed to be 0.
683 * On exit, this is updated to point to the first unmatched
684 * character (or 0 if the substitution didn't match)
685 * @param baseValue A partial parse result that should be
686 * combined with the result of this parse
687 * @param upperBound When searching the rule set for a rule
688 * matching the string passed in, only rules with base values
689 * lower than this are considered
690 * @param lenientParse If true and matching against rules fails,
691 * the substitution will also try matching the text against
692 * numerals using a default-costructed NumberFormat. If false,
693 * no extra work is done. (This value is false whenever the
694 * formatter isn't in lenient-parse mode, but is also false
695 * under some conditions even when the formatter _is_ in
696 * lenient-parse mode.)
697 * @return If there's a match, this is the result of composing
698 * baseValue with whatever was returned from matching the
699 * characters. This will be either a Long or a Double. If there's
700 * no match this is new Long(0) (not null), and parsePosition
704 NFSubstitution::doParse(const UnicodeString
& text
,
705 ParsePosition
& parsePosition
,
709 Formattable
& result
) const
712 fprintf(stderr
, "<nfsubs> %x bv: %g ub: %g\n", this, baseValue
, upperBound
);
714 // figure out the highest base value a rule can have and match
715 // the text being parsed (this varies according to the type of
716 // substitutions: multiplier, modulus, and numerator substitutions
717 // restrict the search to rules with base values lower than their
718 // own; same-value substitutions leave the upper bound wherever
719 // it was, and the others allow any rule to match
720 upperBound
= calcUpperBound(upperBound
);
722 // use our rule set to parse the text. If that fails and
723 // lenient parsing is enabled (this is always false if the
724 // formatter's lenient-parsing mode is off, but it may also
725 // be false even when the formatter's lenient-parse mode is
726 // on), then also try parsing the text using a default-
727 // constructed NumberFormat
728 if (ruleSet
!= NULL
) {
729 ruleSet
->parse(text
, parsePosition
, upperBound
, result
);
730 if (lenientParse
&& !ruleSet
->isFractionRuleSet() && parsePosition
.getIndex() == 0) {
731 UErrorCode status
= U_ZERO_ERROR
;
732 NumberFormat
* fmt
= NumberFormat::createInstance(status
);
733 if (U_SUCCESS(status
)) {
734 fmt
->parse(text
, result
, parsePosition
);
739 // ...or use our DecimalFormat to parse the text
740 } else if (numberFormat
!= NULL
) {
741 numberFormat
->parse(text
, result
, parsePosition
);
744 // if the parse was successful, we've already advanced the caller's
745 // parse position (this is the one function that doesn't have one
746 // of its own). Derive a parse result and return it as a Long,
747 // if possible, or a Double
748 if (parsePosition
.getIndex() != 0) {
749 UErrorCode status
= U_ZERO_ERROR
;
750 double tempResult
= result
.getDouble(status
);
752 // composeRuleValue() produces a full parse result from
753 // the partial parse result passed to this function from
754 // the caller (this is either the owning rule's base value
755 // or the partial result obtained from composing the
756 // owning rule's base value with its other substitution's
757 // parse result) and the partial parse result obtained by
758 // matching the substitution (which will be the same value
759 // the caller would get by parsing just this part of the
760 // text with RuleBasedNumberFormat.parse() ). How the two
761 // values are used to derive the full parse result depends
762 // on the types of substitutions: For a regular rule, the
763 // ultimate result is its multiplier substitution's result
764 // times the rule's divisor (or the rule's base value) plus
765 // the modulus substitution's result (which will actually
766 // supersede part of the rule's base value). For a negative-
767 // number rule, the result is the negative of its substitution's
768 // result. For a fraction rule, it's the sum of its two
769 // substitution results. For a rule in a fraction rule set,
770 // it's the numerator substitution's result divided by
771 // the rule's base value. Results from same-value substitutions
772 // propagate back upard, and null substitutions don't affect
774 tempResult
= composeRuleValue(tempResult
, baseValue
);
775 result
.setDouble(tempResult
);
777 // if the parse was UNsuccessful, return 0
785 NFSubstitution::isNullSubstitution() const {
790 * Returns true if this is a modulus substitution. (We didn't do this
791 * with instanceof partially because it causes source files to
792 * proliferate and partially because we have to port this to C++.)
793 * @return true if this object is an instance of ModulusSubstitution
796 NFSubstitution::isModulusSubstitution() const {
801 * @return true if this is a decimal format-only substitution
804 NFSubstitution::isDecimalFormatSubstitutionOnly() const {
805 return (ruleSet
== NULL
&& getNumberFormat() != NULL
);
809 * @return true if this substitution uses another ruleSet
812 NFSubstitution::isRuleSetSubstitutionOnly() const {
813 return (getNumberFormat() == NULL
&& ruleSet
!= NULL
);
816 //===================================================================
817 // SameValueSubstitution
818 //===================================================================
821 * A substitution that passes the value passed to it through unchanged.
822 * Represented by == in rule descriptions.
824 SameValueSubstitution::SameValueSubstitution(int32_t _pos
,
825 const NFRuleSet
* _ruleSet
,
826 const RuleBasedNumberFormat
* formatter
,
827 const UnicodeString
& description
,
829 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
)
831 if (0 == description
.compare(gEqualsEquals
, 2)) {
832 // throw new IllegalArgumentException("== is not a legal token");
833 status
= U_PARSE_ERROR
;
837 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SameValueSubstitution
)
839 //===================================================================
840 // MultiplierSubstitution
841 //===================================================================
843 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MultiplierSubstitution
)
845 UBool
MultiplierSubstitution::operator==(const NFSubstitution
& rhs
) const
847 return NFSubstitution::operator==(rhs
) &&
848 divisor
== ((const MultiplierSubstitution
*)&rhs
)->divisor
;
852 //===================================================================
853 // ModulusSubstitution
854 //===================================================================
857 * A substitution that divides the number being formatted by the its rule's
858 * divisor and formats the remainder. Represented by ">>" in a
861 ModulusSubstitution::ModulusSubstitution(int32_t _pos
,
863 const NFRule
* predecessor
,
864 const NFRuleSet
* _ruleSet
,
865 const RuleBasedNumberFormat
* formatter
,
866 const UnicodeString
& description
,
868 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
)
872 ldivisor
= util64_fromDouble(_divisor
);
874 // the owning rule's divisor controls the behavior of this
875 // substitution: rather than keeping a backpointer to the rule,
876 // we keep a copy of the divisor
879 status
= U_PARSE_ERROR
;
882 if (0 == description
.compare(gGreaterGreaterGreaterThan
, 3)) {
883 // the >>> token doesn't alter how this substituion calculates the
884 // values it uses for formatting and parsing, but it changes
885 // what's done with that value after it's obtained: >>> short-
886 // circuits the rule-search process and goes straight to the
887 // specified rule to format the substitution value
888 ruleToUse
= predecessor
;
892 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ModulusSubstitution
)
894 UBool
ModulusSubstitution::operator==(const NFSubstitution
& rhs
) const
896 return NFSubstitution::operator==(rhs
) &&
897 divisor
== ((const ModulusSubstitution
*)&rhs
)->divisor
&&
898 ruleToUse
== ((const ModulusSubstitution
*)&rhs
)->ruleToUse
;
901 //-----------------------------------------------------------------------
903 //-----------------------------------------------------------------------
907 * If this is a >>> substitution, use ruleToUse to fill in
908 * the substitution. Otherwise, just use the superclass function.
909 * @param number The number being formatted
910 * @toInsertInto The string to insert the result of this substitution
912 * @param pos The position of the rule text in toInsertInto
915 ModulusSubstitution::doSubstitution(int64_t number
, UnicodeString
& toInsertInto
, int32_t _pos
) const
917 // if this isn't a >>> substitution, just use the inherited version
918 // of this function (which uses either a rule set or a DecimalFormat
919 // to format its substitution value)
920 if (ruleToUse
== NULL
) {
921 NFSubstitution::doSubstitution(number
, toInsertInto
, _pos
);
923 // a >>> substitution goes straight to a particular rule to
924 // format the substitution value
926 int64_t numberToFormat
= transformNumber(number
);
927 ruleToUse
->doFormat(numberToFormat
, toInsertInto
, _pos
+ getPos());
932 * If this is a >>> substitution, use ruleToUse to fill in
933 * the substitution. Otherwise, just use the superclass function.
934 * @param number The number being formatted
935 * @toInsertInto The string to insert the result of this substitution
937 * @param pos The position of the rule text in toInsertInto
940 ModulusSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t _pos
) const
942 // if this isn't a >>> substitution, just use the inherited version
943 // of this function (which uses either a rule set or a DecimalFormat
944 // to format its substitution value)
945 if (ruleToUse
== NULL
) {
946 NFSubstitution::doSubstitution(number
, toInsertInto
, _pos
);
948 // a >>> substitution goes straight to a particular rule to
949 // format the substitution value
951 double numberToFormat
= transformNumber(number
);
953 ruleToUse
->doFormat(numberToFormat
, toInsertInto
, _pos
+ getPos());
957 //-----------------------------------------------------------------------
959 //-----------------------------------------------------------------------
962 * If this is a >>> substitution, match only against ruleToUse.
963 * Otherwise, use the superclass function.
964 * @param text The string to parse
965 * @param parsePosition Ignored on entry, updated on exit to point to
966 * the first unmatched character.
967 * @param baseValue The partial parse result prior to calling this
971 ModulusSubstitution::doParse(const UnicodeString
& text
,
972 ParsePosition
& parsePosition
,
976 Formattable
& result
) const
978 // if this isn't a >>> substitution, we can just use the
979 // inherited parse() routine to do the parsing
980 if (ruleToUse
== NULL
) {
981 return NFSubstitution::doParse(text
, parsePosition
, baseValue
, upperBound
, lenientParse
, result
);
983 // but if it IS a >>> substitution, we have to do it here: we
984 // use the specific rule's doParse() method, and then we have to
985 // do some of the other work of NFRuleSet.parse()
987 ruleToUse
->doParse(text
, parsePosition
, FALSE
, upperBound
, result
);
989 if (parsePosition
.getIndex() != 0) {
990 UErrorCode status
= U_ZERO_ERROR
;
991 double tempResult
= result
.getDouble(status
);
992 tempResult
= composeRuleValue(tempResult
, baseValue
);
993 result
.setDouble(tempResult
);
1000 * Returns a textual description of the substitution
1001 * @return A textual description of the substitution. This might
1002 * not be identical to the description it was created from, but
1003 * it'll produce the same result.
1006 ModulusSubstitution::toString(UnicodeString
& text
) const
1008 // use tokenChar() to get the character at the beginning and
1009 // end of the substitutin token. In between them will go
1010 // either the name of the rule set it uses, or the pattern of
1011 // the DecimalFormat it uses
1013 if ( ruleToUse
!= NULL
) { // Must have been a >>> substitution.
1015 text
.append(tokenChar());
1016 text
.append(tokenChar());
1017 text
.append(tokenChar());
1018 } else { // Otherwise just use the super-class function.
1019 NFSubstitution::toString(text
);
1022 //===================================================================
1023 // IntegralPartSubstitution
1024 //===================================================================
1026 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IntegralPartSubstitution
)
1029 //===================================================================
1030 // FractionalPartSubstitution
1031 //===================================================================
1035 * Constructs a FractionalPartSubstitution. This object keeps a flag
1036 * telling whether it should format by digits or not. In addition,
1037 * it marks the rule set it calls (if any) as a fraction rule set.
1039 FractionalPartSubstitution::FractionalPartSubstitution(int32_t _pos
,
1040 const NFRuleSet
* _ruleSet
,
1041 const RuleBasedNumberFormat
* formatter
,
1042 const UnicodeString
& description
,
1044 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
)
1049 // akk, ruleSet can change in superclass constructor
1050 if (0 == description
.compare(gGreaterGreaterThan
, 2) ||
1051 0 == description
.compare(gGreaterGreaterGreaterThan
, 3) ||
1052 _ruleSet
== getRuleSet()) {
1054 if (0 == description
.compare(gGreaterGreaterGreaterThan
, 3)) {
1059 ((NFRuleSet
*)getRuleSet())->makeIntoFractionRuleSet();
1063 //-----------------------------------------------------------------------
1065 //-----------------------------------------------------------------------
1068 * If in "by digits" mode, fills in the substitution one decimal digit
1069 * at a time using the rule set containing this substitution.
1070 * Otherwise, uses the superclass function.
1071 * @param number The number being formatted
1072 * @param toInsertInto The string to insert the result of formatting
1073 * the substitution into
1074 * @param pos The position of the owning rule's rule text in
1078 FractionalPartSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t _pos
) const
1080 // if we're not in "byDigits" mode, just use the inherited
1081 // doSubstitution() routine
1083 NFSubstitution::doSubstitution(number
, toInsertInto
, _pos
);
1085 // if we're in "byDigits" mode, transform the value into an integer
1086 // by moving the decimal point eight places to the right and
1087 // pulling digits off the right one at a time, formatting each digit
1088 // as an integer using this substitution's owning rule set
1089 // (this is slower, but more accurate, than doing it from the
1092 // int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits));
1093 // // this flag keeps us from formatting trailing zeros. It starts
1094 // // out false because we're pulling from the right, and switches
1095 // // to true the first time we encounter a non-zero digit
1096 // UBool doZeros = FALSE;
1097 // for (int32_t i = 0; i < kMaxDecimalDigits; i++) {
1098 // int64_t digit = numberToFormat % 10;
1099 // if (digit != 0 || doZeros) {
1100 // if (doZeros && useSpaces) {
1101 // toInsertInto.insert(_pos + getPos(), gSpace);
1104 // getRuleSet()->format(digit, toInsertInto, _pos + getPos());
1106 // numberToFormat /= 10;
1111 dl
.roundFixedPoint(20); // round to 20 fraction digits.
1112 dl
.reduce(); // Removes any trailing zeros.
1115 for (int32_t didx
= dl
.getCount()-1; didx
>=dl
.getDecimalAt(); didx
--) {
1116 // Loop iterates over fraction digits, starting with the LSD.
1117 // include both real digits from the number, and zeros
1118 // to the left of the MSD but to the right of the decimal point.
1119 if (pad
&& useSpaces
) {
1120 toInsertInto
.insert(_pos
+ getPos(), gSpace
);
1124 int64_t digit
= didx
>=0 ? dl
.getDigit(didx
) - '0' : 0;
1125 getRuleSet()->format(digit
, toInsertInto
, _pos
+ getPos());
1129 // hack around lack of precision in digitlist. if we would end up with
1130 // "foo point" make sure we add a " zero" to the end.
1131 getRuleSet()->format((int64_t)0, toInsertInto
, _pos
+ getPos());
1136 //-----------------------------------------------------------------------
1138 //-----------------------------------------------------------------------
1141 * If in "by digits" mode, parses the string as if it were a string
1142 * of individual digits; otherwise, uses the superclass function.
1143 * @param text The string to parse
1144 * @param parsePosition Ignored on entry, but updated on exit to point
1145 * to the first unmatched character
1146 * @param baseValue The partial parse result prior to entering this
1148 * @param upperBound Only consider rules with base values lower than
1149 * this when filling in the substitution
1150 * @param lenientParse If true, try matching the text as numerals if
1151 * matching as words doesn't work
1152 * @return If the match was successful, the current partial parse
1153 * result; otherwise new Long(0). The result is either a Long or
1158 FractionalPartSubstitution::doParse(const UnicodeString
& text
,
1159 ParsePosition
& parsePosition
,
1161 double /*upperBound*/,
1163 Formattable
& resVal
) const
1165 // if we're not in byDigits mode, we can just use the inherited
1168 return NFSubstitution::doParse(text
, parsePosition
, baseValue
, 0, lenientParse
, resVal
);
1170 // if we ARE in byDigits mode, parse the text one digit at a time
1171 // using this substitution's owning rule set (we do this by setting
1172 // upperBound to 10 when calling doParse() ) until we reach
1175 UnicodeString
workText(text
);
1176 ParsePosition
workPos(1);
1179 // double p10 = 0.1;
1182 NumberFormat
* fmt
= NULL
;
1183 while (workText
.length() > 0 && workPos
.getIndex() != 0) {
1184 workPos
.setIndex(0);
1186 getRuleSet()->parse(workText
, workPos
, 10, temp
);
1187 UErrorCode status
= U_ZERO_ERROR
;
1188 digit
= temp
.getLong(status
);
1189 // digit = temp.getType() == Formattable::kLong ?
1191 // (int32_t)temp.getDouble();
1193 if (lenientParse
&& workPos
.getIndex() == 0) {
1195 status
= U_ZERO_ERROR
;
1196 fmt
= NumberFormat::createInstance(status
);
1197 if (U_FAILURE(status
)) {
1203 fmt
->parse(workText
, temp
, workPos
);
1204 digit
= temp
.getLong(status
);
1208 if (workPos
.getIndex() != 0) {
1209 dl
.append((char)('0' + digit
));
1210 // result += digit * p10;
1212 parsePosition
.setIndex(parsePosition
.getIndex() + workPos
.getIndex());
1213 workText
.removeBetween(0, workPos
.getIndex());
1214 while (workText
.length() > 0 && workText
.charAt(0) == gSpace
) {
1215 workText
.removeBetween(0, 1);
1216 parsePosition
.setIndex(parsePosition
.getIndex() + 1);
1222 result
= dl
.getCount() == 0 ? 0 : dl
.getDouble();
1223 result
= composeRuleValue(result
, baseValue
);
1224 resVal
.setDouble(result
);
1230 FractionalPartSubstitution::operator==(const NFSubstitution
& rhs
) const
1232 return NFSubstitution::operator==(rhs
) &&
1233 ((const FractionalPartSubstitution
*)&rhs
)->byDigits
== byDigits
;
1236 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FractionalPartSubstitution
)
1239 //===================================================================
1240 // AbsoluteValueSubstitution
1241 //===================================================================
1243 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AbsoluteValueSubstitution
)
1245 //===================================================================
1246 // NumeratorSubstitution
1247 //===================================================================
1250 NumeratorSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t apos
) const {
1251 // perform a transformation on the number being formatted that
1252 // is dependent on the type of substitution this is
1254 double numberToFormat
= transformNumber(number
);
1255 int64_t longNF
= util64_fromDouble(numberToFormat
);
1257 const NFRuleSet
* aruleSet
= getRuleSet();
1258 if (withZeros
&& aruleSet
!= NULL
) {
1259 // if there are leading zeros in the decimal expansion then emit them
1261 int32_t len
= toInsertInto
.length();
1262 while ((nf
*= 10) < denominator
) {
1263 toInsertInto
.insert(apos
+ getPos(), gSpace
);
1264 aruleSet
->format((int64_t)0, toInsertInto
, apos
+ getPos());
1266 apos
+= toInsertInto
.length() - len
;
1269 // if the result is an integer, from here on out we work in integer
1270 // space (saving time and memory and preserving accuracy)
1271 if (numberToFormat
== longNF
&& aruleSet
!= NULL
) {
1272 aruleSet
->format(longNF
, toInsertInto
, apos
+ getPos());
1274 // if the result isn't an integer, then call either our rule set's
1275 // format() method or our DecimalFormat's format() method to
1276 // format the result
1278 if (aruleSet
!= NULL
) {
1279 aruleSet
->format(numberToFormat
, toInsertInto
, apos
+ getPos());
1281 UErrorCode status
= U_ZERO_ERROR
;
1283 getNumberFormat()->format(numberToFormat
, temp
, status
);
1284 toInsertInto
.insert(apos
+ getPos(), temp
);
1290 NumeratorSubstitution::doParse(const UnicodeString
& text
,
1291 ParsePosition
& parsePosition
,
1294 UBool
/*lenientParse*/,
1295 Formattable
& result
) const
1297 // we don't have to do anything special to do the parsing here,
1298 // but we have to turn lenient parsing off-- if we leave it on,
1299 // it SERIOUSLY messes up the algorithm
1301 // if withZeros is true, we need to count the zeros
1302 // and use that to adjust the parse result
1303 UErrorCode status
= U_ZERO_ERROR
;
1304 int32_t zeroCount
= 0;
1305 UnicodeString
workText(text
);
1308 ParsePosition
workPos(1);
1311 while (workText
.length() > 0 && workPos
.getIndex() != 0) {
1312 workPos
.setIndex(0);
1313 getRuleSet()->parse(workText
, workPos
, 1, temp
); // parse zero or nothing at all
1314 if (workPos
.getIndex() == 0) {
1315 // we failed, either there were no more zeros, or the number was formatted with digits
1316 // either way, we're done
1321 parsePosition
.setIndex(parsePosition
.getIndex() + workPos
.getIndex());
1322 workText
.remove(0, workPos
.getIndex());
1323 while (workText
.length() > 0 && workText
.charAt(0) == gSpace
) {
1324 workText
.remove(0, 1);
1325 parsePosition
.setIndex(parsePosition
.getIndex() + 1);
1330 workText
.remove(0, (int32_t)parsePosition
.getIndex());
1331 parsePosition
.setIndex(0);
1334 // we've parsed off the zeros, now let's parse the rest from our current position
1335 NFSubstitution::doParse(workText
, parsePosition
, withZeros
? 1 : baseValue
, upperBound
, FALSE
, result
);
1338 // any base value will do in this case. is there a way to
1339 // force this to not bother trying all the base values?
1341 // compute the 'effective' base and prescale the value down
1342 int64_t n
= result
.getLong(status
); // force conversion!
1349 // now add the zeros
1350 while (zeroCount
> 0) {
1354 // d is now our true denominator
1355 result
.setDouble((double)n
/(double)d
);
1362 NumeratorSubstitution::operator==(const NFSubstitution
& rhs
) const
1364 return NFSubstitution::operator==(rhs
) &&
1365 denominator
== ((const NumeratorSubstitution
*)&rhs
)->denominator
;
1368 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumeratorSubstitution
)
1370 const UChar
NumeratorSubstitution::LTLT
[] = { 0x003c, 0x003c };
1372 //===================================================================
1374 //===================================================================
1376 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NullSubstitution
)