]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/nfsubs.cpp
ICU-6.2.9.tar.gz
[apple/icu.git] / icuSources / i18n / nfsubs.cpp
CommitLineData
b75a7d8f
A
1/*
2******************************************************************************
374ca955 3* Copyright (C) 1997-2004, International Business Machines
b75a7d8f
A
4* Corporation and others. All Rights Reserved.
5******************************************************************************
6* file name: nfsubs.cpp
7* encoding: US-ASCII
8* tab size: 8 (not used)
9* indentation:4
10*
11* Modification history
12* Date Name Comments
13* 10/11/2001 Doug Ported from ICU4J
14*/
15
16#include "nfsubs.h"
374ca955
A
17#include "digitlst.h"
18
19#include <stdio.h>
b75a7d8f
A
20
21#if U_HAVE_RBNF
22
23static const UChar gLessThan = 0x003c;
24static const UChar gEquals = 0x003d;
25static const UChar gGreaterThan = 0x003e;
26static const UChar gPercent = 0x0025;
27static const UChar gPound = 0x0023;
28static const UChar gZero = 0x0030;
29static const UChar gSpace = 0x0020;
30
31static const UChar gEqualsEquals[] =
32{
33 0x3D, 0x3D, 0
34}; /* "==" */
35static const UChar gGreaterGreaterGreaterThan[] =
36{
37 0x3E, 0x3E, 0x3E, 0
38}; /* ">>>" */
39static const UChar gGreaterGreaterThan[] =
40{
41 0x3E, 0x3E, 0
42}; /* ">>" */
43
44NFSubstitution*
45NFSubstitution::makeSubstitution(int32_t pos,
46 const NFRule* rule,
47 const NFRule* predecessor,
48 const NFRuleSet* ruleSet,
49 const RuleBasedNumberFormat* formatter,
50 const UnicodeString& description,
51 UErrorCode& status)
52{
53 // if the description is empty, return a NullSubstitution
54 if (description.length() == 0) {
55 return new NullSubstitution(pos, ruleSet, formatter, description, status);
56 }
57
58 switch (description.charAt(0)) {
59 // if the description begins with '<'...
60 case gLessThan:
61 // throw an exception if the rule is a negative number
62 // rule
63 if (rule->getBaseValue() == NFRule::kNegativeNumberRule) {
64 // throw new IllegalArgumentException("<< not allowed in negative-number rule");
65 status = U_PARSE_ERROR;
66 return NULL;
67 }
68
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);
75 }
76
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);
82 }
83
84 // otherwise, return a MultiplierSubstitution
85 else {
86 return new MultiplierSubstitution(pos, rule->getDivisor(), ruleSet,
87 formatter, description, status);
88 }
89
90 // if the description begins with '>'...
91 case gGreaterThan:
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);
96 }
97
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);
104 }
105
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;
111 return NULL;
112 }
113
114 // otherwise, return a ModulusSubstitution
115 else {
116 return new ModulusSubstitution(pos, rule->getDivisor(), predecessor,
117 ruleSet, formatter, description, status);
118 }
119
120 // if the description begins with '=', always return a
121 // SameValueSubstitution
122 case gEquals:
123 return new SameValueSubstitution(pos, ruleSet, formatter, description, status);
124
125 // and if it's anything else, throw an exception
126 default:
127 // throw new IllegalArgumentException("Illegal substitution character");
128 status = U_PARSE_ERROR;
129 }
130 return NULL;
131}
132
133NFSubstitution::NFSubstitution(int32_t _pos,
134 const NFRuleSet* _ruleSet,
135 const RuleBasedNumberFormat* formatter,
136 const UnicodeString& description,
137 UErrorCode& status)
138 : pos(_pos), ruleSet(NULL), numberFormat(NULL)
139{
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))
147 {
148 workingDescription.remove(description.length() - 1, 1);
149 workingDescription.remove(0, 1);
150 }
151 else if (description.length() != 0) {
152 // throw new IllegalArgumentException("Illegal substitution syntax");
153 status = U_PARSE_ERROR;
154 return;
155 }
156
157 // if the description was just two paired token characters
158 // (i.e., "<<" or ">>"), it uses the rule set it belongs to to
159 // format its result
160 if (workingDescription.length() == 0) {
161 this->ruleSet = _ruleSet;
162 }
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
165 // names rule set
166 else if (workingDescription.charAt(0) == gPercent) {
167 this->ruleSet = formatter->findRuleSet(workingDescription, status);
168 }
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) {
374ca955
A
174 DecimalFormatSymbols* sym = formatter->getDecimalFormatSymbols();
175 if (!sym) {
176 status = U_MISSING_RESOURCE_ERROR;
177 return;
178 }
b75a7d8f
A
179 this->numberFormat = new DecimalFormat(workingDescription, *sym, status);
180 /* test for NULL */
181 if (this->numberFormat == 0) {
182 status = U_MEMORY_ALLOCATION_ERROR;
183 return;
184 }
374ca955
A
185 if (U_FAILURE(status)) {
186 delete (DecimalFormat*)this->numberFormat;
187 this->numberFormat = NULL;
188 return;
189 }
b75a7d8f
A
190 // this->numberFormat->setDecimalFormatSymbols(formatter->getDecimalFormatSymbols());
191 }
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;
202 }
203 // and of the description is none of these things, it's a syntax error
204 else {
205 // throw new IllegalArgumentException("Illegal substitution syntax");
206 status = U_PARSE_ERROR;
207 }
208}
209
210NFSubstitution::~NFSubstitution()
211{
212 // cast away const
213 delete (NumberFormat*)numberFormat; numberFormat = NULL;
214}
215
216/**
217 * Set's the substitution's divisor. Used by NFRule.setBaseValue().
218 * A no-op for all substitutions except multiplier and modulus
219 * substitutions.
220 * @param radix The radix of the divisor
221 * @param exponent The exponent of the divisor
222 */
223void
374ca955 224NFSubstitution::setDivisor(int32_t /*radix*/, int32_t /*exponent*/, UErrorCode& /*status*/) {
b75a7d8f
A
225 // a no-op for all substitutions except multiplier and modulus substitutions
226}
227
228
229//-----------------------------------------------------------------------
230// boilerplate
231//-----------------------------------------------------------------------
232
233const char NFSubstitution::fgClassID = 0;
234
235UClassID
236NFSubstitution::getDynamicClassID() const {
237 return getStaticClassID();
238}
239
240 /**
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
244 */
245UBool
246NFSubstitution::operator==(const NFSubstitution& rhs) const
247{
248 // compare class and all of the fields all substitutions have
249 // in common
250 // this should be called by subclasses before their own equality tests
251 return getDynamicClassID() == rhs.getDynamicClassID()
252 && pos == rhs.pos
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));
258}
259
260 /**
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.
265 */
266void
267NFSubstitution::toString(UnicodeString& text) const
268{
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
273 text.remove();
274 text.append(tokenChar());
275
276 UnicodeString temp;
277 if (ruleSet != NULL) {
278 ruleSet->getName(temp);
279 } else {
280 numberFormat->toPattern(temp);
281 }
282 text.append(temp);
283 text.append(tokenChar());
284}
285
286//-----------------------------------------------------------------------
287// formatting
288//-----------------------------------------------------------------------
289
290/**
291 * Performs a mathematical operation on the number, formats it using
292 * either ruleSet or decimalFormat, and inserts the result into
293 * toInsertInto.
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)
299 */
300void
301NFSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos) const
302{
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 {
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);
316 }
317
318 UnicodeString temp;
319 numberFormat->format(numberToFormat, temp);
320 toInsertInto.insert(_pos + this->pos, temp);
321 }
322}
323
324/**
325 * Performs a mathematical operation on the number, formats it using
326 * either ruleSet or decimalFormat, and inserts the result into
327 * toInsertInto.
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)
333 */
334void
335NFSubstitution::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);
339
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);
344
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
347 // format the result
348 } else {
349 if (ruleSet != NULL) {
350 ruleSet->format(numberToFormat, toInsertInto, _pos + this->pos);
351 } else {
352 UnicodeString temp;
353 numberFormat->format(numberToFormat, temp);
354 toInsertInto.insert(_pos + this->pos, temp);
355 }
356 }
357}
358
359
360 //-----------------------------------------------------------------------
361 // parsing
362 //-----------------------------------------------------------------------
363
364#ifdef RBNF_DEBUG
365#include <stdio.h>
366#endif
367
368/**
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
395 * is left unchanged.
396 */
397UBool
398NFSubstitution::doParse(const UnicodeString& text,
399 ParsePosition& parsePosition,
400 double baseValue,
401 double upperBound,
402 UBool lenientParse,
403 Formattable& result) const
404{
405#ifdef RBNF_DEBUG
406 fprintf(stderr, "<nfsubs> %x bv: %g ub: %g\n", this, baseValue, upperBound);
407#endif
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);
415
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);
429 }
430 delete fmt;
431 }
432
433 // ...or use our DecimalFormat to parse the text
434 } else {
435 numberFormat->parse(text, result, parsePosition);
436 }
437
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) {
374ca955
A
443 UErrorCode status = U_ZERO_ERROR;
444 double tempResult = result.getDouble(status);
b75a7d8f
A
445
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
467 // the result.
468 tempResult = composeRuleValue(tempResult, baseValue);
469 result.setDouble(tempResult);
470 return TRUE;
471 // if the parse was UNsuccessful, return 0
472 } else {
473 result.setLong(0);
474 return FALSE;
475 }
476}
477
478UBool
479NFSubstitution::isNullSubstitution() const {
480 return FALSE;
481}
482
483 /**
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
488 */
489UBool
490NFSubstitution::isModulusSubstitution() const {
491 return FALSE;
492}
493
494//===================================================================
495// SameValueSubstitution
496//===================================================================
497
498/**
499 * A substitution that passes the value passed to it through unchanged.
500 * Represented by == in rule descriptions.
501 */
502SameValueSubstitution::SameValueSubstitution(int32_t _pos,
503 const NFRuleSet* _ruleSet,
504 const RuleBasedNumberFormat* formatter,
505 const UnicodeString& description,
506 UErrorCode& status)
507: NFSubstitution(_pos, _ruleSet, formatter, description, status)
508{
509 if (description == gEqualsEquals) {
510 // throw new IllegalArgumentException("== is not a legal token");
511 status = U_PARSE_ERROR;
512 }
513}
514
515const char SameValueSubstitution::fgClassID = 0;
516
517UClassID
518SameValueSubstitution::getDynamicClassID() const {
519 return getStaticClassID();
520}
521
522
523//===================================================================
524// MultiplierSubstitution
525//===================================================================
526
527const char MultiplierSubstitution::fgClassID = 0;
528
529UClassID
530MultiplierSubstitution::getDynamicClassID() const {
531 return getStaticClassID();
532}
533
534UBool MultiplierSubstitution::operator==(const NFSubstitution& rhs) const
535{
536 return NFSubstitution::operator==(rhs) &&
537 divisor == ((const MultiplierSubstitution*)&rhs)->divisor;
538}
539
540
541//===================================================================
542// ModulusSubstitution
543//===================================================================
544
545/**
546 * A substitution that divides the number being formatted by the its rule's
547 * divisor and formats the remainder. Represented by "&gt;&gt;" in a
548 * regular rule.
549 */
550ModulusSubstitution::ModulusSubstitution(int32_t _pos,
551 double _divisor,
552 const NFRule* predecessor,
553 const NFRuleSet* _ruleSet,
554 const RuleBasedNumberFormat* formatter,
555 const UnicodeString& description,
556 UErrorCode& status)
557 : NFSubstitution(_pos, _ruleSet, formatter, description, status)
558 , divisor(_divisor)
559 , ruleToUse(NULL)
560{
561 ldivisor = util64_fromDouble(_divisor);
562
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
566
374ca955
A
567 if (ldivisor == 0) {
568 status = U_PARSE_ERROR;
569 }
570
b75a7d8f
A
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;
578 }
579}
580
581const char ModulusSubstitution::fgClassID = 0;
582
583UClassID
584ModulusSubstitution::getDynamicClassID() const {
585 return getStaticClassID();
586}
587
588UBool ModulusSubstitution::operator==(const NFSubstitution& rhs) const
589{
590 return NFSubstitution::operator==(rhs) &&
591 divisor == ((const ModulusSubstitution*)&rhs)->divisor &&
592 ruleToUse == ((const ModulusSubstitution*)&rhs)->ruleToUse;
593}
594
595//-----------------------------------------------------------------------
596// formatting
597//-----------------------------------------------------------------------
598
599
600/**
601 * If this is a &gt;&gt;&gt; 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
605 * into
606 * @param pos The position of the rule text in toInsertInto
607 */
608void
609ModulusSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos) const
610{
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);
616
617 // a >>> substitution goes straight to a particular rule to
618 // format the substitution value
619 } else {
620 int64_t numberToFormat = transformNumber(number);
621 ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos());
622 }
623}
624
625/**
626* If this is a &gt;&gt;&gt; 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
630* into
631* @param pos The position of the rule text in toInsertInto
632*/
633void
634ModulusSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) const
635{
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);
641
642 // a >>> substitution goes straight to a particular rule to
643 // format the substitution value
644 } else {
645 double numberToFormat = transformNumber(number);
646
647 ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos());
648 }
649}
650
651//-----------------------------------------------------------------------
652// parsing
653//-----------------------------------------------------------------------
654
655/**
656 * If this is a &gt;&gt;&gt; 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
662 * routine.
663 */
664UBool
665ModulusSubstitution::doParse(const UnicodeString& text,
666 ParsePosition& parsePosition,
667 double baseValue,
668 double upperBound,
669 UBool lenientParse,
670 Formattable& result) const
671{
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);
676
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()
680 } else {
681 ruleToUse->doParse(text, parsePosition, FALSE, upperBound, result);
682
683 if (parsePosition.getIndex() != 0) {
684 double tempResult = result.getDouble();
685 tempResult = composeRuleValue(tempResult, baseValue);
686 result.setDouble(tempResult);
687 }
688
689 return TRUE;
690 }
691}
692
693
694//===================================================================
695// IntegralPartSubstitution
696//===================================================================
697
698const char IntegralPartSubstitution::fgClassID = 0;
699
700UClassID
701IntegralPartSubstitution::getDynamicClassID() const {
702 return getStaticClassID();
703}
704
705
706//===================================================================
707// FractionalPartSubstitution
708//===================================================================
709
710
711 /**
712 * Constructs a FractionalPartSubstitution. This object keeps a flag
713 * telling whether it should format by digits or not. In addition,
714 * it marks the rule set it calls (if any) as a fraction rule set.
715 */
716FractionalPartSubstitution::FractionalPartSubstitution(int32_t _pos,
717 const NFRuleSet* _ruleSet,
718 const RuleBasedNumberFormat* formatter,
719 const UnicodeString& description,
720 UErrorCode& status)
721 : NFSubstitution(_pos, _ruleSet, formatter, description, status)
722 , byDigits(FALSE)
723 , useSpaces(TRUE)
724
725{
726 // akk, ruleSet can change in superclass constructor
727 if (description == gGreaterGreaterThan ||
728 description == gGreaterGreaterGreaterThan ||
729 _ruleSet == getRuleSet()) {
730 byDigits = TRUE;
731 if (description == gGreaterGreaterGreaterThan) {
732 useSpaces = FALSE;
733 }
734 } else {
735 // cast away const
736 ((NFRuleSet*)getRuleSet())->makeIntoFractionRuleSet();
737 }
738}
739
740//-----------------------------------------------------------------------
741// formatting
742//-----------------------------------------------------------------------
743
744/**
745 * If in "by digits" mode, fills in the substitution one decimal digit
746 * at a time using the rule set containing this substitution.
747 * Otherwise, uses the superclass function.
748 * @param number The number being formatted
749 * @param toInsertInto The string to insert the result of formatting
750 * the substitution into
751 * @param pos The position of the owning rule's rule text in
752 * toInsertInto
753 */
754void
755FractionalPartSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) const
756{
374ca955
A
757 // if we're not in "byDigits" mode, just use the inherited
758 // doSubstitution() routine
759 if (!byDigits) {
760 NFSubstitution::doSubstitution(number, toInsertInto, _pos);
761
762 // if we're in "byDigits" mode, transform the value into an integer
763 // by moving the decimal point eight places to the right and
764 // pulling digits off the right one at a time, formatting each digit
765 // as an integer using this substitution's owning rule set
766 // (this is slower, but more accurate, than doing it from the
767 // other end)
768 } else {
769 // int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits));
770 // // this flag keeps us from formatting trailing zeros. It starts
771 // // out false because we're pulling from the right, and switches
772 // // to true the first time we encounter a non-zero digit
773 // UBool doZeros = FALSE;
774 // for (int32_t i = 0; i < kMaxDecimalDigits; i++) {
775 // int64_t digit = numberToFormat % 10;
776 // if (digit != 0 || doZeros) {
777 // if (doZeros && useSpaces) {
778 // toInsertInto.insert(_pos + getPos(), gSpace);
779 // }
780 // doZeros = TRUE;
781 // getRuleSet()->format(digit, toInsertInto, _pos + getPos());
782 // }
783 // numberToFormat /= 10;
784 // }
785
786 DigitList dl;
787 dl.set(number, 20, TRUE);
788
789 UBool pad = FALSE;
790 while (dl.fCount > (dl.fDecimalAt <= 0 ? 0 : dl.fDecimalAt)) {
791 if (pad && useSpaces) {
792 toInsertInto.insert(_pos + getPos(), gSpace);
793 } else {
794 pad = TRUE;
795 }
796 getRuleSet()->format((int64_t)(dl.fDigits[--dl.fCount] - '0'), toInsertInto, _pos + getPos());
797 }
798 while (dl.fDecimalAt < 0) {
799 if (pad && useSpaces) {
800 toInsertInto.insert(_pos + getPos(), gSpace);
801 } else {
802 pad = TRUE;
803 }
804 getRuleSet()->format((int64_t)0, toInsertInto, _pos + getPos());
805 ++dl.fDecimalAt;
806 }
b75a7d8f 807
374ca955
A
808 if (!pad) {
809 // hack around lack of precision in digitlist. if we would end up with
810 // "foo point" make sure we add a " zero" to the end.
811 getRuleSet()->format((int64_t)0, toInsertInto, _pos + getPos());
b75a7d8f 812 }
374ca955 813 }
b75a7d8f
A
814}
815
816//-----------------------------------------------------------------------
817// parsing
818//-----------------------------------------------------------------------
819
820/**
821 * If in "by digits" mode, parses the string as if it were a string
822 * of individual digits; otherwise, uses the superclass function.
823 * @param text The string to parse
824 * @param parsePosition Ignored on entry, but updated on exit to point
825 * to the first unmatched character
826 * @param baseValue The partial parse result prior to entering this
827 * function
828 * @param upperBound Only consider rules with base values lower than
829 * this when filling in the substitution
830 * @param lenientParse If true, try matching the text as numerals if
831 * matching as words doesn't work
832 * @return If the match was successful, the current partial parse
833 * result; otherwise new Long(0). The result is either a Long or
834 * a Double.
835 */
836
837UBool
838FractionalPartSubstitution::doParse(const UnicodeString& text,
839 ParsePosition& parsePosition,
840 double baseValue,
841 double /*upperBound*/,
842 UBool lenientParse,
843 Formattable& resVal) const
844{
845 // if we're not in byDigits mode, we can just use the inherited
846 // doParse()
847 if (!byDigits) {
848 return NFSubstitution::doParse(text, parsePosition, baseValue, 0, lenientParse, resVal);
849
850 // if we ARE in byDigits mode, parse the text one digit at a time
851 // using this substitution's owning rule set (we do this by setting
852 // upperBound to 10 when calling doParse() ) until we reach
853 // nonmatching text
854 } else {
855 UnicodeString workText(text);
856 ParsePosition workPos(1);
857 double result = 0;
858 int32_t digit;
374ca955 859// double p10 = 0.1;
b75a7d8f 860
374ca955 861 DigitList dl;
b75a7d8f
A
862 NumberFormat* fmt = NULL;
863 while (workText.length() > 0 && workPos.getIndex() != 0) {
864 workPos.setIndex(0);
865 Formattable temp;
866 getRuleSet()->parse(workText, workPos, 10, temp);
374ca955
A
867 UErrorCode status = U_ZERO_ERROR;
868 digit = temp.getLong(status);
869// digit = temp.getType() == Formattable::kLong ?
870// temp.getLong() :
871// (int32_t)temp.getDouble();
b75a7d8f
A
872
873 if (lenientParse && workPos.getIndex() == 0) {
874 if (!fmt) {
374ca955 875 status = U_ZERO_ERROR;
b75a7d8f
A
876 fmt = NumberFormat::createInstance(status);
877 if (U_FAILURE(status)) {
878 delete fmt;
879 fmt = NULL;
880 }
881 }
882 if (fmt) {
883 fmt->parse(workText, temp, workPos);
884 digit = temp.getLong();
885 }
886 }
887
888 if (workPos.getIndex() != 0) {
374ca955
A
889 dl.append((char)('0' + digit));
890// result += digit * p10;
891// p10 /= 10;
b75a7d8f
A
892 parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex());
893 workText.removeBetween(0, workPos.getIndex());
894 while (workText.length() > 0 && workText.charAt(0) == gSpace) {
895 workText.removeBetween(0, 1);
896 parsePosition.setIndex(parsePosition.getIndex() + 1);
897 }
898 }
899 }
900 delete fmt;
374ca955 901 result = dl.fCount == 0 ? 0 : dl.getDouble();
b75a7d8f
A
902
903 result = composeRuleValue(result, baseValue);
904 resVal.setDouble(result);
905 return TRUE;
906 }
907}
908
909UBool
910FractionalPartSubstitution::operator==(const NFSubstitution& rhs) const
911{
912 return NFSubstitution::operator==(rhs) &&
913 ((const FractionalPartSubstitution*)&rhs)->byDigits == byDigits;
914}
915
916const char FractionalPartSubstitution::fgClassID = 0;
917
918UClassID
919FractionalPartSubstitution::getDynamicClassID() const {
920 return getStaticClassID();
921}
922
923
924//===================================================================
925// AbsoluteValueSubstitution
926//===================================================================
927
928const char AbsoluteValueSubstitution::fgClassID = 0;
929
930UClassID
931AbsoluteValueSubstitution::getDynamicClassID() const {
932 return getStaticClassID();
933}
934
935//===================================================================
936// NumeratorSubstitution
937//===================================================================
938
939UBool
940NumeratorSubstitution::operator==(const NFSubstitution& rhs) const
941{
942 return NFSubstitution::operator==(rhs) &&
943 denominator == ((const NumeratorSubstitution*)&rhs)->denominator;
944}
945
946const char NumeratorSubstitution::fgClassID = 0;
947
948UClassID
949NumeratorSubstitution::getDynamicClassID() const {
950 return getStaticClassID();
951}
952
953//===================================================================
954// NullSubstitution
955//===================================================================
956
957const char NullSubstitution::fgClassID = 0;
958
959UClassID
960NullSubstitution::getDynamicClassID() const {
961 return getStaticClassID();
962}
963
964/* U_HAVE_RBNF */
965#endif
966