| |
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 |