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