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