]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/nfsubs.cpp
ICU-3.13.tar.gz
[apple/icu.git] / icuSources / i18n / nfsubs.cpp
CommitLineData
b75a7d8f
A
1/*
2******************************************************************************
3* Copyright (C) 1997-2003, International Business Machines
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"
17
18#if U_HAVE_RBNF
19
20static const UChar gLessThan = 0x003c;
21static const UChar gEquals = 0x003d;
22static const UChar gGreaterThan = 0x003e;
23static const UChar gPercent = 0x0025;
24static const UChar gPound = 0x0023;
25static const UChar gZero = 0x0030;
26static const UChar gSpace = 0x0020;
27
28static const UChar gEqualsEquals[] =
29{
30 0x3D, 0x3D, 0
31}; /* "==" */
32static const UChar gGreaterGreaterGreaterThan[] =
33{
34 0x3E, 0x3E, 0x3E, 0
35}; /* ">>>" */
36static const UChar gGreaterGreaterThan[] =
37{
38 0x3E, 0x3E, 0
39}; /* ">>" */
40
41NFSubstitution*
42NFSubstitution::makeSubstitution(int32_t pos,
43 const NFRule* rule,
44 const NFRule* predecessor,
45 const NFRuleSet* ruleSet,
46 const RuleBasedNumberFormat* formatter,
47 const UnicodeString& description,
48 UErrorCode& status)
49{
50 // if the description is empty, return a NullSubstitution
51 if (description.length() == 0) {
52 return new NullSubstitution(pos, ruleSet, formatter, description, status);
53 }
54
55 switch (description.charAt(0)) {
56 // if the description begins with '<'...
57 case gLessThan:
58 // throw an exception if the rule is a negative number
59 // rule
60 if (rule->getBaseValue() == NFRule::kNegativeNumberRule) {
61 // throw new IllegalArgumentException("<< not allowed in negative-number rule");
62 status = U_PARSE_ERROR;
63 return NULL;
64 }
65
66 // if the rule is a fraction rule, return an
67 // IntegralPartSubstitution
68 else if (rule->getBaseValue() == NFRule::kImproperFractionRule
69 || rule->getBaseValue() == NFRule::kProperFractionRule
70 || rule->getBaseValue() == NFRule::kMasterRule) {
71 return new IntegralPartSubstitution(pos, ruleSet, formatter, description, status);
72 }
73
74 // if the rule set containing the rule is a fraction
75 // rule set, return a NumeratorSubstitution
76 else if (ruleSet->isFractionRuleSet()) {
77 return new NumeratorSubstitution(pos, (double)rule->getBaseValue(),
78 formatter->getDefaultRuleSet(), formatter, description, status);
79 }
80
81 // otherwise, return a MultiplierSubstitution
82 else {
83 return new MultiplierSubstitution(pos, rule->getDivisor(), ruleSet,
84 formatter, description, status);
85 }
86
87 // if the description begins with '>'...
88 case gGreaterThan:
89 // if the rule is a negative-number rule, return
90 // an AbsoluteValueSubstitution
91 if (rule->getBaseValue() == NFRule::kNegativeNumberRule) {
92 return new AbsoluteValueSubstitution(pos, ruleSet, formatter, description, status);
93 }
94
95 // if the rule is a fraction rule, return a
96 // FractionalPartSubstitution
97 else if (rule->getBaseValue() == NFRule::kImproperFractionRule
98 || rule->getBaseValue() == NFRule::kProperFractionRule
99 || rule->getBaseValue() == NFRule::kMasterRule) {
100 return new FractionalPartSubstitution(pos, ruleSet, formatter, description, status);
101 }
102
103 // if the rule set owning the rule is a fraction rule set,
104 // throw an exception
105 else if (ruleSet->isFractionRuleSet()) {
106 // throw new IllegalArgumentException(">> not allowed in fraction rule set");
107 status = U_PARSE_ERROR;
108 return NULL;
109 }
110
111 // otherwise, return a ModulusSubstitution
112 else {
113 return new ModulusSubstitution(pos, rule->getDivisor(), predecessor,
114 ruleSet, formatter, description, status);
115 }
116
117 // if the description begins with '=', always return a
118 // SameValueSubstitution
119 case gEquals:
120 return new SameValueSubstitution(pos, ruleSet, formatter, description, status);
121
122 // and if it's anything else, throw an exception
123 default:
124 // throw new IllegalArgumentException("Illegal substitution character");
125 status = U_PARSE_ERROR;
126 }
127 return NULL;
128}
129
130NFSubstitution::NFSubstitution(int32_t _pos,
131 const NFRuleSet* _ruleSet,
132 const RuleBasedNumberFormat* formatter,
133 const UnicodeString& description,
134 UErrorCode& status)
135 : pos(_pos), ruleSet(NULL), numberFormat(NULL)
136{
137 // the description should begin and end with the same character.
138 // If it doesn't that's a syntax error. Otherwise,
139 // makeSubstitution() was the only thing that needed to know
140 // about these characters, so strip them off
141 UnicodeString workingDescription(description);
142 if (description.length() >= 2
143 && description.charAt(0) == description.charAt(description.length() - 1))
144 {
145 workingDescription.remove(description.length() - 1, 1);
146 workingDescription.remove(0, 1);
147 }
148 else if (description.length() != 0) {
149 // throw new IllegalArgumentException("Illegal substitution syntax");
150 status = U_PARSE_ERROR;
151 return;
152 }
153
154 // if the description was just two paired token characters
155 // (i.e., "<<" or ">>"), it uses the rule set it belongs to to
156 // format its result
157 if (workingDescription.length() == 0) {
158 this->ruleSet = _ruleSet;
159 }
160 // if the description contains a rule set name, that's the rule
161 // set we use to format the result: get a reference to the
162 // names rule set
163 else if (workingDescription.charAt(0) == gPercent) {
164 this->ruleSet = formatter->findRuleSet(workingDescription, status);
165 }
166 // if the description begins with 0 or #, treat it as a
167 // DecimalFormat pattern, and initialize a DecimalFormat with
168 // that pattern (then set it to use the DecimalFormatSymbols
169 // belonging to our formatter)
170 else if (workingDescription.charAt(0) == gPound || workingDescription.charAt(0) ==gZero) {
171 DecimalFormatSymbols* sym = formatter->getDecimalFormatSymbols();
172 if (!sym) {
173 status = U_MISSING_RESOURCE_ERROR;
174 return;
175 }
176 this->numberFormat = new DecimalFormat(workingDescription, *sym, status);
177 /* test for NULL */
178 if (this->numberFormat == 0) {
179 status = U_MEMORY_ALLOCATION_ERROR;
180 return;
181 }
182 if (U_FAILURE(status)) {
183 delete (DecimalFormat*)this->numberFormat;
184 this->numberFormat = NULL;
185 return;
186 }
187 // this->numberFormat->setDecimalFormatSymbols(formatter->getDecimalFormatSymbols());
188 }
189 // if the description is ">>>", this substitution bypasses the
190 // usual rule-search process and always uses the rule that precedes
191 // it in its own rule set's rule list (this is used for place-value
192 // notations: formats where you want to see a particular part of
193 // a number even when it's 0)
194 else if (workingDescription.charAt(0) == gGreaterThan) {
195 // this causes problems when >>> is used in a frationalPartSubstitution
196 // this->ruleSet = NULL;
197 this->ruleSet = _ruleSet;
198 this->numberFormat = NULL;
199 }
200 // and of the description is none of these things, it's a syntax error
201 else {
202 // throw new IllegalArgumentException("Illegal substitution syntax");
203 status = U_PARSE_ERROR;
204 }
205}
206
207NFSubstitution::~NFSubstitution()
208{
209 // cast away const
210 delete (NumberFormat*)numberFormat; numberFormat = NULL;
211}
212
213/**
214 * Set's the substitution's divisor. Used by NFRule.setBaseValue().
215 * A no-op for all substitutions except multiplier and modulus
216 * substitutions.
217 * @param radix The radix of the divisor
218 * @param exponent The exponent of the divisor
219 */
220void
221NFSubstitution::setDivisor(int32_t /*radix*/, int32_t /*exponent*/) {
222 // a no-op for all substitutions except multiplier and modulus substitutions
223}
224
225
226//-----------------------------------------------------------------------
227// boilerplate
228//-----------------------------------------------------------------------
229
230const char NFSubstitution::fgClassID = 0;
231
232UClassID
233NFSubstitution::getDynamicClassID() const {
234 return getStaticClassID();
235}
236
237 /**
238 * Compares two substitutions for equality
239 * @param The substitution to compare this one to
240 * @return true if the two substitutions are functionally equivalent
241 */
242UBool
243NFSubstitution::operator==(const NFSubstitution& rhs) const
244{
245 // compare class and all of the fields all substitutions have
246 // in common
247 // this should be called by subclasses before their own equality tests
248 return getDynamicClassID() == rhs.getDynamicClassID()
249 && pos == rhs.pos
250 && (ruleSet == NULL) == (rhs.ruleSet == NULL)
251 // && ruleSet == rhs.ruleSet causes circularity, other checks to make instead?
252 && (numberFormat == NULL
253 ? (rhs.numberFormat == NULL)
254 : (*numberFormat == *rhs.numberFormat));
255}
256
257 /**
258 * Returns a textual description of the substitution
259 * @return A textual description of the substitution. This might
260 * not be identical to the description it was created from, but
261 * it'll produce the same result.
262 */
263void
264NFSubstitution::toString(UnicodeString& text) const
265{
266 // use tokenChar() to get the character at the beginning and
267 // end of the substitutin token. In between them will go
268 // either the name of the rule set it uses, or the pattern of
269 // the DecimalFormat it uses
270 text.remove();
271 text.append(tokenChar());
272
273 UnicodeString temp;
274 if (ruleSet != NULL) {
275 ruleSet->getName(temp);
276 } else {
277 numberFormat->toPattern(temp);
278 }
279 text.append(temp);
280 text.append(tokenChar());
281}
282
283//-----------------------------------------------------------------------
284// formatting
285//-----------------------------------------------------------------------
286
287/**
288 * Performs a mathematical operation on the number, formats it using
289 * either ruleSet or decimalFormat, and inserts the result into
290 * toInsertInto.
291 * @param number The number being formatted.
292 * @param toInsertInto The string we insert the result into
293 * @param pos The position in toInsertInto where the owning rule's
294 * rule text begins (this value is added to this substitution's
295 * position to determine exactly where to insert the new text)
296 */
297void
298NFSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos) const
299{
300 if (ruleSet != NULL) {
301 // perform a transformation on the number that is dependent
302 // on the type of substitution this is, then just call its
303 // rule set's format() method to format the result
304 ruleSet->format(transformNumber(number), toInsertInto, _pos + this->pos);
305 } else {
306 // or perform the transformation on the number (preserving
307 // the result's fractional part if the formatter it set
308 // to show it), then use that formatter's format() method
309 // to format the result
310 double numberToFormat = transformNumber((double)number);
311 if (numberFormat->getMaximumFractionDigits() == 0) {
312 numberToFormat = uprv_floor(numberToFormat);
313 }
314
315 UnicodeString temp;
316 numberFormat->format(numberToFormat, temp);
317 toInsertInto.insert(_pos + this->pos, temp);
318 }
319}
320
321/**
322 * Performs a mathematical operation on the number, formats it using
323 * either ruleSet or decimalFormat, and inserts the result into
324 * toInsertInto.
325 * @param number The number being formatted.
326 * @param toInsertInto The string we insert the result into
327 * @param pos The position in toInsertInto where the owning rule's
328 * rule text begins (this value is added to this substitution's
329 * position to determine exactly where to insert the new text)
330 */
331void
332NFSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) const {
333 // perform a transformation on the number being formatted that
334 // is dependent on the type of substitution this is
335 double numberToFormat = transformNumber(number);
336
337 // if the result is an integer, from here on out we work in integer
338 // space (saving time and memory and preserving accuracy)
339 if (numberToFormat == uprv_floor(numberToFormat) && ruleSet != NULL) {
340 ruleSet->format(util64_fromDouble(numberToFormat), toInsertInto, _pos + this->pos);
341
342 // if the result isn't an integer, then call either our rule set's
343 // format() method or our DecimalFormat's format() method to
344 // format the result
345 } else {
346 if (ruleSet != NULL) {
347 ruleSet->format(numberToFormat, toInsertInto, _pos + this->pos);
348 } else {
349 UnicodeString temp;
350 numberFormat->format(numberToFormat, temp);
351 toInsertInto.insert(_pos + this->pos, temp);
352 }
353 }
354}
355
356
357 //-----------------------------------------------------------------------
358 // parsing
359 //-----------------------------------------------------------------------
360
361#ifdef RBNF_DEBUG
362#include <stdio.h>
363#endif
364
365/**
366 * Parses a string using the rule set or DecimalFormat belonging
367 * to this substitution. If there's a match, a mathematical
368 * operation (the inverse of the one used in formatting) is
369 * performed on the result of the parse and the value passed in
370 * and returned as the result. The parse position is updated to
371 * point to the first unmatched character in the string.
372 * @param text The string to parse
373 * @param parsePosition On entry, ignored, but assumed to be 0.
374 * On exit, this is updated to point to the first unmatched
375 * character (or 0 if the substitution didn't match)
376 * @param baseValue A partial parse result that should be
377 * combined with the result of this parse
378 * @param upperBound When searching the rule set for a rule
379 * matching the string passed in, only rules with base values
380 * lower than this are considered
381 * @param lenientParse If true and matching against rules fails,
382 * the substitution will also try matching the text against
383 * numerals using a default-costructed NumberFormat. If false,
384 * no extra work is done. (This value is false whenever the
385 * formatter isn't in lenient-parse mode, but is also false
386 * under some conditions even when the formatter _is_ in
387 * lenient-parse mode.)
388 * @return If there's a match, this is the result of composing
389 * baseValue with whatever was returned from matching the
390 * characters. This will be either a Long or a Double. If there's
391 * no match this is new Long(0) (not null), and parsePosition
392 * is left unchanged.
393 */
394UBool
395NFSubstitution::doParse(const UnicodeString& text,
396 ParsePosition& parsePosition,
397 double baseValue,
398 double upperBound,
399 UBool lenientParse,
400 Formattable& result) const
401{
402#ifdef RBNF_DEBUG
403 fprintf(stderr, "<nfsubs> %x bv: %g ub: %g\n", this, baseValue, upperBound);
404#endif
405 // figure out the highest base value a rule can have and match
406 // the text being parsed (this varies according to the type of
407 // substitutions: multiplier, modulus, and numerator substitutions
408 // restrict the search to rules with base values lower than their
409 // own; same-value substitutions leave the upper bound wherever
410 // it was, and the others allow any rule to match
411 upperBound = calcUpperBound(upperBound);
412
413 // use our rule set to parse the text. If that fails and
414 // lenient parsing is enabled (this is always false if the
415 // formatter's lenient-parsing mode is off, but it may also
416 // be false even when the formatter's lenient-parse mode is
417 // on), then also try parsing the text using a default-
418 // constructed NumberFormat
419 if (ruleSet != NULL) {
420 ruleSet->parse(text, parsePosition, upperBound, result);
421 if (lenientParse && !ruleSet->isFractionRuleSet() && parsePosition.getIndex() == 0) {
422 UErrorCode status = U_ZERO_ERROR;
423 NumberFormat* fmt = NumberFormat::createInstance(status);
424 if (U_SUCCESS(status)) {
425 fmt->parse(text, result, parsePosition);
426 }
427 delete fmt;
428 }
429
430 // ...or use our DecimalFormat to parse the text
431 } else {
432 numberFormat->parse(text, result, parsePosition);
433 }
434
435 // if the parse was successful, we've already advanced the caller's
436 // parse position (this is the one function that doesn't have one
437 // of its own). Derive a parse result and return it as a Long,
438 // if possible, or a Double
439 if (parsePosition.getIndex() != 0) {
440 double tempResult = (result.getType() == Formattable::kLong) ?
441 (double)result.getLong() :
442 result.getDouble();
443
444 // composeRuleValue() produces a full parse result from
445 // the partial parse result passed to this function from
446 // the caller (this is either the owning rule's base value
447 // or the partial result obtained from composing the
448 // owning rule's base value with its other substitution's
449 // parse result) and the partial parse result obtained by
450 // matching the substitution (which will be the same value
451 // the caller would get by parsing just this part of the
452 // text with RuleBasedNumberFormat.parse() ). How the two
453 // values are used to derive the full parse result depends
454 // on the types of substitutions: For a regular rule, the
455 // ultimate result is its multiplier substitution's result
456 // times the rule's divisor (or the rule's base value) plus
457 // the modulus substitution's result (which will actually
458 // supersede part of the rule's base value). For a negative-
459 // number rule, the result is the negative of its substitution's
460 // result. For a fraction rule, it's the sum of its two
461 // substitution results. For a rule in a fraction rule set,
462 // it's the numerator substitution's result divided by
463 // the rule's base value. Results from same-value substitutions
464 // propagate back upard, and null substitutions don't affect
465 // the result.
466 tempResult = composeRuleValue(tempResult, baseValue);
467 result.setDouble(tempResult);
468 return TRUE;
469 // if the parse was UNsuccessful, return 0
470 } else {
471 result.setLong(0);
472 return FALSE;
473 }
474}
475
476UBool
477NFSubstitution::isNullSubstitution() const {
478 return FALSE;
479}
480
481 /**
482 * Returns true if this is a modulus substitution. (We didn't do this
483 * with instanceof partially because it causes source files to
484 * proliferate and partially because we have to port this to C++.)
485 * @return true if this object is an instance of ModulusSubstitution
486 */
487UBool
488NFSubstitution::isModulusSubstitution() const {
489 return FALSE;
490}
491
492//===================================================================
493// SameValueSubstitution
494//===================================================================
495
496/**
497 * A substitution that passes the value passed to it through unchanged.
498 * Represented by == in rule descriptions.
499 */
500SameValueSubstitution::SameValueSubstitution(int32_t _pos,
501 const NFRuleSet* _ruleSet,
502 const RuleBasedNumberFormat* formatter,
503 const UnicodeString& description,
504 UErrorCode& status)
505: NFSubstitution(_pos, _ruleSet, formatter, description, status)
506{
507 if (description == gEqualsEquals) {
508 // throw new IllegalArgumentException("== is not a legal token");
509 status = U_PARSE_ERROR;
510 }
511}
512
513const char SameValueSubstitution::fgClassID = 0;
514
515UClassID
516SameValueSubstitution::getDynamicClassID() const {
517 return getStaticClassID();
518}
519
520
521//===================================================================
522// MultiplierSubstitution
523//===================================================================
524
525const char MultiplierSubstitution::fgClassID = 0;
526
527UClassID
528MultiplierSubstitution::getDynamicClassID() const {
529 return getStaticClassID();
530}
531
532UBool MultiplierSubstitution::operator==(const NFSubstitution& rhs) const
533{
534 return NFSubstitution::operator==(rhs) &&
535 divisor == ((const MultiplierSubstitution*)&rhs)->divisor;
536}
537
538
539//===================================================================
540// ModulusSubstitution
541//===================================================================
542
543/**
544 * A substitution that divides the number being formatted by the its rule's
545 * divisor and formats the remainder. Represented by "&gt;&gt;" in a
546 * regular rule.
547 */
548ModulusSubstitution::ModulusSubstitution(int32_t _pos,
549 double _divisor,
550 const NFRule* predecessor,
551 const NFRuleSet* _ruleSet,
552 const RuleBasedNumberFormat* formatter,
553 const UnicodeString& description,
554 UErrorCode& status)
555 : NFSubstitution(_pos, _ruleSet, formatter, description, status)
556 , divisor(_divisor)
557 , ruleToUse(NULL)
558{
559 ldivisor = util64_fromDouble(_divisor);
560
561 // the owning rule's divisor controls the behavior of this
562 // substitution: rather than keeping a backpointer to the rule,
563 // we keep a copy of the divisor
564
565 if (description == gGreaterGreaterGreaterThan) {
566 // the >>> token doesn't alter how this substituion calculates the
567 // values it uses for formatting and parsing, but it changes
568 // what's done with that value after it's obtained: >>> short-
569 // circuits the rule-search process and goes straight to the
570 // specified rule to format the substitution value
571 ruleToUse = predecessor;
572 }
573}
574
575const char ModulusSubstitution::fgClassID = 0;
576
577UClassID
578ModulusSubstitution::getDynamicClassID() const {
579 return getStaticClassID();
580}
581
582UBool ModulusSubstitution::operator==(const NFSubstitution& rhs) const
583{
584 return NFSubstitution::operator==(rhs) &&
585 divisor == ((const ModulusSubstitution*)&rhs)->divisor &&
586 ruleToUse == ((const ModulusSubstitution*)&rhs)->ruleToUse;
587}
588
589//-----------------------------------------------------------------------
590// formatting
591//-----------------------------------------------------------------------
592
593
594/**
595 * If this is a &gt;&gt;&gt; substitution, use ruleToUse to fill in
596 * the substitution. Otherwise, just use the superclass function.
597 * @param number The number being formatted
598 * @toInsertInto The string to insert the result of this substitution
599 * into
600 * @param pos The position of the rule text in toInsertInto
601 */
602void
603ModulusSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos) const
604{
605 // if this isn't a >>> substitution, just use the inherited version
606 // of this function (which uses either a rule set or a DecimalFormat
607 // to format its substitution value)
608 if (ruleToUse == NULL) {
609 NFSubstitution::doSubstitution(number, toInsertInto, _pos);
610
611 // a >>> substitution goes straight to a particular rule to
612 // format the substitution value
613 } else {
614 int64_t numberToFormat = transformNumber(number);
615 ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos());
616 }
617}
618
619/**
620* If this is a &gt;&gt;&gt; substitution, use ruleToUse to fill in
621* the substitution. Otherwise, just use the superclass function.
622* @param number The number being formatted
623* @toInsertInto The string to insert the result of this substitution
624* into
625* @param pos The position of the rule text in toInsertInto
626*/
627void
628ModulusSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) const
629{
630 // if this isn't a >>> substitution, just use the inherited version
631 // of this function (which uses either a rule set or a DecimalFormat
632 // to format its substitution value)
633 if (ruleToUse == NULL) {
634 NFSubstitution::doSubstitution(number, toInsertInto, _pos);
635
636 // a >>> substitution goes straight to a particular rule to
637 // format the substitution value
638 } else {
639 double numberToFormat = transformNumber(number);
640
641 ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos());
642 }
643}
644
645//-----------------------------------------------------------------------
646// parsing
647//-----------------------------------------------------------------------
648
649/**
650 * If this is a &gt;&gt;&gt; substitution, match only against ruleToUse.
651 * Otherwise, use the superclass function.
652 * @param text The string to parse
653 * @param parsePosition Ignored on entry, updated on exit to point to
654 * the first unmatched character.
655 * @param baseValue The partial parse result prior to calling this
656 * routine.
657 */
658UBool
659ModulusSubstitution::doParse(const UnicodeString& text,
660 ParsePosition& parsePosition,
661 double baseValue,
662 double upperBound,
663 UBool lenientParse,
664 Formattable& result) const
665{
666 // if this isn't a >>> substitution, we can just use the
667 // inherited parse() routine to do the parsing
668 if (ruleToUse == NULL) {
669 return NFSubstitution::doParse(text, parsePosition, baseValue, upperBound, lenientParse, result);
670
671 // but if it IS a >>> substitution, we have to do it here: we
672 // use the specific rule's doParse() method, and then we have to
673 // do some of the other work of NFRuleSet.parse()
674 } else {
675 ruleToUse->doParse(text, parsePosition, FALSE, upperBound, result);
676
677 if (parsePosition.getIndex() != 0) {
678 double tempResult = result.getDouble();
679 tempResult = composeRuleValue(tempResult, baseValue);
680 result.setDouble(tempResult);
681 }
682
683 return TRUE;
684 }
685}
686
687
688//===================================================================
689// IntegralPartSubstitution
690//===================================================================
691
692const char IntegralPartSubstitution::fgClassID = 0;
693
694UClassID
695IntegralPartSubstitution::getDynamicClassID() const {
696 return getStaticClassID();
697}
698
699
700//===================================================================
701// FractionalPartSubstitution
702//===================================================================
703
704
705 /**
706 * Constructs a FractionalPartSubstitution. This object keeps a flag
707 * telling whether it should format by digits or not. In addition,
708 * it marks the rule set it calls (if any) as a fraction rule set.
709 */
710FractionalPartSubstitution::FractionalPartSubstitution(int32_t _pos,
711 const NFRuleSet* _ruleSet,
712 const RuleBasedNumberFormat* formatter,
713 const UnicodeString& description,
714 UErrorCode& status)
715 : NFSubstitution(_pos, _ruleSet, formatter, description, status)
716 , byDigits(FALSE)
717 , useSpaces(TRUE)
718
719{
720 // akk, ruleSet can change in superclass constructor
721 if (description == gGreaterGreaterThan ||
722 description == gGreaterGreaterGreaterThan ||
723 _ruleSet == getRuleSet()) {
724 byDigits = TRUE;
725 if (description == gGreaterGreaterGreaterThan) {
726 useSpaces = FALSE;
727 }
728 } else {
729 // cast away const
730 ((NFRuleSet*)getRuleSet())->makeIntoFractionRuleSet();
731 }
732}
733
734//-----------------------------------------------------------------------
735// formatting
736//-----------------------------------------------------------------------
737
738/**
739 * If in "by digits" mode, fills in the substitution one decimal digit
740 * at a time using the rule set containing this substitution.
741 * Otherwise, uses the superclass function.
742 * @param number The number being formatted
743 * @param toInsertInto The string to insert the result of formatting
744 * the substitution into
745 * @param pos The position of the owning rule's rule text in
746 * toInsertInto
747 */
748void
749FractionalPartSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) const
750{
751 // if we're not in "byDigits" mode, just use the inherited
752 // doSubstitution() routine
753 if (!byDigits) {
754 NFSubstitution::doSubstitution(number, toInsertInto, _pos);
755
756 // if we're in "byDigits" mode, transform the value into an integer
757 // by moving the decimal point eight places to the right and
758 // pulling digits off the right one at a time, formatting each digit
759 // as an integer using this substitution's owning rule set
760 // (this is slower, but more accurate, than doing it from the
761 // other end)
762 } else {
763 int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits));
764 // this flag keeps us from formatting trailing zeros. It starts
765 // out false because we're pulling from the right, and switches
766 // to true the first time we encounter a non-zero digit
767 UBool doZeros = FALSE;
768 for (int32_t i = 0; i < kMaxDecimalDigits; i++) {
769 int64_t digit = numberToFormat % 10;
770 if (digit != 0 || doZeros) {
771 if (doZeros && useSpaces) {
772 toInsertInto.insert(_pos + getPos(), gSpace);
773 }
774 doZeros = TRUE;
775 getRuleSet()->format(digit, toInsertInto, _pos + getPos());
776 }
777 numberToFormat /= 10;
778 }
779 }
780}
781
782//-----------------------------------------------------------------------
783// parsing
784//-----------------------------------------------------------------------
785
786/**
787 * If in "by digits" mode, parses the string as if it were a string
788 * of individual digits; otherwise, uses the superclass function.
789 * @param text The string to parse
790 * @param parsePosition Ignored on entry, but updated on exit to point
791 * to the first unmatched character
792 * @param baseValue The partial parse result prior to entering this
793 * function
794 * @param upperBound Only consider rules with base values lower than
795 * this when filling in the substitution
796 * @param lenientParse If true, try matching the text as numerals if
797 * matching as words doesn't work
798 * @return If the match was successful, the current partial parse
799 * result; otherwise new Long(0). The result is either a Long or
800 * a Double.
801 */
802
803UBool
804FractionalPartSubstitution::doParse(const UnicodeString& text,
805 ParsePosition& parsePosition,
806 double baseValue,
807 double /*upperBound*/,
808 UBool lenientParse,
809 Formattable& resVal) const
810{
811 // if we're not in byDigits mode, we can just use the inherited
812 // doParse()
813 if (!byDigits) {
814 return NFSubstitution::doParse(text, parsePosition, baseValue, 0, lenientParse, resVal);
815
816 // if we ARE in byDigits mode, parse the text one digit at a time
817 // using this substitution's owning rule set (we do this by setting
818 // upperBound to 10 when calling doParse() ) until we reach
819 // nonmatching text
820 } else {
821 UnicodeString workText(text);
822 ParsePosition workPos(1);
823 double result = 0;
824 int32_t digit;
825 double p10 = 0.1;
826
827 NumberFormat* fmt = NULL;
828 while (workText.length() > 0 && workPos.getIndex() != 0) {
829 workPos.setIndex(0);
830 Formattable temp;
831 getRuleSet()->parse(workText, workPos, 10, temp);
832 digit = temp.getType() == Formattable::kLong ?
833 temp.getLong() :
834 (int32_t)temp.getDouble();
835
836 if (lenientParse && workPos.getIndex() == 0) {
837 if (!fmt) {
838 UErrorCode status = U_ZERO_ERROR;
839 fmt = NumberFormat::createInstance(status);
840 if (U_FAILURE(status)) {
841 delete fmt;
842 fmt = NULL;
843 }
844 }
845 if (fmt) {
846 fmt->parse(workText, temp, workPos);
847 digit = temp.getLong();
848 }
849 }
850
851 if (workPos.getIndex() != 0) {
852 result += digit * p10;
853 p10 /= 10;
854 parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex());
855 workText.removeBetween(0, workPos.getIndex());
856 while (workText.length() > 0 && workText.charAt(0) == gSpace) {
857 workText.removeBetween(0, 1);
858 parsePosition.setIndex(parsePosition.getIndex() + 1);
859 }
860 }
861 }
862 delete fmt;
863
864 result = composeRuleValue(result, baseValue);
865 resVal.setDouble(result);
866 return TRUE;
867 }
868}
869
870UBool
871FractionalPartSubstitution::operator==(const NFSubstitution& rhs) const
872{
873 return NFSubstitution::operator==(rhs) &&
874 ((const FractionalPartSubstitution*)&rhs)->byDigits == byDigits;
875}
876
877const char FractionalPartSubstitution::fgClassID = 0;
878
879UClassID
880FractionalPartSubstitution::getDynamicClassID() const {
881 return getStaticClassID();
882}
883
884
885//===================================================================
886// AbsoluteValueSubstitution
887//===================================================================
888
889const char AbsoluteValueSubstitution::fgClassID = 0;
890
891UClassID
892AbsoluteValueSubstitution::getDynamicClassID() const {
893 return getStaticClassID();
894}
895
896//===================================================================
897// NumeratorSubstitution
898//===================================================================
899
900UBool
901NumeratorSubstitution::operator==(const NFSubstitution& rhs) const
902{
903 return NFSubstitution::operator==(rhs) &&
904 denominator == ((const NumeratorSubstitution*)&rhs)->denominator;
905}
906
907const char NumeratorSubstitution::fgClassID = 0;
908
909UClassID
910NumeratorSubstitution::getDynamicClassID() const {
911 return getStaticClassID();
912}
913
914//===================================================================
915// NullSubstitution
916//===================================================================
917
918const char NullSubstitution::fgClassID = 0;
919
920UClassID
921NullSubstitution::getDynamicClassID() const {
922 return getStaticClassID();
923}
924
925/* U_HAVE_RBNF */
926#endif
927