2 ******************************************************************************
3 * Copyright (C) 1997-2007, 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
23 static const UChar gLessThan
= 0x003c;
24 static const UChar gEquals
= 0x003d;
25 static const UChar gGreaterThan
= 0x003e;
26 static const UChar gPercent
= 0x0025;
27 static const UChar gPound
= 0x0023;
28 static const UChar gZero
= 0x0030;
29 static const UChar gSpace
= 0x0020;
31 static const UChar gEqualsEquals
[] =
35 static const UChar gGreaterGreaterGreaterThan
[] =
39 static const UChar gGreaterGreaterThan
[] =
46 class SameValueSubstitution
: public NFSubstitution
{
48 SameValueSubstitution(int32_t pos
,
49 const NFRuleSet
* ruleset
,
50 const RuleBasedNumberFormat
* formatter
,
51 const UnicodeString
& description
,
54 virtual int64_t transformNumber(int64_t number
) const { return number
; }
55 virtual double transformNumber(double number
) const { return number
; }
56 virtual double composeRuleValue(double newRuleValue
, double /*oldRuleValue*/) const { return newRuleValue
; }
57 virtual double calcUpperBound(double oldUpperBound
) const { return oldUpperBound
; }
58 virtual UChar
tokenChar() const { return (UChar
)0x003d; } // '='
61 static UClassID
getStaticClassID(void);
62 virtual UClassID
getDynamicClassID(void) const;
65 class MultiplierSubstitution
: public NFSubstitution
{
70 MultiplierSubstitution(int32_t _pos
,
72 const NFRuleSet
* _ruleSet
,
73 const RuleBasedNumberFormat
* formatter
,
74 const UnicodeString
& description
,
76 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
), divisor(_divisor
)
78 ldivisor
= util64_fromDouble(divisor
);
80 status
= U_PARSE_ERROR
;
84 virtual void setDivisor(int32_t radix
, int32_t exponent
, UErrorCode
& status
) {
85 divisor
= uprv_pow(radix
, exponent
);
86 ldivisor
= util64_fromDouble(divisor
);
89 status
= U_PARSE_ERROR
;
93 virtual UBool
operator==(const NFSubstitution
& rhs
) const;
95 virtual int64_t transformNumber(int64_t number
) const {
96 return number
/ ldivisor
;
99 virtual double transformNumber(double number
) const {
101 return uprv_floor(number
/ divisor
);
103 return number
/divisor
;
107 virtual double composeRuleValue(double newRuleValue
, double /*oldRuleValue*/) const {
108 return newRuleValue
* divisor
;
111 virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor
; }
113 virtual UChar
tokenChar() const { return (UChar
)0x003c; } // '<'
116 static UClassID
getStaticClassID(void);
117 virtual UClassID
getDynamicClassID(void) const;
120 class ModulusSubstitution
: public NFSubstitution
{
123 const NFRule
* ruleToUse
;
125 ModulusSubstitution(int32_t pos
,
127 const NFRule
* rulePredecessor
,
128 const NFRuleSet
* ruleSet
,
129 const RuleBasedNumberFormat
* formatter
,
130 const UnicodeString
& description
,
133 virtual void setDivisor(int32_t radix
, int32_t exponent
, UErrorCode
& status
) {
134 divisor
= uprv_pow(radix
, exponent
);
135 ldivisor
= util64_fromDouble(divisor
);
138 status
= U_PARSE_ERROR
;
142 virtual UBool
operator==(const NFSubstitution
& rhs
) const;
144 virtual void doSubstitution(int64_t number
, UnicodeString
& toInsertInto
, int32_t pos
) const;
145 virtual void doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t pos
) const;
147 virtual int64_t transformNumber(int64_t number
) const { return number
% ldivisor
; }
148 virtual double transformNumber(double number
) const { return uprv_fmod(number
, divisor
); }
150 virtual UBool
doParse(const UnicodeString
& text
,
151 ParsePosition
& parsePosition
,
155 Formattable
& result
) const;
157 virtual double composeRuleValue(double newRuleValue
, double oldRuleValue
) const {
158 return oldRuleValue
- uprv_fmod(oldRuleValue
, divisor
) + newRuleValue
;
161 virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor
; }
163 virtual UBool
isModulusSubstitution() const { return TRUE
; }
165 virtual UChar
tokenChar() const { return (UChar
)0x003e; } // '>'
168 static UClassID
getStaticClassID(void);
169 virtual UClassID
getDynamicClassID(void) const;
172 class IntegralPartSubstitution
: public NFSubstitution
{
174 IntegralPartSubstitution(int32_t _pos
,
175 const NFRuleSet
* _ruleSet
,
176 const RuleBasedNumberFormat
* formatter
,
177 const UnicodeString
& description
,
179 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
) {}
181 virtual int64_t transformNumber(int64_t number
) const { return number
; }
182 virtual double transformNumber(double number
) const { return uprv_floor(number
); }
183 virtual double composeRuleValue(double newRuleValue
, double oldRuleValue
) const { return newRuleValue
+ oldRuleValue
; }
184 virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX
; }
185 virtual UChar
tokenChar() const { return (UChar
)0x003c; } // '<'
188 static UClassID
getStaticClassID(void);
189 virtual UClassID
getDynamicClassID(void) const;
192 class FractionalPartSubstitution
: public NFSubstitution
{
195 enum { kMaxDecimalDigits
= 8 };
197 FractionalPartSubstitution(int32_t pos
,
198 const NFRuleSet
* ruleSet
,
199 const RuleBasedNumberFormat
* formatter
,
200 const UnicodeString
& description
,
203 virtual UBool
operator==(const NFSubstitution
& rhs
) const;
205 virtual void doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t pos
) const;
206 virtual void doSubstitution(int64_t /*number*/, UnicodeString
& /*toInsertInto*/, int32_t /*_pos*/) const {}
207 virtual int64_t transformNumber(int64_t /*number*/) const { return 0; }
208 virtual double transformNumber(double number
) const { return number
- uprv_floor(number
); }
210 virtual UBool
doParse(const UnicodeString
& text
,
211 ParsePosition
& parsePosition
,
215 Formattable
& result
) const;
217 virtual double composeRuleValue(double newRuleValue
, double oldRuleValue
) const { return newRuleValue
+ oldRuleValue
; }
218 virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0.0; }
219 virtual UChar
tokenChar() const { return (UChar
)0x003e; } // '>'
222 static UClassID
getStaticClassID(void);
223 virtual UClassID
getDynamicClassID(void) const;
226 class AbsoluteValueSubstitution
: public NFSubstitution
{
228 AbsoluteValueSubstitution(int32_t _pos
,
229 const NFRuleSet
* _ruleSet
,
230 const RuleBasedNumberFormat
* formatter
,
231 const UnicodeString
& description
,
233 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
) {}
235 virtual int64_t transformNumber(int64_t number
) const { return number
>= 0 ? number
: -number
; }
236 virtual double transformNumber(double number
) const { return uprv_fabs(number
); }
237 virtual double composeRuleValue(double newRuleValue
, double /*oldRuleValue*/) const { return -newRuleValue
; }
238 virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX
; }
239 virtual UChar
tokenChar() const { return (UChar
)0x003e; } // '>'
242 static UClassID
getStaticClassID(void);
243 virtual UClassID
getDynamicClassID(void) const;
246 class NumeratorSubstitution
: public NFSubstitution
{
248 int64_t ldenominator
;
251 static inline UnicodeString
fixdesc(const UnicodeString
& desc
) {
252 if (desc
.endsWith(LTLT
, 2)) {
253 UnicodeString
result(desc
, 0, desc
.length()-1);
258 NumeratorSubstitution(int32_t _pos
,
260 const NFRuleSet
* _ruleSet
,
261 const RuleBasedNumberFormat
* formatter
,
262 const UnicodeString
& description
,
264 : NFSubstitution(_pos
, _ruleSet
, formatter
, fixdesc(description
), status
), denominator(_denominator
)
266 ldenominator
= util64_fromDouble(denominator
);
267 withZeros
= description
.endsWith(LTLT
, 2);
270 virtual UBool
operator==(const NFSubstitution
& rhs
) const;
272 virtual int64_t transformNumber(int64_t number
) const { return number
* ldenominator
; }
273 virtual double transformNumber(double number
) const { return uprv_round(number
* denominator
); }
275 virtual void doSubstitution(int64_t /*number*/, UnicodeString
& /*toInsertInto*/, int32_t /*_pos*/) const {}
276 virtual void doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t pos
) const;
277 virtual UBool
doParse(const UnicodeString
& text
,
278 ParsePosition
& parsePosition
,
281 UBool
/*lenientParse*/,
282 Formattable
& result
) const;
284 virtual double composeRuleValue(double newRuleValue
, double oldRuleValue
) const { return newRuleValue
/ oldRuleValue
; }
285 virtual double calcUpperBound(double /*oldUpperBound*/) const { return denominator
; }
286 virtual UChar
tokenChar() const { return (UChar
)0x003c; } // '<'
288 static const UChar LTLT
[2];
291 static UClassID
getStaticClassID(void);
292 virtual UClassID
getDynamicClassID(void) const;
295 class NullSubstitution
: public NFSubstitution
{
297 NullSubstitution(int32_t _pos
,
298 const NFRuleSet
* _ruleSet
,
299 const RuleBasedNumberFormat
* formatter
,
300 const UnicodeString
& description
,
302 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
) {}
304 virtual void toString(UnicodeString
& /*result*/) const {}
305 virtual void doSubstitution(double /*number*/, UnicodeString
& /*toInsertInto*/, int32_t /*_pos*/) const {}
306 virtual void doSubstitution(int64_t /*number*/, UnicodeString
& /*toInsertInto*/, int32_t /*_pos*/) const {}
307 virtual int64_t transformNumber(int64_t /*number*/) const { return 0; }
308 virtual double transformNumber(double /*number*/) const { return 0; }
309 virtual UBool
doParse(const UnicodeString
& /*text*/,
310 ParsePosition
& /*parsePosition*/,
312 double /*upperBound*/,
313 UBool
/*lenientParse*/,
314 Formattable
& result
) const
315 { result
.setDouble(baseValue
); return TRUE
; }
316 virtual double composeRuleValue(double /*newRuleValue*/, double /*oldRuleValue*/) const { return 0.0; } // never called
317 virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0; } // never called
318 virtual UBool
isNullSubstitution() const { return TRUE
; }
319 virtual UChar
tokenChar() const { return (UChar
)0x0020; } // ' ' never called
322 static UClassID
getStaticClassID(void);
323 virtual UClassID
getDynamicClassID(void) const;
327 NFSubstitution::makeSubstitution(int32_t pos
,
329 const NFRule
* predecessor
,
330 const NFRuleSet
* ruleSet
,
331 const RuleBasedNumberFormat
* formatter
,
332 const UnicodeString
& description
,
335 // if the description is empty, return a NullSubstitution
336 if (description
.length() == 0) {
337 return new NullSubstitution(pos
, ruleSet
, formatter
, description
, status
);
340 switch (description
.charAt(0)) {
341 // if the description begins with '<'...
343 // throw an exception if the rule is a negative number
345 if (rule
->getBaseValue() == NFRule::kNegativeNumberRule
) {
346 // throw new IllegalArgumentException("<< not allowed in negative-number rule");
347 status
= U_PARSE_ERROR
;
351 // if the rule is a fraction rule, return an
352 // IntegralPartSubstitution
353 else if (rule
->getBaseValue() == NFRule::kImproperFractionRule
354 || rule
->getBaseValue() == NFRule::kProperFractionRule
355 || rule
->getBaseValue() == NFRule::kMasterRule
) {
356 return new IntegralPartSubstitution(pos
, ruleSet
, formatter
, description
, status
);
359 // if the rule set containing the rule is a fraction
360 // rule set, return a NumeratorSubstitution
361 else if (ruleSet
->isFractionRuleSet()) {
362 return new NumeratorSubstitution(pos
, (double)rule
->getBaseValue(),
363 formatter
->getDefaultRuleSet(), formatter
, description
, status
);
366 // otherwise, return a MultiplierSubstitution
368 return new MultiplierSubstitution(pos
, rule
->getDivisor(), ruleSet
,
369 formatter
, description
, status
);
372 // if the description begins with '>'...
374 // if the rule is a negative-number rule, return
375 // an AbsoluteValueSubstitution
376 if (rule
->getBaseValue() == NFRule::kNegativeNumberRule
) {
377 return new AbsoluteValueSubstitution(pos
, ruleSet
, formatter
, description
, status
);
380 // if the rule is a fraction rule, return a
381 // FractionalPartSubstitution
382 else if (rule
->getBaseValue() == NFRule::kImproperFractionRule
383 || rule
->getBaseValue() == NFRule::kProperFractionRule
384 || rule
->getBaseValue() == NFRule::kMasterRule
) {
385 return new FractionalPartSubstitution(pos
, ruleSet
, formatter
, description
, status
);
388 // if the rule set owning the rule is a fraction rule set,
389 // throw an exception
390 else if (ruleSet
->isFractionRuleSet()) {
391 // throw new IllegalArgumentException(">> not allowed in fraction rule set");
392 status
= U_PARSE_ERROR
;
396 // otherwise, return a ModulusSubstitution
398 return new ModulusSubstitution(pos
, rule
->getDivisor(), predecessor
,
399 ruleSet
, formatter
, description
, status
);
402 // if the description begins with '=', always return a
403 // SameValueSubstitution
405 return new SameValueSubstitution(pos
, ruleSet
, formatter
, description
, status
);
407 // and if it's anything else, throw an exception
409 // throw new IllegalArgumentException("Illegal substitution character");
410 status
= U_PARSE_ERROR
;
415 NFSubstitution::NFSubstitution(int32_t _pos
,
416 const NFRuleSet
* _ruleSet
,
417 const RuleBasedNumberFormat
* formatter
,
418 const UnicodeString
& description
,
420 : pos(_pos
), ruleSet(NULL
), numberFormat(NULL
)
422 // the description should begin and end with the same character.
423 // If it doesn't that's a syntax error. Otherwise,
424 // makeSubstitution() was the only thing that needed to know
425 // about these characters, so strip them off
426 UnicodeString
workingDescription(description
);
427 if (description
.length() >= 2
428 && description
.charAt(0) == description
.charAt(description
.length() - 1))
430 workingDescription
.remove(description
.length() - 1, 1);
431 workingDescription
.remove(0, 1);
433 else if (description
.length() != 0) {
434 // throw new IllegalArgumentException("Illegal substitution syntax");
435 status
= U_PARSE_ERROR
;
439 // if the description was just two paired token characters
440 // (i.e., "<<" or ">>"), it uses the rule set it belongs to to
442 if (workingDescription
.length() == 0) {
443 this->ruleSet
= _ruleSet
;
445 // if the description contains a rule set name, that's the rule
446 // set we use to format the result: get a reference to the
448 else if (workingDescription
.charAt(0) == gPercent
) {
449 this->ruleSet
= formatter
->findRuleSet(workingDescription
, status
);
451 // if the description begins with 0 or #, treat it as a
452 // DecimalFormat pattern, and initialize a DecimalFormat with
453 // that pattern (then set it to use the DecimalFormatSymbols
454 // belonging to our formatter)
455 else if (workingDescription
.charAt(0) == gPound
|| workingDescription
.charAt(0) ==gZero
) {
456 DecimalFormatSymbols
* sym
= formatter
->getDecimalFormatSymbols();
458 status
= U_MISSING_RESOURCE_ERROR
;
461 this->numberFormat
= new DecimalFormat(workingDescription
, *sym
, status
);
463 if (this->numberFormat
== 0) {
464 status
= U_MEMORY_ALLOCATION_ERROR
;
467 if (U_FAILURE(status
)) {
468 delete (DecimalFormat
*)this->numberFormat
;
469 this->numberFormat
= NULL
;
472 // this->numberFormat->setDecimalFormatSymbols(formatter->getDecimalFormatSymbols());
474 // if the description is ">>>", this substitution bypasses the
475 // usual rule-search process and always uses the rule that precedes
476 // it in its own rule set's rule list (this is used for place-value
477 // notations: formats where you want to see a particular part of
478 // a number even when it's 0)
479 else if (workingDescription
.charAt(0) == gGreaterThan
) {
480 // this causes problems when >>> is used in a frationalPartSubstitution
481 // this->ruleSet = NULL;
482 this->ruleSet
= _ruleSet
;
483 this->numberFormat
= NULL
;
485 // and of the description is none of these things, it's a syntax error
487 // throw new IllegalArgumentException("Illegal substitution syntax");
488 status
= U_PARSE_ERROR
;
492 NFSubstitution::~NFSubstitution()
495 delete (NumberFormat
*)numberFormat
; numberFormat
= NULL
;
499 * Set's the substitution's divisor. Used by NFRule.setBaseValue().
500 * A no-op for all substitutions except multiplier and modulus
502 * @param radix The radix of the divisor
503 * @param exponent The exponent of the divisor
506 NFSubstitution::setDivisor(int32_t /*radix*/, int32_t /*exponent*/, UErrorCode
& /*status*/) {
507 // a no-op for all substitutions except multiplier and modulus substitutions
511 //-----------------------------------------------------------------------
513 //-----------------------------------------------------------------------
515 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NFSubstitution
)
518 * Compares two substitutions for equality
519 * @param The substitution to compare this one to
520 * @return true if the two substitutions are functionally equivalent
523 NFSubstitution::operator==(const NFSubstitution
& rhs
) const
525 // compare class and all of the fields all substitutions have
527 // this should be called by subclasses before their own equality tests
528 return getDynamicClassID() == rhs
.getDynamicClassID()
530 && (ruleSet
== NULL
) == (rhs
.ruleSet
== NULL
)
531 // && ruleSet == rhs.ruleSet causes circularity, other checks to make instead?
532 && (numberFormat
== NULL
533 ? (rhs
.numberFormat
== NULL
)
534 : (*numberFormat
== *rhs
.numberFormat
));
538 * Returns a textual description of the substitution
539 * @return A textual description of the substitution. This might
540 * not be identical to the description it was created from, but
541 * it'll produce the same result.
544 NFSubstitution::toString(UnicodeString
& text
) const
546 // use tokenChar() to get the character at the beginning and
547 // end of the substitutin token. In between them will go
548 // either the name of the rule set it uses, or the pattern of
549 // the DecimalFormat it uses
551 text
.append(tokenChar());
554 if (ruleSet
!= NULL
) {
555 ruleSet
->getName(temp
);
556 } else if (numberFormat
!= NULL
) {
557 numberFormat
->toPattern(temp
);
560 text
.append(tokenChar());
563 //-----------------------------------------------------------------------
565 //-----------------------------------------------------------------------
568 * Performs a mathematical operation on the number, formats it using
569 * either ruleSet or decimalFormat, and inserts the result into
571 * @param number The number being formatted.
572 * @param toInsertInto The string we insert the result into
573 * @param pos The position in toInsertInto where the owning rule's
574 * rule text begins (this value is added to this substitution's
575 * position to determine exactly where to insert the new text)
578 NFSubstitution::doSubstitution(int64_t number
, UnicodeString
& toInsertInto
, int32_t _pos
) const
580 if (ruleSet
!= NULL
) {
581 // perform a transformation on the number that is dependent
582 // on the type of substitution this is, then just call its
583 // rule set's format() method to format the result
584 ruleSet
->format(transformNumber(number
), toInsertInto
, _pos
+ this->pos
);
585 } else if (numberFormat
!= NULL
) {
586 // or perform the transformation on the number (preserving
587 // the result's fractional part if the formatter it set
588 // to show it), then use that formatter's format() method
589 // to format the result
590 double numberToFormat
= transformNumber((double)number
);
591 if (numberFormat
->getMaximumFractionDigits() == 0) {
592 numberToFormat
= uprv_floor(numberToFormat
);
596 numberFormat
->format(numberToFormat
, temp
);
597 toInsertInto
.insert(_pos
+ this->pos
, temp
);
602 * Performs a mathematical operation on the number, formats it using
603 * either ruleSet or decimalFormat, and inserts the result into
605 * @param number The number being formatted.
606 * @param toInsertInto The string we insert the result into
607 * @param pos The position in toInsertInto where the owning rule's
608 * rule text begins (this value is added to this substitution's
609 * position to determine exactly where to insert the new text)
612 NFSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t _pos
) const {
613 // perform a transformation on the number being formatted that
614 // is dependent on the type of substitution this is
615 double numberToFormat
= transformNumber(number
);
617 // if the result is an integer, from here on out we work in integer
618 // space (saving time and memory and preserving accuracy)
619 if (numberToFormat
== uprv_floor(numberToFormat
) && ruleSet
!= NULL
) {
620 ruleSet
->format(util64_fromDouble(numberToFormat
), toInsertInto
, _pos
+ this->pos
);
622 // if the result isn't an integer, then call either our rule set's
623 // format() method or our DecimalFormat's format() method to
626 if (ruleSet
!= NULL
) {
627 ruleSet
->format(numberToFormat
, toInsertInto
, _pos
+ this->pos
);
628 } else if (numberFormat
!= NULL
) {
630 numberFormat
->format(numberToFormat
, temp
);
631 toInsertInto
.insert(_pos
+ this->pos
, temp
);
637 //-----------------------------------------------------------------------
639 //-----------------------------------------------------------------------
646 * Parses a string using the rule set or DecimalFormat belonging
647 * to this substitution. If there's a match, a mathematical
648 * operation (the inverse of the one used in formatting) is
649 * performed on the result of the parse and the value passed in
650 * and returned as the result. The parse position is updated to
651 * point to the first unmatched character in the string.
652 * @param text The string to parse
653 * @param parsePosition On entry, ignored, but assumed to be 0.
654 * On exit, this is updated to point to the first unmatched
655 * character (or 0 if the substitution didn't match)
656 * @param baseValue A partial parse result that should be
657 * combined with the result of this parse
658 * @param upperBound When searching the rule set for a rule
659 * matching the string passed in, only rules with base values
660 * lower than this are considered
661 * @param lenientParse If true and matching against rules fails,
662 * the substitution will also try matching the text against
663 * numerals using a default-costructed NumberFormat. If false,
664 * no extra work is done. (This value is false whenever the
665 * formatter isn't in lenient-parse mode, but is also false
666 * under some conditions even when the formatter _is_ in
667 * lenient-parse mode.)
668 * @return If there's a match, this is the result of composing
669 * baseValue with whatever was returned from matching the
670 * characters. This will be either a Long or a Double. If there's
671 * no match this is new Long(0) (not null), and parsePosition
675 NFSubstitution::doParse(const UnicodeString
& text
,
676 ParsePosition
& parsePosition
,
680 Formattable
& result
) const
683 fprintf(stderr
, "<nfsubs> %x bv: %g ub: %g\n", this, baseValue
, upperBound
);
685 // figure out the highest base value a rule can have and match
686 // the text being parsed (this varies according to the type of
687 // substitutions: multiplier, modulus, and numerator substitutions
688 // restrict the search to rules with base values lower than their
689 // own; same-value substitutions leave the upper bound wherever
690 // it was, and the others allow any rule to match
691 upperBound
= calcUpperBound(upperBound
);
693 // use our rule set to parse the text. If that fails and
694 // lenient parsing is enabled (this is always false if the
695 // formatter's lenient-parsing mode is off, but it may also
696 // be false even when the formatter's lenient-parse mode is
697 // on), then also try parsing the text using a default-
698 // constructed NumberFormat
699 if (ruleSet
!= NULL
) {
700 ruleSet
->parse(text
, parsePosition
, upperBound
, result
);
701 if (lenientParse
&& !ruleSet
->isFractionRuleSet() && parsePosition
.getIndex() == 0) {
702 UErrorCode status
= U_ZERO_ERROR
;
703 NumberFormat
* fmt
= NumberFormat::createInstance(status
);
704 if (U_SUCCESS(status
)) {
705 fmt
->parse(text
, result
, parsePosition
);
710 // ...or use our DecimalFormat to parse the text
711 } else if (numberFormat
!= NULL
) {
712 numberFormat
->parse(text
, result
, parsePosition
);
715 // if the parse was successful, we've already advanced the caller's
716 // parse position (this is the one function that doesn't have one
717 // of its own). Derive a parse result and return it as a Long,
718 // if possible, or a Double
719 if (parsePosition
.getIndex() != 0) {
720 UErrorCode status
= U_ZERO_ERROR
;
721 double tempResult
= result
.getDouble(status
);
723 // composeRuleValue() produces a full parse result from
724 // the partial parse result passed to this function from
725 // the caller (this is either the owning rule's base value
726 // or the partial result obtained from composing the
727 // owning rule's base value with its other substitution's
728 // parse result) and the partial parse result obtained by
729 // matching the substitution (which will be the same value
730 // the caller would get by parsing just this part of the
731 // text with RuleBasedNumberFormat.parse() ). How the two
732 // values are used to derive the full parse result depends
733 // on the types of substitutions: For a regular rule, the
734 // ultimate result is its multiplier substitution's result
735 // times the rule's divisor (or the rule's base value) plus
736 // the modulus substitution's result (which will actually
737 // supersede part of the rule's base value). For a negative-
738 // number rule, the result is the negative of its substitution's
739 // result. For a fraction rule, it's the sum of its two
740 // substitution results. For a rule in a fraction rule set,
741 // it's the numerator substitution's result divided by
742 // the rule's base value. Results from same-value substitutions
743 // propagate back upard, and null substitutions don't affect
745 tempResult
= composeRuleValue(tempResult
, baseValue
);
746 result
.setDouble(tempResult
);
748 // if the parse was UNsuccessful, return 0
756 NFSubstitution::isNullSubstitution() const {
761 * Returns true if this is a modulus substitution. (We didn't do this
762 * with instanceof partially because it causes source files to
763 * proliferate and partially because we have to port this to C++.)
764 * @return true if this object is an instance of ModulusSubstitution
767 NFSubstitution::isModulusSubstitution() const {
771 //===================================================================
772 // SameValueSubstitution
773 //===================================================================
776 * A substitution that passes the value passed to it through unchanged.
777 * Represented by == in rule descriptions.
779 SameValueSubstitution::SameValueSubstitution(int32_t _pos
,
780 const NFRuleSet
* _ruleSet
,
781 const RuleBasedNumberFormat
* formatter
,
782 const UnicodeString
& description
,
784 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
)
786 if (description
== gEqualsEquals
) {
787 // throw new IllegalArgumentException("== is not a legal token");
788 status
= U_PARSE_ERROR
;
792 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SameValueSubstitution
)
794 //===================================================================
795 // MultiplierSubstitution
796 //===================================================================
798 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MultiplierSubstitution
)
800 UBool
MultiplierSubstitution::operator==(const NFSubstitution
& rhs
) const
802 return NFSubstitution::operator==(rhs
) &&
803 divisor
== ((const MultiplierSubstitution
*)&rhs
)->divisor
;
807 //===================================================================
808 // ModulusSubstitution
809 //===================================================================
812 * A substitution that divides the number being formatted by the its rule's
813 * divisor and formats the remainder. Represented by ">>" in a
816 ModulusSubstitution::ModulusSubstitution(int32_t _pos
,
818 const NFRule
* predecessor
,
819 const NFRuleSet
* _ruleSet
,
820 const RuleBasedNumberFormat
* formatter
,
821 const UnicodeString
& description
,
823 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
)
827 ldivisor
= util64_fromDouble(_divisor
);
829 // the owning rule's divisor controls the behavior of this
830 // substitution: rather than keeping a backpointer to the rule,
831 // we keep a copy of the divisor
834 status
= U_PARSE_ERROR
;
837 if (description
== gGreaterGreaterGreaterThan
) {
838 // the >>> token doesn't alter how this substituion calculates the
839 // values it uses for formatting and parsing, but it changes
840 // what's done with that value after it's obtained: >>> short-
841 // circuits the rule-search process and goes straight to the
842 // specified rule to format the substitution value
843 ruleToUse
= predecessor
;
847 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ModulusSubstitution
)
849 UBool
ModulusSubstitution::operator==(const NFSubstitution
& rhs
) const
851 return NFSubstitution::operator==(rhs
) &&
852 divisor
== ((const ModulusSubstitution
*)&rhs
)->divisor
&&
853 ruleToUse
== ((const ModulusSubstitution
*)&rhs
)->ruleToUse
;
856 //-----------------------------------------------------------------------
858 //-----------------------------------------------------------------------
862 * If this is a >>> substitution, use ruleToUse to fill in
863 * the substitution. Otherwise, just use the superclass function.
864 * @param number The number being formatted
865 * @toInsertInto The string to insert the result of this substitution
867 * @param pos The position of the rule text in toInsertInto
870 ModulusSubstitution::doSubstitution(int64_t number
, UnicodeString
& toInsertInto
, int32_t _pos
) const
872 // if this isn't a >>> substitution, just use the inherited version
873 // of this function (which uses either a rule set or a DecimalFormat
874 // to format its substitution value)
875 if (ruleToUse
== NULL
) {
876 NFSubstitution::doSubstitution(number
, toInsertInto
, _pos
);
878 // a >>> substitution goes straight to a particular rule to
879 // format the substitution value
881 int64_t numberToFormat
= transformNumber(number
);
882 ruleToUse
->doFormat(numberToFormat
, toInsertInto
, _pos
+ getPos());
887 * If this is a >>> substitution, use ruleToUse to fill in
888 * the substitution. Otherwise, just use the superclass function.
889 * @param number The number being formatted
890 * @toInsertInto The string to insert the result of this substitution
892 * @param pos The position of the rule text in toInsertInto
895 ModulusSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t _pos
) const
897 // if this isn't a >>> substitution, just use the inherited version
898 // of this function (which uses either a rule set or a DecimalFormat
899 // to format its substitution value)
900 if (ruleToUse
== NULL
) {
901 NFSubstitution::doSubstitution(number
, toInsertInto
, _pos
);
903 // a >>> substitution goes straight to a particular rule to
904 // format the substitution value
906 double numberToFormat
= transformNumber(number
);
908 ruleToUse
->doFormat(numberToFormat
, toInsertInto
, _pos
+ getPos());
912 //-----------------------------------------------------------------------
914 //-----------------------------------------------------------------------
917 * If this is a >>> substitution, match only against ruleToUse.
918 * Otherwise, use the superclass function.
919 * @param text The string to parse
920 * @param parsePosition Ignored on entry, updated on exit to point to
921 * the first unmatched character.
922 * @param baseValue The partial parse result prior to calling this
926 ModulusSubstitution::doParse(const UnicodeString
& text
,
927 ParsePosition
& parsePosition
,
931 Formattable
& result
) const
933 // if this isn't a >>> substitution, we can just use the
934 // inherited parse() routine to do the parsing
935 if (ruleToUse
== NULL
) {
936 return NFSubstitution::doParse(text
, parsePosition
, baseValue
, upperBound
, lenientParse
, result
);
938 // but if it IS a >>> substitution, we have to do it here: we
939 // use the specific rule's doParse() method, and then we have to
940 // do some of the other work of NFRuleSet.parse()
942 ruleToUse
->doParse(text
, parsePosition
, FALSE
, upperBound
, result
);
944 if (parsePosition
.getIndex() != 0) {
945 UErrorCode status
= U_ZERO_ERROR
;
946 double tempResult
= result
.getDouble(status
);
947 tempResult
= composeRuleValue(tempResult
, baseValue
);
948 result
.setDouble(tempResult
);
956 //===================================================================
957 // IntegralPartSubstitution
958 //===================================================================
960 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IntegralPartSubstitution
)
963 //===================================================================
964 // FractionalPartSubstitution
965 //===================================================================
969 * Constructs a FractionalPartSubstitution. This object keeps a flag
970 * telling whether it should format by digits or not. In addition,
971 * it marks the rule set it calls (if any) as a fraction rule set.
973 FractionalPartSubstitution::FractionalPartSubstitution(int32_t _pos
,
974 const NFRuleSet
* _ruleSet
,
975 const RuleBasedNumberFormat
* formatter
,
976 const UnicodeString
& description
,
978 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
)
983 // akk, ruleSet can change in superclass constructor
984 if (description
== gGreaterGreaterThan
||
985 description
== gGreaterGreaterGreaterThan
||
986 _ruleSet
== getRuleSet()) {
988 if (description
== gGreaterGreaterGreaterThan
) {
993 ((NFRuleSet
*)getRuleSet())->makeIntoFractionRuleSet();
997 //-----------------------------------------------------------------------
999 //-----------------------------------------------------------------------
1002 * If in "by digits" mode, fills in the substitution one decimal digit
1003 * at a time using the rule set containing this substitution.
1004 * Otherwise, uses the superclass function.
1005 * @param number The number being formatted
1006 * @param toInsertInto The string to insert the result of formatting
1007 * the substitution into
1008 * @param pos The position of the owning rule's rule text in
1012 FractionalPartSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t _pos
) const
1014 // if we're not in "byDigits" mode, just use the inherited
1015 // doSubstitution() routine
1017 NFSubstitution::doSubstitution(number
, toInsertInto
, _pos
);
1019 // if we're in "byDigits" mode, transform the value into an integer
1020 // by moving the decimal point eight places to the right and
1021 // pulling digits off the right one at a time, formatting each digit
1022 // as an integer using this substitution's owning rule set
1023 // (this is slower, but more accurate, than doing it from the
1026 // int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits));
1027 // // this flag keeps us from formatting trailing zeros. It starts
1028 // // out false because we're pulling from the right, and switches
1029 // // to true the first time we encounter a non-zero digit
1030 // UBool doZeros = FALSE;
1031 // for (int32_t i = 0; i < kMaxDecimalDigits; i++) {
1032 // int64_t digit = numberToFormat % 10;
1033 // if (digit != 0 || doZeros) {
1034 // if (doZeros && useSpaces) {
1035 // toInsertInto.insert(_pos + getPos(), gSpace);
1038 // getRuleSet()->format(digit, toInsertInto, _pos + getPos());
1040 // numberToFormat /= 10;
1044 dl
.set(number
, 20, TRUE
);
1047 while (dl
.fCount
> (dl
.fDecimalAt
<= 0 ? 0 : dl
.fDecimalAt
)) {
1048 if (pad
&& useSpaces
) {
1049 toInsertInto
.insert(_pos
+ getPos(), gSpace
);
1053 getRuleSet()->format((int64_t)(dl
.fDigits
[--dl
.fCount
] - '0'), toInsertInto
, _pos
+ getPos());
1055 while (dl
.fDecimalAt
< 0) {
1056 if (pad
&& useSpaces
) {
1057 toInsertInto
.insert(_pos
+ getPos(), gSpace
);
1061 getRuleSet()->format((int64_t)0, toInsertInto
, _pos
+ getPos());
1066 // hack around lack of precision in digitlist. if we would end up with
1067 // "foo point" make sure we add a " zero" to the end.
1068 getRuleSet()->format((int64_t)0, toInsertInto
, _pos
+ getPos());
1073 //-----------------------------------------------------------------------
1075 //-----------------------------------------------------------------------
1078 * If in "by digits" mode, parses the string as if it were a string
1079 * of individual digits; otherwise, uses the superclass function.
1080 * @param text The string to parse
1081 * @param parsePosition Ignored on entry, but updated on exit to point
1082 * to the first unmatched character
1083 * @param baseValue The partial parse result prior to entering this
1085 * @param upperBound Only consider rules with base values lower than
1086 * this when filling in the substitution
1087 * @param lenientParse If true, try matching the text as numerals if
1088 * matching as words doesn't work
1089 * @return If the match was successful, the current partial parse
1090 * result; otherwise new Long(0). The result is either a Long or
1095 FractionalPartSubstitution::doParse(const UnicodeString
& text
,
1096 ParsePosition
& parsePosition
,
1098 double /*upperBound*/,
1100 Formattable
& resVal
) const
1102 // if we're not in byDigits mode, we can just use the inherited
1105 return NFSubstitution::doParse(text
, parsePosition
, baseValue
, 0, lenientParse
, resVal
);
1107 // if we ARE in byDigits mode, parse the text one digit at a time
1108 // using this substitution's owning rule set (we do this by setting
1109 // upperBound to 10 when calling doParse() ) until we reach
1112 UnicodeString
workText(text
);
1113 ParsePosition
workPos(1);
1116 // double p10 = 0.1;
1119 NumberFormat
* fmt
= NULL
;
1120 while (workText
.length() > 0 && workPos
.getIndex() != 0) {
1121 workPos
.setIndex(0);
1123 getRuleSet()->parse(workText
, workPos
, 10, temp
);
1124 UErrorCode status
= U_ZERO_ERROR
;
1125 digit
= temp
.getLong(status
);
1126 // digit = temp.getType() == Formattable::kLong ?
1128 // (int32_t)temp.getDouble();
1130 if (lenientParse
&& workPos
.getIndex() == 0) {
1132 status
= U_ZERO_ERROR
;
1133 fmt
= NumberFormat::createInstance(status
);
1134 if (U_FAILURE(status
)) {
1140 fmt
->parse(workText
, temp
, workPos
);
1141 digit
= temp
.getLong(status
);
1145 if (workPos
.getIndex() != 0) {
1146 dl
.append((char)('0' + digit
));
1147 // result += digit * p10;
1149 parsePosition
.setIndex(parsePosition
.getIndex() + workPos
.getIndex());
1150 workText
.removeBetween(0, workPos
.getIndex());
1151 while (workText
.length() > 0 && workText
.charAt(0) == gSpace
) {
1152 workText
.removeBetween(0, 1);
1153 parsePosition
.setIndex(parsePosition
.getIndex() + 1);
1159 result
= dl
.fCount
== 0 ? 0 : dl
.getDouble();
1160 result
= composeRuleValue(result
, baseValue
);
1161 resVal
.setDouble(result
);
1167 FractionalPartSubstitution::operator==(const NFSubstitution
& rhs
) const
1169 return NFSubstitution::operator==(rhs
) &&
1170 ((const FractionalPartSubstitution
*)&rhs
)->byDigits
== byDigits
;
1173 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FractionalPartSubstitution
)
1176 //===================================================================
1177 // AbsoluteValueSubstitution
1178 //===================================================================
1180 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AbsoluteValueSubstitution
)
1182 //===================================================================
1183 // NumeratorSubstitution
1184 //===================================================================
1187 NumeratorSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t apos
) const {
1188 // perform a transformation on the number being formatted that
1189 // is dependent on the type of substitution this is
1191 double numberToFormat
= transformNumber(number
);
1192 int64_t longNF
= util64_fromDouble(numberToFormat
);
1194 const NFRuleSet
* aruleSet
= getRuleSet();
1195 if (withZeros
&& aruleSet
!= NULL
) {
1196 // if there are leading zeros in the decimal expansion then emit them
1198 int32_t len
= toInsertInto
.length();
1199 while ((nf
*= 10) < denominator
) {
1200 toInsertInto
.insert(apos
+ getPos(), gSpace
);
1201 aruleSet
->format((int64_t)0, toInsertInto
, apos
+ getPos());
1203 apos
+= toInsertInto
.length() - len
;
1206 // if the result is an integer, from here on out we work in integer
1207 // space (saving time and memory and preserving accuracy)
1208 if (numberToFormat
== longNF
&& aruleSet
!= NULL
) {
1209 aruleSet
->format(longNF
, toInsertInto
, apos
+ getPos());
1211 // if the result isn't an integer, then call either our rule set's
1212 // format() method or our DecimalFormat's format() method to
1213 // format the result
1215 if (aruleSet
!= NULL
) {
1216 aruleSet
->format(numberToFormat
, toInsertInto
, apos
+ getPos());
1218 UErrorCode status
= U_ZERO_ERROR
;
1220 getNumberFormat()->format(numberToFormat
, temp
, status
);
1221 toInsertInto
.insert(apos
+ getPos(), temp
);
1227 NumeratorSubstitution::doParse(const UnicodeString
& text
,
1228 ParsePosition
& parsePosition
,
1231 UBool
/*lenientParse*/,
1232 Formattable
& result
) const
1234 // we don't have to do anything special to do the parsing here,
1235 // but we have to turn lenient parsing off-- if we leave it on,
1236 // it SERIOUSLY messes up the algorithm
1238 // if withZeros is true, we need to count the zeros
1239 // and use that to adjust the parse result
1240 UErrorCode status
= U_ZERO_ERROR
;
1241 int32_t zeroCount
= 0;
1242 UnicodeString
workText(text
);
1245 ParsePosition
workPos(1);
1248 while (workText
.length() > 0 && workPos
.getIndex() != 0) {
1249 workPos
.setIndex(0);
1250 getRuleSet()->parse(workText
, workPos
, 1, temp
); // parse zero or nothing at all
1251 if (workPos
.getIndex() == 0) {
1252 // we failed, either there were no more zeros, or the number was formatted with digits
1253 // either way, we're done
1258 parsePosition
.setIndex(parsePosition
.getIndex() + workPos
.getIndex());
1259 workText
.remove(0, workPos
.getIndex());
1260 while (workText
.length() > 0 && workText
.charAt(0) == gSpace
) {
1261 workText
.remove(0, 1);
1262 parsePosition
.setIndex(parsePosition
.getIndex() + 1);
1267 workText
.remove(0, (int32_t)parsePosition
.getIndex());
1268 parsePosition
.setIndex(0);
1271 // we've parsed off the zeros, now let's parse the rest from our current position
1272 NFSubstitution::doParse(workText
, parsePosition
, withZeros
? 1 : baseValue
, upperBound
, FALSE
, result
);
1275 // any base value will do in this case. is there a way to
1276 // force this to not bother trying all the base values?
1278 // compute the 'effective' base and prescale the value down
1279 int64_t n
= result
.getLong(status
); // force conversion!
1286 // now add the zeros
1287 while (zeroCount
> 0) {
1291 // d is now our true denominator
1292 result
.setDouble((double)n
/(double)d
);
1299 NumeratorSubstitution::operator==(const NFSubstitution
& rhs
) const
1301 return NFSubstitution::operator==(rhs
) &&
1302 denominator
== ((const NumeratorSubstitution
*)&rhs
)->denominator
;
1305 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumeratorSubstitution
)
1307 const UChar
NumeratorSubstitution::LTLT
[] = { 0x003c, 0x003c };
1309 //===================================================================
1311 //===================================================================
1313 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NullSubstitution
)