Line data Source code
1 : // © 2016 and later: Unicode, Inc. and others.
2 : // License & terms of use: http://www.unicode.org/copyright.html
3 : /*
4 : ******************************************************************************
5 : * Copyright (C) 1997-2015, International Business Machines
6 : * Corporation and others. All Rights Reserved.
7 : ******************************************************************************
8 : * file name: nfsubs.cpp
9 : * encoding: UTF-8
10 : * tab size: 8 (not used)
11 : * indentation:4
12 : *
13 : * Modification history
14 : * Date Name Comments
15 : * 10/11/2001 Doug Ported from ICU4J
16 : */
17 :
18 : #include <stdio.h>
19 : #include "utypeinfo.h" // for 'typeid' to work
20 :
21 : #include "nfsubs.h"
22 : #include "digitlst.h"
23 : #include "fmtableimp.h"
24 :
25 : #if U_HAVE_RBNF
26 :
27 : static const UChar gLessThan = 0x003c;
28 : static const UChar gEquals = 0x003d;
29 : static const UChar gGreaterThan = 0x003e;
30 : static const UChar gPercent = 0x0025;
31 : static const UChar gPound = 0x0023;
32 : static const UChar gZero = 0x0030;
33 : static const UChar gSpace = 0x0020;
34 :
35 : static const UChar gEqualsEquals[] =
36 : {
37 : 0x3D, 0x3D, 0
38 : }; /* "==" */
39 : static const UChar gGreaterGreaterGreaterThan[] =
40 : {
41 : 0x3E, 0x3E, 0x3E, 0
42 : }; /* ">>>" */
43 : static const UChar gGreaterGreaterThan[] =
44 : {
45 : 0x3E, 0x3E, 0
46 : }; /* ">>" */
47 :
48 : U_NAMESPACE_BEGIN
49 :
50 : class SameValueSubstitution : public NFSubstitution {
51 : public:
52 : SameValueSubstitution(int32_t pos,
53 : const NFRuleSet* ruleset,
54 : const UnicodeString& description,
55 : UErrorCode& status);
56 : virtual ~SameValueSubstitution();
57 :
58 0 : virtual int64_t transformNumber(int64_t number) const { return number; }
59 0 : virtual double transformNumber(double number) const { return number; }
60 0 : virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const { return newRuleValue; }
61 0 : virtual double calcUpperBound(double oldUpperBound) const { return oldUpperBound; }
62 0 : virtual UChar tokenChar() const { return (UChar)0x003d; } // '='
63 :
64 : public:
65 : static UClassID getStaticClassID(void);
66 : virtual UClassID getDynamicClassID(void) const;
67 : };
68 :
69 0 : SameValueSubstitution::~SameValueSubstitution() {}
70 :
71 : class MultiplierSubstitution : public NFSubstitution {
72 : int64_t divisor;
73 :
74 : public:
75 0 : MultiplierSubstitution(int32_t _pos,
76 : const NFRule *rule,
77 : const NFRuleSet* _ruleSet,
78 : const UnicodeString& description,
79 : UErrorCode& status)
80 0 : : NFSubstitution(_pos, _ruleSet, description, status), divisor(rule->getDivisor())
81 : {
82 0 : if (divisor == 0) {
83 0 : status = U_PARSE_ERROR;
84 : }
85 0 : }
86 : virtual ~MultiplierSubstitution();
87 :
88 0 : virtual void setDivisor(int32_t radix, int16_t exponent, UErrorCode& status) {
89 0 : divisor = util64_pow(radix, exponent);
90 :
91 0 : if(divisor == 0) {
92 0 : status = U_PARSE_ERROR;
93 : }
94 0 : }
95 :
96 : virtual UBool operator==(const NFSubstitution& rhs) const;
97 :
98 0 : virtual int64_t transformNumber(int64_t number) const {
99 0 : return number / divisor;
100 : }
101 :
102 0 : virtual double transformNumber(double number) const {
103 0 : if (getRuleSet()) {
104 0 : return uprv_floor(number / divisor);
105 : } else {
106 0 : return number / divisor;
107 : }
108 : }
109 :
110 0 : virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const {
111 0 : return newRuleValue * divisor;
112 : }
113 :
114 0 : virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor; }
115 :
116 0 : virtual UChar tokenChar() const { return (UChar)0x003c; } // '<'
117 :
118 : public:
119 : static UClassID getStaticClassID(void);
120 : virtual UClassID getDynamicClassID(void) const;
121 : };
122 :
123 0 : MultiplierSubstitution::~MultiplierSubstitution() {}
124 :
125 : class ModulusSubstitution : public NFSubstitution {
126 : int64_t divisor;
127 : const NFRule* ruleToUse;
128 : public:
129 : ModulusSubstitution(int32_t pos,
130 : const NFRule* rule,
131 : const NFRule* rulePredecessor,
132 : const NFRuleSet* ruleSet,
133 : const UnicodeString& description,
134 : UErrorCode& status);
135 : virtual ~ModulusSubstitution();
136 :
137 0 : virtual void setDivisor(int32_t radix, int16_t exponent, UErrorCode& status) {
138 0 : divisor = util64_pow(radix, exponent);
139 :
140 0 : if (divisor == 0) {
141 0 : status = U_PARSE_ERROR;
142 : }
143 0 : }
144 :
145 : virtual UBool operator==(const NFSubstitution& rhs) const;
146 :
147 : virtual void doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const;
148 : virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const;
149 :
150 0 : virtual int64_t transformNumber(int64_t number) const { return number % divisor; }
151 0 : virtual double transformNumber(double number) const { return uprv_fmod(number, divisor); }
152 :
153 : virtual UBool doParse(const UnicodeString& text,
154 : ParsePosition& parsePosition,
155 : double baseValue,
156 : double upperBound,
157 : UBool lenientParse,
158 : Formattable& result) const;
159 :
160 0 : virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const {
161 0 : return oldRuleValue - uprv_fmod(oldRuleValue, divisor) + newRuleValue;
162 : }
163 :
164 0 : virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor; }
165 :
166 0 : virtual UBool isModulusSubstitution() const { return TRUE; }
167 :
168 0 : virtual UChar tokenChar() const { return (UChar)0x003e; } // '>'
169 :
170 : virtual void toString(UnicodeString& result) const;
171 :
172 : public:
173 : static UClassID getStaticClassID(void);
174 : virtual UClassID getDynamicClassID(void) const;
175 : };
176 :
177 0 : ModulusSubstitution::~ModulusSubstitution() {}
178 :
179 : class IntegralPartSubstitution : public NFSubstitution {
180 : public:
181 0 : IntegralPartSubstitution(int32_t _pos,
182 : const NFRuleSet* _ruleSet,
183 : const UnicodeString& description,
184 : UErrorCode& status)
185 0 : : NFSubstitution(_pos, _ruleSet, description, status) {}
186 : virtual ~IntegralPartSubstitution();
187 :
188 0 : virtual int64_t transformNumber(int64_t number) const { return number; }
189 0 : virtual double transformNumber(double number) const { return uprv_floor(number); }
190 0 : virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue + oldRuleValue; }
191 0 : virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX; }
192 0 : virtual UChar tokenChar() const { return (UChar)0x003c; } // '<'
193 :
194 : public:
195 : static UClassID getStaticClassID(void);
196 : virtual UClassID getDynamicClassID(void) const;
197 : };
198 :
199 0 : IntegralPartSubstitution::~IntegralPartSubstitution() {}
200 :
201 : class FractionalPartSubstitution : public NFSubstitution {
202 : UBool byDigits;
203 : UBool useSpaces;
204 : enum { kMaxDecimalDigits = 8 };
205 : public:
206 : FractionalPartSubstitution(int32_t pos,
207 : const NFRuleSet* ruleSet,
208 : const UnicodeString& description,
209 : UErrorCode& status);
210 : virtual ~FractionalPartSubstitution();
211 :
212 : virtual UBool operator==(const NFSubstitution& rhs) const;
213 :
214 : virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const;
215 0 : virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/, int32_t /*recursionCount*/, UErrorCode& /*status*/) const {}
216 0 : virtual int64_t transformNumber(int64_t /*number*/) const { return 0; }
217 0 : virtual double transformNumber(double number) const { return number - uprv_floor(number); }
218 :
219 : virtual UBool doParse(const UnicodeString& text,
220 : ParsePosition& parsePosition,
221 : double baseValue,
222 : double upperBound,
223 : UBool lenientParse,
224 : Formattable& result) const;
225 :
226 0 : virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue + oldRuleValue; }
227 0 : virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0.0; }
228 0 : virtual UChar tokenChar() const { return (UChar)0x003e; } // '>'
229 :
230 : public:
231 : static UClassID getStaticClassID(void);
232 : virtual UClassID getDynamicClassID(void) const;
233 : };
234 :
235 0 : FractionalPartSubstitution::~FractionalPartSubstitution() {}
236 :
237 : class AbsoluteValueSubstitution : public NFSubstitution {
238 : public:
239 0 : AbsoluteValueSubstitution(int32_t _pos,
240 : const NFRuleSet* _ruleSet,
241 : const UnicodeString& description,
242 : UErrorCode& status)
243 0 : : NFSubstitution(_pos, _ruleSet, description, status) {}
244 : virtual ~AbsoluteValueSubstitution();
245 :
246 0 : virtual int64_t transformNumber(int64_t number) const { return number >= 0 ? number : -number; }
247 0 : virtual double transformNumber(double number) const { return uprv_fabs(number); }
248 0 : virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const { return -newRuleValue; }
249 0 : virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX; }
250 0 : virtual UChar tokenChar() const { return (UChar)0x003e; } // '>'
251 :
252 : public:
253 : static UClassID getStaticClassID(void);
254 : virtual UClassID getDynamicClassID(void) const;
255 : };
256 :
257 0 : AbsoluteValueSubstitution::~AbsoluteValueSubstitution() {}
258 :
259 : class NumeratorSubstitution : public NFSubstitution {
260 : double denominator;
261 : int64_t ldenominator;
262 : UBool withZeros;
263 : public:
264 0 : static inline UnicodeString fixdesc(const UnicodeString& desc) {
265 0 : if (desc.endsWith(LTLT, 2)) {
266 0 : UnicodeString result(desc, 0, desc.length()-1);
267 0 : return result;
268 : }
269 0 : return desc;
270 : }
271 0 : NumeratorSubstitution(int32_t _pos,
272 : double _denominator,
273 : NFRuleSet* _ruleSet,
274 : const UnicodeString& description,
275 : UErrorCode& status)
276 0 : : NFSubstitution(_pos, _ruleSet, fixdesc(description), status), denominator(_denominator)
277 : {
278 0 : ldenominator = util64_fromDouble(denominator);
279 0 : withZeros = description.endsWith(LTLT, 2);
280 0 : }
281 : virtual ~NumeratorSubstitution();
282 :
283 : virtual UBool operator==(const NFSubstitution& rhs) const;
284 :
285 0 : virtual int64_t transformNumber(int64_t number) const { return number * ldenominator; }
286 0 : virtual double transformNumber(double number) const { return uprv_round(number * denominator); }
287 :
288 0 : virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/, int32_t /*recursionCount*/, UErrorCode& /*status*/) const {}
289 : virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const;
290 : virtual UBool doParse(const UnicodeString& text,
291 : ParsePosition& parsePosition,
292 : double baseValue,
293 : double upperBound,
294 : UBool /*lenientParse*/,
295 : Formattable& result) const;
296 :
297 0 : virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue / oldRuleValue; }
298 0 : virtual double calcUpperBound(double /*oldUpperBound*/) const { return denominator; }
299 0 : virtual UChar tokenChar() const { return (UChar)0x003c; } // '<'
300 : private:
301 : static const UChar LTLT[2];
302 :
303 : public:
304 : static UClassID getStaticClassID(void);
305 : virtual UClassID getDynamicClassID(void) const;
306 : };
307 :
308 0 : NumeratorSubstitution::~NumeratorSubstitution() {}
309 :
310 : NFSubstitution*
311 0 : NFSubstitution::makeSubstitution(int32_t pos,
312 : const NFRule* rule,
313 : const NFRule* predecessor,
314 : const NFRuleSet* ruleSet,
315 : const RuleBasedNumberFormat* formatter,
316 : const UnicodeString& description,
317 : UErrorCode& status)
318 : {
319 : // if the description is empty, return a NullSubstitution
320 0 : if (description.length() == 0) {
321 0 : return NULL;
322 : }
323 :
324 0 : switch (description.charAt(0)) {
325 : // if the description begins with '<'...
326 : case gLessThan:
327 : // throw an exception if the rule is a negative number
328 : // rule
329 0 : if (rule->getBaseValue() == NFRule::kNegativeNumberRule) {
330 : // throw new IllegalArgumentException("<< not allowed in negative-number rule");
331 0 : status = U_PARSE_ERROR;
332 0 : return NULL;
333 : }
334 :
335 : // if the rule is a fraction rule, return an
336 : // IntegralPartSubstitution
337 0 : else if (rule->getBaseValue() == NFRule::kImproperFractionRule
338 0 : || rule->getBaseValue() == NFRule::kProperFractionRule
339 0 : || rule->getBaseValue() == NFRule::kMasterRule) {
340 0 : return new IntegralPartSubstitution(pos, ruleSet, description, status);
341 : }
342 :
343 : // if the rule set containing the rule is a fraction
344 : // rule set, return a NumeratorSubstitution
345 0 : else if (ruleSet->isFractionRuleSet()) {
346 0 : return new NumeratorSubstitution(pos, (double)rule->getBaseValue(),
347 0 : formatter->getDefaultRuleSet(), description, status);
348 : }
349 :
350 : // otherwise, return a MultiplierSubstitution
351 : else {
352 : return new MultiplierSubstitution(pos, rule, ruleSet,
353 0 : description, status);
354 : }
355 :
356 : // if the description begins with '>'...
357 : case gGreaterThan:
358 : // if the rule is a negative-number rule, return
359 : // an AbsoluteValueSubstitution
360 0 : if (rule->getBaseValue() == NFRule::kNegativeNumberRule) {
361 0 : return new AbsoluteValueSubstitution(pos, ruleSet, description, status);
362 : }
363 :
364 : // if the rule is a fraction rule, return a
365 : // FractionalPartSubstitution
366 0 : else if (rule->getBaseValue() == NFRule::kImproperFractionRule
367 0 : || rule->getBaseValue() == NFRule::kProperFractionRule
368 0 : || rule->getBaseValue() == NFRule::kMasterRule) {
369 0 : return new FractionalPartSubstitution(pos, ruleSet, description, status);
370 : }
371 :
372 : // if the rule set owning the rule is a fraction rule set,
373 : // throw an exception
374 0 : else if (ruleSet->isFractionRuleSet()) {
375 : // throw new IllegalArgumentException(">> not allowed in fraction rule set");
376 0 : status = U_PARSE_ERROR;
377 0 : return NULL;
378 : }
379 :
380 : // otherwise, return a ModulusSubstitution
381 : else {
382 : return new ModulusSubstitution(pos, rule, predecessor,
383 0 : ruleSet, description, status);
384 : }
385 :
386 : // if the description begins with '=', always return a
387 : // SameValueSubstitution
388 : case gEquals:
389 0 : return new SameValueSubstitution(pos, ruleSet, description, status);
390 :
391 : // and if it's anything else, throw an exception
392 : default:
393 : // throw new IllegalArgumentException("Illegal substitution character");
394 0 : status = U_PARSE_ERROR;
395 : }
396 0 : return NULL;
397 : }
398 :
399 0 : NFSubstitution::NFSubstitution(int32_t _pos,
400 : const NFRuleSet* _ruleSet,
401 : const UnicodeString& description,
402 0 : UErrorCode& status)
403 0 : : pos(_pos), ruleSet(NULL), numberFormat(NULL)
404 : {
405 : // the description should begin and end with the same character.
406 : // If it doesn't that's a syntax error. Otherwise,
407 : // makeSubstitution() was the only thing that needed to know
408 : // about these characters, so strip them off
409 0 : UnicodeString workingDescription(description);
410 0 : if (description.length() >= 2
411 0 : && description.charAt(0) == description.charAt(description.length() - 1))
412 : {
413 0 : workingDescription.remove(description.length() - 1, 1);
414 0 : workingDescription.remove(0, 1);
415 : }
416 0 : else if (description.length() != 0) {
417 : // throw new IllegalArgumentException("Illegal substitution syntax");
418 0 : status = U_PARSE_ERROR;
419 0 : return;
420 : }
421 :
422 0 : if (workingDescription.length() == 0) {
423 : // if the description was just two paired token characters
424 : // (i.e., "<<" or ">>"), it uses the rule set it belongs to to
425 : // format its result
426 0 : this->ruleSet = _ruleSet;
427 : }
428 0 : else if (workingDescription.charAt(0) == gPercent) {
429 : // if the description contains a rule set name, that's the rule
430 : // set we use to format the result: get a reference to the
431 : // names rule set
432 0 : this->ruleSet = _ruleSet->getOwner()->findRuleSet(workingDescription, status);
433 : }
434 0 : else if (workingDescription.charAt(0) == gPound || workingDescription.charAt(0) ==gZero) {
435 : // if the description begins with 0 or #, treat it as a
436 : // DecimalFormat pattern, and initialize a DecimalFormat with
437 : // that pattern (then set it to use the DecimalFormatSymbols
438 : // belonging to our formatter)
439 0 : const DecimalFormatSymbols* sym = _ruleSet->getOwner()->getDecimalFormatSymbols();
440 0 : if (!sym) {
441 0 : status = U_MISSING_RESOURCE_ERROR;
442 0 : return;
443 : }
444 0 : DecimalFormat *tempNumberFormat = new DecimalFormat(workingDescription, *sym, status);
445 : /* test for NULL */
446 0 : if (!tempNumberFormat) {
447 0 : status = U_MEMORY_ALLOCATION_ERROR;
448 0 : return;
449 : }
450 0 : if (U_FAILURE(status)) {
451 0 : delete tempNumberFormat;
452 0 : return;
453 : }
454 0 : this->numberFormat = tempNumberFormat;
455 : }
456 0 : else if (workingDescription.charAt(0) == gGreaterThan) {
457 : // if the description is ">>>", this substitution bypasses the
458 : // usual rule-search process and always uses the rule that precedes
459 : // it in its own rule set's rule list (this is used for place-value
460 : // notations: formats where you want to see a particular part of
461 : // a number even when it's 0)
462 :
463 : // this causes problems when >>> is used in a frationalPartSubstitution
464 : // this->ruleSet = NULL;
465 0 : this->ruleSet = _ruleSet;
466 0 : this->numberFormat = NULL;
467 : }
468 : else {
469 : // and of the description is none of these things, it's a syntax error
470 :
471 : // throw new IllegalArgumentException("Illegal substitution syntax");
472 0 : status = U_PARSE_ERROR;
473 : }
474 : }
475 :
476 0 : NFSubstitution::~NFSubstitution()
477 : {
478 0 : delete numberFormat;
479 0 : numberFormat = NULL;
480 0 : }
481 :
482 : /**
483 : * Set's the substitution's divisor. Used by NFRule.setBaseValue().
484 : * A no-op for all substitutions except multiplier and modulus
485 : * substitutions.
486 : * @param radix The radix of the divisor
487 : * @param exponent The exponent of the divisor
488 : */
489 : void
490 0 : NFSubstitution::setDivisor(int32_t /*radix*/, int16_t /*exponent*/, UErrorCode& /*status*/) {
491 : // a no-op for all substitutions except multiplier and modulus substitutions
492 0 : }
493 :
494 : void
495 0 : NFSubstitution::setDecimalFormatSymbols(const DecimalFormatSymbols &newSymbols, UErrorCode& /*status*/) {
496 0 : if (numberFormat != NULL) {
497 0 : numberFormat->setDecimalFormatSymbols(newSymbols);
498 : }
499 0 : }
500 :
501 : //-----------------------------------------------------------------------
502 : // boilerplate
503 : //-----------------------------------------------------------------------
504 :
505 0 : UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NFSubstitution)
506 :
507 : /**
508 : * Compares two substitutions for equality
509 : * @param The substitution to compare this one to
510 : * @return true if the two substitutions are functionally equivalent
511 : */
512 : UBool
513 0 : NFSubstitution::operator==(const NFSubstitution& rhs) const
514 : {
515 : // compare class and all of the fields all substitutions have
516 : // in common
517 : // this should be called by subclasses before their own equality tests
518 0 : return typeid(*this) == typeid(rhs)
519 0 : && pos == rhs.pos
520 0 : && (ruleSet == NULL) == (rhs.ruleSet == NULL)
521 : // && ruleSet == rhs.ruleSet causes circularity, other checks to make instead?
522 0 : && (numberFormat == NULL
523 0 : ? (rhs.numberFormat == NULL)
524 0 : : (*numberFormat == *rhs.numberFormat));
525 : }
526 :
527 : /**
528 : * Returns a textual description of the substitution
529 : * @return A textual description of the substitution. This might
530 : * not be identical to the description it was created from, but
531 : * it'll produce the same result.
532 : */
533 : void
534 0 : NFSubstitution::toString(UnicodeString& text) const
535 : {
536 : // use tokenChar() to get the character at the beginning and
537 : // end of the substitutin token. In between them will go
538 : // either the name of the rule set it uses, or the pattern of
539 : // the DecimalFormat it uses
540 0 : text.remove();
541 0 : text.append(tokenChar());
542 :
543 0 : UnicodeString temp;
544 0 : if (ruleSet != NULL) {
545 0 : ruleSet->getName(temp);
546 0 : } else if (numberFormat != NULL) {
547 0 : numberFormat->toPattern(temp);
548 : }
549 0 : text.append(temp);
550 0 : text.append(tokenChar());
551 0 : }
552 :
553 : //-----------------------------------------------------------------------
554 : // formatting
555 : //-----------------------------------------------------------------------
556 :
557 : /**
558 : * Performs a mathematical operation on the number, formats it using
559 : * either ruleSet or decimalFormat, and inserts the result into
560 : * toInsertInto.
561 : * @param number The number being formatted.
562 : * @param toInsertInto The string we insert the result into
563 : * @param pos The position in toInsertInto where the owning rule's
564 : * rule text begins (this value is added to this substitution's
565 : * position to determine exactly where to insert the new text)
566 : */
567 : void
568 0 : NFSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const
569 : {
570 0 : if (ruleSet != NULL) {
571 : // Perform a transformation on the number that is dependent
572 : // on the type of substitution this is, then just call its
573 : // rule set's format() method to format the result
574 0 : ruleSet->format(transformNumber(number), toInsertInto, _pos + this->pos, recursionCount, status);
575 0 : } else if (numberFormat != NULL) {
576 0 : if (number <= MAX_INT64_IN_DOUBLE) {
577 : // or perform the transformation on the number (preserving
578 : // the result's fractional part if the formatter it set
579 : // to show it), then use that formatter's format() method
580 : // to format the result
581 0 : double numberToFormat = transformNumber((double)number);
582 0 : if (numberFormat->getMaximumFractionDigits() == 0) {
583 0 : numberToFormat = uprv_floor(numberToFormat);
584 : }
585 :
586 0 : UnicodeString temp;
587 0 : numberFormat->format(numberToFormat, temp, status);
588 0 : toInsertInto.insert(_pos + this->pos, temp);
589 : }
590 : else {
591 : // We have gone beyond double precision. Something has to give.
592 : // We're favoring accuracy of the large number over potential rules
593 : // that round like a CompactDecimalFormat, which is not a common use case.
594 : //
595 : // Perform a transformation on the number that is dependent
596 : // on the type of substitution this is, then just call its
597 : // rule set's format() method to format the result
598 0 : int64_t numberToFormat = transformNumber(number);
599 0 : UnicodeString temp;
600 0 : numberFormat->format(numberToFormat, temp, status);
601 0 : toInsertInto.insert(_pos + this->pos, temp);
602 : }
603 : }
604 0 : }
605 :
606 : /**
607 : * Performs a mathematical operation on the number, formats it using
608 : * either ruleSet or decimalFormat, and inserts the result into
609 : * toInsertInto.
610 : * @param number The number being formatted.
611 : * @param toInsertInto The string we insert the result into
612 : * @param pos The position in toInsertInto where the owning rule's
613 : * rule text begins (this value is added to this substitution's
614 : * position to determine exactly where to insert the new text)
615 : */
616 : void
617 0 : NFSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const {
618 : // perform a transformation on the number being formatted that
619 : // is dependent on the type of substitution this is
620 0 : double numberToFormat = transformNumber(number);
621 :
622 0 : if (uprv_isInfinite(numberToFormat)) {
623 : // This is probably a minus rule. Combine it with an infinite rule.
624 0 : const NFRule *infiniteRule = ruleSet->findDoubleRule(uprv_getInfinity());
625 0 : infiniteRule->doFormat(numberToFormat, toInsertInto, _pos + this->pos, recursionCount, status);
626 0 : return;
627 : }
628 :
629 : // if the result is an integer, from here on out we work in integer
630 : // space (saving time and memory and preserving accuracy)
631 0 : if (numberToFormat == uprv_floor(numberToFormat) && ruleSet != NULL) {
632 0 : ruleSet->format(util64_fromDouble(numberToFormat), toInsertInto, _pos + this->pos, recursionCount, status);
633 :
634 : // if the result isn't an integer, then call either our rule set's
635 : // format() method or our DecimalFormat's format() method to
636 : // format the result
637 : } else {
638 0 : if (ruleSet != NULL) {
639 0 : ruleSet->format(numberToFormat, toInsertInto, _pos + this->pos, recursionCount, status);
640 0 : } else if (numberFormat != NULL) {
641 0 : UnicodeString temp;
642 0 : numberFormat->format(numberToFormat, temp);
643 0 : toInsertInto.insert(_pos + this->pos, temp);
644 : }
645 : }
646 : }
647 :
648 :
649 : //-----------------------------------------------------------------------
650 : // parsing
651 : //-----------------------------------------------------------------------
652 :
653 : #ifdef RBNF_DEBUG
654 : #include <stdio.h>
655 : #endif
656 :
657 : /**
658 : * Parses a string using the rule set or DecimalFormat belonging
659 : * to this substitution. If there's a match, a mathematical
660 : * operation (the inverse of the one used in formatting) is
661 : * performed on the result of the parse and the value passed in
662 : * and returned as the result. The parse position is updated to
663 : * point to the first unmatched character in the string.
664 : * @param text The string to parse
665 : * @param parsePosition On entry, ignored, but assumed to be 0.
666 : * On exit, this is updated to point to the first unmatched
667 : * character (or 0 if the substitution didn't match)
668 : * @param baseValue A partial parse result that should be
669 : * combined with the result of this parse
670 : * @param upperBound When searching the rule set for a rule
671 : * matching the string passed in, only rules with base values
672 : * lower than this are considered
673 : * @param lenientParse If true and matching against rules fails,
674 : * the substitution will also try matching the text against
675 : * numerals using a default-costructed NumberFormat. If false,
676 : * no extra work is done. (This value is false whenever the
677 : * formatter isn't in lenient-parse mode, but is also false
678 : * under some conditions even when the formatter _is_ in
679 : * lenient-parse mode.)
680 : * @return If there's a match, this is the result of composing
681 : * baseValue with whatever was returned from matching the
682 : * characters. This will be either a Long or a Double. If there's
683 : * no match this is new Long(0) (not null), and parsePosition
684 : * is left unchanged.
685 : */
686 : UBool
687 0 : NFSubstitution::doParse(const UnicodeString& text,
688 : ParsePosition& parsePosition,
689 : double baseValue,
690 : double upperBound,
691 : UBool lenientParse,
692 : Formattable& result) const
693 : {
694 : #ifdef RBNF_DEBUG
695 : fprintf(stderr, "<nfsubs> %x bv: %g ub: %g\n", this, baseValue, upperBound);
696 : #endif
697 : // figure out the highest base value a rule can have and match
698 : // the text being parsed (this varies according to the type of
699 : // substitutions: multiplier, modulus, and numerator substitutions
700 : // restrict the search to rules with base values lower than their
701 : // own; same-value substitutions leave the upper bound wherever
702 : // it was, and the others allow any rule to match
703 0 : upperBound = calcUpperBound(upperBound);
704 :
705 : // use our rule set to parse the text. If that fails and
706 : // lenient parsing is enabled (this is always false if the
707 : // formatter's lenient-parsing mode is off, but it may also
708 : // be false even when the formatter's lenient-parse mode is
709 : // on), then also try parsing the text using a default-
710 : // constructed NumberFormat
711 0 : if (ruleSet != NULL) {
712 0 : ruleSet->parse(text, parsePosition, upperBound, result);
713 0 : if (lenientParse && !ruleSet->isFractionRuleSet() && parsePosition.getIndex() == 0) {
714 0 : UErrorCode status = U_ZERO_ERROR;
715 0 : NumberFormat* fmt = NumberFormat::createInstance(status);
716 0 : if (U_SUCCESS(status)) {
717 0 : fmt->parse(text, result, parsePosition);
718 : }
719 0 : delete fmt;
720 : }
721 :
722 : // ...or use our DecimalFormat to parse the text
723 0 : } else if (numberFormat != NULL) {
724 0 : numberFormat->parse(text, result, parsePosition);
725 : }
726 :
727 : // if the parse was successful, we've already advanced the caller's
728 : // parse position (this is the one function that doesn't have one
729 : // of its own). Derive a parse result and return it as a Long,
730 : // if possible, or a Double
731 0 : if (parsePosition.getIndex() != 0) {
732 0 : UErrorCode status = U_ZERO_ERROR;
733 0 : double tempResult = result.getDouble(status);
734 :
735 : // composeRuleValue() produces a full parse result from
736 : // the partial parse result passed to this function from
737 : // the caller (this is either the owning rule's base value
738 : // or the partial result obtained from composing the
739 : // owning rule's base value with its other substitution's
740 : // parse result) and the partial parse result obtained by
741 : // matching the substitution (which will be the same value
742 : // the caller would get by parsing just this part of the
743 : // text with RuleBasedNumberFormat.parse() ). How the two
744 : // values are used to derive the full parse result depends
745 : // on the types of substitutions: For a regular rule, the
746 : // ultimate result is its multiplier substitution's result
747 : // times the rule's divisor (or the rule's base value) plus
748 : // the modulus substitution's result (which will actually
749 : // supersede part of the rule's base value). For a negative-
750 : // number rule, the result is the negative of its substitution's
751 : // result. For a fraction rule, it's the sum of its two
752 : // substitution results. For a rule in a fraction rule set,
753 : // it's the numerator substitution's result divided by
754 : // the rule's base value. Results from same-value substitutions
755 : // propagate back upard, and null substitutions don't affect
756 : // the result.
757 0 : tempResult = composeRuleValue(tempResult, baseValue);
758 0 : result.setDouble(tempResult);
759 0 : return TRUE;
760 : // if the parse was UNsuccessful, return 0
761 : } else {
762 0 : result.setLong(0);
763 0 : return FALSE;
764 : }
765 : }
766 :
767 : /**
768 : * Returns true if this is a modulus substitution. (We didn't do this
769 : * with instanceof partially because it causes source files to
770 : * proliferate and partially because we have to port this to C++.)
771 : * @return true if this object is an instance of ModulusSubstitution
772 : */
773 : UBool
774 0 : NFSubstitution::isModulusSubstitution() const {
775 0 : return FALSE;
776 : }
777 :
778 : //===================================================================
779 : // SameValueSubstitution
780 : //===================================================================
781 :
782 : /**
783 : * A substitution that passes the value passed to it through unchanged.
784 : * Represented by == in rule descriptions.
785 : */
786 0 : SameValueSubstitution::SameValueSubstitution(int32_t _pos,
787 : const NFRuleSet* _ruleSet,
788 : const UnicodeString& description,
789 0 : UErrorCode& status)
790 0 : : NFSubstitution(_pos, _ruleSet, description, status)
791 : {
792 0 : if (0 == description.compare(gEqualsEquals, 2)) {
793 : // throw new IllegalArgumentException("== is not a legal token");
794 0 : status = U_PARSE_ERROR;
795 : }
796 0 : }
797 :
798 0 : UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SameValueSubstitution)
799 :
800 : //===================================================================
801 : // MultiplierSubstitution
802 : //===================================================================
803 :
804 0 : UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MultiplierSubstitution)
805 :
806 0 : UBool MultiplierSubstitution::operator==(const NFSubstitution& rhs) const
807 : {
808 0 : return NFSubstitution::operator==(rhs) &&
809 0 : divisor == ((const MultiplierSubstitution*)&rhs)->divisor;
810 : }
811 :
812 :
813 : //===================================================================
814 : // ModulusSubstitution
815 : //===================================================================
816 :
817 : /**
818 : * A substitution that divides the number being formatted by the its rule's
819 : * divisor and formats the remainder. Represented by ">>" in a
820 : * regular rule.
821 : */
822 0 : ModulusSubstitution::ModulusSubstitution(int32_t _pos,
823 : const NFRule* rule,
824 : const NFRule* predecessor,
825 : const NFRuleSet* _ruleSet,
826 : const UnicodeString& description,
827 0 : UErrorCode& status)
828 : : NFSubstitution(_pos, _ruleSet, description, status)
829 0 : , divisor(rule->getDivisor())
830 0 : , ruleToUse(NULL)
831 : {
832 : // the owning rule's divisor controls the behavior of this
833 : // substitution: rather than keeping a backpointer to the rule,
834 : // we keep a copy of the divisor
835 :
836 0 : if (divisor == 0) {
837 0 : status = U_PARSE_ERROR;
838 : }
839 :
840 0 : if (0 == description.compare(gGreaterGreaterGreaterThan, 3)) {
841 : // the >>> token doesn't alter how this substituion calculates the
842 : // values it uses for formatting and parsing, but it changes
843 : // what's done with that value after it's obtained: >>> short-
844 : // circuits the rule-search process and goes straight to the
845 : // specified rule to format the substitution value
846 0 : ruleToUse = predecessor;
847 : }
848 0 : }
849 :
850 0 : UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ModulusSubstitution)
851 :
852 0 : UBool ModulusSubstitution::operator==(const NFSubstitution& rhs) const
853 : {
854 0 : return NFSubstitution::operator==(rhs) &&
855 0 : divisor == ((const ModulusSubstitution*)&rhs)->divisor &&
856 0 : ruleToUse == ((const ModulusSubstitution*)&rhs)->ruleToUse;
857 : }
858 :
859 : //-----------------------------------------------------------------------
860 : // formatting
861 : //-----------------------------------------------------------------------
862 :
863 :
864 : /**
865 : * If this is a >>> substitution, use ruleToUse to fill in
866 : * the substitution. Otherwise, just use the superclass function.
867 : * @param number The number being formatted
868 : * @toInsertInto The string to insert the result of this substitution
869 : * into
870 : * @param pos The position of the rule text in toInsertInto
871 : */
872 : void
873 0 : ModulusSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const
874 : {
875 : // if this isn't a >>> substitution, just use the inherited version
876 : // of this function (which uses either a rule set or a DecimalFormat
877 : // to format its substitution value)
878 0 : if (ruleToUse == NULL) {
879 0 : NFSubstitution::doSubstitution(number, toInsertInto, _pos, recursionCount, status);
880 :
881 : // a >>> substitution goes straight to a particular rule to
882 : // format the substitution value
883 : } else {
884 0 : int64_t numberToFormat = transformNumber(number);
885 0 : ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos(), recursionCount, status);
886 : }
887 0 : }
888 :
889 : /**
890 : * If this is a >>> substitution, use ruleToUse to fill in
891 : * the substitution. Otherwise, just use the superclass function.
892 : * @param number The number being formatted
893 : * @toInsertInto The string to insert the result of this substitution
894 : * into
895 : * @param pos The position of the rule text in toInsertInto
896 : */
897 : void
898 0 : ModulusSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const
899 : {
900 : // if this isn't a >>> substitution, just use the inherited version
901 : // of this function (which uses either a rule set or a DecimalFormat
902 : // to format its substitution value)
903 0 : if (ruleToUse == NULL) {
904 0 : NFSubstitution::doSubstitution(number, toInsertInto, _pos, recursionCount, status);
905 :
906 : // a >>> substitution goes straight to a particular rule to
907 : // format the substitution value
908 : } else {
909 0 : double numberToFormat = transformNumber(number);
910 :
911 0 : ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos(), recursionCount, status);
912 : }
913 0 : }
914 :
915 : //-----------------------------------------------------------------------
916 : // parsing
917 : //-----------------------------------------------------------------------
918 :
919 : /**
920 : * If this is a >>> substitution, match only against ruleToUse.
921 : * Otherwise, use the superclass function.
922 : * @param text The string to parse
923 : * @param parsePosition Ignored on entry, updated on exit to point to
924 : * the first unmatched character.
925 : * @param baseValue The partial parse result prior to calling this
926 : * routine.
927 : */
928 : UBool
929 0 : ModulusSubstitution::doParse(const UnicodeString& text,
930 : ParsePosition& parsePosition,
931 : double baseValue,
932 : double upperBound,
933 : UBool lenientParse,
934 : Formattable& result) const
935 : {
936 : // if this isn't a >>> substitution, we can just use the
937 : // inherited parse() routine to do the parsing
938 0 : if (ruleToUse == NULL) {
939 0 : return NFSubstitution::doParse(text, parsePosition, baseValue, upperBound, lenientParse, result);
940 :
941 : // but if it IS a >>> substitution, we have to do it here: we
942 : // use the specific rule's doParse() method, and then we have to
943 : // do some of the other work of NFRuleSet.parse()
944 : } else {
945 0 : ruleToUse->doParse(text, parsePosition, FALSE, upperBound, result);
946 :
947 0 : if (parsePosition.getIndex() != 0) {
948 0 : UErrorCode status = U_ZERO_ERROR;
949 0 : double tempResult = result.getDouble(status);
950 0 : tempResult = composeRuleValue(tempResult, baseValue);
951 0 : result.setDouble(tempResult);
952 : }
953 :
954 0 : return TRUE;
955 : }
956 : }
957 : /**
958 : * Returns a textual description of the substitution
959 : * @return A textual description of the substitution. This might
960 : * not be identical to the description it was created from, but
961 : * it'll produce the same result.
962 : */
963 : void
964 0 : ModulusSubstitution::toString(UnicodeString& text) const
965 : {
966 : // use tokenChar() to get the character at the beginning and
967 : // end of the substitutin token. In between them will go
968 : // either the name of the rule set it uses, or the pattern of
969 : // the DecimalFormat it uses
970 :
971 0 : if ( ruleToUse != NULL ) { // Must have been a >>> substitution.
972 0 : text.remove();
973 0 : text.append(tokenChar());
974 0 : text.append(tokenChar());
975 0 : text.append(tokenChar());
976 : } else { // Otherwise just use the super-class function.
977 0 : NFSubstitution::toString(text);
978 : }
979 0 : }
980 : //===================================================================
981 : // IntegralPartSubstitution
982 : //===================================================================
983 :
984 0 : UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IntegralPartSubstitution)
985 :
986 :
987 : //===================================================================
988 : // FractionalPartSubstitution
989 : //===================================================================
990 :
991 :
992 : /**
993 : * Constructs a FractionalPartSubstitution. This object keeps a flag
994 : * telling whether it should format by digits or not. In addition,
995 : * it marks the rule set it calls (if any) as a fraction rule set.
996 : */
997 0 : FractionalPartSubstitution::FractionalPartSubstitution(int32_t _pos,
998 : const NFRuleSet* _ruleSet,
999 : const UnicodeString& description,
1000 0 : UErrorCode& status)
1001 : : NFSubstitution(_pos, _ruleSet, description, status)
1002 : , byDigits(FALSE)
1003 0 : , useSpaces(TRUE)
1004 :
1005 : {
1006 : // akk, ruleSet can change in superclass constructor
1007 0 : if (0 == description.compare(gGreaterGreaterThan, 2) ||
1008 0 : 0 == description.compare(gGreaterGreaterGreaterThan, 3) ||
1009 0 : _ruleSet == getRuleSet()) {
1010 0 : byDigits = TRUE;
1011 0 : if (0 == description.compare(gGreaterGreaterGreaterThan, 3)) {
1012 0 : useSpaces = FALSE;
1013 : }
1014 : } else {
1015 : // cast away const
1016 0 : ((NFRuleSet*)getRuleSet())->makeIntoFractionRuleSet();
1017 : }
1018 0 : }
1019 :
1020 : //-----------------------------------------------------------------------
1021 : // formatting
1022 : //-----------------------------------------------------------------------
1023 :
1024 : /**
1025 : * If in "by digits" mode, fills in the substitution one decimal digit
1026 : * at a time using the rule set containing this substitution.
1027 : * Otherwise, uses the superclass function.
1028 : * @param number The number being formatted
1029 : * @param toInsertInto The string to insert the result of formatting
1030 : * the substitution into
1031 : * @param pos The position of the owning rule's rule text in
1032 : * toInsertInto
1033 : */
1034 : void
1035 0 : FractionalPartSubstitution::doSubstitution(double number, UnicodeString& toInsertInto,
1036 : int32_t _pos, int32_t recursionCount, UErrorCode& status) const
1037 : {
1038 : // if we're not in "byDigits" mode, just use the inherited
1039 : // doSubstitution() routine
1040 0 : if (!byDigits) {
1041 0 : NFSubstitution::doSubstitution(number, toInsertInto, _pos, recursionCount, status);
1042 :
1043 : // if we're in "byDigits" mode, transform the value into an integer
1044 : // by moving the decimal point eight places to the right and
1045 : // pulling digits off the right one at a time, formatting each digit
1046 : // as an integer using this substitution's owning rule set
1047 : // (this is slower, but more accurate, than doing it from the
1048 : // other end)
1049 : } else {
1050 : // int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits));
1051 : // // this flag keeps us from formatting trailing zeros. It starts
1052 : // // out false because we're pulling from the right, and switches
1053 : // // to true the first time we encounter a non-zero digit
1054 : // UBool doZeros = FALSE;
1055 : // for (int32_t i = 0; i < kMaxDecimalDigits; i++) {
1056 : // int64_t digit = numberToFormat % 10;
1057 : // if (digit != 0 || doZeros) {
1058 : // if (doZeros && useSpaces) {
1059 : // toInsertInto.insert(_pos + getPos(), gSpace);
1060 : // }
1061 : // doZeros = TRUE;
1062 : // getRuleSet()->format(digit, toInsertInto, _pos + getPos());
1063 : // }
1064 : // numberToFormat /= 10;
1065 : // }
1066 :
1067 0 : DigitList dl;
1068 0 : dl.set(number);
1069 0 : dl.roundFixedPoint(20); // round to 20 fraction digits.
1070 0 : dl.reduce(); // Removes any trailing zeros.
1071 :
1072 0 : UBool pad = FALSE;
1073 0 : for (int32_t didx = dl.getCount()-1; didx>=dl.getDecimalAt(); didx--) {
1074 : // Loop iterates over fraction digits, starting with the LSD.
1075 : // include both real digits from the number, and zeros
1076 : // to the left of the MSD but to the right of the decimal point.
1077 0 : if (pad && useSpaces) {
1078 0 : toInsertInto.insert(_pos + getPos(), gSpace);
1079 : } else {
1080 0 : pad = TRUE;
1081 : }
1082 0 : int64_t digit = didx>=0 ? dl.getDigit(didx) - '0' : 0;
1083 0 : getRuleSet()->format(digit, toInsertInto, _pos + getPos(), recursionCount, status);
1084 : }
1085 :
1086 0 : if (!pad) {
1087 : // hack around lack of precision in digitlist. if we would end up with
1088 : // "foo point" make sure we add a " zero" to the end.
1089 0 : getRuleSet()->format((int64_t)0, toInsertInto, _pos + getPos(), recursionCount, status);
1090 : }
1091 : }
1092 0 : }
1093 :
1094 : //-----------------------------------------------------------------------
1095 : // parsing
1096 : //-----------------------------------------------------------------------
1097 :
1098 : /**
1099 : * If in "by digits" mode, parses the string as if it were a string
1100 : * of individual digits; otherwise, uses the superclass function.
1101 : * @param text The string to parse
1102 : * @param parsePosition Ignored on entry, but updated on exit to point
1103 : * to the first unmatched character
1104 : * @param baseValue The partial parse result prior to entering this
1105 : * function
1106 : * @param upperBound Only consider rules with base values lower than
1107 : * this when filling in the substitution
1108 : * @param lenientParse If true, try matching the text as numerals if
1109 : * matching as words doesn't work
1110 : * @return If the match was successful, the current partial parse
1111 : * result; otherwise new Long(0). The result is either a Long or
1112 : * a Double.
1113 : */
1114 :
1115 : UBool
1116 0 : FractionalPartSubstitution::doParse(const UnicodeString& text,
1117 : ParsePosition& parsePosition,
1118 : double baseValue,
1119 : double /*upperBound*/,
1120 : UBool lenientParse,
1121 : Formattable& resVal) const
1122 : {
1123 : // if we're not in byDigits mode, we can just use the inherited
1124 : // doParse()
1125 0 : if (!byDigits) {
1126 0 : return NFSubstitution::doParse(text, parsePosition, baseValue, 0, lenientParse, resVal);
1127 :
1128 : // if we ARE in byDigits mode, parse the text one digit at a time
1129 : // using this substitution's owning rule set (we do this by setting
1130 : // upperBound to 10 when calling doParse() ) until we reach
1131 : // nonmatching text
1132 : } else {
1133 0 : UnicodeString workText(text);
1134 0 : ParsePosition workPos(1);
1135 0 : double result = 0;
1136 : int32_t digit;
1137 : // double p10 = 0.1;
1138 :
1139 0 : DigitList dl;
1140 0 : NumberFormat* fmt = NULL;
1141 0 : while (workText.length() > 0 && workPos.getIndex() != 0) {
1142 0 : workPos.setIndex(0);
1143 0 : Formattable temp;
1144 0 : getRuleSet()->parse(workText, workPos, 10, temp);
1145 0 : UErrorCode status = U_ZERO_ERROR;
1146 0 : digit = temp.getLong(status);
1147 : // digit = temp.getType() == Formattable::kLong ?
1148 : // temp.getLong() :
1149 : // (int32_t)temp.getDouble();
1150 :
1151 0 : if (lenientParse && workPos.getIndex() == 0) {
1152 0 : if (!fmt) {
1153 0 : status = U_ZERO_ERROR;
1154 0 : fmt = NumberFormat::createInstance(status);
1155 0 : if (U_FAILURE(status)) {
1156 0 : delete fmt;
1157 0 : fmt = NULL;
1158 : }
1159 : }
1160 0 : if (fmt) {
1161 0 : fmt->parse(workText, temp, workPos);
1162 0 : digit = temp.getLong(status);
1163 : }
1164 : }
1165 :
1166 0 : if (workPos.getIndex() != 0) {
1167 0 : dl.append((char)('0' + digit));
1168 : // result += digit * p10;
1169 : // p10 /= 10;
1170 0 : parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex());
1171 0 : workText.removeBetween(0, workPos.getIndex());
1172 0 : while (workText.length() > 0 && workText.charAt(0) == gSpace) {
1173 0 : workText.removeBetween(0, 1);
1174 0 : parsePosition.setIndex(parsePosition.getIndex() + 1);
1175 : }
1176 : }
1177 : }
1178 0 : delete fmt;
1179 :
1180 0 : result = dl.getCount() == 0 ? 0 : dl.getDouble();
1181 0 : result = composeRuleValue(result, baseValue);
1182 0 : resVal.setDouble(result);
1183 0 : return TRUE;
1184 : }
1185 : }
1186 :
1187 : UBool
1188 0 : FractionalPartSubstitution::operator==(const NFSubstitution& rhs) const
1189 : {
1190 0 : return NFSubstitution::operator==(rhs) &&
1191 0 : ((const FractionalPartSubstitution*)&rhs)->byDigits == byDigits;
1192 : }
1193 :
1194 0 : UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FractionalPartSubstitution)
1195 :
1196 :
1197 : //===================================================================
1198 : // AbsoluteValueSubstitution
1199 : //===================================================================
1200 :
1201 0 : UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AbsoluteValueSubstitution)
1202 :
1203 : //===================================================================
1204 : // NumeratorSubstitution
1205 : //===================================================================
1206 :
1207 : void
1208 0 : NumeratorSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t apos, int32_t recursionCount, UErrorCode& status) const {
1209 : // perform a transformation on the number being formatted that
1210 : // is dependent on the type of substitution this is
1211 :
1212 0 : double numberToFormat = transformNumber(number);
1213 0 : int64_t longNF = util64_fromDouble(numberToFormat);
1214 :
1215 0 : const NFRuleSet* aruleSet = getRuleSet();
1216 0 : if (withZeros && aruleSet != NULL) {
1217 : // if there are leading zeros in the decimal expansion then emit them
1218 0 : int64_t nf =longNF;
1219 0 : int32_t len = toInsertInto.length();
1220 0 : while ((nf *= 10) < denominator) {
1221 0 : toInsertInto.insert(apos + getPos(), gSpace);
1222 0 : aruleSet->format((int64_t)0, toInsertInto, apos + getPos(), recursionCount, status);
1223 : }
1224 0 : apos += toInsertInto.length() - len;
1225 : }
1226 :
1227 : // if the result is an integer, from here on out we work in integer
1228 : // space (saving time and memory and preserving accuracy)
1229 0 : if (numberToFormat == longNF && aruleSet != NULL) {
1230 0 : aruleSet->format(longNF, toInsertInto, apos + getPos(), recursionCount, status);
1231 :
1232 : // if the result isn't an integer, then call either our rule set's
1233 : // format() method or our DecimalFormat's format() method to
1234 : // format the result
1235 : } else {
1236 0 : if (aruleSet != NULL) {
1237 0 : aruleSet->format(numberToFormat, toInsertInto, apos + getPos(), recursionCount, status);
1238 : } else {
1239 0 : UnicodeString temp;
1240 0 : getNumberFormat()->format(numberToFormat, temp, status);
1241 0 : toInsertInto.insert(apos + getPos(), temp);
1242 : }
1243 : }
1244 0 : }
1245 :
1246 : UBool
1247 0 : NumeratorSubstitution::doParse(const UnicodeString& text,
1248 : ParsePosition& parsePosition,
1249 : double baseValue,
1250 : double upperBound,
1251 : UBool /*lenientParse*/,
1252 : Formattable& result) const
1253 : {
1254 : // we don't have to do anything special to do the parsing here,
1255 : // but we have to turn lenient parsing off-- if we leave it on,
1256 : // it SERIOUSLY messes up the algorithm
1257 :
1258 : // if withZeros is true, we need to count the zeros
1259 : // and use that to adjust the parse result
1260 0 : UErrorCode status = U_ZERO_ERROR;
1261 0 : int32_t zeroCount = 0;
1262 0 : UnicodeString workText(text);
1263 :
1264 0 : if (withZeros) {
1265 0 : ParsePosition workPos(1);
1266 0 : Formattable temp;
1267 :
1268 0 : while (workText.length() > 0 && workPos.getIndex() != 0) {
1269 0 : workPos.setIndex(0);
1270 0 : getRuleSet()->parse(workText, workPos, 1, temp); // parse zero or nothing at all
1271 0 : if (workPos.getIndex() == 0) {
1272 : // we failed, either there were no more zeros, or the number was formatted with digits
1273 : // either way, we're done
1274 0 : break;
1275 : }
1276 :
1277 0 : ++zeroCount;
1278 0 : parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex());
1279 0 : workText.remove(0, workPos.getIndex());
1280 0 : while (workText.length() > 0 && workText.charAt(0) == gSpace) {
1281 0 : workText.remove(0, 1);
1282 0 : parsePosition.setIndex(parsePosition.getIndex() + 1);
1283 : }
1284 : }
1285 :
1286 0 : workText = text;
1287 0 : workText.remove(0, (int32_t)parsePosition.getIndex());
1288 0 : parsePosition.setIndex(0);
1289 : }
1290 :
1291 : // we've parsed off the zeros, now let's parse the rest from our current position
1292 0 : NFSubstitution::doParse(workText, parsePosition, withZeros ? 1 : baseValue, upperBound, FALSE, result);
1293 :
1294 0 : if (withZeros) {
1295 : // any base value will do in this case. is there a way to
1296 : // force this to not bother trying all the base values?
1297 :
1298 : // compute the 'effective' base and prescale the value down
1299 0 : int64_t n = result.getLong(status); // force conversion!
1300 0 : int64_t d = 1;
1301 0 : int32_t pow = 0;
1302 0 : while (d <= n) {
1303 0 : d *= 10;
1304 0 : ++pow;
1305 : }
1306 : // now add the zeros
1307 0 : while (zeroCount > 0) {
1308 0 : d *= 10;
1309 0 : --zeroCount;
1310 : }
1311 : // d is now our true denominator
1312 0 : result.setDouble((double)n/(double)d);
1313 : }
1314 :
1315 0 : return TRUE;
1316 : }
1317 :
1318 : UBool
1319 0 : NumeratorSubstitution::operator==(const NFSubstitution& rhs) const
1320 : {
1321 0 : return NFSubstitution::operator==(rhs) &&
1322 0 : denominator == ((const NumeratorSubstitution*)&rhs)->denominator;
1323 : }
1324 :
1325 0 : UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumeratorSubstitution)
1326 :
1327 : const UChar NumeratorSubstitution::LTLT[] = { 0x003c, 0x003c };
1328 :
1329 : U_NAMESPACE_END
1330 :
1331 : /* U_HAVE_RBNF */
1332 : #endif
1333 :
|