/src/icu/source/i18n/rbnf.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 Corporation  | 
6  |  | * and others. All Rights Reserved.  | 
7  |  | *******************************************************************************  | 
8  |  | */  | 
9  |  |  | 
10  |  | #include "unicode/utypes.h"  | 
11  |  | #include "utypeinfo.h"  // for 'typeid' to work  | 
12  |  |  | 
13  |  | #include "unicode/rbnf.h"  | 
14  |  |  | 
15  |  | #if U_HAVE_RBNF  | 
16  |  |  | 
17  |  | #include "unicode/normlzr.h"  | 
18  |  | #include "unicode/plurfmt.h"  | 
19  |  | #include "unicode/tblcoll.h"  | 
20  |  | #include "unicode/uchar.h"  | 
21  |  | #include "unicode/ucol.h"  | 
22  |  | #include "unicode/uloc.h"  | 
23  |  | #include "unicode/unum.h"  | 
24  |  | #include "unicode/ures.h"  | 
25  |  | #include "unicode/ustring.h"  | 
26  |  | #include "unicode/utf16.h"  | 
27  |  | #include "unicode/udata.h"  | 
28  |  | #include "unicode/udisplaycontext.h"  | 
29  |  | #include "unicode/brkiter.h"  | 
30  |  | #include "unicode/ucasemap.h"  | 
31  |  |  | 
32  |  | #include "cmemory.h"  | 
33  |  | #include "cstring.h"  | 
34  |  | #include "patternprops.h"  | 
35  |  | #include "uresimp.h"  | 
36  |  | #include "nfrs.h"  | 
37  |  | #include "number_decimalquantity.h"  | 
38  |  |  | 
39  |  | // debugging  | 
40  |  | // #define RBNF_DEBUG  | 
41  |  |  | 
42  |  | #ifdef RBNF_DEBUG  | 
43  |  | #include <stdio.h>  | 
44  |  | #endif  | 
45  |  |  | 
46  | 0  | #define U_ICUDATA_RBNF U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "rbnf"  | 
47  |  |  | 
48  |  | static const UChar gPercentPercent[] =  | 
49  |  | { | 
50  |  |     0x25, 0x25, 0  | 
51  |  | }; /* "%%" */  | 
52  |  |  | 
53  |  | // All urbnf objects are created through openRules, so we init all of the  | 
54  |  | // Unicode string constants required by rbnf, nfrs, or nfr here.  | 
55  |  | static const UChar gLenientParse[] =  | 
56  |  | { | 
57  |  |     0x25, 0x25, 0x6C, 0x65, 0x6E, 0x69, 0x65, 0x6E, 0x74, 0x2D, 0x70, 0x61, 0x72, 0x73, 0x65, 0x3A, 0  | 
58  |  | }; /* "%%lenient-parse:" */  | 
59  |  | static const UChar gSemiColon = 0x003B;  | 
60  |  | static const UChar gSemiPercent[] =  | 
61  |  | { | 
62  |  |     0x3B, 0x25, 0  | 
63  |  | }; /* ";%" */  | 
64  |  |  | 
65  | 0  | #define kSomeNumberOfBitsDiv2 22  | 
66  | 0  | #define kHalfMaxDouble (double)(1 << kSomeNumberOfBitsDiv2)  | 
67  | 0  | #define kMaxDouble (kHalfMaxDouble * kHalfMaxDouble)  | 
68  |  |  | 
69  |  | U_NAMESPACE_BEGIN  | 
70  |  |  | 
71  |  | using number::impl::DecimalQuantity;  | 
72  |  |  | 
73  |  | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedNumberFormat)  | 
74  |  |  | 
75  |  | /*  | 
76  |  | This is a utility class. It does not use ICU's RTTI.  | 
77  |  | If ICU's RTTI is needed again, you can uncomment the RTTI code and derive from UObject.  | 
78  |  | Please make sure that intltest passes on Windows in Release mode,  | 
79  |  | since the string pooling per compilation unit will mess up how RTTI works.  | 
80  |  | The RTTI code was also removed due to lack of code coverage.  | 
81  |  | */  | 
82  |  | class LocalizationInfo : public UMemory { | 
83  |  | protected:  | 
84  |  |     virtual ~LocalizationInfo();  | 
85  |  |     uint32_t refcount;  | 
86  |  |       | 
87  |  | public:  | 
88  | 0  |     LocalizationInfo() : refcount(0) {} | 
89  |  |       | 
90  | 0  |     LocalizationInfo* ref(void) { | 
91  | 0  |         ++refcount;  | 
92  | 0  |         return this;  | 
93  | 0  |     }  | 
94  |  |       | 
95  | 0  |     LocalizationInfo* unref(void) { | 
96  | 0  |         if (refcount && --refcount == 0) { | 
97  | 0  |             delete this;  | 
98  | 0  |         }  | 
99  | 0  |         return NULL;  | 
100  | 0  |     }  | 
101  |  |       | 
102  |  |     virtual bool operator==(const LocalizationInfo* rhs) const;  | 
103  | 0  |     inline  bool operator!=(const LocalizationInfo* rhs) const { return !operator==(rhs); } | 
104  |  |       | 
105  |  |     virtual int32_t getNumberOfRuleSets(void) const = 0;  | 
106  |  |     virtual const UChar* getRuleSetName(int32_t index) const = 0;  | 
107  |  |     virtual int32_t getNumberOfDisplayLocales(void) const = 0;  | 
108  |  |     virtual const UChar* getLocaleName(int32_t index) const = 0;  | 
109  |  |     virtual const UChar* getDisplayName(int32_t localeIndex, int32_t ruleIndex) const = 0;  | 
110  |  |       | 
111  |  |     virtual int32_t indexForLocale(const UChar* locale) const;  | 
112  |  |     virtual int32_t indexForRuleSet(const UChar* ruleset) const;  | 
113  |  |       | 
114  |  | //    virtual UClassID getDynamicClassID() const = 0;  | 
115  |  | //    static UClassID getStaticClassID(void);  | 
116  |  | };  | 
117  |  |  | 
118  | 0  | LocalizationInfo::~LocalizationInfo() {} | 
119  |  |  | 
120  |  | //UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(LocalizationInfo)  | 
121  |  |  | 
122  |  | // if both strings are NULL, this returns TRUE  | 
123  |  | static UBool   | 
124  | 0  | streq(const UChar* lhs, const UChar* rhs) { | 
125  | 0  |     if (rhs == lhs) { | 
126  | 0  |         return TRUE;  | 
127  | 0  |     }  | 
128  | 0  |     if (lhs && rhs) { | 
129  | 0  |         return u_strcmp(lhs, rhs) == 0;  | 
130  | 0  |     }  | 
131  | 0  |     return FALSE;  | 
132  | 0  | }  | 
133  |  |  | 
134  |  | bool  | 
135  | 0  | LocalizationInfo::operator==(const LocalizationInfo* rhs) const { | 
136  | 0  |     if (rhs) { | 
137  | 0  |         if (this == rhs) { | 
138  | 0  |             return TRUE;  | 
139  | 0  |         }  | 
140  |  |           | 
141  | 0  |         int32_t rsc = getNumberOfRuleSets();  | 
142  | 0  |         if (rsc == rhs->getNumberOfRuleSets()) { | 
143  | 0  |             for (int i = 0; i < rsc; ++i) { | 
144  | 0  |                 if (!streq(getRuleSetName(i), rhs->getRuleSetName(i))) { | 
145  | 0  |                     return FALSE;  | 
146  | 0  |                 }  | 
147  | 0  |             }  | 
148  | 0  |             int32_t dlc = getNumberOfDisplayLocales();  | 
149  | 0  |             if (dlc == rhs->getNumberOfDisplayLocales()) { | 
150  | 0  |                 for (int i = 0; i < dlc; ++i) { | 
151  | 0  |                     const UChar* locale = getLocaleName(i);  | 
152  | 0  |                     int32_t ix = rhs->indexForLocale(locale);  | 
153  |  |                     // if no locale, ix is -1, getLocaleName returns null, so streq returns false  | 
154  | 0  |                     if (!streq(locale, rhs->getLocaleName(ix))) { | 
155  | 0  |                         return FALSE;  | 
156  | 0  |                     }  | 
157  | 0  |                     for (int j = 0; j < rsc; ++j) { | 
158  | 0  |                         if (!streq(getDisplayName(i, j), rhs->getDisplayName(ix, j))) { | 
159  | 0  |                             return FALSE;  | 
160  | 0  |                         }  | 
161  | 0  |                     }  | 
162  | 0  |                 }  | 
163  | 0  |                 return TRUE;  | 
164  | 0  |             }  | 
165  | 0  |         }  | 
166  | 0  |     }  | 
167  | 0  |     return FALSE;  | 
168  | 0  | }  | 
169  |  |  | 
170  |  | int32_t  | 
171  | 0  | LocalizationInfo::indexForLocale(const UChar* locale) const { | 
172  | 0  |     for (int i = 0; i < getNumberOfDisplayLocales(); ++i) { | 
173  | 0  |         if (streq(locale, getLocaleName(i))) { | 
174  | 0  |             return i;  | 
175  | 0  |         }  | 
176  | 0  |     }  | 
177  | 0  |     return -1;  | 
178  | 0  | }  | 
179  |  |  | 
180  |  | int32_t  | 
181  | 0  | LocalizationInfo::indexForRuleSet(const UChar* ruleset) const { | 
182  | 0  |     if (ruleset) { | 
183  | 0  |         for (int i = 0; i < getNumberOfRuleSets(); ++i) { | 
184  | 0  |             if (streq(ruleset, getRuleSetName(i))) { | 
185  | 0  |                 return i;  | 
186  | 0  |             }  | 
187  | 0  |         }  | 
188  | 0  |     }  | 
189  | 0  |     return -1;  | 
190  | 0  | }  | 
191  |  |  | 
192  |  |  | 
193  |  | typedef void (*Fn_Deleter)(void*);  | 
194  |  |  | 
195  |  | class VArray { | 
196  |  |     void** buf;  | 
197  |  |     int32_t cap;  | 
198  |  |     int32_t size;  | 
199  |  |     Fn_Deleter deleter;  | 
200  |  | public:  | 
201  | 0  |     VArray() : buf(NULL), cap(0), size(0), deleter(NULL) {} | 
202  |  |       | 
203  | 0  |     VArray(Fn_Deleter del) : buf(NULL), cap(0), size(0), deleter(del) {} | 
204  |  |       | 
205  | 0  |     ~VArray() { | 
206  | 0  |         if (deleter) { | 
207  | 0  |             for (int i = 0; i < size; ++i) { | 
208  | 0  |                 (*deleter)(buf[i]);  | 
209  | 0  |             }  | 
210  | 0  |         }  | 
211  | 0  |         uprv_free(buf);   | 
212  | 0  |     }  | 
213  |  |       | 
214  | 0  |     int32_t length() { | 
215  | 0  |         return size;  | 
216  | 0  |     }  | 
217  |  |       | 
218  | 0  |     void add(void* elem, UErrorCode& status) { | 
219  | 0  |         if (U_SUCCESS(status)) { | 
220  | 0  |             if (size == cap) { | 
221  | 0  |                 if (cap == 0) { | 
222  | 0  |                     cap = 1;  | 
223  | 0  |                 } else if (cap < 256) { | 
224  | 0  |                     cap *= 2;  | 
225  | 0  |                 } else { | 
226  | 0  |                     cap += 256;  | 
227  | 0  |                 }  | 
228  | 0  |                 if (buf == NULL) { | 
229  | 0  |                     buf = (void**)uprv_malloc(cap * sizeof(void*));  | 
230  | 0  |                 } else { | 
231  | 0  |                     buf = (void**)uprv_realloc(buf, cap * sizeof(void*));  | 
232  | 0  |                 }  | 
233  | 0  |                 if (buf == NULL) { | 
234  |  |                     // if we couldn't realloc, we leak the memory we've already allocated, but we're in deep trouble anyway  | 
235  | 0  |                     status = U_MEMORY_ALLOCATION_ERROR;  | 
236  | 0  |                     return;  | 
237  | 0  |                 }  | 
238  | 0  |                 void* start = &buf[size];  | 
239  | 0  |                 size_t count = (cap - size) * sizeof(void*);  | 
240  | 0  |                 uprv_memset(start, 0, count); // fill with nulls, just because  | 
241  | 0  |             }  | 
242  | 0  |             buf[size++] = elem;  | 
243  | 0  |         }  | 
244  | 0  |     }  | 
245  |  |       | 
246  | 0  |     void** release(void) { | 
247  | 0  |         void** result = buf;  | 
248  | 0  |         buf = NULL;  | 
249  | 0  |         cap = 0;  | 
250  | 0  |         size = 0;  | 
251  | 0  |         return result;  | 
252  | 0  |     }  | 
253  |  | };  | 
254  |  |  | 
255  |  | class LocDataParser;  | 
256  |  |  | 
257  |  | class StringLocalizationInfo : public LocalizationInfo { | 
258  |  |     UChar* info;  | 
259  |  |     UChar*** data;  | 
260  |  |     int32_t numRuleSets;  | 
261  |  |     int32_t numLocales;  | 
262  |  |  | 
263  |  | friend class LocDataParser;  | 
264  |  |  | 
265  |  |     StringLocalizationInfo(UChar* i, UChar*** d, int32_t numRS, int32_t numLocs)  | 
266  | 0  |         : info(i), data(d), numRuleSets(numRS), numLocales(numLocs)  | 
267  | 0  |     { | 
268  | 0  |     }  | 
269  |  |       | 
270  |  | public:  | 
271  |  |     static StringLocalizationInfo* create(const UnicodeString& info, UParseError& perror, UErrorCode& status);  | 
272  |  |       | 
273  |  |     virtual ~StringLocalizationInfo();  | 
274  | 0  |     virtual int32_t getNumberOfRuleSets(void) const { return numRuleSets; } | 
275  |  |     virtual const UChar* getRuleSetName(int32_t index) const;  | 
276  | 0  |     virtual int32_t getNumberOfDisplayLocales(void) const { return numLocales; } | 
277  |  |     virtual const UChar* getLocaleName(int32_t index) const;  | 
278  |  |     virtual const UChar* getDisplayName(int32_t localeIndex, int32_t ruleIndex) const;  | 
279  |  |       | 
280  |  | //    virtual UClassID getDynamicClassID() const;  | 
281  |  | //    static UClassID getStaticClassID(void);  | 
282  |  |       | 
283  |  | private:  | 
284  |  |     void init(UErrorCode& status) const;  | 
285  |  | };  | 
286  |  |  | 
287  |  |  | 
288  |  | enum { | 
289  |  |     OPEN_ANGLE = 0x003c, /* '<' */  | 
290  |  |     CLOSE_ANGLE = 0x003e, /* '>' */  | 
291  |  |     COMMA = 0x002c,  | 
292  |  |     TICK = 0x0027,  | 
293  |  |     QUOTE = 0x0022,  | 
294  |  |     SPACE = 0x0020  | 
295  |  | };  | 
296  |  |  | 
297  |  | /**  | 
298  |  |  * Utility for parsing a localization string and returning a StringLocalizationInfo*.  | 
299  |  |  */  | 
300  |  | class LocDataParser { | 
301  |  |     UChar* data;  | 
302  |  |     const UChar* e;  | 
303  |  |     UChar* p;  | 
304  |  |     UChar ch;  | 
305  |  |     UParseError& pe;  | 
306  |  |     UErrorCode& ec;  | 
307  |  |       | 
308  |  | public:  | 
309  |  |     LocDataParser(UParseError& parseError, UErrorCode& status)   | 
310  | 0  |         : data(NULL), e(NULL), p(NULL), ch(0xffff), pe(parseError), ec(status) {} | 
311  | 0  |     ~LocDataParser() {} | 
312  |  |       | 
313  |  |     /*  | 
314  |  |     * On a successful parse, return a StringLocalizationInfo*, otherwise delete locData, set perror and status,  | 
315  |  |     * and return NULL.  The StringLocalizationInfo will adopt locData if it is created.  | 
316  |  |     */  | 
317  |  |     StringLocalizationInfo* parse(UChar* data, int32_t len);  | 
318  |  |       | 
319  |  | private:  | 
320  |  |       | 
321  | 0  |     inline void inc(void) { | 
322  | 0  |         ++p;  | 
323  | 0  |         ch = 0xffff;  | 
324  | 0  |     }  | 
325  | 0  |     inline UBool checkInc(UChar c) { | 
326  | 0  |         if (p < e && (ch == c || *p == c)) { | 
327  | 0  |             inc();  | 
328  | 0  |             return TRUE;  | 
329  | 0  |         }  | 
330  | 0  |         return FALSE;  | 
331  | 0  |     }  | 
332  | 0  |     inline UBool check(UChar c) { | 
333  | 0  |         return p < e && (ch == c || *p == c);  | 
334  | 0  |     }  | 
335  | 0  |     inline void skipWhitespace(void) { | 
336  | 0  |         while (p < e && PatternProps::isWhiteSpace(ch != 0xffff ? ch : *p)) { | 
337  | 0  |             inc();  | 
338  | 0  |         }  | 
339  | 0  |     }  | 
340  | 0  |     inline UBool inList(UChar c, const UChar* list) const { | 
341  | 0  |         if (*list == SPACE && PatternProps::isWhiteSpace(c)) { | 
342  | 0  |             return TRUE;  | 
343  | 0  |         }  | 
344  | 0  |         while (*list && *list != c) { | 
345  | 0  |             ++list;  | 
346  | 0  |         }  | 
347  | 0  |         return *list == c;  | 
348  | 0  |     }  | 
349  |  |     void parseError(const char* msg);  | 
350  |  |       | 
351  |  |     StringLocalizationInfo* doParse(void);  | 
352  |  |           | 
353  |  |     UChar** nextArray(int32_t& requiredLength);  | 
354  |  |     UChar*  nextString(void);  | 
355  |  | };  | 
356  |  |  | 
357  |  | #ifdef RBNF_DEBUG  | 
358  |  | #define ERROR(msg) UPRV_BLOCK_MACRO_BEGIN { \ | 
359  |  |     parseError(msg); \  | 
360  |  |     return NULL; \  | 
361  |  | } UPRV_BLOCK_MACRO_END  | 
362  |  | #define EXPLANATION_ARG explanationArg  | 
363  |  | #else  | 
364  | 0  | #define ERROR(msg) UPRV_BLOCK_MACRO_BEGIN { \ | 
365  | 0  |     parseError(NULL); \  | 
366  | 0  |     return NULL; \  | 
367  | 0  | } UPRV_BLOCK_MACRO_END  | 
368  |  | #define EXPLANATION_ARG  | 
369  |  | #endif  | 
370  |  |           | 
371  |  |  | 
372  |  | static const UChar DQUOTE_STOPLIST[] = {  | 
373  |  |     QUOTE, 0  | 
374  |  | };  | 
375  |  |  | 
376  |  | static const UChar SQUOTE_STOPLIST[] = {  | 
377  |  |     TICK, 0  | 
378  |  | };  | 
379  |  |  | 
380  |  | static const UChar NOQUOTE_STOPLIST[] = {  | 
381  |  |     SPACE, COMMA, CLOSE_ANGLE, OPEN_ANGLE, TICK, QUOTE, 0  | 
382  |  | };  | 
383  |  |  | 
384  |  | static void  | 
385  | 0  | DeleteFn(void* p) { | 
386  | 0  |   uprv_free(p);  | 
387  | 0  | }  | 
388  |  |  | 
389  |  | StringLocalizationInfo*  | 
390  | 0  | LocDataParser::parse(UChar* _data, int32_t len) { | 
391  | 0  |     if (U_FAILURE(ec)) { | 
392  | 0  |         if (_data) uprv_free(_data);  | 
393  | 0  |         return NULL;  | 
394  | 0  |     }  | 
395  |  |  | 
396  | 0  |     pe.line = 0;  | 
397  | 0  |     pe.offset = -1;  | 
398  | 0  |     pe.postContext[0] = 0;  | 
399  | 0  |     pe.preContext[0] = 0;  | 
400  |  | 
  | 
401  | 0  |     if (_data == NULL) { | 
402  | 0  |         ec = U_ILLEGAL_ARGUMENT_ERROR;  | 
403  | 0  |         return NULL;  | 
404  | 0  |     }  | 
405  |  |  | 
406  | 0  |     if (len <= 0) { | 
407  | 0  |         ec = U_ILLEGAL_ARGUMENT_ERROR;  | 
408  | 0  |         uprv_free(_data);  | 
409  | 0  |         return NULL;  | 
410  | 0  |     }  | 
411  |  |  | 
412  | 0  |     data = _data;  | 
413  | 0  |     e = data + len;  | 
414  | 0  |     p = _data;  | 
415  | 0  |     ch = 0xffff;  | 
416  |  | 
  | 
417  | 0  |     return doParse();  | 
418  | 0  | }  | 
419  |  |  | 
420  |  |  | 
421  |  | StringLocalizationInfo*  | 
422  | 0  | LocDataParser::doParse(void) { | 
423  | 0  |     skipWhitespace();  | 
424  | 0  |     if (!checkInc(OPEN_ANGLE)) { | 
425  | 0  |         ERROR("Missing open angle"); | 
426  | 0  |     } else { | 
427  | 0  |         VArray array(DeleteFn);  | 
428  | 0  |         UBool mightHaveNext = TRUE;  | 
429  | 0  |         int32_t requiredLength = -1;  | 
430  | 0  |         while (mightHaveNext) { | 
431  | 0  |             mightHaveNext = FALSE;  | 
432  | 0  |             UChar** elem = nextArray(requiredLength);  | 
433  | 0  |             skipWhitespace();  | 
434  | 0  |             UBool haveComma = check(COMMA);  | 
435  | 0  |             if (elem) { | 
436  | 0  |                 array.add(elem, ec);  | 
437  | 0  |                 if (haveComma) { | 
438  | 0  |                     inc();  | 
439  | 0  |                     mightHaveNext = TRUE;  | 
440  | 0  |                 }  | 
441  | 0  |             } else if (haveComma) { | 
442  | 0  |                 ERROR("Unexpected character"); | 
443  | 0  |             }  | 
444  | 0  |         }  | 
445  |  |  | 
446  | 0  |         skipWhitespace();  | 
447  | 0  |         if (!checkInc(CLOSE_ANGLE)) { | 
448  | 0  |             if (check(OPEN_ANGLE)) { | 
449  | 0  |                 ERROR("Missing comma in outer array"); | 
450  | 0  |             } else { | 
451  | 0  |                 ERROR("Missing close angle bracket in outer array"); | 
452  | 0  |             }  | 
453  | 0  |         }  | 
454  |  |  | 
455  | 0  |         skipWhitespace();  | 
456  | 0  |         if (p != e) { | 
457  | 0  |             ERROR("Extra text after close of localization data"); | 
458  | 0  |         }  | 
459  |  |  | 
460  | 0  |         array.add(NULL, ec);  | 
461  | 0  |         if (U_SUCCESS(ec)) { | 
462  | 0  |             int32_t numLocs = array.length() - 2; // subtract first, NULL  | 
463  | 0  |             UChar*** result = (UChar***)array.release();  | 
464  |  |               | 
465  | 0  |             return new StringLocalizationInfo(data, result, requiredLength-2, numLocs); // subtract first, NULL  | 
466  | 0  |         }  | 
467  | 0  |     }  | 
468  |  |     | 
469  | 0  |     ERROR("Unknown error"); | 
470  | 0  | }  | 
471  |  |  | 
472  |  | UChar**  | 
473  | 0  | LocDataParser::nextArray(int32_t& requiredLength) { | 
474  | 0  |     if (U_FAILURE(ec)) { | 
475  | 0  |         return NULL;  | 
476  | 0  |     }  | 
477  |  |       | 
478  | 0  |     skipWhitespace();  | 
479  | 0  |     if (!checkInc(OPEN_ANGLE)) { | 
480  | 0  |         ERROR("Missing open angle"); | 
481  | 0  |     }  | 
482  |  |  | 
483  | 0  |     VArray array;  | 
484  | 0  |     UBool mightHaveNext = TRUE;  | 
485  | 0  |     while (mightHaveNext) { | 
486  | 0  |         mightHaveNext = FALSE;  | 
487  | 0  |         UChar* elem = nextString();  | 
488  | 0  |         skipWhitespace();  | 
489  | 0  |         UBool haveComma = check(COMMA);  | 
490  | 0  |         if (elem) { | 
491  | 0  |             array.add(elem, ec);  | 
492  | 0  |             if (haveComma) { | 
493  | 0  |                 inc();  | 
494  | 0  |                 mightHaveNext = TRUE;  | 
495  | 0  |             }  | 
496  | 0  |         } else if (haveComma) { | 
497  | 0  |             ERROR("Unexpected comma"); | 
498  | 0  |         }  | 
499  | 0  |     }  | 
500  | 0  |     skipWhitespace();  | 
501  | 0  |     if (!checkInc(CLOSE_ANGLE)) { | 
502  | 0  |         if (check(OPEN_ANGLE)) { | 
503  | 0  |             ERROR("Missing close angle bracket in inner array"); | 
504  | 0  |         } else { | 
505  | 0  |             ERROR("Missing comma in inner array"); | 
506  | 0  |         }  | 
507  | 0  |     }  | 
508  |  |  | 
509  | 0  |     array.add(NULL, ec);  | 
510  | 0  |     if (U_SUCCESS(ec)) { | 
511  | 0  |         if (requiredLength == -1) { | 
512  | 0  |             requiredLength = array.length() + 1;  | 
513  | 0  |         } else if (array.length() != requiredLength) { | 
514  | 0  |             ec = U_ILLEGAL_ARGUMENT_ERROR;  | 
515  | 0  |             ERROR("Array not of required length"); | 
516  | 0  |         }  | 
517  |  |           | 
518  | 0  |         return (UChar**)array.release();  | 
519  | 0  |     }  | 
520  | 0  |     ERROR("Unknown Error"); | 
521  | 0  | }  | 
522  |  |  | 
523  |  | UChar*  | 
524  | 0  | LocDataParser::nextString() { | 
525  | 0  |     UChar* result = NULL;  | 
526  |  |       | 
527  | 0  |     skipWhitespace();  | 
528  | 0  |     if (p < e) { | 
529  | 0  |         const UChar* terminators;  | 
530  | 0  |         UChar c = *p;  | 
531  | 0  |         UBool haveQuote = c == QUOTE || c == TICK;  | 
532  | 0  |         if (haveQuote) { | 
533  | 0  |             inc();  | 
534  | 0  |             terminators = c == QUOTE ? DQUOTE_STOPLIST : SQUOTE_STOPLIST;  | 
535  | 0  |         } else { | 
536  | 0  |             terminators = NOQUOTE_STOPLIST;  | 
537  | 0  |         }  | 
538  | 0  |         UChar* start = p;  | 
539  | 0  |         while (p < e && !inList(*p, terminators)) ++p;  | 
540  | 0  |         if (p == e) { | 
541  | 0  |             ERROR("Unexpected end of data"); | 
542  | 0  |         }  | 
543  |  |           | 
544  | 0  |         UChar x = *p;  | 
545  | 0  |         if (p > start) { | 
546  | 0  |             ch = x;  | 
547  | 0  |             *p = 0x0; // terminate by writing to data  | 
548  | 0  |             result = start; // just point into data  | 
549  | 0  |         }  | 
550  | 0  |         if (haveQuote) { | 
551  | 0  |             if (x != c) { | 
552  | 0  |                 ERROR("Missing matching quote"); | 
553  | 0  |             } else if (p == start) { | 
554  | 0  |                 ERROR("Empty string"); | 
555  | 0  |             }  | 
556  | 0  |             inc();  | 
557  | 0  |         } else if (x == OPEN_ANGLE || x == TICK || x == QUOTE) { | 
558  | 0  |             ERROR("Unexpected character in string"); | 
559  | 0  |         }  | 
560  | 0  |     }  | 
561  |  |  | 
562  |  |     // ok for there to be no next string  | 
563  | 0  |     return result;  | 
564  | 0  | }  | 
565  |  |  | 
566  |  | void LocDataParser::parseError(const char* EXPLANATION_ARG)  | 
567  | 0  | { | 
568  | 0  |     if (!data) { | 
569  | 0  |         return;  | 
570  | 0  |     }  | 
571  |  |  | 
572  | 0  |     const UChar* start = p - U_PARSE_CONTEXT_LEN - 1;  | 
573  | 0  |     if (start < data) { | 
574  | 0  |         start = data;  | 
575  | 0  |     }  | 
576  | 0  |     for (UChar* x = p; --x >= start;) { | 
577  | 0  |         if (!*x) { | 
578  | 0  |             start = x+1;  | 
579  | 0  |             break;  | 
580  | 0  |         }  | 
581  | 0  |     }  | 
582  | 0  |     const UChar* limit = p + U_PARSE_CONTEXT_LEN - 1;  | 
583  | 0  |     if (limit > e) { | 
584  | 0  |         limit = e;  | 
585  | 0  |     }  | 
586  | 0  |     u_strncpy(pe.preContext, start, (int32_t)(p-start));  | 
587  | 0  |     pe.preContext[p-start] = 0;  | 
588  | 0  |     u_strncpy(pe.postContext, p, (int32_t)(limit-p));  | 
589  | 0  |     pe.postContext[limit-p] = 0;  | 
590  | 0  |     pe.offset = (int32_t)(p - data);  | 
591  |  |       | 
592  |  | #ifdef RBNF_DEBUG  | 
593  |  |     fprintf(stderr, "%s at or near character %ld: ", EXPLANATION_ARG, p-data);  | 
594  |  |  | 
595  |  |     UnicodeString msg;  | 
596  |  |     msg.append(start, p - start);  | 
597  |  |     msg.append((UChar)0x002f); /* SOLIDUS/SLASH */  | 
598  |  |     msg.append(p, limit-p);  | 
599  |  |     msg.append(UNICODE_STRING_SIMPLE("'")); | 
600  |  |       | 
601  |  |     char buf[128];  | 
602  |  |     int32_t len = msg.extract(0, msg.length(), buf, 128);  | 
603  |  |     if (len >= 128) { | 
604  |  |         buf[127] = 0;  | 
605  |  |     } else { | 
606  |  |         buf[len] = 0;  | 
607  |  |     }  | 
608  |  |     fprintf(stderr, "%s\n", buf);  | 
609  |  |     fflush(stderr);  | 
610  |  | #endif  | 
611  |  |       | 
612  | 0  |     uprv_free(data);  | 
613  | 0  |     data = NULL;  | 
614  | 0  |     p = NULL;  | 
615  | 0  |     e = NULL;  | 
616  |  |       | 
617  | 0  |     if (U_SUCCESS(ec)) { | 
618  | 0  |         ec = U_PARSE_ERROR;  | 
619  | 0  |     }  | 
620  | 0  | }  | 
621  |  |  | 
622  |  | //UOBJECT_DEFINE_RTTI_IMPLEMENTATION(StringLocalizationInfo)  | 
623  |  |  | 
624  |  | StringLocalizationInfo*   | 
625  | 0  | StringLocalizationInfo::create(const UnicodeString& info, UParseError& perror, UErrorCode& status) { | 
626  | 0  |     if (U_FAILURE(status)) { | 
627  | 0  |         return NULL;  | 
628  | 0  |     }  | 
629  |  |       | 
630  | 0  |     int32_t len = info.length();  | 
631  | 0  |     if (len == 0) { | 
632  | 0  |         return NULL; // no error;  | 
633  | 0  |     }  | 
634  |  |       | 
635  | 0  |     UChar* p = (UChar*)uprv_malloc(len * sizeof(UChar));  | 
636  | 0  |     if (!p) { | 
637  | 0  |         status = U_MEMORY_ALLOCATION_ERROR;  | 
638  | 0  |         return NULL;  | 
639  | 0  |     }  | 
640  | 0  |     info.extract(p, len, status);  | 
641  | 0  |     if (!U_FAILURE(status)) { | 
642  | 0  |         status = U_ZERO_ERROR; // clear warning about non-termination  | 
643  | 0  |     }  | 
644  |  |       | 
645  | 0  |     LocDataParser parser(perror, status);  | 
646  | 0  |     return parser.parse(p, len);  | 
647  | 0  | }  | 
648  |  |  | 
649  | 0  | StringLocalizationInfo::~StringLocalizationInfo() { | 
650  | 0  |     for (UChar*** p = (UChar***)data; *p; ++p) { | 
651  |  |         // remaining data is simply pointer into our unicode string data.  | 
652  | 0  |         if (*p) uprv_free(*p);  | 
653  | 0  |     }  | 
654  | 0  |     if (data) uprv_free(data);  | 
655  | 0  |     if (info) uprv_free(info);  | 
656  | 0  | }  | 
657  |  |  | 
658  |  |  | 
659  |  | const UChar*  | 
660  | 0  | StringLocalizationInfo::getRuleSetName(int32_t index) const { | 
661  | 0  |     if (index >= 0 && index < getNumberOfRuleSets()) { | 
662  | 0  |         return data[0][index];  | 
663  | 0  |     }  | 
664  | 0  |     return NULL;  | 
665  | 0  | }  | 
666  |  |  | 
667  |  | const UChar*  | 
668  | 0  | StringLocalizationInfo::getLocaleName(int32_t index) const { | 
669  | 0  |     if (index >= 0 && index < getNumberOfDisplayLocales()) { | 
670  | 0  |         return data[index+1][0];  | 
671  | 0  |     }  | 
672  | 0  |     return NULL;  | 
673  | 0  | }  | 
674  |  |  | 
675  |  | const UChar*  | 
676  | 0  | StringLocalizationInfo::getDisplayName(int32_t localeIndex, int32_t ruleIndex) const { | 
677  | 0  |     if (localeIndex >= 0 && localeIndex < getNumberOfDisplayLocales() &&  | 
678  | 0  |         ruleIndex >= 0 && ruleIndex < getNumberOfRuleSets()) { | 
679  | 0  |         return data[localeIndex+1][ruleIndex+1];  | 
680  | 0  |     }  | 
681  | 0  |     return NULL;  | 
682  | 0  | }  | 
683  |  |  | 
684  |  | // ----------  | 
685  |  |  | 
686  |  | RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,   | 
687  |  |                                              const UnicodeString& locs,  | 
688  |  |                                              const Locale& alocale, UParseError& perror, UErrorCode& status)  | 
689  |  |   : fRuleSets(NULL)  | 
690  |  |   , ruleSetDescriptions(NULL)  | 
691  | 0  |   , numRuleSets(0)  | 
692  |  |   , defaultRuleSet(NULL)  | 
693  | 0  |   , locale(alocale)  | 
694  |  |   , collator(NULL)  | 
695  |  |   , decimalFormatSymbols(NULL)  | 
696  |  |   , defaultInfinityRule(NULL)  | 
697  |  |   , defaultNaNRule(NULL)  | 
698  | 0  |   , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)  | 
699  | 0  |   , lenient(FALSE)  | 
700  |  |   , lenientParseRules(NULL)  | 
701  |  |   , localizations(NULL)  | 
702  | 0  |   , capitalizationInfoSet(FALSE)  | 
703  | 0  |   , capitalizationForUIListMenu(FALSE)  | 
704  | 0  |   , capitalizationForStandAlone(FALSE)  | 
705  |  |   , capitalizationBrkIter(NULL)  | 
706  | 0  | { | 
707  | 0  |   LocalizationInfo* locinfo = StringLocalizationInfo::create(locs, perror, status);  | 
708  | 0  |   init(description, locinfo, perror, status);  | 
709  | 0  | }  | 
710  |  |  | 
711  |  | RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,   | 
712  |  |                                              const UnicodeString& locs,  | 
713  |  |                                              UParseError& perror, UErrorCode& status)  | 
714  |  |   : fRuleSets(NULL)  | 
715  |  |   , ruleSetDescriptions(NULL)  | 
716  | 0  |   , numRuleSets(0)  | 
717  |  |   , defaultRuleSet(NULL)  | 
718  | 0  |   , locale(Locale::getDefault())  | 
719  |  |   , collator(NULL)  | 
720  |  |   , decimalFormatSymbols(NULL)  | 
721  |  |   , defaultInfinityRule(NULL)  | 
722  |  |   , defaultNaNRule(NULL)  | 
723  | 0  |   , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)  | 
724  | 0  |   , lenient(FALSE)  | 
725  |  |   , lenientParseRules(NULL)  | 
726  |  |   , localizations(NULL)  | 
727  | 0  |   , capitalizationInfoSet(FALSE)  | 
728  | 0  |   , capitalizationForUIListMenu(FALSE)  | 
729  | 0  |   , capitalizationForStandAlone(FALSE)  | 
730  |  |   , capitalizationBrkIter(NULL)  | 
731  | 0  | { | 
732  | 0  |   LocalizationInfo* locinfo = StringLocalizationInfo::create(locs, perror, status);  | 
733  | 0  |   init(description, locinfo, perror, status);  | 
734  | 0  | }  | 
735  |  |  | 
736  |  | RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,   | 
737  |  |                                              LocalizationInfo* info,  | 
738  |  |                                              const Locale& alocale, UParseError& perror, UErrorCode& status)  | 
739  |  |   : fRuleSets(NULL)  | 
740  |  |   , ruleSetDescriptions(NULL)  | 
741  | 0  |   , numRuleSets(0)  | 
742  |  |   , defaultRuleSet(NULL)  | 
743  | 0  |   , locale(alocale)  | 
744  |  |   , collator(NULL)  | 
745  |  |   , decimalFormatSymbols(NULL)  | 
746  |  |   , defaultInfinityRule(NULL)  | 
747  |  |   , defaultNaNRule(NULL)  | 
748  | 0  |   , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)  | 
749  | 0  |   , lenient(FALSE)  | 
750  |  |   , lenientParseRules(NULL)  | 
751  |  |   , localizations(NULL)  | 
752  | 0  |   , capitalizationInfoSet(FALSE)  | 
753  | 0  |   , capitalizationForUIListMenu(FALSE)  | 
754  | 0  |   , capitalizationForStandAlone(FALSE)  | 
755  |  |   , capitalizationBrkIter(NULL)  | 
756  | 0  | { | 
757  | 0  |   init(description, info, perror, status);  | 
758  | 0  | }  | 
759  |  |  | 
760  |  | RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,   | 
761  |  |                          UParseError& perror,   | 
762  |  |                          UErrorCode& status)   | 
763  |  |   : fRuleSets(NULL)  | 
764  |  |   , ruleSetDescriptions(NULL)  | 
765  | 0  |   , numRuleSets(0)  | 
766  |  |   , defaultRuleSet(NULL)  | 
767  | 0  |   , locale(Locale::getDefault())  | 
768  |  |   , collator(NULL)  | 
769  |  |   , decimalFormatSymbols(NULL)  | 
770  |  |   , defaultInfinityRule(NULL)  | 
771  |  |   , defaultNaNRule(NULL)  | 
772  | 0  |   , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)  | 
773  | 0  |   , lenient(FALSE)  | 
774  |  |   , lenientParseRules(NULL)  | 
775  |  |   , localizations(NULL)  | 
776  | 0  |   , capitalizationInfoSet(FALSE)  | 
777  | 0  |   , capitalizationForUIListMenu(FALSE)  | 
778  | 0  |   , capitalizationForStandAlone(FALSE)  | 
779  |  |   , capitalizationBrkIter(NULL)  | 
780  | 0  | { | 
781  | 0  |     init(description, NULL, perror, status);  | 
782  | 0  | }  | 
783  |  |  | 
784  |  | RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,   | 
785  |  |                          const Locale& aLocale,  | 
786  |  |                          UParseError& perror,   | 
787  |  |                          UErrorCode& status)   | 
788  |  |   : fRuleSets(NULL)  | 
789  |  |   , ruleSetDescriptions(NULL)  | 
790  | 0  |   , numRuleSets(0)  | 
791  |  |   , defaultRuleSet(NULL)  | 
792  | 0  |   , locale(aLocale)  | 
793  |  |   , collator(NULL)  | 
794  |  |   , decimalFormatSymbols(NULL)  | 
795  |  |   , defaultInfinityRule(NULL)  | 
796  |  |   , defaultNaNRule(NULL)  | 
797  | 0  |   , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)  | 
798  | 0  |   , lenient(FALSE)  | 
799  |  |   , lenientParseRules(NULL)  | 
800  |  |   , localizations(NULL)  | 
801  | 0  |   , capitalizationInfoSet(FALSE)  | 
802  | 0  |   , capitalizationForUIListMenu(FALSE)  | 
803  | 0  |   , capitalizationForStandAlone(FALSE)  | 
804  |  |   , capitalizationBrkIter(NULL)  | 
805  | 0  | { | 
806  | 0  |     init(description, NULL, perror, status);  | 
807  | 0  | }  | 
808  |  |  | 
809  |  | RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag, const Locale& alocale, UErrorCode& status)  | 
810  |  |   : fRuleSets(NULL)  | 
811  |  |   , ruleSetDescriptions(NULL)  | 
812  | 0  |   , numRuleSets(0)  | 
813  |  |   , defaultRuleSet(NULL)  | 
814  | 0  |   , locale(alocale)  | 
815  |  |   , collator(NULL)  | 
816  |  |   , decimalFormatSymbols(NULL)  | 
817  |  |   , defaultInfinityRule(NULL)  | 
818  |  |   , defaultNaNRule(NULL)  | 
819  | 0  |   , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)  | 
820  | 0  |   , lenient(FALSE)  | 
821  |  |   , lenientParseRules(NULL)  | 
822  |  |   , localizations(NULL)  | 
823  | 0  |   , capitalizationInfoSet(FALSE)  | 
824  | 0  |   , capitalizationForUIListMenu(FALSE)  | 
825  | 0  |   , capitalizationForStandAlone(FALSE)  | 
826  |  |   , capitalizationBrkIter(NULL)  | 
827  | 0  | { | 
828  | 0  |     if (U_FAILURE(status)) { | 
829  | 0  |         return;  | 
830  | 0  |     }  | 
831  |  |  | 
832  | 0  |     const char* rules_tag = "RBNFRules";  | 
833  | 0  |     const char* fmt_tag = "";  | 
834  | 0  |     switch (tag) { | 
835  | 0  |     case URBNF_SPELLOUT: fmt_tag = "SpelloutRules"; break;  | 
836  | 0  |     case URBNF_ORDINAL: fmt_tag = "OrdinalRules"; break;  | 
837  | 0  |     case URBNF_DURATION: fmt_tag = "DurationRules"; break;  | 
838  | 0  |     case URBNF_NUMBERING_SYSTEM: fmt_tag = "NumberingSystemRules"; break;  | 
839  | 0  |     default: status = U_ILLEGAL_ARGUMENT_ERROR; return;  | 
840  | 0  |     }  | 
841  |  |  | 
842  |  |     // TODO: read localization info from resource  | 
843  | 0  |     LocalizationInfo* locinfo = NULL;  | 
844  |  | 
  | 
845  | 0  |     UResourceBundle* nfrb = ures_open(U_ICUDATA_RBNF, locale.getName(), &status);  | 
846  | 0  |     if (U_SUCCESS(status)) { | 
847  | 0  |         setLocaleIDs(ures_getLocaleByType(nfrb, ULOC_VALID_LOCALE, &status),  | 
848  | 0  |                      ures_getLocaleByType(nfrb, ULOC_ACTUAL_LOCALE, &status));  | 
849  |  | 
  | 
850  | 0  |         UResourceBundle* rbnfRules = ures_getByKeyWithFallback(nfrb, rules_tag, NULL, &status);  | 
851  | 0  |         if (U_FAILURE(status)) { | 
852  | 0  |             ures_close(nfrb);  | 
853  | 0  |         }  | 
854  | 0  |         UResourceBundle* ruleSets = ures_getByKeyWithFallback(rbnfRules, fmt_tag, NULL, &status);  | 
855  | 0  |         if (U_FAILURE(status)) { | 
856  | 0  |             ures_close(rbnfRules);  | 
857  | 0  |             ures_close(nfrb);  | 
858  | 0  |             return;  | 
859  | 0  |         }  | 
860  |  |  | 
861  | 0  |         UnicodeString desc;  | 
862  | 0  |         while (ures_hasNext(ruleSets)) { | 
863  | 0  |            desc.append(ures_getNextUnicodeString(ruleSets,NULL,&status));  | 
864  | 0  |         }  | 
865  | 0  |         UParseError perror;  | 
866  |  | 
  | 
867  | 0  |         init(desc, locinfo, perror, status);  | 
868  |  | 
  | 
869  | 0  |         ures_close(ruleSets);  | 
870  | 0  |         ures_close(rbnfRules);  | 
871  | 0  |     }  | 
872  | 0  |     ures_close(nfrb);  | 
873  | 0  | }  | 
874  |  |  | 
875  |  | RuleBasedNumberFormat::RuleBasedNumberFormat(const RuleBasedNumberFormat& rhs)  | 
876  | 0  |   : NumberFormat(rhs)  | 
877  |  |   , fRuleSets(NULL)  | 
878  |  |   , ruleSetDescriptions(NULL)  | 
879  | 0  |   , numRuleSets(0)  | 
880  |  |   , defaultRuleSet(NULL)  | 
881  | 0  |   , locale(rhs.locale)  | 
882  |  |   , collator(NULL)  | 
883  |  |   , decimalFormatSymbols(NULL)  | 
884  |  |   , defaultInfinityRule(NULL)  | 
885  |  |   , defaultNaNRule(NULL)  | 
886  | 0  |   , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)  | 
887  | 0  |   , lenient(FALSE)  | 
888  |  |   , lenientParseRules(NULL)  | 
889  |  |   , localizations(NULL)  | 
890  | 0  |   , capitalizationInfoSet(FALSE)  | 
891  | 0  |   , capitalizationForUIListMenu(FALSE)  | 
892  | 0  |   , capitalizationForStandAlone(FALSE)  | 
893  |  |   , capitalizationBrkIter(NULL)  | 
894  | 0  | { | 
895  | 0  |     this->operator=(rhs);  | 
896  | 0  | }  | 
897  |  |  | 
898  |  | // --------  | 
899  |  |  | 
900  |  | RuleBasedNumberFormat&  | 
901  |  | RuleBasedNumberFormat::operator=(const RuleBasedNumberFormat& rhs)  | 
902  | 0  | { | 
903  | 0  |     if (this == &rhs) { | 
904  | 0  |         return *this;  | 
905  | 0  |     }  | 
906  | 0  |     NumberFormat::operator=(rhs);  | 
907  | 0  |     UErrorCode status = U_ZERO_ERROR;  | 
908  | 0  |     dispose();  | 
909  | 0  |     locale = rhs.locale;  | 
910  | 0  |     lenient = rhs.lenient;  | 
911  |  | 
  | 
912  | 0  |     UParseError perror;  | 
913  | 0  |     setDecimalFormatSymbols(*rhs.getDecimalFormatSymbols());  | 
914  | 0  |     init(rhs.originalDescription, rhs.localizations ? rhs.localizations->ref() : NULL, perror, status);  | 
915  | 0  |     setDefaultRuleSet(rhs.getDefaultRuleSetName(), status);  | 
916  | 0  |     setRoundingMode(rhs.getRoundingMode());  | 
917  |  | 
  | 
918  | 0  |     capitalizationInfoSet = rhs.capitalizationInfoSet;  | 
919  | 0  |     capitalizationForUIListMenu = rhs.capitalizationForUIListMenu;  | 
920  | 0  |     capitalizationForStandAlone = rhs.capitalizationForStandAlone;  | 
921  | 0  | #if !UCONFIG_NO_BREAK_ITERATION  | 
922  | 0  |     capitalizationBrkIter = (rhs.capitalizationBrkIter!=NULL)? rhs.capitalizationBrkIter->clone(): NULL;  | 
923  | 0  | #endif  | 
924  |  | 
  | 
925  | 0  |     return *this;  | 
926  | 0  | }  | 
927  |  |  | 
928  |  | RuleBasedNumberFormat::~RuleBasedNumberFormat()  | 
929  | 0  | { | 
930  | 0  |     dispose();  | 
931  | 0  | }  | 
932  |  |  | 
933  |  | RuleBasedNumberFormat*  | 
934  |  | RuleBasedNumberFormat::clone() const  | 
935  | 0  | { | 
936  | 0  |     return new RuleBasedNumberFormat(*this);  | 
937  | 0  | }  | 
938  |  |  | 
939  |  | bool  | 
940  |  | RuleBasedNumberFormat::operator==(const Format& other) const  | 
941  | 0  | { | 
942  | 0  |     if (this == &other) { | 
943  | 0  |         return TRUE;  | 
944  | 0  |     }  | 
945  |  |  | 
946  | 0  |     if (typeid(*this) == typeid(other)) { | 
947  | 0  |         const RuleBasedNumberFormat& rhs = (const RuleBasedNumberFormat&)other;  | 
948  |  |         // test for capitalization info equality is adequately handled  | 
949  |  |         // by the NumberFormat test for fCapitalizationContext equality;  | 
950  |  |         // the info here is just derived from that.  | 
951  | 0  |         if (locale == rhs.locale &&  | 
952  | 0  |             lenient == rhs.lenient &&  | 
953  | 0  |             (localizations == NULL   | 
954  | 0  |                 ? rhs.localizations == NULL   | 
955  | 0  |                 : (rhs.localizations == NULL   | 
956  | 0  |                     ? FALSE  | 
957  | 0  |                     : *localizations == rhs.localizations))) { | 
958  |  | 
  | 
959  | 0  |             NFRuleSet** p = fRuleSets;  | 
960  | 0  |             NFRuleSet** q = rhs.fRuleSets;  | 
961  | 0  |             if (p == NULL) { | 
962  | 0  |                 return q == NULL;  | 
963  | 0  |             } else if (q == NULL) { | 
964  | 0  |                 return FALSE;  | 
965  | 0  |             }  | 
966  | 0  |             while (*p && *q && (**p == **q)) { | 
967  | 0  |                 ++p;  | 
968  | 0  |                 ++q;  | 
969  | 0  |             }  | 
970  | 0  |             return *q == NULL && *p == NULL;  | 
971  | 0  |         }  | 
972  | 0  |     }  | 
973  |  |  | 
974  | 0  |     return FALSE;  | 
975  | 0  | }  | 
976  |  |  | 
977  |  | UnicodeString  | 
978  |  | RuleBasedNumberFormat::getRules() const  | 
979  | 0  | { | 
980  | 0  |     UnicodeString result;  | 
981  | 0  |     if (fRuleSets != NULL) { | 
982  | 0  |         for (NFRuleSet** p = fRuleSets; *p; ++p) { | 
983  | 0  |             (*p)->appendRules(result);  | 
984  | 0  |         }  | 
985  | 0  |     }  | 
986  | 0  |     return result;  | 
987  | 0  | }  | 
988  |  |  | 
989  |  | UnicodeString  | 
990  |  | RuleBasedNumberFormat::getRuleSetName(int32_t index) const  | 
991  | 0  | { | 
992  | 0  |     if (localizations) { | 
993  | 0  |         UnicodeString string(TRUE, localizations->getRuleSetName(index), (int32_t)-1);  | 
994  | 0  |         return string;  | 
995  | 0  |     }  | 
996  | 0  |     else if (fRuleSets) { | 
997  | 0  |         UnicodeString result;  | 
998  | 0  |         for (NFRuleSet** p = fRuleSets; *p; ++p) { | 
999  | 0  |             NFRuleSet* rs = *p;  | 
1000  | 0  |             if (rs->isPublic()) { | 
1001  | 0  |                 if (--index == -1) { | 
1002  | 0  |                     rs->getName(result);  | 
1003  | 0  |                     return result;  | 
1004  | 0  |                 }  | 
1005  | 0  |             }  | 
1006  | 0  |         }  | 
1007  | 0  |     }  | 
1008  | 0  |     UnicodeString empty;  | 
1009  | 0  |     return empty;  | 
1010  | 0  | }  | 
1011  |  |  | 
1012  |  | int32_t  | 
1013  |  | RuleBasedNumberFormat::getNumberOfRuleSetNames() const  | 
1014  | 0  | { | 
1015  | 0  |     int32_t result = 0;  | 
1016  | 0  |     if (localizations) { | 
1017  | 0  |         result = localizations->getNumberOfRuleSets();  | 
1018  | 0  |     }  | 
1019  | 0  |     else if (fRuleSets) { | 
1020  | 0  |         for (NFRuleSet** p = fRuleSets; *p; ++p) { | 
1021  | 0  |             if ((**p).isPublic()) { | 
1022  | 0  |                 ++result;  | 
1023  | 0  |             }  | 
1024  | 0  |         }  | 
1025  | 0  |     }  | 
1026  | 0  |     return result;  | 
1027  | 0  | }  | 
1028  |  |  | 
1029  |  | int32_t   | 
1030  | 0  | RuleBasedNumberFormat::getNumberOfRuleSetDisplayNameLocales(void) const { | 
1031  | 0  |     if (localizations) { | 
1032  | 0  |         return localizations->getNumberOfDisplayLocales();  | 
1033  | 0  |     }  | 
1034  | 0  |     return 0;  | 
1035  | 0  | }  | 
1036  |  |  | 
1037  |  | Locale   | 
1038  | 0  | RuleBasedNumberFormat::getRuleSetDisplayNameLocale(int32_t index, UErrorCode& status) const { | 
1039  | 0  |     if (U_FAILURE(status)) { | 
1040  | 0  |         return Locale(""); | 
1041  | 0  |     }  | 
1042  | 0  |     if (localizations && index >= 0 && index < localizations->getNumberOfDisplayLocales()) { | 
1043  | 0  |         UnicodeString name(TRUE, localizations->getLocaleName(index), -1);  | 
1044  | 0  |         char buffer[64];  | 
1045  | 0  |         int32_t cap = name.length() + 1;  | 
1046  | 0  |         char* bp = buffer;  | 
1047  | 0  |         if (cap > 64) { | 
1048  | 0  |             bp = (char *)uprv_malloc(cap);  | 
1049  | 0  |             if (bp == NULL) { | 
1050  | 0  |                 status = U_MEMORY_ALLOCATION_ERROR;  | 
1051  | 0  |                 return Locale(""); | 
1052  | 0  |             }  | 
1053  | 0  |         }  | 
1054  | 0  |         name.extract(0, name.length(), bp, cap, UnicodeString::kInvariant);  | 
1055  | 0  |         Locale retLocale(bp);  | 
1056  | 0  |         if (bp != buffer) { | 
1057  | 0  |             uprv_free(bp);  | 
1058  | 0  |         }  | 
1059  | 0  |         return retLocale;  | 
1060  | 0  |     }  | 
1061  | 0  |     status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1062  | 0  |     Locale retLocale;  | 
1063  | 0  |     return retLocale;  | 
1064  | 0  | }  | 
1065  |  |  | 
1066  |  | UnicodeString   | 
1067  | 0  | RuleBasedNumberFormat::getRuleSetDisplayName(int32_t index, const Locale& localeParam) { | 
1068  | 0  |     if (localizations && index >= 0 && index < localizations->getNumberOfRuleSets()) { | 
1069  | 0  |         UnicodeString localeName(localeParam.getBaseName(), -1, UnicodeString::kInvariant);   | 
1070  | 0  |         int32_t len = localeName.length();  | 
1071  | 0  |         UChar* localeStr = localeName.getBuffer(len + 1);  | 
1072  | 0  |         while (len >= 0) { | 
1073  | 0  |             localeStr[len] = 0;  | 
1074  | 0  |             int32_t ix = localizations->indexForLocale(localeStr);  | 
1075  | 0  |             if (ix >= 0) { | 
1076  | 0  |                 UnicodeString name(TRUE, localizations->getDisplayName(ix, index), -1);  | 
1077  | 0  |                 return name;  | 
1078  | 0  |             }  | 
1079  |  |               | 
1080  |  |             // trim trailing portion, skipping over omitted sections  | 
1081  | 0  |             do { --len;} while (len > 0 && localeStr[len] != 0x005f); // underscore | 
1082  | 0  |             while (len > 0 && localeStr[len-1] == 0x005F) --len;  | 
1083  | 0  |         }  | 
1084  | 0  |         UnicodeString name(TRUE, localizations->getRuleSetName(index), -1);  | 
1085  | 0  |         return name;  | 
1086  | 0  |     }  | 
1087  | 0  |     UnicodeString bogus;  | 
1088  | 0  |     bogus.setToBogus();  | 
1089  | 0  |     return bogus;  | 
1090  | 0  | }  | 
1091  |  |  | 
1092  |  | UnicodeString   | 
1093  | 0  | RuleBasedNumberFormat::getRuleSetDisplayName(const UnicodeString& ruleSetName, const Locale& localeParam) { | 
1094  | 0  |     if (localizations) { | 
1095  | 0  |         UnicodeString rsn(ruleSetName);  | 
1096  | 0  |         int32_t ix = localizations->indexForRuleSet(rsn.getTerminatedBuffer());  | 
1097  | 0  |         return getRuleSetDisplayName(ix, localeParam);  | 
1098  | 0  |     }  | 
1099  | 0  |     UnicodeString bogus;  | 
1100  | 0  |     bogus.setToBogus();  | 
1101  | 0  |     return bogus;  | 
1102  | 0  | }  | 
1103  |  |  | 
1104  |  | NFRuleSet*  | 
1105  |  | RuleBasedNumberFormat::findRuleSet(const UnicodeString& name, UErrorCode& status) const  | 
1106  | 0  | { | 
1107  | 0  |     if (U_SUCCESS(status) && fRuleSets) { | 
1108  | 0  |         for (NFRuleSet** p = fRuleSets; *p; ++p) { | 
1109  | 0  |             NFRuleSet* rs = *p;  | 
1110  | 0  |             if (rs->isNamed(name)) { | 
1111  | 0  |                 return rs;  | 
1112  | 0  |             }  | 
1113  | 0  |         }  | 
1114  | 0  |         status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1115  | 0  |     }  | 
1116  | 0  |     return NULL;  | 
1117  | 0  | }  | 
1118  |  |  | 
1119  |  | UnicodeString&  | 
1120  |  | RuleBasedNumberFormat::format(const DecimalQuantity &number,  | 
1121  |  |                      UnicodeString& appendTo,  | 
1122  |  |                      FieldPosition& pos,  | 
1123  | 0  |                      UErrorCode &status) const { | 
1124  | 0  |     if (U_FAILURE(status)) { | 
1125  | 0  |         return appendTo;  | 
1126  | 0  |     }  | 
1127  | 0  |     DecimalQuantity copy(number);  | 
1128  | 0  |     if (copy.fitsInLong()) { | 
1129  | 0  |         format(number.toLong(), appendTo, pos, status);  | 
1130  | 0  |     }  | 
1131  | 0  |     else { | 
1132  | 0  |         copy.roundToMagnitude(0, number::impl::RoundingMode::UNUM_ROUND_HALFEVEN, status);  | 
1133  | 0  |         if (copy.fitsInLong()) { | 
1134  | 0  |             format(number.toDouble(), appendTo, pos, status);  | 
1135  | 0  |         }  | 
1136  | 0  |         else { | 
1137  |  |             // We're outside of our normal range that this framework can handle.  | 
1138  |  |             // The DecimalFormat will provide more accurate results.  | 
1139  |  |  | 
1140  |  |             // TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J.  | 
1141  | 0  |             LocalPointer<NumberFormat> decimalFormat(NumberFormat::createInstance(locale, UNUM_DECIMAL, status), status);  | 
1142  | 0  |             if (decimalFormat.isNull()) { | 
1143  | 0  |                 return appendTo;  | 
1144  | 0  |             }  | 
1145  | 0  |             Formattable f;  | 
1146  | 0  |             LocalPointer<DecimalQuantity> decimalQuantity(new DecimalQuantity(number), status);  | 
1147  | 0  |             if (decimalQuantity.isNull()) { | 
1148  | 0  |                 return appendTo;  | 
1149  | 0  |             }  | 
1150  | 0  |             f.adoptDecimalQuantity(decimalQuantity.orphan()); // f now owns decimalQuantity.  | 
1151  | 0  |             decimalFormat->format(f, appendTo, pos, status);  | 
1152  | 0  |         }  | 
1153  | 0  |     }  | 
1154  | 0  |     return appendTo;  | 
1155  | 0  | }  | 
1156  |  |  | 
1157  |  | UnicodeString&  | 
1158  |  | RuleBasedNumberFormat::format(int32_t number,  | 
1159  |  |                               UnicodeString& toAppendTo,  | 
1160  |  |                               FieldPosition& pos) const  | 
1161  | 0  | { | 
1162  | 0  |     return format((int64_t)number, toAppendTo, pos);  | 
1163  | 0  | }  | 
1164  |  |  | 
1165  |  |  | 
1166  |  | UnicodeString&  | 
1167  |  | RuleBasedNumberFormat::format(int64_t number,  | 
1168  |  |                               UnicodeString& toAppendTo,  | 
1169  |  |                               FieldPosition& /* pos */) const  | 
1170  | 0  | { | 
1171  | 0  |     if (defaultRuleSet) { | 
1172  | 0  |         UErrorCode status = U_ZERO_ERROR;  | 
1173  | 0  |         format(number, defaultRuleSet, toAppendTo, status);  | 
1174  | 0  |     }  | 
1175  | 0  |     return toAppendTo;  | 
1176  | 0  | }  | 
1177  |  |  | 
1178  |  |  | 
1179  |  | UnicodeString&  | 
1180  |  | RuleBasedNumberFormat::format(double number,  | 
1181  |  |                               UnicodeString& toAppendTo,  | 
1182  |  |                               FieldPosition& /* pos */) const  | 
1183  | 0  | { | 
1184  | 0  |     UErrorCode status = U_ZERO_ERROR;  | 
1185  | 0  |     if (defaultRuleSet) { | 
1186  | 0  |         format(number, *defaultRuleSet, toAppendTo, status);  | 
1187  | 0  |     }  | 
1188  | 0  |     return toAppendTo;  | 
1189  | 0  | }  | 
1190  |  |  | 
1191  |  |  | 
1192  |  | UnicodeString&  | 
1193  |  | RuleBasedNumberFormat::format(int32_t number,  | 
1194  |  |                               const UnicodeString& ruleSetName,  | 
1195  |  |                               UnicodeString& toAppendTo,  | 
1196  |  |                               FieldPosition& pos,  | 
1197  |  |                               UErrorCode& status) const  | 
1198  | 0  | { | 
1199  | 0  |     return format((int64_t)number, ruleSetName, toAppendTo, pos, status);  | 
1200  | 0  | }  | 
1201  |  |  | 
1202  |  |  | 
1203  |  | UnicodeString&  | 
1204  |  | RuleBasedNumberFormat::format(int64_t number,  | 
1205  |  |                               const UnicodeString& ruleSetName,  | 
1206  |  |                               UnicodeString& toAppendTo,  | 
1207  |  |                               FieldPosition& /* pos */,  | 
1208  |  |                               UErrorCode& status) const  | 
1209  | 0  | { | 
1210  | 0  |     if (U_SUCCESS(status)) { | 
1211  | 0  |         if (ruleSetName.indexOf(gPercentPercent, 2, 0) == 0) { | 
1212  |  |             // throw new IllegalArgumentException("Can't use internal rule set"); | 
1213  | 0  |             status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1214  | 0  |         } else { | 
1215  | 0  |             NFRuleSet *rs = findRuleSet(ruleSetName, status);  | 
1216  | 0  |             if (rs) { | 
1217  | 0  |                 format(number, rs, toAppendTo, status);  | 
1218  | 0  |             }  | 
1219  | 0  |         }  | 
1220  | 0  |     }  | 
1221  | 0  |     return toAppendTo;  | 
1222  | 0  | }  | 
1223  |  |  | 
1224  |  |  | 
1225  |  | UnicodeString&  | 
1226  |  | RuleBasedNumberFormat::format(double number,  | 
1227  |  |                               const UnicodeString& ruleSetName,  | 
1228  |  |                               UnicodeString& toAppendTo,  | 
1229  |  |                               FieldPosition& /* pos */,  | 
1230  |  |                               UErrorCode& status) const  | 
1231  | 0  | { | 
1232  | 0  |     if (U_SUCCESS(status)) { | 
1233  | 0  |         if (ruleSetName.indexOf(gPercentPercent, 2, 0) == 0) { | 
1234  |  |             // throw new IllegalArgumentException("Can't use internal rule set"); | 
1235  | 0  |             status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1236  | 0  |         } else { | 
1237  | 0  |             NFRuleSet *rs = findRuleSet(ruleSetName, status);  | 
1238  | 0  |             if (rs) { | 
1239  | 0  |                 format(number, *rs, toAppendTo, status);  | 
1240  | 0  |             }  | 
1241  | 0  |         }  | 
1242  | 0  |     }  | 
1243  | 0  |     return toAppendTo;  | 
1244  | 0  | }  | 
1245  |  |  | 
1246  |  | void  | 
1247  |  | RuleBasedNumberFormat::format(double number,  | 
1248  |  |                               NFRuleSet& rs,  | 
1249  |  |                               UnicodeString& toAppendTo,  | 
1250  |  |                               UErrorCode& status) const  | 
1251  | 0  | { | 
1252  | 0  |     int32_t startPos = toAppendTo.length();  | 
1253  | 0  |     if (getRoundingMode() != DecimalFormat::ERoundingMode::kRoundUnnecessary && !uprv_isNaN(number) && !uprv_isInfinite(number)) { | 
1254  | 0  |         DecimalQuantity digitList;  | 
1255  | 0  |         digitList.setToDouble(number);  | 
1256  | 0  |         digitList.roundToMagnitude(  | 
1257  | 0  |                 -getMaximumFractionDigits(),  | 
1258  | 0  |                 static_cast<UNumberFormatRoundingMode>(getRoundingMode()),  | 
1259  | 0  |                 status);  | 
1260  | 0  |         number = digitList.toDouble();  | 
1261  | 0  |     }  | 
1262  | 0  |     rs.format(number, toAppendTo, toAppendTo.length(), 0, status);  | 
1263  | 0  |     adjustForCapitalizationContext(startPos, toAppendTo, status);  | 
1264  | 0  | }  | 
1265  |  |  | 
1266  |  | /**  | 
1267  |  |  * Bottleneck through which all the public format() methods  | 
1268  |  |  * that take a long pass. By the time we get here, we know  | 
1269  |  |  * which rule set we're using to do the formatting.  | 
1270  |  |  * @param number The number to format  | 
1271  |  |  * @param ruleSet The rule set to use to format the number  | 
1272  |  |  * @return The text that resulted from formatting the number  | 
1273  |  |  */  | 
1274  |  | UnicodeString&  | 
1275  |  | RuleBasedNumberFormat::format(int64_t number, NFRuleSet *ruleSet, UnicodeString& toAppendTo, UErrorCode& status) const  | 
1276  | 0  | { | 
1277  |  |     // all API format() routines that take a double vector through  | 
1278  |  |     // here.  We have these two identical functions-- one taking a  | 
1279  |  |     // double and one taking a long-- the couple digits of precision  | 
1280  |  |     // that long has but double doesn't (both types are 8 bytes long,  | 
1281  |  |     // but double has to borrow some of the mantissa bits to hold  | 
1282  |  |     // the exponent).  | 
1283  |  |     // Create an empty string buffer where the result will  | 
1284  |  |     // be built, and pass it to the rule set (along with an insertion  | 
1285  |  |     // position of 0 and the number being formatted) to the rule set  | 
1286  |  |     // for formatting  | 
1287  |  | 
  | 
1288  | 0  |     if (U_SUCCESS(status)) { | 
1289  | 0  |         if (number == U_INT64_MIN) { | 
1290  |  |             // We can't handle this value right now. Provide an accurate default value.  | 
1291  |  |  | 
1292  |  |             // TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J.  | 
1293  | 0  |             NumberFormat *decimalFormat = NumberFormat::createInstance(locale, UNUM_DECIMAL, status);  | 
1294  | 0  |             if (decimalFormat == nullptr) { | 
1295  | 0  |                 return toAppendTo;  | 
1296  | 0  |             }  | 
1297  | 0  |             Formattable f;  | 
1298  | 0  |             FieldPosition pos(FieldPosition::DONT_CARE);  | 
1299  | 0  |             DecimalQuantity *decimalQuantity = new DecimalQuantity();  | 
1300  | 0  |             if (decimalQuantity == nullptr) { | 
1301  | 0  |                 status = U_MEMORY_ALLOCATION_ERROR;  | 
1302  | 0  |                 delete decimalFormat;  | 
1303  | 0  |                 return toAppendTo;  | 
1304  | 0  |             }  | 
1305  | 0  |             decimalQuantity->setToLong(number);  | 
1306  | 0  |             f.adoptDecimalQuantity(decimalQuantity); // f now owns decimalQuantity.  | 
1307  | 0  |             decimalFormat->format(f, toAppendTo, pos, status);  | 
1308  | 0  |             delete decimalFormat;  | 
1309  | 0  |         }  | 
1310  | 0  |         else { | 
1311  | 0  |             int32_t startPos = toAppendTo.length();  | 
1312  | 0  |             ruleSet->format(number, toAppendTo, toAppendTo.length(), 0, status);  | 
1313  | 0  |             adjustForCapitalizationContext(startPos, toAppendTo, status);  | 
1314  | 0  |         }  | 
1315  | 0  |     }  | 
1316  | 0  |     return toAppendTo;  | 
1317  | 0  | }  | 
1318  |  |  | 
1319  |  | UnicodeString&  | 
1320  |  | RuleBasedNumberFormat::adjustForCapitalizationContext(int32_t startPos,  | 
1321  |  |                                                       UnicodeString& currentResult,  | 
1322  |  |                                                       UErrorCode& status) const  | 
1323  | 0  | { | 
1324  | 0  | #if !UCONFIG_NO_BREAK_ITERATION  | 
1325  | 0  |     UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status);  | 
1326  | 0  |     if (capitalizationContext != UDISPCTX_CAPITALIZATION_NONE && startPos == 0 && currentResult.length() > 0) { | 
1327  |  |         // capitalize currentResult according to context  | 
1328  | 0  |         UChar32 ch = currentResult.char32At(0);  | 
1329  | 0  |         if (u_islower(ch) && U_SUCCESS(status) && capitalizationBrkIter != NULL &&  | 
1330  | 0  |               ( capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||  | 
1331  | 0  |                 (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationForUIListMenu) ||  | 
1332  | 0  |                 (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_STANDALONE && capitalizationForStandAlone)) ) { | 
1333  |  |             // titlecase first word of currentResult, here use sentence iterator unlike current implementations  | 
1334  |  |             // in LocaleDisplayNamesImpl::adjustForUsageAndContext and RelativeDateFormat::format  | 
1335  | 0  |             currentResult.toTitle(capitalizationBrkIter, locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);  | 
1336  | 0  |         }  | 
1337  | 0  |     }  | 
1338  | 0  | #endif  | 
1339  | 0  |     return currentResult;  | 
1340  | 0  | }  | 
1341  |  |  | 
1342  |  |  | 
1343  |  | void  | 
1344  |  | RuleBasedNumberFormat::parse(const UnicodeString& text,  | 
1345  |  |                              Formattable& result,  | 
1346  |  |                              ParsePosition& parsePosition) const  | 
1347  | 0  | { | 
1348  | 0  |     if (!fRuleSets) { | 
1349  | 0  |         parsePosition.setErrorIndex(0);  | 
1350  | 0  |         return;  | 
1351  | 0  |     }  | 
1352  |  |  | 
1353  | 0  |     UnicodeString workingText(text, parsePosition.getIndex());  | 
1354  | 0  |     ParsePosition workingPos(0);  | 
1355  |  | 
  | 
1356  | 0  |     ParsePosition high_pp(0);  | 
1357  | 0  |     Formattable high_result;  | 
1358  |  | 
  | 
1359  | 0  |     for (NFRuleSet** p = fRuleSets; *p; ++p) { | 
1360  | 0  |         NFRuleSet *rp = *p;  | 
1361  | 0  |         if (rp->isPublic() && rp->isParseable()) { | 
1362  | 0  |             ParsePosition working_pp(0);  | 
1363  | 0  |             Formattable working_result;  | 
1364  |  | 
  | 
1365  | 0  |             rp->parse(workingText, working_pp, kMaxDouble, 0, working_result);  | 
1366  | 0  |             if (working_pp.getIndex() > high_pp.getIndex()) { | 
1367  | 0  |                 high_pp = working_pp;  | 
1368  | 0  |                 high_result = working_result;  | 
1369  |  | 
  | 
1370  | 0  |                 if (high_pp.getIndex() == workingText.length()) { | 
1371  | 0  |                     break;  | 
1372  | 0  |                 }  | 
1373  | 0  |             }  | 
1374  | 0  |         }  | 
1375  | 0  |     }  | 
1376  |  | 
  | 
1377  | 0  |     int32_t startIndex = parsePosition.getIndex();  | 
1378  | 0  |     parsePosition.setIndex(startIndex + high_pp.getIndex());  | 
1379  | 0  |     if (high_pp.getIndex() > 0) { | 
1380  | 0  |         parsePosition.setErrorIndex(-1);  | 
1381  | 0  |     } else { | 
1382  | 0  |         int32_t errorIndex = (high_pp.getErrorIndex()>0)? high_pp.getErrorIndex(): 0;  | 
1383  | 0  |         parsePosition.setErrorIndex(startIndex + errorIndex);  | 
1384  | 0  |     }  | 
1385  | 0  |     result = high_result;  | 
1386  | 0  |     if (result.getType() == Formattable::kDouble) { | 
1387  | 0  |         double d = result.getDouble();  | 
1388  | 0  |         if (!uprv_isNaN(d) && d == uprv_trunc(d) && INT32_MIN <= d && d <= INT32_MAX) { | 
1389  |  |             // Note: casting a double to an int when the double is too large or small  | 
1390  |  |             //       to fit the destination is undefined behavior. The explicit range checks,  | 
1391  |  |             //       above, are required. Just casting and checking the result value is undefined.  | 
1392  | 0  |             result.setLong(static_cast<int32_t>(d));  | 
1393  | 0  |         }  | 
1394  | 0  |     }  | 
1395  | 0  | }  | 
1396  |  |  | 
1397  |  | #if !UCONFIG_NO_COLLATION  | 
1398  |  |  | 
1399  |  | void  | 
1400  |  | RuleBasedNumberFormat::setLenient(UBool enabled)  | 
1401  | 0  | { | 
1402  | 0  |     lenient = enabled;  | 
1403  | 0  |     if (!enabled && collator) { | 
1404  | 0  |         delete collator;  | 
1405  | 0  |         collator = NULL;  | 
1406  | 0  |     }  | 
1407  | 0  | }  | 
1408  |  |  | 
1409  |  | #endif  | 
1410  |  |  | 
1411  |  | void   | 
1412  | 0  | RuleBasedNumberFormat::setDefaultRuleSet(const UnicodeString& ruleSetName, UErrorCode& status) { | 
1413  | 0  |     if (U_SUCCESS(status)) { | 
1414  | 0  |         if (ruleSetName.isEmpty()) { | 
1415  | 0  |           if (localizations) { | 
1416  | 0  |               UnicodeString name(TRUE, localizations->getRuleSetName(0), -1);  | 
1417  | 0  |               defaultRuleSet = findRuleSet(name, status);  | 
1418  | 0  |           } else { | 
1419  | 0  |             initDefaultRuleSet();  | 
1420  | 0  |           }  | 
1421  | 0  |         } else if (ruleSetName.startsWith(UNICODE_STRING_SIMPLE("%%"))) { | 
1422  | 0  |             status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1423  | 0  |         } else { | 
1424  | 0  |             NFRuleSet* result = findRuleSet(ruleSetName, status);  | 
1425  | 0  |             if (result != NULL) { | 
1426  | 0  |                 defaultRuleSet = result;  | 
1427  | 0  |             }  | 
1428  | 0  |         }  | 
1429  | 0  |     }  | 
1430  | 0  | }  | 
1431  |  |  | 
1432  |  | UnicodeString  | 
1433  | 0  | RuleBasedNumberFormat::getDefaultRuleSetName() const { | 
1434  | 0  |     UnicodeString result;  | 
1435  | 0  |     if (defaultRuleSet && defaultRuleSet->isPublic()) { | 
1436  | 0  |         defaultRuleSet->getName(result);  | 
1437  | 0  |     } else { | 
1438  | 0  |         result.setToBogus();  | 
1439  | 0  |     }  | 
1440  | 0  |     return result;  | 
1441  | 0  | }  | 
1442  |  |  | 
1443  |  | void   | 
1444  |  | RuleBasedNumberFormat::initDefaultRuleSet()  | 
1445  | 0  | { | 
1446  | 0  |     defaultRuleSet = NULL;  | 
1447  | 0  |     if (!fRuleSets) { | 
1448  | 0  |         return;  | 
1449  | 0  |     }  | 
1450  |  |  | 
1451  | 0  |     const UnicodeString spellout(UNICODE_STRING_SIMPLE("%spellout-numbering")); | 
1452  | 0  |     const UnicodeString ordinal(UNICODE_STRING_SIMPLE("%digits-ordinal")); | 
1453  | 0  |     const UnicodeString duration(UNICODE_STRING_SIMPLE("%duration")); | 
1454  |  | 
  | 
1455  | 0  |     NFRuleSet**p = &fRuleSets[0];  | 
1456  | 0  |     while (*p) { | 
1457  | 0  |         if ((*p)->isNamed(spellout) || (*p)->isNamed(ordinal) || (*p)->isNamed(duration)) { | 
1458  | 0  |             defaultRuleSet = *p;  | 
1459  | 0  |             return;  | 
1460  | 0  |         } else { | 
1461  | 0  |             ++p;  | 
1462  | 0  |         }  | 
1463  | 0  |     }  | 
1464  |  |  | 
1465  | 0  |     defaultRuleSet = *--p;  | 
1466  | 0  |     if (!defaultRuleSet->isPublic()) { | 
1467  | 0  |         while (p != fRuleSets) { | 
1468  | 0  |             if ((*--p)->isPublic()) { | 
1469  | 0  |                 defaultRuleSet = *p;  | 
1470  | 0  |                 break;  | 
1471  | 0  |             }  | 
1472  | 0  |         }  | 
1473  | 0  |     }  | 
1474  | 0  | }  | 
1475  |  |  | 
1476  |  |  | 
1477  |  | void  | 
1478  |  | RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* localizationInfos,  | 
1479  |  |                             UParseError& pErr, UErrorCode& status)  | 
1480  | 0  | { | 
1481  |  |     // TODO: implement UParseError  | 
1482  | 0  |     uprv_memset(&pErr, 0, sizeof(UParseError));  | 
1483  |  |     // Note: this can leave ruleSets == NULL, so remaining code should check  | 
1484  | 0  |     if (U_FAILURE(status)) { | 
1485  | 0  |         return;  | 
1486  | 0  |     }  | 
1487  |  |  | 
1488  | 0  |     initializeDecimalFormatSymbols(status);  | 
1489  | 0  |     initializeDefaultInfinityRule(status);  | 
1490  | 0  |     initializeDefaultNaNRule(status);  | 
1491  | 0  |     if (U_FAILURE(status)) { | 
1492  | 0  |         return;  | 
1493  | 0  |     }  | 
1494  |  |  | 
1495  | 0  |     this->localizations = localizationInfos == NULL ? NULL : localizationInfos->ref();  | 
1496  |  | 
  | 
1497  | 0  |     UnicodeString description(rules);  | 
1498  | 0  |     if (!description.length()) { | 
1499  | 0  |         status = U_MEMORY_ALLOCATION_ERROR;  | 
1500  | 0  |         return;  | 
1501  | 0  |     }  | 
1502  |  |  | 
1503  |  |     // start by stripping the trailing whitespace from all the rules  | 
1504  |  |     // (this is all the whitespace following each semicolon in the  | 
1505  |  |     // description).  This allows us to look for rule-set boundaries  | 
1506  |  |     // by searching for ";%" without having to worry about whitespace  | 
1507  |  |     // between the ; and the %  | 
1508  | 0  |     stripWhitespace(description);  | 
1509  |  |  | 
1510  |  |     // check to see if there's a set of lenient-parse rules.  If there  | 
1511  |  |     // is, pull them out into our temporary holding place for them,  | 
1512  |  |     // and delete them from the description before the real desciption-  | 
1513  |  |     // parsing code sees them  | 
1514  | 0  |     int32_t lp = description.indexOf(gLenientParse, -1, 0);  | 
1515  | 0  |     if (lp != -1) { | 
1516  |  |         // we've got to make sure we're not in the middle of a rule  | 
1517  |  |         // (where "%%lenient-parse" would actually get treated as  | 
1518  |  |         // rule text)  | 
1519  | 0  |         if (lp == 0 || description.charAt(lp - 1) == gSemiColon) { | 
1520  |  |             // locate the beginning and end of the actual collation  | 
1521  |  |             // rules (there may be whitespace between the name and  | 
1522  |  |             // the first token in the description)  | 
1523  | 0  |             int lpEnd = description.indexOf(gSemiPercent, 2, lp);  | 
1524  |  | 
  | 
1525  | 0  |             if (lpEnd == -1) { | 
1526  | 0  |                 lpEnd = description.length() - 1;  | 
1527  | 0  |             }  | 
1528  | 0  |             int lpStart = lp + u_strlen(gLenientParse);  | 
1529  | 0  |             while (PatternProps::isWhiteSpace(description.charAt(lpStart))) { | 
1530  | 0  |                 ++lpStart;  | 
1531  | 0  |             }  | 
1532  |  |  | 
1533  |  |             // copy out the lenient-parse rules and delete them  | 
1534  |  |             // from the description  | 
1535  | 0  |             lenientParseRules = new UnicodeString();  | 
1536  |  |             /* test for NULL */  | 
1537  | 0  |             if (lenientParseRules == nullptr) { | 
1538  | 0  |                 status = U_MEMORY_ALLOCATION_ERROR;  | 
1539  | 0  |                 return;  | 
1540  | 0  |             }  | 
1541  | 0  |             lenientParseRules->setTo(description, lpStart, lpEnd - lpStart);  | 
1542  |  | 
  | 
1543  | 0  |             description.remove(lp, lpEnd + 1 - lp);  | 
1544  | 0  |         }  | 
1545  | 0  |     }  | 
1546  |  |  | 
1547  |  |     // pre-flight parsing the description and count the number of  | 
1548  |  |     // rule sets (";%" marks the end of one rule set and the beginning | 
1549  |  |     // of the next)  | 
1550  | 0  |     numRuleSets = 0;  | 
1551  | 0  |     for (int32_t p = description.indexOf(gSemiPercent, 2, 0); p != -1; p = description.indexOf(gSemiPercent, 2, p)) { | 
1552  | 0  |         ++numRuleSets;  | 
1553  | 0  |         ++p;  | 
1554  | 0  |     }  | 
1555  | 0  |     ++numRuleSets;  | 
1556  |  |  | 
1557  |  |     // our rule list is an array of the appropriate size  | 
1558  | 0  |     fRuleSets = (NFRuleSet **)uprv_malloc((numRuleSets + 1) * sizeof(NFRuleSet *));  | 
1559  |  |     /* test for NULL */  | 
1560  | 0  |     if (fRuleSets == 0) { | 
1561  | 0  |         status = U_MEMORY_ALLOCATION_ERROR;  | 
1562  | 0  |         return;  | 
1563  | 0  |     }  | 
1564  |  |  | 
1565  | 0  |     for (int i = 0; i <= numRuleSets; ++i) { | 
1566  | 0  |         fRuleSets[i] = NULL;  | 
1567  | 0  |     }  | 
1568  |  |  | 
1569  |  |     // divide up the descriptions into individual rule-set descriptions  | 
1570  |  |     // and store them in a temporary array.  At each step, we also  | 
1571  |  |     // new up a rule set, but all this does is initialize its name  | 
1572  |  |     // and remove it from its description.  We can't actually parse  | 
1573  |  |     // the rest of the descriptions and finish initializing everything  | 
1574  |  |     // because we have to know the names and locations of all the rule  | 
1575  |  |     // sets before we can actually set everything up  | 
1576  | 0  |     if(!numRuleSets) { | 
1577  | 0  |         status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1578  | 0  |         return;  | 
1579  | 0  |     }  | 
1580  |  |  | 
1581  | 0  |     ruleSetDescriptions = new UnicodeString[numRuleSets];  | 
1582  | 0  |     if (ruleSetDescriptions == nullptr) { | 
1583  | 0  |         status = U_MEMORY_ALLOCATION_ERROR;  | 
1584  | 0  |         return;  | 
1585  | 0  |     }  | 
1586  |  |  | 
1587  | 0  |     { | 
1588  | 0  |         int curRuleSet = 0;  | 
1589  | 0  |         int32_t start = 0;  | 
1590  | 0  |         for (int32_t p = description.indexOf(gSemiPercent, 2, 0); p != -1; p = description.indexOf(gSemiPercent, 2, start)) { | 
1591  | 0  |             ruleSetDescriptions[curRuleSet].setTo(description, start, p + 1 - start);  | 
1592  | 0  |             fRuleSets[curRuleSet] = new NFRuleSet(this, ruleSetDescriptions, curRuleSet, status);  | 
1593  | 0  |             if (fRuleSets[curRuleSet] == nullptr) { | 
1594  | 0  |                 status = U_MEMORY_ALLOCATION_ERROR;  | 
1595  | 0  |                 return;  | 
1596  | 0  |             }  | 
1597  | 0  |             ++curRuleSet;  | 
1598  | 0  |             start = p + 1;  | 
1599  | 0  |         }  | 
1600  | 0  |         ruleSetDescriptions[curRuleSet].setTo(description, start, description.length() - start);  | 
1601  | 0  |         fRuleSets[curRuleSet] = new NFRuleSet(this, ruleSetDescriptions, curRuleSet, status);  | 
1602  | 0  |         if (fRuleSets[curRuleSet] == nullptr) { | 
1603  | 0  |             status = U_MEMORY_ALLOCATION_ERROR;  | 
1604  | 0  |             return;  | 
1605  | 0  |         }  | 
1606  | 0  |     }  | 
1607  |  |  | 
1608  |  |     // now we can take note of the formatter's default rule set, which  | 
1609  |  |     // is the last public rule set in the description (it's the last  | 
1610  |  |     // rather than the first so that a user can create a new formatter  | 
1611  |  |     // from an existing formatter and change its default behavior just  | 
1612  |  |     // by appending more rule sets to the end)  | 
1613  |  |  | 
1614  |  |     // {dlf} Initialization of a fraction rule set requires the default rule | 
1615  |  |     // set to be known.  For purposes of initialization, this is always the   | 
1616  |  |     // last public rule set, no matter what the localization data says.  | 
1617  | 0  |     initDefaultRuleSet();  | 
1618  |  |  | 
1619  |  |     // finally, we can go back through the temporary descriptions  | 
1620  |  |     // list and finish setting up the substructure (and we throw  | 
1621  |  |     // away the temporary descriptions as we go)  | 
1622  | 0  |     { | 
1623  | 0  |         for (int i = 0; i < numRuleSets; i++) { | 
1624  | 0  |             fRuleSets[i]->parseRules(ruleSetDescriptions[i], status);  | 
1625  | 0  |         }  | 
1626  | 0  |     }  | 
1627  |  |  | 
1628  |  |     // Now that the rules are initialized, the 'real' default rule  | 
1629  |  |     // set can be adjusted by the localization data.  | 
1630  |  |  | 
1631  |  |     // The C code keeps the localization array as is, rather than building  | 
1632  |  |     // a separate array of the public rule set names, so we have less work  | 
1633  |  |     // to do here-- but we still need to check the names.  | 
1634  |  |       | 
1635  | 0  |     if (localizationInfos) { | 
1636  |  |         // confirm the names, if any aren't in the rules, that's an error  | 
1637  |  |         // it is ok if the rules contain public rule sets that are not in this list  | 
1638  | 0  |         for (int32_t i = 0; i < localizationInfos->getNumberOfRuleSets(); ++i) { | 
1639  | 0  |             UnicodeString name(TRUE, localizationInfos->getRuleSetName(i), -1);  | 
1640  | 0  |             NFRuleSet* rs = findRuleSet(name, status);  | 
1641  | 0  |             if (rs == NULL) { | 
1642  | 0  |                 break; // error  | 
1643  | 0  |             }  | 
1644  | 0  |             if (i == 0) { | 
1645  | 0  |                 defaultRuleSet = rs;  | 
1646  | 0  |             }  | 
1647  | 0  |         }  | 
1648  | 0  |     } else { | 
1649  | 0  |         defaultRuleSet = getDefaultRuleSet();  | 
1650  | 0  |     }  | 
1651  | 0  |     originalDescription = rules;  | 
1652  | 0  | }  | 
1653  |  |  | 
1654  |  | // override the NumberFormat implementation in order to  | 
1655  |  | // lazily initialize relevant items  | 
1656  |  | void  | 
1657  |  | RuleBasedNumberFormat::setContext(UDisplayContext value, UErrorCode& status)  | 
1658  | 0  | { | 
1659  | 0  |     NumberFormat::setContext(value, status);  | 
1660  | 0  |     if (U_SUCCESS(status)) { | 
1661  | 0  |       if (!capitalizationInfoSet &&  | 
1662  | 0  |               (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE)) { | 
1663  | 0  |           initCapitalizationContextInfo(locale);  | 
1664  | 0  |           capitalizationInfoSet = TRUE;  | 
1665  | 0  |         }  | 
1666  | 0  | #if !UCONFIG_NO_BREAK_ITERATION  | 
1667  | 0  |         if ( capitalizationBrkIter == NULL && (value==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||  | 
1668  | 0  |                 (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationForUIListMenu) ||  | 
1669  | 0  |                 (value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && capitalizationForStandAlone)) ) { | 
1670  | 0  |             status = U_ZERO_ERROR;  | 
1671  | 0  |             capitalizationBrkIter = BreakIterator::createSentenceInstance(locale, status);  | 
1672  | 0  |             if (U_FAILURE(status)) { | 
1673  | 0  |                 delete capitalizationBrkIter;  | 
1674  | 0  |                 capitalizationBrkIter = NULL;  | 
1675  | 0  |             }  | 
1676  | 0  |         }  | 
1677  | 0  | #endif  | 
1678  | 0  |     }  | 
1679  | 0  | }  | 
1680  |  |  | 
1681  |  | void  | 
1682  |  | RuleBasedNumberFormat::initCapitalizationContextInfo(const Locale& thelocale)  | 
1683  | 0  | { | 
1684  | 0  | #if !UCONFIG_NO_BREAK_ITERATION  | 
1685  | 0  |     const char * localeID = (thelocale != NULL)? thelocale.getBaseName(): NULL;  | 
1686  | 0  |     UErrorCode status = U_ZERO_ERROR;  | 
1687  | 0  |     UResourceBundle *rb = ures_open(NULL, localeID, &status);  | 
1688  | 0  |     rb = ures_getByKeyWithFallback(rb, "contextTransforms", rb, &status);  | 
1689  | 0  |     rb = ures_getByKeyWithFallback(rb, "number-spellout", rb, &status);  | 
1690  | 0  |     if (U_SUCCESS(status) && rb != NULL) { | 
1691  | 0  |         int32_t len = 0;  | 
1692  | 0  |         const int32_t * intVector = ures_getIntVector(rb, &len, &status);  | 
1693  | 0  |         if (U_SUCCESS(status) && intVector != NULL && len >= 2) { | 
1694  | 0  |             capitalizationForUIListMenu = static_cast<UBool>(intVector[0]);  | 
1695  | 0  |             capitalizationForStandAlone = static_cast<UBool>(intVector[1]);  | 
1696  | 0  |         }  | 
1697  | 0  |     }  | 
1698  | 0  |     ures_close(rb);  | 
1699  | 0  | #endif  | 
1700  | 0  | }  | 
1701  |  |  | 
1702  |  | void  | 
1703  |  | RuleBasedNumberFormat::stripWhitespace(UnicodeString& description)  | 
1704  | 0  | { | 
1705  |  |     // iterate through the characters...  | 
1706  | 0  |     UnicodeString result;  | 
1707  |  | 
  | 
1708  | 0  |     int start = 0;  | 
1709  | 0  |     while (start != -1 && start < description.length()) { | 
1710  |  |         // seek to the first non-whitespace character...  | 
1711  | 0  |         while (start < description.length()  | 
1712  | 0  |             && PatternProps::isWhiteSpace(description.charAt(start))) { | 
1713  | 0  |             ++start;  | 
1714  | 0  |         }  | 
1715  |  |  | 
1716  |  |         // locate the next semicolon in the text and copy the text from  | 
1717  |  |         // our current position up to that semicolon into the result  | 
1718  | 0  |         int32_t p = description.indexOf(gSemiColon, start);  | 
1719  | 0  |         if (p == -1) { | 
1720  |  |             // or if we don't find a semicolon, just copy the rest of  | 
1721  |  |             // the string into the result  | 
1722  | 0  |             result.append(description, start, description.length() - start);  | 
1723  | 0  |             start = -1;  | 
1724  | 0  |         }  | 
1725  | 0  |         else if (p < description.length()) { | 
1726  | 0  |             result.append(description, start, p + 1 - start);  | 
1727  | 0  |             start = p + 1;  | 
1728  | 0  |         }  | 
1729  |  |  | 
1730  |  |         // when we get here, we've seeked off the end of the string, and  | 
1731  |  |         // we terminate the loop (we continue until *start* is -1 rather  | 
1732  |  |         // than until *p* is -1, because otherwise we'd miss the last  | 
1733  |  |         // rule in the description)  | 
1734  | 0  |         else { | 
1735  | 0  |             start = -1;  | 
1736  | 0  |         }  | 
1737  | 0  |     }  | 
1738  |  | 
  | 
1739  | 0  |     description.setTo(result);  | 
1740  | 0  | }  | 
1741  |  |  | 
1742  |  |  | 
1743  |  | void  | 
1744  |  | RuleBasedNumberFormat::dispose()  | 
1745  | 0  | { | 
1746  | 0  |     if (fRuleSets) { | 
1747  | 0  |         for (NFRuleSet** p = fRuleSets; *p; ++p) { | 
1748  | 0  |             delete *p;  | 
1749  | 0  |         }  | 
1750  | 0  |         uprv_free(fRuleSets);  | 
1751  | 0  |         fRuleSets = NULL;  | 
1752  | 0  |     }  | 
1753  |  | 
  | 
1754  | 0  |     if (ruleSetDescriptions) { | 
1755  | 0  |         delete [] ruleSetDescriptions;  | 
1756  | 0  |         ruleSetDescriptions = NULL;  | 
1757  | 0  |     }  | 
1758  |  | 
  | 
1759  | 0  | #if !UCONFIG_NO_COLLATION  | 
1760  | 0  |     delete collator;  | 
1761  | 0  | #endif  | 
1762  | 0  |     collator = NULL;  | 
1763  |  | 
  | 
1764  | 0  |     delete decimalFormatSymbols;  | 
1765  | 0  |     decimalFormatSymbols = NULL;  | 
1766  |  | 
  | 
1767  | 0  |     delete defaultInfinityRule;  | 
1768  | 0  |     defaultInfinityRule = NULL;  | 
1769  |  | 
  | 
1770  | 0  |     delete defaultNaNRule;  | 
1771  | 0  |     defaultNaNRule = NULL;  | 
1772  |  | 
  | 
1773  | 0  |     delete lenientParseRules;  | 
1774  | 0  |     lenientParseRules = NULL;  | 
1775  |  | 
  | 
1776  | 0  | #if !UCONFIG_NO_BREAK_ITERATION  | 
1777  | 0  |     delete capitalizationBrkIter;  | 
1778  | 0  |     capitalizationBrkIter = NULL;  | 
1779  | 0  | #endif  | 
1780  |  | 
  | 
1781  | 0  |     if (localizations) { | 
1782  | 0  |         localizations = localizations->unref();  | 
1783  | 0  |     }  | 
1784  | 0  | }  | 
1785  |  |  | 
1786  |  |  | 
1787  |  | //-----------------------------------------------------------------------  | 
1788  |  | // package-internal API  | 
1789  |  | //-----------------------------------------------------------------------  | 
1790  |  |  | 
1791  |  | /**  | 
1792  |  |  * Returns the collator to use for lenient parsing.  The collator is lazily created:  | 
1793  |  |  * this function creates it the first time it's called.  | 
1794  |  |  * @return The collator to use for lenient parsing, or null if lenient parsing  | 
1795  |  |  * is turned off.  | 
1796  |  | */  | 
1797  |  | const RuleBasedCollator*  | 
1798  |  | RuleBasedNumberFormat::getCollator() const  | 
1799  | 0  | { | 
1800  | 0  | #if !UCONFIG_NO_COLLATION  | 
1801  | 0  |     if (!fRuleSets) { | 
1802  | 0  |         return NULL;  | 
1803  | 0  |     }  | 
1804  |  |  | 
1805  |  |     // lazy-evaluate the collator  | 
1806  | 0  |     if (collator == NULL && lenient) { | 
1807  |  |         // create a default collator based on the formatter's locale,  | 
1808  |  |         // then pull out that collator's rules, append any additional  | 
1809  |  |         // rules specified in the description, and create a _new_  | 
1810  |  |         // collator based on the combination of those rules  | 
1811  |  | 
  | 
1812  | 0  |         UErrorCode status = U_ZERO_ERROR;  | 
1813  |  | 
  | 
1814  | 0  |         Collator* temp = Collator::createInstance(locale, status);  | 
1815  | 0  |         RuleBasedCollator* newCollator;  | 
1816  | 0  |         if (U_SUCCESS(status) && (newCollator = dynamic_cast<RuleBasedCollator*>(temp)) != NULL) { | 
1817  | 0  |             if (lenientParseRules) { | 
1818  | 0  |                 UnicodeString rules(newCollator->getRules());  | 
1819  | 0  |                 rules.append(*lenientParseRules);  | 
1820  |  | 
  | 
1821  | 0  |                 newCollator = new RuleBasedCollator(rules, status);  | 
1822  |  |                 // Exit if newCollator could not be created.  | 
1823  | 0  |                 if (newCollator == NULL) { | 
1824  | 0  |                     return NULL;  | 
1825  | 0  |                 }  | 
1826  | 0  |             } else { | 
1827  | 0  |                 temp = NULL;  | 
1828  | 0  |             }  | 
1829  | 0  |             if (U_SUCCESS(status)) { | 
1830  | 0  |                 newCollator->setAttribute(UCOL_DECOMPOSITION_MODE, UCOL_ON, status);  | 
1831  |  |                 // cast away const  | 
1832  | 0  |                 ((RuleBasedNumberFormat*)this)->collator = newCollator;  | 
1833  | 0  |             } else { | 
1834  | 0  |                 delete newCollator;  | 
1835  | 0  |             }  | 
1836  | 0  |         }  | 
1837  | 0  |         delete temp;  | 
1838  | 0  |     }  | 
1839  | 0  | #endif  | 
1840  |  |  | 
1841  |  |     // if lenient-parse mode is off, this will be null  | 
1842  |  |     // (see setLenientParseMode())  | 
1843  | 0  |     return collator;  | 
1844  | 0  | }  | 
1845  |  |  | 
1846  |  |  | 
1847  |  | DecimalFormatSymbols*  | 
1848  |  | RuleBasedNumberFormat::initializeDecimalFormatSymbols(UErrorCode &status)  | 
1849  | 0  | { | 
1850  |  |     // lazy-evaluate the DecimalFormatSymbols object.  This object  | 
1851  |  |     // is shared by all DecimalFormat instances belonging to this  | 
1852  |  |     // formatter  | 
1853  | 0  |     if (decimalFormatSymbols == nullptr) { | 
1854  | 0  |         LocalPointer<DecimalFormatSymbols> temp(new DecimalFormatSymbols(locale, status), status);  | 
1855  | 0  |         if (U_SUCCESS(status)) { | 
1856  | 0  |             decimalFormatSymbols = temp.orphan();  | 
1857  | 0  |         }  | 
1858  | 0  |     }  | 
1859  | 0  |     return decimalFormatSymbols;  | 
1860  | 0  | }  | 
1861  |  |  | 
1862  |  | /**  | 
1863  |  |  * Returns the DecimalFormatSymbols object that should be used by all DecimalFormat  | 
1864  |  |  * instances owned by this formatter.  | 
1865  |  | */  | 
1866  |  | const DecimalFormatSymbols*  | 
1867  |  | RuleBasedNumberFormat::getDecimalFormatSymbols() const  | 
1868  | 0  | { | 
1869  | 0  |     return decimalFormatSymbols;  | 
1870  | 0  | }  | 
1871  |  |  | 
1872  |  | NFRule*  | 
1873  |  | RuleBasedNumberFormat::initializeDefaultInfinityRule(UErrorCode &status)  | 
1874  | 0  | { | 
1875  | 0  |     if (U_FAILURE(status)) { | 
1876  | 0  |         return nullptr;  | 
1877  | 0  |     }  | 
1878  | 0  |     if (defaultInfinityRule == NULL) { | 
1879  | 0  |         UnicodeString rule(UNICODE_STRING_SIMPLE("Inf: ")); | 
1880  | 0  |         rule.append(getDecimalFormatSymbols()->getSymbol(DecimalFormatSymbols::kInfinitySymbol));  | 
1881  | 0  |         LocalPointer<NFRule> temp(new NFRule(this, rule, status), status);  | 
1882  | 0  |         if (U_SUCCESS(status)) { | 
1883  | 0  |             defaultInfinityRule = temp.orphan();  | 
1884  | 0  |         }  | 
1885  | 0  |     }  | 
1886  | 0  |     return defaultInfinityRule;  | 
1887  | 0  | }  | 
1888  |  |  | 
1889  |  | const NFRule*  | 
1890  |  | RuleBasedNumberFormat::getDefaultInfinityRule() const  | 
1891  | 0  | { | 
1892  | 0  |     return defaultInfinityRule;  | 
1893  | 0  | }  | 
1894  |  |  | 
1895  |  | NFRule*  | 
1896  |  | RuleBasedNumberFormat::initializeDefaultNaNRule(UErrorCode &status)  | 
1897  | 0  | { | 
1898  | 0  |     if (U_FAILURE(status)) { | 
1899  | 0  |         return nullptr;  | 
1900  | 0  |     }  | 
1901  | 0  |     if (defaultNaNRule == nullptr) { | 
1902  | 0  |         UnicodeString rule(UNICODE_STRING_SIMPLE("NaN: ")); | 
1903  | 0  |         rule.append(getDecimalFormatSymbols()->getSymbol(DecimalFormatSymbols::kNaNSymbol));  | 
1904  | 0  |         LocalPointer<NFRule> temp(new NFRule(this, rule, status), status);  | 
1905  | 0  |         if (U_SUCCESS(status)) { | 
1906  | 0  |             defaultNaNRule = temp.orphan();  | 
1907  | 0  |         }  | 
1908  | 0  |     }  | 
1909  | 0  |     return defaultNaNRule;  | 
1910  | 0  | }  | 
1911  |  |  | 
1912  |  | const NFRule*  | 
1913  |  | RuleBasedNumberFormat::getDefaultNaNRule() const  | 
1914  | 0  | { | 
1915  | 0  |     return defaultNaNRule;  | 
1916  | 0  | }  | 
1917  |  |  | 
1918  |  | // De-owning the current localized symbols and adopt the new symbols.  | 
1919  |  | void  | 
1920  |  | RuleBasedNumberFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt)  | 
1921  | 0  | { | 
1922  | 0  |     if (symbolsToAdopt == NULL) { | 
1923  | 0  |         return; // do not allow caller to set decimalFormatSymbols to NULL  | 
1924  | 0  |     }  | 
1925  |  |  | 
1926  | 0  |     if (decimalFormatSymbols != NULL) { | 
1927  | 0  |         delete decimalFormatSymbols;  | 
1928  | 0  |     }  | 
1929  |  | 
  | 
1930  | 0  |     decimalFormatSymbols = symbolsToAdopt;  | 
1931  |  | 
  | 
1932  | 0  |     { | 
1933  |  |         // Apply the new decimalFormatSymbols by reparsing the rulesets  | 
1934  | 0  |         UErrorCode status = U_ZERO_ERROR;  | 
1935  |  | 
  | 
1936  | 0  |         delete defaultInfinityRule;  | 
1937  | 0  |         defaultInfinityRule = NULL;  | 
1938  | 0  |         initializeDefaultInfinityRule(status); // Reset with the new DecimalFormatSymbols  | 
1939  |  | 
  | 
1940  | 0  |         delete defaultNaNRule;  | 
1941  | 0  |         defaultNaNRule = NULL;  | 
1942  | 0  |         initializeDefaultNaNRule(status); // Reset with the new DecimalFormatSymbols  | 
1943  |  | 
  | 
1944  | 0  |         if (fRuleSets) { | 
1945  | 0  |             for (int32_t i = 0; i < numRuleSets; i++) { | 
1946  | 0  |                 fRuleSets[i]->setDecimalFormatSymbols(*symbolsToAdopt, status);  | 
1947  | 0  |             }  | 
1948  | 0  |         }  | 
1949  | 0  |     }  | 
1950  | 0  | }  | 
1951  |  |  | 
1952  |  | // Setting the symbols is equivalent to adopting a newly created localized symbols.  | 
1953  |  | void  | 
1954  |  | RuleBasedNumberFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols)  | 
1955  | 0  | { | 
1956  | 0  |     adoptDecimalFormatSymbols(new DecimalFormatSymbols(symbols));  | 
1957  | 0  | }  | 
1958  |  |  | 
1959  |  | PluralFormat *  | 
1960  |  | RuleBasedNumberFormat::createPluralFormat(UPluralType pluralType,  | 
1961  |  |                                           const UnicodeString &pattern,  | 
1962  |  |                                           UErrorCode& status) const  | 
1963  | 0  | { | 
1964  | 0  |     auto *pf = new PluralFormat(locale, pluralType, pattern, status);  | 
1965  | 0  |     if (pf == nullptr) { | 
1966  | 0  |         status = U_MEMORY_ALLOCATION_ERROR;  | 
1967  | 0  |     }  | 
1968  | 0  |     return pf;  | 
1969  | 0  | }  | 
1970  |  |  | 
1971  |  | /**  | 
1972  |  |  * Get the rounding mode.  | 
1973  |  |  * @return A rounding mode  | 
1974  |  |  */  | 
1975  | 0  | DecimalFormat::ERoundingMode RuleBasedNumberFormat::getRoundingMode() const { | 
1976  | 0  |     return fRoundingMode;  | 
1977  | 0  | }  | 
1978  |  |  | 
1979  |  | /**  | 
1980  |  |  * Set the rounding mode.  This has no effect unless the rounding  | 
1981  |  |  * increment is greater than zero.  | 
1982  |  |  * @param roundingMode A rounding mode  | 
1983  |  |  */  | 
1984  | 0  | void RuleBasedNumberFormat::setRoundingMode(DecimalFormat::ERoundingMode roundingMode) { | 
1985  | 0  |     fRoundingMode = roundingMode;  | 
1986  | 0  | }  | 
1987  |  |  | 
1988  |  | U_NAMESPACE_END  | 
1989  |  |  | 
1990  |  | /* U_HAVE_RBNF */  | 
1991  |  | #endif  |