2 ******************************************************************************
3 * Copyright (C) 1997-2005, 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
[] =
45 NFSubstitution::makeSubstitution(int32_t pos
,
47 const NFRule
* predecessor
,
48 const NFRuleSet
* ruleSet
,
49 const RuleBasedNumberFormat
* formatter
,
50 const UnicodeString
& description
,
53 // if the description is empty, return a NullSubstitution
54 if (description
.length() == 0) {
55 return new NullSubstitution(pos
, ruleSet
, formatter
, description
, status
);
58 switch (description
.charAt(0)) {
59 // if the description begins with '<'...
61 // throw an exception if the rule is a negative number
63 if (rule
->getBaseValue() == NFRule::kNegativeNumberRule
) {
64 // throw new IllegalArgumentException("<< not allowed in negative-number rule");
65 status
= U_PARSE_ERROR
;
69 // if the rule is a fraction rule, return an
70 // IntegralPartSubstitution
71 else if (rule
->getBaseValue() == NFRule::kImproperFractionRule
72 || rule
->getBaseValue() == NFRule::kProperFractionRule
73 || rule
->getBaseValue() == NFRule::kMasterRule
) {
74 return new IntegralPartSubstitution(pos
, ruleSet
, formatter
, description
, status
);
77 // if the rule set containing the rule is a fraction
78 // rule set, return a NumeratorSubstitution
79 else if (ruleSet
->isFractionRuleSet()) {
80 return new NumeratorSubstitution(pos
, (double)rule
->getBaseValue(),
81 formatter
->getDefaultRuleSet(), formatter
, description
, status
);
84 // otherwise, return a MultiplierSubstitution
86 return new MultiplierSubstitution(pos
, rule
->getDivisor(), ruleSet
,
87 formatter
, description
, status
);
90 // if the description begins with '>'...
92 // if the rule is a negative-number rule, return
93 // an AbsoluteValueSubstitution
94 if (rule
->getBaseValue() == NFRule::kNegativeNumberRule
) {
95 return new AbsoluteValueSubstitution(pos
, ruleSet
, formatter
, description
, status
);
98 // if the rule is a fraction rule, return a
99 // FractionalPartSubstitution
100 else if (rule
->getBaseValue() == NFRule::kImproperFractionRule
101 || rule
->getBaseValue() == NFRule::kProperFractionRule
102 || rule
->getBaseValue() == NFRule::kMasterRule
) {
103 return new FractionalPartSubstitution(pos
, ruleSet
, formatter
, description
, status
);
106 // if the rule set owning the rule is a fraction rule set,
107 // throw an exception
108 else if (ruleSet
->isFractionRuleSet()) {
109 // throw new IllegalArgumentException(">> not allowed in fraction rule set");
110 status
= U_PARSE_ERROR
;
114 // otherwise, return a ModulusSubstitution
116 return new ModulusSubstitution(pos
, rule
->getDivisor(), predecessor
,
117 ruleSet
, formatter
, description
, status
);
120 // if the description begins with '=', always return a
121 // SameValueSubstitution
123 return new SameValueSubstitution(pos
, ruleSet
, formatter
, description
, status
);
125 // and if it's anything else, throw an exception
127 // throw new IllegalArgumentException("Illegal substitution character");
128 status
= U_PARSE_ERROR
;
133 NFSubstitution::NFSubstitution(int32_t _pos
,
134 const NFRuleSet
* _ruleSet
,
135 const RuleBasedNumberFormat
* formatter
,
136 const UnicodeString
& description
,
138 : pos(_pos
), ruleSet(NULL
), numberFormat(NULL
)
140 // the description should begin and end with the same character.
141 // If it doesn't that's a syntax error. Otherwise,
142 // makeSubstitution() was the only thing that needed to know
143 // about these characters, so strip them off
144 UnicodeString
workingDescription(description
);
145 if (description
.length() >= 2
146 && description
.charAt(0) == description
.charAt(description
.length() - 1))
148 workingDescription
.remove(description
.length() - 1, 1);
149 workingDescription
.remove(0, 1);
151 else if (description
.length() != 0) {
152 // throw new IllegalArgumentException("Illegal substitution syntax");
153 status
= U_PARSE_ERROR
;
157 // if the description was just two paired token characters
158 // (i.e., "<<" or ">>"), it uses the rule set it belongs to to
160 if (workingDescription
.length() == 0) {
161 this->ruleSet
= _ruleSet
;
163 // if the description contains a rule set name, that's the rule
164 // set we use to format the result: get a reference to the
166 else if (workingDescription
.charAt(0) == gPercent
) {
167 this->ruleSet
= formatter
->findRuleSet(workingDescription
, status
);
169 // if the description begins with 0 or #, treat it as a
170 // DecimalFormat pattern, and initialize a DecimalFormat with
171 // that pattern (then set it to use the DecimalFormatSymbols
172 // belonging to our formatter)
173 else if (workingDescription
.charAt(0) == gPound
|| workingDescription
.charAt(0) ==gZero
) {
174 DecimalFormatSymbols
* sym
= formatter
->getDecimalFormatSymbols();
176 status
= U_MISSING_RESOURCE_ERROR
;
179 this->numberFormat
= new DecimalFormat(workingDescription
, *sym
, status
);
181 if (this->numberFormat
== 0) {
182 status
= U_MEMORY_ALLOCATION_ERROR
;
185 if (U_FAILURE(status
)) {
186 delete (DecimalFormat
*)this->numberFormat
;
187 this->numberFormat
= NULL
;
190 // this->numberFormat->setDecimalFormatSymbols(formatter->getDecimalFormatSymbols());
192 // if the description is ">>>", this substitution bypasses the
193 // usual rule-search process and always uses the rule that precedes
194 // it in its own rule set's rule list (this is used for place-value
195 // notations: formats where you want to see a particular part of
196 // a number even when it's 0)
197 else if (workingDescription
.charAt(0) == gGreaterThan
) {
198 // this causes problems when >>> is used in a frationalPartSubstitution
199 // this->ruleSet = NULL;
200 this->ruleSet
= _ruleSet
;
201 this->numberFormat
= NULL
;
203 // and of the description is none of these things, it's a syntax error
205 // throw new IllegalArgumentException("Illegal substitution syntax");
206 status
= U_PARSE_ERROR
;
210 NFSubstitution::~NFSubstitution()
213 delete (NumberFormat
*)numberFormat
; numberFormat
= NULL
;
217 * Set's the substitution's divisor. Used by NFRule.setBaseValue().
218 * A no-op for all substitutions except multiplier and modulus
220 * @param radix The radix of the divisor
221 * @param exponent The exponent of the divisor
224 NFSubstitution::setDivisor(int32_t /*radix*/, int32_t /*exponent*/, UErrorCode
& /*status*/) {
225 // a no-op for all substitutions except multiplier and modulus substitutions
229 //-----------------------------------------------------------------------
231 //-----------------------------------------------------------------------
233 const char NFSubstitution::fgClassID
= 0;
236 NFSubstitution::getDynamicClassID() const {
237 return getStaticClassID();
241 * Compares two substitutions for equality
242 * @param The substitution to compare this one to
243 * @return true if the two substitutions are functionally equivalent
246 NFSubstitution::operator==(const NFSubstitution
& rhs
) const
248 // compare class and all of the fields all substitutions have
250 // this should be called by subclasses before their own equality tests
251 return getDynamicClassID() == rhs
.getDynamicClassID()
253 && (ruleSet
== NULL
) == (rhs
.ruleSet
== NULL
)
254 // && ruleSet == rhs.ruleSet causes circularity, other checks to make instead?
255 && (numberFormat
== NULL
256 ? (rhs
.numberFormat
== NULL
)
257 : (*numberFormat
== *rhs
.numberFormat
));
261 * Returns a textual description of the substitution
262 * @return A textual description of the substitution. This might
263 * not be identical to the description it was created from, but
264 * it'll produce the same result.
267 NFSubstitution::toString(UnicodeString
& text
) const
269 // use tokenChar() to get the character at the beginning and
270 // end of the substitutin token. In between them will go
271 // either the name of the rule set it uses, or the pattern of
272 // the DecimalFormat it uses
274 text
.append(tokenChar());
277 if (ruleSet
!= NULL
) {
278 ruleSet
->getName(temp
);
279 } else if (numberFormat
!= NULL
) {
280 numberFormat
->toPattern(temp
);
283 text
.append(tokenChar());
286 //-----------------------------------------------------------------------
288 //-----------------------------------------------------------------------
291 * Performs a mathematical operation on the number, formats it using
292 * either ruleSet or decimalFormat, and inserts the result into
294 * @param number The number being formatted.
295 * @param toInsertInto The string we insert the result into
296 * @param pos The position in toInsertInto where the owning rule's
297 * rule text begins (this value is added to this substitution's
298 * position to determine exactly where to insert the new text)
301 NFSubstitution::doSubstitution(int64_t number
, UnicodeString
& toInsertInto
, int32_t _pos
) const
303 if (ruleSet
!= NULL
) {
304 // perform a transformation on the number that is dependent
305 // on the type of substitution this is, then just call its
306 // rule set's format() method to format the result
307 ruleSet
->format(transformNumber(number
), toInsertInto
, _pos
+ this->pos
);
308 } else if (numberFormat
!= NULL
) {
309 // or perform the transformation on the number (preserving
310 // the result's fractional part if the formatter it set
311 // to show it), then use that formatter's format() method
312 // to format the result
313 double numberToFormat
= transformNumber((double)number
);
314 if (numberFormat
->getMaximumFractionDigits() == 0) {
315 numberToFormat
= uprv_floor(numberToFormat
);
319 numberFormat
->format(numberToFormat
, temp
);
320 toInsertInto
.insert(_pos
+ this->pos
, temp
);
325 * Performs a mathematical operation on the number, formats it using
326 * either ruleSet or decimalFormat, and inserts the result into
328 * @param number The number being formatted.
329 * @param toInsertInto The string we insert the result into
330 * @param pos The position in toInsertInto where the owning rule's
331 * rule text begins (this value is added to this substitution's
332 * position to determine exactly where to insert the new text)
335 NFSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t _pos
) const {
336 // perform a transformation on the number being formatted that
337 // is dependent on the type of substitution this is
338 double numberToFormat
= transformNumber(number
);
340 // if the result is an integer, from here on out we work in integer
341 // space (saving time and memory and preserving accuracy)
342 if (numberToFormat
== uprv_floor(numberToFormat
) && ruleSet
!= NULL
) {
343 ruleSet
->format(util64_fromDouble(numberToFormat
), toInsertInto
, _pos
+ this->pos
);
345 // if the result isn't an integer, then call either our rule set's
346 // format() method or our DecimalFormat's format() method to
349 if (ruleSet
!= NULL
) {
350 ruleSet
->format(numberToFormat
, toInsertInto
, _pos
+ this->pos
);
351 } else if (numberFormat
!= NULL
) {
353 numberFormat
->format(numberToFormat
, temp
);
354 toInsertInto
.insert(_pos
+ this->pos
, temp
);
360 //-----------------------------------------------------------------------
362 //-----------------------------------------------------------------------
369 * Parses a string using the rule set or DecimalFormat belonging
370 * to this substitution. If there's a match, a mathematical
371 * operation (the inverse of the one used in formatting) is
372 * performed on the result of the parse and the value passed in
373 * and returned as the result. The parse position is updated to
374 * point to the first unmatched character in the string.
375 * @param text The string to parse
376 * @param parsePosition On entry, ignored, but assumed to be 0.
377 * On exit, this is updated to point to the first unmatched
378 * character (or 0 if the substitution didn't match)
379 * @param baseValue A partial parse result that should be
380 * combined with the result of this parse
381 * @param upperBound When searching the rule set for a rule
382 * matching the string passed in, only rules with base values
383 * lower than this are considered
384 * @param lenientParse If true and matching against rules fails,
385 * the substitution will also try matching the text against
386 * numerals using a default-costructed NumberFormat. If false,
387 * no extra work is done. (This value is false whenever the
388 * formatter isn't in lenient-parse mode, but is also false
389 * under some conditions even when the formatter _is_ in
390 * lenient-parse mode.)
391 * @return If there's a match, this is the result of composing
392 * baseValue with whatever was returned from matching the
393 * characters. This will be either a Long or a Double. If there's
394 * no match this is new Long(0) (not null), and parsePosition
398 NFSubstitution::doParse(const UnicodeString
& text
,
399 ParsePosition
& parsePosition
,
403 Formattable
& result
) const
406 fprintf(stderr
, "<nfsubs> %x bv: %g ub: %g\n", this, baseValue
, upperBound
);
408 // figure out the highest base value a rule can have and match
409 // the text being parsed (this varies according to the type of
410 // substitutions: multiplier, modulus, and numerator substitutions
411 // restrict the search to rules with base values lower than their
412 // own; same-value substitutions leave the upper bound wherever
413 // it was, and the others allow any rule to match
414 upperBound
= calcUpperBound(upperBound
);
416 // use our rule set to parse the text. If that fails and
417 // lenient parsing is enabled (this is always false if the
418 // formatter's lenient-parsing mode is off, but it may also
419 // be false even when the formatter's lenient-parse mode is
420 // on), then also try parsing the text using a default-
421 // constructed NumberFormat
422 if (ruleSet
!= NULL
) {
423 ruleSet
->parse(text
, parsePosition
, upperBound
, result
);
424 if (lenientParse
&& !ruleSet
->isFractionRuleSet() && parsePosition
.getIndex() == 0) {
425 UErrorCode status
= U_ZERO_ERROR
;
426 NumberFormat
* fmt
= NumberFormat::createInstance(status
);
427 if (U_SUCCESS(status
)) {
428 fmt
->parse(text
, result
, parsePosition
);
433 // ...or use our DecimalFormat to parse the text
434 } else if (numberFormat
!= NULL
) {
435 numberFormat
->parse(text
, result
, parsePosition
);
438 // if the parse was successful, we've already advanced the caller's
439 // parse position (this is the one function that doesn't have one
440 // of its own). Derive a parse result and return it as a Long,
441 // if possible, or a Double
442 if (parsePosition
.getIndex() != 0) {
443 UErrorCode status
= U_ZERO_ERROR
;
444 double tempResult
= result
.getDouble(status
);
446 // composeRuleValue() produces a full parse result from
447 // the partial parse result passed to this function from
448 // the caller (this is either the owning rule's base value
449 // or the partial result obtained from composing the
450 // owning rule's base value with its other substitution's
451 // parse result) and the partial parse result obtained by
452 // matching the substitution (which will be the same value
453 // the caller would get by parsing just this part of the
454 // text with RuleBasedNumberFormat.parse() ). How the two
455 // values are used to derive the full parse result depends
456 // on the types of substitutions: For a regular rule, the
457 // ultimate result is its multiplier substitution's result
458 // times the rule's divisor (or the rule's base value) plus
459 // the modulus substitution's result (which will actually
460 // supersede part of the rule's base value). For a negative-
461 // number rule, the result is the negative of its substitution's
462 // result. For a fraction rule, it's the sum of its two
463 // substitution results. For a rule in a fraction rule set,
464 // it's the numerator substitution's result divided by
465 // the rule's base value. Results from same-value substitutions
466 // propagate back upard, and null substitutions don't affect
468 tempResult
= composeRuleValue(tempResult
, baseValue
);
469 result
.setDouble(tempResult
);
471 // if the parse was UNsuccessful, return 0
479 NFSubstitution::isNullSubstitution() const {
484 * Returns true if this is a modulus substitution. (We didn't do this
485 * with instanceof partially because it causes source files to
486 * proliferate and partially because we have to port this to C++.)
487 * @return true if this object is an instance of ModulusSubstitution
490 NFSubstitution::isModulusSubstitution() const {
494 //===================================================================
495 // SameValueSubstitution
496 //===================================================================
499 * A substitution that passes the value passed to it through unchanged.
500 * Represented by == in rule descriptions.
502 SameValueSubstitution::SameValueSubstitution(int32_t _pos
,
503 const NFRuleSet
* _ruleSet
,
504 const RuleBasedNumberFormat
* formatter
,
505 const UnicodeString
& description
,
507 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
)
509 if (description
== gEqualsEquals
) {
510 // throw new IllegalArgumentException("== is not a legal token");
511 status
= U_PARSE_ERROR
;
515 const char SameValueSubstitution::fgClassID
= 0;
518 SameValueSubstitution::getDynamicClassID() const {
519 return getStaticClassID();
523 //===================================================================
524 // MultiplierSubstitution
525 //===================================================================
527 const char MultiplierSubstitution::fgClassID
= 0;
530 MultiplierSubstitution::getDynamicClassID() const {
531 return getStaticClassID();
534 UBool
MultiplierSubstitution::operator==(const NFSubstitution
& rhs
) const
536 return NFSubstitution::operator==(rhs
) &&
537 divisor
== ((const MultiplierSubstitution
*)&rhs
)->divisor
;
541 //===================================================================
542 // ModulusSubstitution
543 //===================================================================
546 * A substitution that divides the number being formatted by the its rule's
547 * divisor and formats the remainder. Represented by ">>" in a
550 ModulusSubstitution::ModulusSubstitution(int32_t _pos
,
552 const NFRule
* predecessor
,
553 const NFRuleSet
* _ruleSet
,
554 const RuleBasedNumberFormat
* formatter
,
555 const UnicodeString
& description
,
557 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
)
561 ldivisor
= util64_fromDouble(_divisor
);
563 // the owning rule's divisor controls the behavior of this
564 // substitution: rather than keeping a backpointer to the rule,
565 // we keep a copy of the divisor
568 status
= U_PARSE_ERROR
;
571 if (description
== gGreaterGreaterGreaterThan
) {
572 // the >>> token doesn't alter how this substituion calculates the
573 // values it uses for formatting and parsing, but it changes
574 // what's done with that value after it's obtained: >>> short-
575 // circuits the rule-search process and goes straight to the
576 // specified rule to format the substitution value
577 ruleToUse
= predecessor
;
581 const char ModulusSubstitution::fgClassID
= 0;
584 ModulusSubstitution::getDynamicClassID() const {
585 return getStaticClassID();
588 UBool
ModulusSubstitution::operator==(const NFSubstitution
& rhs
) const
590 return NFSubstitution::operator==(rhs
) &&
591 divisor
== ((const ModulusSubstitution
*)&rhs
)->divisor
&&
592 ruleToUse
== ((const ModulusSubstitution
*)&rhs
)->ruleToUse
;
595 //-----------------------------------------------------------------------
597 //-----------------------------------------------------------------------
601 * If this is a >>> substitution, use ruleToUse to fill in
602 * the substitution. Otherwise, just use the superclass function.
603 * @param number The number being formatted
604 * @toInsertInto The string to insert the result of this substitution
606 * @param pos The position of the rule text in toInsertInto
609 ModulusSubstitution::doSubstitution(int64_t number
, UnicodeString
& toInsertInto
, int32_t _pos
) const
611 // if this isn't a >>> substitution, just use the inherited version
612 // of this function (which uses either a rule set or a DecimalFormat
613 // to format its substitution value)
614 if (ruleToUse
== NULL
) {
615 NFSubstitution::doSubstitution(number
, toInsertInto
, _pos
);
617 // a >>> substitution goes straight to a particular rule to
618 // format the substitution value
620 int64_t numberToFormat
= transformNumber(number
);
621 ruleToUse
->doFormat(numberToFormat
, toInsertInto
, _pos
+ getPos());
626 * If this is a >>> substitution, use ruleToUse to fill in
627 * the substitution. Otherwise, just use the superclass function.
628 * @param number The number being formatted
629 * @toInsertInto The string to insert the result of this substitution
631 * @param pos The position of the rule text in toInsertInto
634 ModulusSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t _pos
) const
636 // if this isn't a >>> substitution, just use the inherited version
637 // of this function (which uses either a rule set or a DecimalFormat
638 // to format its substitution value)
639 if (ruleToUse
== NULL
) {
640 NFSubstitution::doSubstitution(number
, toInsertInto
, _pos
);
642 // a >>> substitution goes straight to a particular rule to
643 // format the substitution value
645 double numberToFormat
= transformNumber(number
);
647 ruleToUse
->doFormat(numberToFormat
, toInsertInto
, _pos
+ getPos());
651 //-----------------------------------------------------------------------
653 //-----------------------------------------------------------------------
656 * If this is a >>> substitution, match only against ruleToUse.
657 * Otherwise, use the superclass function.
658 * @param text The string to parse
659 * @param parsePosition Ignored on entry, updated on exit to point to
660 * the first unmatched character.
661 * @param baseValue The partial parse result prior to calling this
665 ModulusSubstitution::doParse(const UnicodeString
& text
,
666 ParsePosition
& parsePosition
,
670 Formattable
& result
) const
672 // if this isn't a >>> substitution, we can just use the
673 // inherited parse() routine to do the parsing
674 if (ruleToUse
== NULL
) {
675 return NFSubstitution::doParse(text
, parsePosition
, baseValue
, upperBound
, lenientParse
, result
);
677 // but if it IS a >>> substitution, we have to do it here: we
678 // use the specific rule's doParse() method, and then we have to
679 // do some of the other work of NFRuleSet.parse()
681 ruleToUse
->doParse(text
, parsePosition
, FALSE
, upperBound
, result
);
683 if (parsePosition
.getIndex() != 0) {
684 UErrorCode status
= U_ZERO_ERROR
;
685 double tempResult
= result
.getDouble(status
);
686 tempResult
= composeRuleValue(tempResult
, baseValue
);
687 result
.setDouble(tempResult
);
695 //===================================================================
696 // IntegralPartSubstitution
697 //===================================================================
699 const char IntegralPartSubstitution::fgClassID
= 0;
702 IntegralPartSubstitution::getDynamicClassID() const {
703 return getStaticClassID();
707 //===================================================================
708 // FractionalPartSubstitution
709 //===================================================================
713 * Constructs a FractionalPartSubstitution. This object keeps a flag
714 * telling whether it should format by digits or not. In addition,
715 * it marks the rule set it calls (if any) as a fraction rule set.
717 FractionalPartSubstitution::FractionalPartSubstitution(int32_t _pos
,
718 const NFRuleSet
* _ruleSet
,
719 const RuleBasedNumberFormat
* formatter
,
720 const UnicodeString
& description
,
722 : NFSubstitution(_pos
, _ruleSet
, formatter
, description
, status
)
727 // akk, ruleSet can change in superclass constructor
728 if (description
== gGreaterGreaterThan
||
729 description
== gGreaterGreaterGreaterThan
||
730 _ruleSet
== getRuleSet()) {
732 if (description
== gGreaterGreaterGreaterThan
) {
737 ((NFRuleSet
*)getRuleSet())->makeIntoFractionRuleSet();
741 //-----------------------------------------------------------------------
743 //-----------------------------------------------------------------------
746 * If in "by digits" mode, fills in the substitution one decimal digit
747 * at a time using the rule set containing this substitution.
748 * Otherwise, uses the superclass function.
749 * @param number The number being formatted
750 * @param toInsertInto The string to insert the result of formatting
751 * the substitution into
752 * @param pos The position of the owning rule's rule text in
756 FractionalPartSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t _pos
) const
758 // if we're not in "byDigits" mode, just use the inherited
759 // doSubstitution() routine
761 NFSubstitution::doSubstitution(number
, toInsertInto
, _pos
);
763 // if we're in "byDigits" mode, transform the value into an integer
764 // by moving the decimal point eight places to the right and
765 // pulling digits off the right one at a time, formatting each digit
766 // as an integer using this substitution's owning rule set
767 // (this is slower, but more accurate, than doing it from the
770 // int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits));
771 // // this flag keeps us from formatting trailing zeros. It starts
772 // // out false because we're pulling from the right, and switches
773 // // to true the first time we encounter a non-zero digit
774 // UBool doZeros = FALSE;
775 // for (int32_t i = 0; i < kMaxDecimalDigits; i++) {
776 // int64_t digit = numberToFormat % 10;
777 // if (digit != 0 || doZeros) {
778 // if (doZeros && useSpaces) {
779 // toInsertInto.insert(_pos + getPos(), gSpace);
782 // getRuleSet()->format(digit, toInsertInto, _pos + getPos());
784 // numberToFormat /= 10;
788 dl
.set(number
, 20, TRUE
);
791 while (dl
.fCount
> (dl
.fDecimalAt
<= 0 ? 0 : dl
.fDecimalAt
)) {
792 if (pad
&& useSpaces
) {
793 toInsertInto
.insert(_pos
+ getPos(), gSpace
);
797 getRuleSet()->format((int64_t)(dl
.fDigits
[--dl
.fCount
] - '0'), toInsertInto
, _pos
+ getPos());
799 while (dl
.fDecimalAt
< 0) {
800 if (pad
&& useSpaces
) {
801 toInsertInto
.insert(_pos
+ getPos(), gSpace
);
805 getRuleSet()->format((int64_t)0, toInsertInto
, _pos
+ getPos());
810 // hack around lack of precision in digitlist. if we would end up with
811 // "foo point" make sure we add a " zero" to the end.
812 getRuleSet()->format((int64_t)0, toInsertInto
, _pos
+ getPos());
817 //-----------------------------------------------------------------------
819 //-----------------------------------------------------------------------
822 * If in "by digits" mode, parses the string as if it were a string
823 * of individual digits; otherwise, uses the superclass function.
824 * @param text The string to parse
825 * @param parsePosition Ignored on entry, but updated on exit to point
826 * to the first unmatched character
827 * @param baseValue The partial parse result prior to entering this
829 * @param upperBound Only consider rules with base values lower than
830 * this when filling in the substitution
831 * @param lenientParse If true, try matching the text as numerals if
832 * matching as words doesn't work
833 * @return If the match was successful, the current partial parse
834 * result; otherwise new Long(0). The result is either a Long or
839 FractionalPartSubstitution::doParse(const UnicodeString
& text
,
840 ParsePosition
& parsePosition
,
842 double /*upperBound*/,
844 Formattable
& resVal
) const
846 // if we're not in byDigits mode, we can just use the inherited
849 return NFSubstitution::doParse(text
, parsePosition
, baseValue
, 0, lenientParse
, resVal
);
851 // if we ARE in byDigits mode, parse the text one digit at a time
852 // using this substitution's owning rule set (we do this by setting
853 // upperBound to 10 when calling doParse() ) until we reach
856 UnicodeString
workText(text
);
857 ParsePosition
workPos(1);
863 NumberFormat
* fmt
= NULL
;
864 while (workText
.length() > 0 && workPos
.getIndex() != 0) {
867 getRuleSet()->parse(workText
, workPos
, 10, temp
);
868 UErrorCode status
= U_ZERO_ERROR
;
869 digit
= temp
.getLong(status
);
870 // digit = temp.getType() == Formattable::kLong ?
872 // (int32_t)temp.getDouble();
874 if (lenientParse
&& workPos
.getIndex() == 0) {
876 status
= U_ZERO_ERROR
;
877 fmt
= NumberFormat::createInstance(status
);
878 if (U_FAILURE(status
)) {
884 fmt
->parse(workText
, temp
, workPos
);
885 digit
= temp
.getLong(status
);
889 if (workPos
.getIndex() != 0) {
890 dl
.append((char)('0' + digit
));
891 // result += digit * p10;
893 parsePosition
.setIndex(parsePosition
.getIndex() + workPos
.getIndex());
894 workText
.removeBetween(0, workPos
.getIndex());
895 while (workText
.length() > 0 && workText
.charAt(0) == gSpace
) {
896 workText
.removeBetween(0, 1);
897 parsePosition
.setIndex(parsePosition
.getIndex() + 1);
903 result
= dl
.fCount
== 0 ? 0 : dl
.getDouble();
904 result
= composeRuleValue(result
, baseValue
);
905 resVal
.setDouble(result
);
911 FractionalPartSubstitution::operator==(const NFSubstitution
& rhs
) const
913 return NFSubstitution::operator==(rhs
) &&
914 ((const FractionalPartSubstitution
*)&rhs
)->byDigits
== byDigits
;
917 const char FractionalPartSubstitution::fgClassID
= 0;
920 FractionalPartSubstitution::getDynamicClassID() const {
921 return getStaticClassID();
925 //===================================================================
926 // AbsoluteValueSubstitution
927 //===================================================================
929 const char AbsoluteValueSubstitution::fgClassID
= 0;
932 AbsoluteValueSubstitution::getDynamicClassID() const {
933 return getStaticClassID();
936 //===================================================================
937 // NumeratorSubstitution
938 //===================================================================
941 NumeratorSubstitution::doSubstitution(double number
, UnicodeString
& toInsertInto
, int32_t apos
) const {
942 // perform a transformation on the number being formatted that
943 // is dependent on the type of substitution this is
945 double numberToFormat
= transformNumber(number
);
946 int64_t longNF
= util64_fromDouble(numberToFormat
);
948 const NFRuleSet
* aruleSet
= getRuleSet();
949 if (withZeros
&& aruleSet
!= NULL
) {
950 // if there are leading zeros in the decimal expansion then emit them
952 int32_t len
= toInsertInto
.length();
953 while ((nf
*= 10) < denominator
) {
954 toInsertInto
.insert(apos
+ getPos(), gSpace
);
955 aruleSet
->format((int64_t)0, toInsertInto
, apos
+ getPos());
957 apos
+= toInsertInto
.length() - len
;
960 // if the result is an integer, from here on out we work in integer
961 // space (saving time and memory and preserving accuracy)
962 if (numberToFormat
== longNF
&& aruleSet
!= NULL
) {
963 aruleSet
->format(longNF
, toInsertInto
, apos
+ getPos());
965 // if the result isn't an integer, then call either our rule set's
966 // format() method or our DecimalFormat's format() method to
969 if (aruleSet
!= NULL
) {
970 aruleSet
->format(numberToFormat
, toInsertInto
, apos
+ getPos());
972 UErrorCode status
= U_ZERO_ERROR
;
974 getNumberFormat()->format(numberToFormat
, temp
, status
);
975 toInsertInto
.insert(apos
+ getPos(), temp
);
981 NumeratorSubstitution::doParse(const UnicodeString
& text
,
982 ParsePosition
& parsePosition
,
985 UBool
/*lenientParse*/,
986 Formattable
& result
) const
988 // we don't have to do anything special to do the parsing here,
989 // but we have to turn lenient parsing off-- if we leave it on,
990 // it SERIOUSLY messes up the algorithm
992 // if withZeros is true, we need to count the zeros
993 // and use that to adjust the parse result
994 UErrorCode status
= U_ZERO_ERROR
;
995 int32_t zeroCount
= 0;
996 UnicodeString
workText(text
);
999 ParsePosition
workPos(1);
1002 while (workText
.length() > 0 && workPos
.getIndex() != 0) {
1003 workPos
.setIndex(0);
1004 getRuleSet()->parse(workText
, workPos
, 1, temp
); // parse zero or nothing at all
1005 if (workPos
.getIndex() == 0) {
1006 // we failed, either there were no more zeros, or the number was formatted with digits
1007 // either way, we're done
1012 parsePosition
.setIndex(parsePosition
.getIndex() + workPos
.getIndex());
1013 workText
.remove(0, workPos
.getIndex());
1014 while (workText
.length() > 0 && workText
.charAt(0) == gSpace
) {
1015 workText
.remove(0, 1);
1016 parsePosition
.setIndex(parsePosition
.getIndex() + 1);
1021 workText
.remove(0, (int32_t)parsePosition
.getIndex());
1022 parsePosition
.setIndex(0);
1025 // we've parsed off the zeros, now let's parse the rest from our current position
1026 NFSubstitution::doParse(workText
, parsePosition
, withZeros
? 1 : baseValue
, upperBound
, FALSE
, result
);
1029 // any base value will do in this case. is there a way to
1030 // force this to not bother trying all the base values?
1032 // compute the 'effective' base and prescale the value down
1033 int64_t n
= result
.getLong(status
); // force conversion!
1040 // now add the zeros
1041 while (zeroCount
> 0) {
1045 // d is now our true denominator
1046 result
.setDouble((double)n
/(double)d
);
1053 NumeratorSubstitution::operator==(const NFSubstitution
& rhs
) const
1055 return NFSubstitution::operator==(rhs
) &&
1056 denominator
== ((const NumeratorSubstitution
*)&rhs
)->denominator
;
1059 const char NumeratorSubstitution::fgClassID
= 0;
1062 NumeratorSubstitution::getDynamicClassID() const {
1063 return getStaticClassID();
1066 const UChar
NumeratorSubstitution::LTLT
[] = { 0x003c, 0x003c };
1068 //===================================================================
1070 //===================================================================
1072 const char NullSubstitution::fgClassID
= 0;
1075 NullSubstitution::getDynamicClassID() const {
1076 return getStaticClassID();