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