|
1 /* |
|
2 ****************************************************************************** |
|
3 * Copyright (C) 1997-2012, 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 |
|
56 virtual int64_t transformNumber(int64_t number) const { return number; } |
|
57 virtual double transformNumber(double number) const { return number; } |
|
58 virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const { return newRuleValue; } |
|
59 virtual double calcUpperBound(double oldUpperBound) const { return oldUpperBound; } |
|
60 virtual UChar tokenChar() const { return (UChar)0x003d; } // '=' |
|
61 |
|
62 public: |
|
63 static UClassID getStaticClassID(void); |
|
64 virtual UClassID getDynamicClassID(void) const; |
|
65 }; |
|
66 |
|
67 SameValueSubstitution::~SameValueSubstitution() {} |
|
68 |
|
69 class MultiplierSubstitution : public NFSubstitution { |
|
70 double divisor; |
|
71 int64_t ldivisor; |
|
72 |
|
73 public: |
|
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 |
|
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 |
|
100 virtual int64_t transformNumber(int64_t number) const { |
|
101 return number / ldivisor; |
|
102 } |
|
103 |
|
104 virtual double transformNumber(double number) const { |
|
105 if (getRuleSet()) { |
|
106 return uprv_floor(number / divisor); |
|
107 } else { |
|
108 return number/divisor; |
|
109 } |
|
110 } |
|
111 |
|
112 virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const { |
|
113 return newRuleValue * divisor; |
|
114 } |
|
115 |
|
116 virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor; } |
|
117 |
|
118 virtual UChar tokenChar() const { return (UChar)0x003c; } // '<' |
|
119 |
|
120 public: |
|
121 static UClassID getStaticClassID(void); |
|
122 virtual UClassID getDynamicClassID(void) const; |
|
123 }; |
|
124 |
|
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 |
|
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) const; |
|
153 virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos) const; |
|
154 |
|
155 virtual int64_t transformNumber(int64_t number) const { return number % ldivisor; } |
|
156 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 |
|
165 virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { |
|
166 return oldRuleValue - uprv_fmod(oldRuleValue, divisor) + newRuleValue; |
|
167 } |
|
168 |
|
169 virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor; } |
|
170 |
|
171 virtual UBool isModulusSubstitution() const { return TRUE; } |
|
172 |
|
173 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 |
|
182 ModulusSubstitution::~ModulusSubstitution() {} |
|
183 |
|
184 class IntegralPartSubstitution : public NFSubstitution { |
|
185 public: |
|
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 |
|
194 virtual int64_t transformNumber(int64_t number) const { return number; } |
|
195 virtual double transformNumber(double number) const { return uprv_floor(number); } |
|
196 virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue + oldRuleValue; } |
|
197 virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX; } |
|
198 virtual UChar tokenChar() const { return (UChar)0x003c; } // '<' |
|
199 |
|
200 public: |
|
201 static UClassID getStaticClassID(void); |
|
202 virtual UClassID getDynamicClassID(void) const; |
|
203 }; |
|
204 |
|
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) const; |
|
222 virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/) const {} |
|
223 virtual int64_t transformNumber(int64_t /*number*/) const { return 0; } |
|
224 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 |
|
233 virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue + oldRuleValue; } |
|
234 virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0.0; } |
|
235 virtual UChar tokenChar() const { return (UChar)0x003e; } // '>' |
|
236 |
|
237 public: |
|
238 static UClassID getStaticClassID(void); |
|
239 virtual UClassID getDynamicClassID(void) const; |
|
240 }; |
|
241 |
|
242 FractionalPartSubstitution::~FractionalPartSubstitution() {} |
|
243 |
|
244 class AbsoluteValueSubstitution : public NFSubstitution { |
|
245 public: |
|
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 |
|
254 virtual int64_t transformNumber(int64_t number) const { return number >= 0 ? number : -number; } |
|
255 virtual double transformNumber(double number) const { return uprv_fabs(number); } |
|
256 virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const { return -newRuleValue; } |
|
257 virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX; } |
|
258 virtual UChar tokenChar() const { return (UChar)0x003e; } // '>' |
|
259 |
|
260 public: |
|
261 static UClassID getStaticClassID(void); |
|
262 virtual UClassID getDynamicClassID(void) const; |
|
263 }; |
|
264 |
|
265 AbsoluteValueSubstitution::~AbsoluteValueSubstitution() {} |
|
266 |
|
267 class NumeratorSubstitution : public NFSubstitution { |
|
268 double denominator; |
|
269 int64_t ldenominator; |
|
270 UBool withZeros; |
|
271 public: |
|
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 } |
|
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 |
|
294 virtual int64_t transformNumber(int64_t number) const { return number * ldenominator; } |
|
295 virtual double transformNumber(double number) const { return uprv_round(number * denominator); } |
|
296 |
|
297 virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/) const {} |
|
298 virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos) 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 |
|
306 virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue / oldRuleValue; } |
|
307 virtual double calcUpperBound(double /*oldUpperBound*/) const { return denominator; } |
|
308 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 |
|
317 NumeratorSubstitution::~NumeratorSubstitution() {} |
|
318 |
|
319 class NullSubstitution : public NFSubstitution { |
|
320 public: |
|
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 |
|
329 virtual void toString(UnicodeString& /*result*/) const {} |
|
330 virtual void doSubstitution(double /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/) const {} |
|
331 virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/) const {} |
|
332 virtual int64_t transformNumber(int64_t /*number*/) const { return 0; } |
|
333 virtual double transformNumber(double /*number*/) const { return 0; } |
|
334 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; } |
|
341 virtual double composeRuleValue(double /*newRuleValue*/, double /*oldRuleValue*/) const { return 0.0; } // never called |
|
342 virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0; } // never called |
|
343 virtual UBool isNullSubstitution() const { return TRUE; } |
|
344 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 |
|
351 NullSubstitution::~NullSubstitution() {} |
|
352 |
|
353 NFSubstitution* |
|
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 |
|
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 |
|
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 |
|
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 |
|
571 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 |
|
605 NFSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos) 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); |
|
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); |
|
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 |
|
639 NFSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) 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); |
|
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); |
|
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 |
|
702 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 |
|
783 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 |
|
794 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 */ |
|
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 */ |
|
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 |
|
897 ModulusSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos) 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); |
|
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()); |
|
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 |
|
922 ModulusSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) 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); |
|
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()); |
|
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 |
|
953 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 |
|
988 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 |
|
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 |
|
1060 FractionalPartSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) const |
|
1061 { |
|
1062 // if we're not in "byDigits" mode, just use the inherited |
|
1063 // doSubstitution() routine |
|
1064 if (!byDigits) { |
|
1065 NFSubstitution::doSubstitution(number, toInsertInto, _pos); |
|
1066 |
|
1067 // if we're in "byDigits" mode, transform the value into an integer |
|
1068 // by moving the decimal point eight places to the right and |
|
1069 // pulling digits off the right one at a time, formatting each digit |
|
1070 // as an integer using this substitution's owning rule set |
|
1071 // (this is slower, but more accurate, than doing it from the |
|
1072 // other end) |
|
1073 } else { |
|
1074 // int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits)); |
|
1075 // // this flag keeps us from formatting trailing zeros. It starts |
|
1076 // // out false because we're pulling from the right, and switches |
|
1077 // // to true the first time we encounter a non-zero digit |
|
1078 // UBool doZeros = FALSE; |
|
1079 // for (int32_t i = 0; i < kMaxDecimalDigits; i++) { |
|
1080 // int64_t digit = numberToFormat % 10; |
|
1081 // if (digit != 0 || doZeros) { |
|
1082 // if (doZeros && useSpaces) { |
|
1083 // toInsertInto.insert(_pos + getPos(), gSpace); |
|
1084 // } |
|
1085 // doZeros = TRUE; |
|
1086 // getRuleSet()->format(digit, toInsertInto, _pos + getPos()); |
|
1087 // } |
|
1088 // numberToFormat /= 10; |
|
1089 // } |
|
1090 |
|
1091 DigitList dl; |
|
1092 dl.set(number); |
|
1093 dl.roundFixedPoint(20); // round to 20 fraction digits. |
|
1094 dl.reduce(); // Removes any trailing zeros. |
|
1095 |
|
1096 UBool pad = FALSE; |
|
1097 for (int32_t didx = dl.getCount()-1; didx>=dl.getDecimalAt(); didx--) { |
|
1098 // Loop iterates over fraction digits, starting with the LSD. |
|
1099 // include both real digits from the number, and zeros |
|
1100 // to the left of the MSD but to the right of the decimal point. |
|
1101 if (pad && useSpaces) { |
|
1102 toInsertInto.insert(_pos + getPos(), gSpace); |
|
1103 } else { |
|
1104 pad = TRUE; |
|
1105 } |
|
1106 int64_t digit = didx>=0 ? dl.getDigit(didx) - '0' : 0; |
|
1107 getRuleSet()->format(digit, toInsertInto, _pos + getPos()); |
|
1108 } |
|
1109 |
|
1110 if (!pad) { |
|
1111 // hack around lack of precision in digitlist. if we would end up with |
|
1112 // "foo point" make sure we add a " zero" to the end. |
|
1113 getRuleSet()->format((int64_t)0, toInsertInto, _pos + getPos()); |
|
1114 } |
|
1115 } |
|
1116 } |
|
1117 |
|
1118 //----------------------------------------------------------------------- |
|
1119 // parsing |
|
1120 //----------------------------------------------------------------------- |
|
1121 |
|
1122 /** |
|
1123 * If in "by digits" mode, parses the string as if it were a string |
|
1124 * of individual digits; otherwise, uses the superclass function. |
|
1125 * @param text The string to parse |
|
1126 * @param parsePosition Ignored on entry, but updated on exit to point |
|
1127 * to the first unmatched character |
|
1128 * @param baseValue The partial parse result prior to entering this |
|
1129 * function |
|
1130 * @param upperBound Only consider rules with base values lower than |
|
1131 * this when filling in the substitution |
|
1132 * @param lenientParse If true, try matching the text as numerals if |
|
1133 * matching as words doesn't work |
|
1134 * @return If the match was successful, the current partial parse |
|
1135 * result; otherwise new Long(0). The result is either a Long or |
|
1136 * a Double. |
|
1137 */ |
|
1138 |
|
1139 UBool |
|
1140 FractionalPartSubstitution::doParse(const UnicodeString& text, |
|
1141 ParsePosition& parsePosition, |
|
1142 double baseValue, |
|
1143 double /*upperBound*/, |
|
1144 UBool lenientParse, |
|
1145 Formattable& resVal) const |
|
1146 { |
|
1147 // if we're not in byDigits mode, we can just use the inherited |
|
1148 // doParse() |
|
1149 if (!byDigits) { |
|
1150 return NFSubstitution::doParse(text, parsePosition, baseValue, 0, lenientParse, resVal); |
|
1151 |
|
1152 // if we ARE in byDigits mode, parse the text one digit at a time |
|
1153 // using this substitution's owning rule set (we do this by setting |
|
1154 // upperBound to 10 when calling doParse() ) until we reach |
|
1155 // nonmatching text |
|
1156 } else { |
|
1157 UnicodeString workText(text); |
|
1158 ParsePosition workPos(1); |
|
1159 double result = 0; |
|
1160 int32_t digit; |
|
1161 // double p10 = 0.1; |
|
1162 |
|
1163 DigitList dl; |
|
1164 NumberFormat* fmt = NULL; |
|
1165 while (workText.length() > 0 && workPos.getIndex() != 0) { |
|
1166 workPos.setIndex(0); |
|
1167 Formattable temp; |
|
1168 getRuleSet()->parse(workText, workPos, 10, temp); |
|
1169 UErrorCode status = U_ZERO_ERROR; |
|
1170 digit = temp.getLong(status); |
|
1171 // digit = temp.getType() == Formattable::kLong ? |
|
1172 // temp.getLong() : |
|
1173 // (int32_t)temp.getDouble(); |
|
1174 |
|
1175 if (lenientParse && workPos.getIndex() == 0) { |
|
1176 if (!fmt) { |
|
1177 status = U_ZERO_ERROR; |
|
1178 fmt = NumberFormat::createInstance(status); |
|
1179 if (U_FAILURE(status)) { |
|
1180 delete fmt; |
|
1181 fmt = NULL; |
|
1182 } |
|
1183 } |
|
1184 if (fmt) { |
|
1185 fmt->parse(workText, temp, workPos); |
|
1186 digit = temp.getLong(status); |
|
1187 } |
|
1188 } |
|
1189 |
|
1190 if (workPos.getIndex() != 0) { |
|
1191 dl.append((char)('0' + digit)); |
|
1192 // result += digit * p10; |
|
1193 // p10 /= 10; |
|
1194 parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()); |
|
1195 workText.removeBetween(0, workPos.getIndex()); |
|
1196 while (workText.length() > 0 && workText.charAt(0) == gSpace) { |
|
1197 workText.removeBetween(0, 1); |
|
1198 parsePosition.setIndex(parsePosition.getIndex() + 1); |
|
1199 } |
|
1200 } |
|
1201 } |
|
1202 delete fmt; |
|
1203 |
|
1204 result = dl.getCount() == 0 ? 0 : dl.getDouble(); |
|
1205 result = composeRuleValue(result, baseValue); |
|
1206 resVal.setDouble(result); |
|
1207 return TRUE; |
|
1208 } |
|
1209 } |
|
1210 |
|
1211 UBool |
|
1212 FractionalPartSubstitution::operator==(const NFSubstitution& rhs) const |
|
1213 { |
|
1214 return NFSubstitution::operator==(rhs) && |
|
1215 ((const FractionalPartSubstitution*)&rhs)->byDigits == byDigits; |
|
1216 } |
|
1217 |
|
1218 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FractionalPartSubstitution) |
|
1219 |
|
1220 |
|
1221 //=================================================================== |
|
1222 // AbsoluteValueSubstitution |
|
1223 //=================================================================== |
|
1224 |
|
1225 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AbsoluteValueSubstitution) |
|
1226 |
|
1227 //=================================================================== |
|
1228 // NumeratorSubstitution |
|
1229 //=================================================================== |
|
1230 |
|
1231 void |
|
1232 NumeratorSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t apos) const { |
|
1233 // perform a transformation on the number being formatted that |
|
1234 // is dependent on the type of substitution this is |
|
1235 |
|
1236 double numberToFormat = transformNumber(number); |
|
1237 int64_t longNF = util64_fromDouble(numberToFormat); |
|
1238 |
|
1239 const NFRuleSet* aruleSet = getRuleSet(); |
|
1240 if (withZeros && aruleSet != NULL) { |
|
1241 // if there are leading zeros in the decimal expansion then emit them |
|
1242 int64_t nf =longNF; |
|
1243 int32_t len = toInsertInto.length(); |
|
1244 while ((nf *= 10) < denominator) { |
|
1245 toInsertInto.insert(apos + getPos(), gSpace); |
|
1246 aruleSet->format((int64_t)0, toInsertInto, apos + getPos()); |
|
1247 } |
|
1248 apos += toInsertInto.length() - len; |
|
1249 } |
|
1250 |
|
1251 // if the result is an integer, from here on out we work in integer |
|
1252 // space (saving time and memory and preserving accuracy) |
|
1253 if (numberToFormat == longNF && aruleSet != NULL) { |
|
1254 aruleSet->format(longNF, toInsertInto, apos + getPos()); |
|
1255 |
|
1256 // if the result isn't an integer, then call either our rule set's |
|
1257 // format() method or our DecimalFormat's format() method to |
|
1258 // format the result |
|
1259 } else { |
|
1260 if (aruleSet != NULL) { |
|
1261 aruleSet->format(numberToFormat, toInsertInto, apos + getPos()); |
|
1262 } else { |
|
1263 UErrorCode status = U_ZERO_ERROR; |
|
1264 UnicodeString temp; |
|
1265 getNumberFormat()->format(numberToFormat, temp, status); |
|
1266 toInsertInto.insert(apos + getPos(), temp); |
|
1267 } |
|
1268 } |
|
1269 } |
|
1270 |
|
1271 UBool |
|
1272 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 |
|
1344 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 |