|
1 /* |
|
2 ******************************************************************************* |
|
3 * Copyright (C) 1997-2013, International Business Machines Corporation |
|
4 * and others. All Rights Reserved. |
|
5 ******************************************************************************* |
|
6 */ |
|
7 |
|
8 #include "utypeinfo.h" // for 'typeid' to work |
|
9 |
|
10 #include "unicode/rbnf.h" |
|
11 |
|
12 #if U_HAVE_RBNF |
|
13 |
|
14 #include "unicode/normlzr.h" |
|
15 #include "unicode/tblcoll.h" |
|
16 #include "unicode/uchar.h" |
|
17 #include "unicode/ucol.h" |
|
18 #include "unicode/uloc.h" |
|
19 #include "unicode/unum.h" |
|
20 #include "unicode/ures.h" |
|
21 #include "unicode/ustring.h" |
|
22 #include "unicode/utf16.h" |
|
23 #include "unicode/udata.h" |
|
24 #include "nfrs.h" |
|
25 |
|
26 #include "cmemory.h" |
|
27 #include "cstring.h" |
|
28 #include "patternprops.h" |
|
29 #include "uresimp.h" |
|
30 |
|
31 // debugging |
|
32 // #define DEBUG |
|
33 |
|
34 #ifdef DEBUG |
|
35 #include "stdio.h" |
|
36 #endif |
|
37 |
|
38 #define U_ICUDATA_RBNF U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "rbnf" |
|
39 |
|
40 static const UChar gPercentPercent[] = |
|
41 { |
|
42 0x25, 0x25, 0 |
|
43 }; /* "%%" */ |
|
44 |
|
45 // All urbnf objects are created through openRules, so we init all of the |
|
46 // Unicode string constants required by rbnf, nfrs, or nfr here. |
|
47 static const UChar gLenientParse[] = |
|
48 { |
|
49 0x25, 0x25, 0x6C, 0x65, 0x6E, 0x69, 0x65, 0x6E, 0x74, 0x2D, 0x70, 0x61, 0x72, 0x73, 0x65, 0x3A, 0 |
|
50 }; /* "%%lenient-parse:" */ |
|
51 static const UChar gSemiColon = 0x003B; |
|
52 static const UChar gSemiPercent[] = |
|
53 { |
|
54 0x3B, 0x25, 0 |
|
55 }; /* ";%" */ |
|
56 |
|
57 #define kSomeNumberOfBitsDiv2 22 |
|
58 #define kHalfMaxDouble (double)(1 << kSomeNumberOfBitsDiv2) |
|
59 #define kMaxDouble (kHalfMaxDouble * kHalfMaxDouble) |
|
60 |
|
61 U_NAMESPACE_BEGIN |
|
62 |
|
63 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedNumberFormat) |
|
64 |
|
65 /* |
|
66 This is a utility class. It does not use ICU's RTTI. |
|
67 If ICU's RTTI is needed again, you can uncomment the RTTI code and derive from UObject. |
|
68 Please make sure that intltest passes on Windows in Release mode, |
|
69 since the string pooling per compilation unit will mess up how RTTI works. |
|
70 The RTTI code was also removed due to lack of code coverage. |
|
71 */ |
|
72 class LocalizationInfo : public UMemory { |
|
73 protected: |
|
74 virtual ~LocalizationInfo(); |
|
75 uint32_t refcount; |
|
76 |
|
77 public: |
|
78 LocalizationInfo() : refcount(0) {} |
|
79 |
|
80 LocalizationInfo* ref(void) { |
|
81 ++refcount; |
|
82 return this; |
|
83 } |
|
84 |
|
85 LocalizationInfo* unref(void) { |
|
86 if (refcount && --refcount == 0) { |
|
87 delete this; |
|
88 } |
|
89 return NULL; |
|
90 } |
|
91 |
|
92 virtual UBool operator==(const LocalizationInfo* rhs) const; |
|
93 inline UBool operator!=(const LocalizationInfo* rhs) const { return !operator==(rhs); } |
|
94 |
|
95 virtual int32_t getNumberOfRuleSets(void) const = 0; |
|
96 virtual const UChar* getRuleSetName(int32_t index) const = 0; |
|
97 virtual int32_t getNumberOfDisplayLocales(void) const = 0; |
|
98 virtual const UChar* getLocaleName(int32_t index) const = 0; |
|
99 virtual const UChar* getDisplayName(int32_t localeIndex, int32_t ruleIndex) const = 0; |
|
100 |
|
101 virtual int32_t indexForLocale(const UChar* locale) const; |
|
102 virtual int32_t indexForRuleSet(const UChar* ruleset) const; |
|
103 |
|
104 // virtual UClassID getDynamicClassID() const = 0; |
|
105 // static UClassID getStaticClassID(void); |
|
106 }; |
|
107 |
|
108 LocalizationInfo::~LocalizationInfo() {} |
|
109 |
|
110 //UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(LocalizationInfo) |
|
111 |
|
112 // if both strings are NULL, this returns TRUE |
|
113 static UBool |
|
114 streq(const UChar* lhs, const UChar* rhs) { |
|
115 if (rhs == lhs) { |
|
116 return TRUE; |
|
117 } |
|
118 if (lhs && rhs) { |
|
119 return u_strcmp(lhs, rhs) == 0; |
|
120 } |
|
121 return FALSE; |
|
122 } |
|
123 |
|
124 UBool |
|
125 LocalizationInfo::operator==(const LocalizationInfo* rhs) const { |
|
126 if (rhs) { |
|
127 if (this == rhs) { |
|
128 return TRUE; |
|
129 } |
|
130 |
|
131 int32_t rsc = getNumberOfRuleSets(); |
|
132 if (rsc == rhs->getNumberOfRuleSets()) { |
|
133 for (int i = 0; i < rsc; ++i) { |
|
134 if (!streq(getRuleSetName(i), rhs->getRuleSetName(i))) { |
|
135 return FALSE; |
|
136 } |
|
137 } |
|
138 int32_t dlc = getNumberOfDisplayLocales(); |
|
139 if (dlc == rhs->getNumberOfDisplayLocales()) { |
|
140 for (int i = 0; i < dlc; ++i) { |
|
141 const UChar* locale = getLocaleName(i); |
|
142 int32_t ix = rhs->indexForLocale(locale); |
|
143 // if no locale, ix is -1, getLocaleName returns null, so streq returns false |
|
144 if (!streq(locale, rhs->getLocaleName(ix))) { |
|
145 return FALSE; |
|
146 } |
|
147 for (int j = 0; j < rsc; ++j) { |
|
148 if (!streq(getDisplayName(i, j), rhs->getDisplayName(ix, j))) { |
|
149 return FALSE; |
|
150 } |
|
151 } |
|
152 } |
|
153 return TRUE; |
|
154 } |
|
155 } |
|
156 } |
|
157 return FALSE; |
|
158 } |
|
159 |
|
160 int32_t |
|
161 LocalizationInfo::indexForLocale(const UChar* locale) const { |
|
162 for (int i = 0; i < getNumberOfDisplayLocales(); ++i) { |
|
163 if (streq(locale, getLocaleName(i))) { |
|
164 return i; |
|
165 } |
|
166 } |
|
167 return -1; |
|
168 } |
|
169 |
|
170 int32_t |
|
171 LocalizationInfo::indexForRuleSet(const UChar* ruleset) const { |
|
172 if (ruleset) { |
|
173 for (int i = 0; i < getNumberOfRuleSets(); ++i) { |
|
174 if (streq(ruleset, getRuleSetName(i))) { |
|
175 return i; |
|
176 } |
|
177 } |
|
178 } |
|
179 return -1; |
|
180 } |
|
181 |
|
182 |
|
183 typedef void (*Fn_Deleter)(void*); |
|
184 |
|
185 class VArray { |
|
186 void** buf; |
|
187 int32_t cap; |
|
188 int32_t size; |
|
189 Fn_Deleter deleter; |
|
190 public: |
|
191 VArray() : buf(NULL), cap(0), size(0), deleter(NULL) {} |
|
192 |
|
193 VArray(Fn_Deleter del) : buf(NULL), cap(0), size(0), deleter(del) {} |
|
194 |
|
195 ~VArray() { |
|
196 if (deleter) { |
|
197 for (int i = 0; i < size; ++i) { |
|
198 (*deleter)(buf[i]); |
|
199 } |
|
200 } |
|
201 uprv_free(buf); |
|
202 } |
|
203 |
|
204 int32_t length() { |
|
205 return size; |
|
206 } |
|
207 |
|
208 void add(void* elem, UErrorCode& status) { |
|
209 if (U_SUCCESS(status)) { |
|
210 if (size == cap) { |
|
211 if (cap == 0) { |
|
212 cap = 1; |
|
213 } else if (cap < 256) { |
|
214 cap *= 2; |
|
215 } else { |
|
216 cap += 256; |
|
217 } |
|
218 if (buf == NULL) { |
|
219 buf = (void**)uprv_malloc(cap * sizeof(void*)); |
|
220 } else { |
|
221 buf = (void**)uprv_realloc(buf, cap * sizeof(void*)); |
|
222 } |
|
223 if (buf == NULL) { |
|
224 // if we couldn't realloc, we leak the memory we've already allocated, but we're in deep trouble anyway |
|
225 status = U_MEMORY_ALLOCATION_ERROR; |
|
226 return; |
|
227 } |
|
228 void* start = &buf[size]; |
|
229 size_t count = (cap - size) * sizeof(void*); |
|
230 uprv_memset(start, 0, count); // fill with nulls, just because |
|
231 } |
|
232 buf[size++] = elem; |
|
233 } |
|
234 } |
|
235 |
|
236 void** release(void) { |
|
237 void** result = buf; |
|
238 buf = NULL; |
|
239 cap = 0; |
|
240 size = 0; |
|
241 return result; |
|
242 } |
|
243 }; |
|
244 |
|
245 class LocDataParser; |
|
246 |
|
247 class StringLocalizationInfo : public LocalizationInfo { |
|
248 UChar* info; |
|
249 UChar*** data; |
|
250 int32_t numRuleSets; |
|
251 int32_t numLocales; |
|
252 |
|
253 friend class LocDataParser; |
|
254 |
|
255 StringLocalizationInfo(UChar* i, UChar*** d, int32_t numRS, int32_t numLocs) |
|
256 : info(i), data(d), numRuleSets(numRS), numLocales(numLocs) |
|
257 { |
|
258 } |
|
259 |
|
260 public: |
|
261 static StringLocalizationInfo* create(const UnicodeString& info, UParseError& perror, UErrorCode& status); |
|
262 |
|
263 virtual ~StringLocalizationInfo(); |
|
264 virtual int32_t getNumberOfRuleSets(void) const { return numRuleSets; } |
|
265 virtual const UChar* getRuleSetName(int32_t index) const; |
|
266 virtual int32_t getNumberOfDisplayLocales(void) const { return numLocales; } |
|
267 virtual const UChar* getLocaleName(int32_t index) const; |
|
268 virtual const UChar* getDisplayName(int32_t localeIndex, int32_t ruleIndex) const; |
|
269 |
|
270 // virtual UClassID getDynamicClassID() const; |
|
271 // static UClassID getStaticClassID(void); |
|
272 |
|
273 private: |
|
274 void init(UErrorCode& status) const; |
|
275 }; |
|
276 |
|
277 |
|
278 enum { |
|
279 OPEN_ANGLE = 0x003c, /* '<' */ |
|
280 CLOSE_ANGLE = 0x003e, /* '>' */ |
|
281 COMMA = 0x002c, |
|
282 TICK = 0x0027, |
|
283 QUOTE = 0x0022, |
|
284 SPACE = 0x0020 |
|
285 }; |
|
286 |
|
287 /** |
|
288 * Utility for parsing a localization string and returning a StringLocalizationInfo*. |
|
289 */ |
|
290 class LocDataParser { |
|
291 UChar* data; |
|
292 const UChar* e; |
|
293 UChar* p; |
|
294 UChar ch; |
|
295 UParseError& pe; |
|
296 UErrorCode& ec; |
|
297 |
|
298 public: |
|
299 LocDataParser(UParseError& parseError, UErrorCode& status) |
|
300 : data(NULL), e(NULL), p(NULL), ch(0xffff), pe(parseError), ec(status) {} |
|
301 ~LocDataParser() {} |
|
302 |
|
303 /* |
|
304 * On a successful parse, return a StringLocalizationInfo*, otherwise delete locData, set perror and status, |
|
305 * and return NULL. The StringLocalizationInfo will adopt locData if it is created. |
|
306 */ |
|
307 StringLocalizationInfo* parse(UChar* data, int32_t len); |
|
308 |
|
309 private: |
|
310 |
|
311 void inc(void) { ++p; ch = 0xffff; } |
|
312 UBool checkInc(UChar c) { if (p < e && (ch == c || *p == c)) { inc(); return TRUE; } return FALSE; } |
|
313 UBool check(UChar c) { return p < e && (ch == c || *p == c); } |
|
314 void skipWhitespace(void) { while (p < e && PatternProps::isWhiteSpace(ch != 0xffff ? ch : *p)) inc();} |
|
315 UBool inList(UChar c, const UChar* list) const { |
|
316 if (*list == SPACE && PatternProps::isWhiteSpace(c)) return TRUE; |
|
317 while (*list && *list != c) ++list; return *list == c; |
|
318 } |
|
319 void parseError(const char* msg); |
|
320 |
|
321 StringLocalizationInfo* doParse(void); |
|
322 |
|
323 UChar** nextArray(int32_t& requiredLength); |
|
324 UChar* nextString(void); |
|
325 }; |
|
326 |
|
327 #ifdef DEBUG |
|
328 #define ERROR(msg) parseError(msg); return NULL; |
|
329 #else |
|
330 #define ERROR(msg) parseError(NULL); return NULL; |
|
331 #endif |
|
332 |
|
333 |
|
334 static const UChar DQUOTE_STOPLIST[] = { |
|
335 QUOTE, 0 |
|
336 }; |
|
337 |
|
338 static const UChar SQUOTE_STOPLIST[] = { |
|
339 TICK, 0 |
|
340 }; |
|
341 |
|
342 static const UChar NOQUOTE_STOPLIST[] = { |
|
343 SPACE, COMMA, CLOSE_ANGLE, OPEN_ANGLE, TICK, QUOTE, 0 |
|
344 }; |
|
345 |
|
346 static void |
|
347 DeleteFn(void* p) { |
|
348 uprv_free(p); |
|
349 } |
|
350 |
|
351 StringLocalizationInfo* |
|
352 LocDataParser::parse(UChar* _data, int32_t len) { |
|
353 if (U_FAILURE(ec)) { |
|
354 if (_data) uprv_free(_data); |
|
355 return NULL; |
|
356 } |
|
357 |
|
358 pe.line = 0; |
|
359 pe.offset = -1; |
|
360 pe.postContext[0] = 0; |
|
361 pe.preContext[0] = 0; |
|
362 |
|
363 if (_data == NULL) { |
|
364 ec = U_ILLEGAL_ARGUMENT_ERROR; |
|
365 return NULL; |
|
366 } |
|
367 |
|
368 if (len <= 0) { |
|
369 ec = U_ILLEGAL_ARGUMENT_ERROR; |
|
370 uprv_free(_data); |
|
371 return NULL; |
|
372 } |
|
373 |
|
374 data = _data; |
|
375 e = data + len; |
|
376 p = _data; |
|
377 ch = 0xffff; |
|
378 |
|
379 return doParse(); |
|
380 } |
|
381 |
|
382 |
|
383 StringLocalizationInfo* |
|
384 LocDataParser::doParse(void) { |
|
385 skipWhitespace(); |
|
386 if (!checkInc(OPEN_ANGLE)) { |
|
387 ERROR("Missing open angle"); |
|
388 } else { |
|
389 VArray array(DeleteFn); |
|
390 UBool mightHaveNext = TRUE; |
|
391 int32_t requiredLength = -1; |
|
392 while (mightHaveNext) { |
|
393 mightHaveNext = FALSE; |
|
394 UChar** elem = nextArray(requiredLength); |
|
395 skipWhitespace(); |
|
396 UBool haveComma = check(COMMA); |
|
397 if (elem) { |
|
398 array.add(elem, ec); |
|
399 if (haveComma) { |
|
400 inc(); |
|
401 mightHaveNext = TRUE; |
|
402 } |
|
403 } else if (haveComma) { |
|
404 ERROR("Unexpected character"); |
|
405 } |
|
406 } |
|
407 |
|
408 skipWhitespace(); |
|
409 if (!checkInc(CLOSE_ANGLE)) { |
|
410 if (check(OPEN_ANGLE)) { |
|
411 ERROR("Missing comma in outer array"); |
|
412 } else { |
|
413 ERROR("Missing close angle bracket in outer array"); |
|
414 } |
|
415 } |
|
416 |
|
417 skipWhitespace(); |
|
418 if (p != e) { |
|
419 ERROR("Extra text after close of localization data"); |
|
420 } |
|
421 |
|
422 array.add(NULL, ec); |
|
423 if (U_SUCCESS(ec)) { |
|
424 int32_t numLocs = array.length() - 2; // subtract first, NULL |
|
425 UChar*** result = (UChar***)array.release(); |
|
426 |
|
427 return new StringLocalizationInfo(data, result, requiredLength-2, numLocs); // subtract first, NULL |
|
428 } |
|
429 } |
|
430 |
|
431 ERROR("Unknown error"); |
|
432 } |
|
433 |
|
434 UChar** |
|
435 LocDataParser::nextArray(int32_t& requiredLength) { |
|
436 if (U_FAILURE(ec)) { |
|
437 return NULL; |
|
438 } |
|
439 |
|
440 skipWhitespace(); |
|
441 if (!checkInc(OPEN_ANGLE)) { |
|
442 ERROR("Missing open angle"); |
|
443 } |
|
444 |
|
445 VArray array; |
|
446 UBool mightHaveNext = TRUE; |
|
447 while (mightHaveNext) { |
|
448 mightHaveNext = FALSE; |
|
449 UChar* elem = nextString(); |
|
450 skipWhitespace(); |
|
451 UBool haveComma = check(COMMA); |
|
452 if (elem) { |
|
453 array.add(elem, ec); |
|
454 if (haveComma) { |
|
455 inc(); |
|
456 mightHaveNext = TRUE; |
|
457 } |
|
458 } else if (haveComma) { |
|
459 ERROR("Unexpected comma"); |
|
460 } |
|
461 } |
|
462 skipWhitespace(); |
|
463 if (!checkInc(CLOSE_ANGLE)) { |
|
464 if (check(OPEN_ANGLE)) { |
|
465 ERROR("Missing close angle bracket in inner array"); |
|
466 } else { |
|
467 ERROR("Missing comma in inner array"); |
|
468 } |
|
469 } |
|
470 |
|
471 array.add(NULL, ec); |
|
472 if (U_SUCCESS(ec)) { |
|
473 if (requiredLength == -1) { |
|
474 requiredLength = array.length() + 1; |
|
475 } else if (array.length() != requiredLength) { |
|
476 ec = U_ILLEGAL_ARGUMENT_ERROR; |
|
477 ERROR("Array not of required length"); |
|
478 } |
|
479 |
|
480 return (UChar**)array.release(); |
|
481 } |
|
482 ERROR("Unknown Error"); |
|
483 } |
|
484 |
|
485 UChar* |
|
486 LocDataParser::nextString() { |
|
487 UChar* result = NULL; |
|
488 |
|
489 skipWhitespace(); |
|
490 if (p < e) { |
|
491 const UChar* terminators; |
|
492 UChar c = *p; |
|
493 UBool haveQuote = c == QUOTE || c == TICK; |
|
494 if (haveQuote) { |
|
495 inc(); |
|
496 terminators = c == QUOTE ? DQUOTE_STOPLIST : SQUOTE_STOPLIST; |
|
497 } else { |
|
498 terminators = NOQUOTE_STOPLIST; |
|
499 } |
|
500 UChar* start = p; |
|
501 while (p < e && !inList(*p, terminators)) ++p; |
|
502 if (p == e) { |
|
503 ERROR("Unexpected end of data"); |
|
504 } |
|
505 |
|
506 UChar x = *p; |
|
507 if (p > start) { |
|
508 ch = x; |
|
509 *p = 0x0; // terminate by writing to data |
|
510 result = start; // just point into data |
|
511 } |
|
512 if (haveQuote) { |
|
513 if (x != c) { |
|
514 ERROR("Missing matching quote"); |
|
515 } else if (p == start) { |
|
516 ERROR("Empty string"); |
|
517 } |
|
518 inc(); |
|
519 } else if (x == OPEN_ANGLE || x == TICK || x == QUOTE) { |
|
520 ERROR("Unexpected character in string"); |
|
521 } |
|
522 } |
|
523 |
|
524 // ok for there to be no next string |
|
525 return result; |
|
526 } |
|
527 |
|
528 void |
|
529 LocDataParser::parseError(const char* /*str*/) { |
|
530 if (!data) { |
|
531 return; |
|
532 } |
|
533 |
|
534 const UChar* start = p - U_PARSE_CONTEXT_LEN - 1; |
|
535 if (start < data) { |
|
536 start = data; |
|
537 } |
|
538 for (UChar* x = p; --x >= start;) { |
|
539 if (!*x) { |
|
540 start = x+1; |
|
541 break; |
|
542 } |
|
543 } |
|
544 const UChar* limit = p + U_PARSE_CONTEXT_LEN - 1; |
|
545 if (limit > e) { |
|
546 limit = e; |
|
547 } |
|
548 u_strncpy(pe.preContext, start, (int32_t)(p-start)); |
|
549 pe.preContext[p-start] = 0; |
|
550 u_strncpy(pe.postContext, p, (int32_t)(limit-p)); |
|
551 pe.postContext[limit-p] = 0; |
|
552 pe.offset = (int32_t)(p - data); |
|
553 |
|
554 #ifdef DEBUG |
|
555 fprintf(stderr, "%s at or near character %d: ", str, p-data); |
|
556 |
|
557 UnicodeString msg; |
|
558 msg.append(start, p - start); |
|
559 msg.append((UChar)0x002f); /* SOLIDUS/SLASH */ |
|
560 msg.append(p, limit-p); |
|
561 msg.append("'"); |
|
562 |
|
563 char buf[128]; |
|
564 int32_t len = msg.extract(0, msg.length(), buf, 128); |
|
565 if (len >= 128) { |
|
566 buf[127] = 0; |
|
567 } else { |
|
568 buf[len] = 0; |
|
569 } |
|
570 fprintf(stderr, "%s\n", buf); |
|
571 fflush(stderr); |
|
572 #endif |
|
573 |
|
574 uprv_free(data); |
|
575 data = NULL; |
|
576 p = NULL; |
|
577 e = NULL; |
|
578 |
|
579 if (U_SUCCESS(ec)) { |
|
580 ec = U_PARSE_ERROR; |
|
581 } |
|
582 } |
|
583 |
|
584 //UOBJECT_DEFINE_RTTI_IMPLEMENTATION(StringLocalizationInfo) |
|
585 |
|
586 StringLocalizationInfo* |
|
587 StringLocalizationInfo::create(const UnicodeString& info, UParseError& perror, UErrorCode& status) { |
|
588 if (U_FAILURE(status)) { |
|
589 return NULL; |
|
590 } |
|
591 |
|
592 int32_t len = info.length(); |
|
593 if (len == 0) { |
|
594 return NULL; // no error; |
|
595 } |
|
596 |
|
597 UChar* p = (UChar*)uprv_malloc(len * sizeof(UChar)); |
|
598 if (!p) { |
|
599 status = U_MEMORY_ALLOCATION_ERROR; |
|
600 return NULL; |
|
601 } |
|
602 info.extract(p, len, status); |
|
603 if (!U_FAILURE(status)) { |
|
604 status = U_ZERO_ERROR; // clear warning about non-termination |
|
605 } |
|
606 |
|
607 LocDataParser parser(perror, status); |
|
608 return parser.parse(p, len); |
|
609 } |
|
610 |
|
611 StringLocalizationInfo::~StringLocalizationInfo() { |
|
612 for (UChar*** p = (UChar***)data; *p; ++p) { |
|
613 // remaining data is simply pointer into our unicode string data. |
|
614 if (*p) uprv_free(*p); |
|
615 } |
|
616 if (data) uprv_free(data); |
|
617 if (info) uprv_free(info); |
|
618 } |
|
619 |
|
620 |
|
621 const UChar* |
|
622 StringLocalizationInfo::getRuleSetName(int32_t index) const { |
|
623 if (index >= 0 && index < getNumberOfRuleSets()) { |
|
624 return data[0][index]; |
|
625 } |
|
626 return NULL; |
|
627 } |
|
628 |
|
629 const UChar* |
|
630 StringLocalizationInfo::getLocaleName(int32_t index) const { |
|
631 if (index >= 0 && index < getNumberOfDisplayLocales()) { |
|
632 return data[index+1][0]; |
|
633 } |
|
634 return NULL; |
|
635 } |
|
636 |
|
637 const UChar* |
|
638 StringLocalizationInfo::getDisplayName(int32_t localeIndex, int32_t ruleIndex) const { |
|
639 if (localeIndex >= 0 && localeIndex < getNumberOfDisplayLocales() && |
|
640 ruleIndex >= 0 && ruleIndex < getNumberOfRuleSets()) { |
|
641 return data[localeIndex+1][ruleIndex+1]; |
|
642 } |
|
643 return NULL; |
|
644 } |
|
645 |
|
646 // ---------- |
|
647 |
|
648 RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, |
|
649 const UnicodeString& locs, |
|
650 const Locale& alocale, UParseError& perror, UErrorCode& status) |
|
651 : ruleSets(NULL) |
|
652 , ruleSetDescriptions(NULL) |
|
653 , numRuleSets(0) |
|
654 , defaultRuleSet(NULL) |
|
655 , locale(alocale) |
|
656 , collator(NULL) |
|
657 , decimalFormatSymbols(NULL) |
|
658 , lenient(FALSE) |
|
659 , lenientParseRules(NULL) |
|
660 , localizations(NULL) |
|
661 { |
|
662 LocalizationInfo* locinfo = StringLocalizationInfo::create(locs, perror, status); |
|
663 init(description, locinfo, perror, status); |
|
664 } |
|
665 |
|
666 RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, |
|
667 const UnicodeString& locs, |
|
668 UParseError& perror, UErrorCode& status) |
|
669 : ruleSets(NULL) |
|
670 , ruleSetDescriptions(NULL) |
|
671 , numRuleSets(0) |
|
672 , defaultRuleSet(NULL) |
|
673 , locale(Locale::getDefault()) |
|
674 , collator(NULL) |
|
675 , decimalFormatSymbols(NULL) |
|
676 , lenient(FALSE) |
|
677 , lenientParseRules(NULL) |
|
678 , localizations(NULL) |
|
679 { |
|
680 LocalizationInfo* locinfo = StringLocalizationInfo::create(locs, perror, status); |
|
681 init(description, locinfo, perror, status); |
|
682 } |
|
683 |
|
684 RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, |
|
685 LocalizationInfo* info, |
|
686 const Locale& alocale, UParseError& perror, UErrorCode& status) |
|
687 : ruleSets(NULL) |
|
688 , ruleSetDescriptions(NULL) |
|
689 , numRuleSets(0) |
|
690 , defaultRuleSet(NULL) |
|
691 , locale(alocale) |
|
692 , collator(NULL) |
|
693 , decimalFormatSymbols(NULL) |
|
694 , lenient(FALSE) |
|
695 , lenientParseRules(NULL) |
|
696 , localizations(NULL) |
|
697 { |
|
698 init(description, info, perror, status); |
|
699 } |
|
700 |
|
701 RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, |
|
702 UParseError& perror, |
|
703 UErrorCode& status) |
|
704 : ruleSets(NULL) |
|
705 , ruleSetDescriptions(NULL) |
|
706 , numRuleSets(0) |
|
707 , defaultRuleSet(NULL) |
|
708 , locale(Locale::getDefault()) |
|
709 , collator(NULL) |
|
710 , decimalFormatSymbols(NULL) |
|
711 , lenient(FALSE) |
|
712 , lenientParseRules(NULL) |
|
713 , localizations(NULL) |
|
714 { |
|
715 init(description, NULL, perror, status); |
|
716 } |
|
717 |
|
718 RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, |
|
719 const Locale& aLocale, |
|
720 UParseError& perror, |
|
721 UErrorCode& status) |
|
722 : ruleSets(NULL) |
|
723 , ruleSetDescriptions(NULL) |
|
724 , numRuleSets(0) |
|
725 , defaultRuleSet(NULL) |
|
726 , locale(aLocale) |
|
727 , collator(NULL) |
|
728 , decimalFormatSymbols(NULL) |
|
729 , lenient(FALSE) |
|
730 , lenientParseRules(NULL) |
|
731 , localizations(NULL) |
|
732 { |
|
733 init(description, NULL, perror, status); |
|
734 } |
|
735 |
|
736 RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag, const Locale& alocale, UErrorCode& status) |
|
737 : ruleSets(NULL) |
|
738 , ruleSetDescriptions(NULL) |
|
739 , numRuleSets(0) |
|
740 , defaultRuleSet(NULL) |
|
741 , locale(alocale) |
|
742 , collator(NULL) |
|
743 , decimalFormatSymbols(NULL) |
|
744 , lenient(FALSE) |
|
745 , lenientParseRules(NULL) |
|
746 , localizations(NULL) |
|
747 { |
|
748 if (U_FAILURE(status)) { |
|
749 return; |
|
750 } |
|
751 |
|
752 const char* rules_tag = "RBNFRules"; |
|
753 const char* fmt_tag = ""; |
|
754 switch (tag) { |
|
755 case URBNF_SPELLOUT: fmt_tag = "SpelloutRules"; break; |
|
756 case URBNF_ORDINAL: fmt_tag = "OrdinalRules"; break; |
|
757 case URBNF_DURATION: fmt_tag = "DurationRules"; break; |
|
758 case URBNF_NUMBERING_SYSTEM: fmt_tag = "NumberingSystemRules"; break; |
|
759 default: status = U_ILLEGAL_ARGUMENT_ERROR; return; |
|
760 } |
|
761 |
|
762 // TODO: read localization info from resource |
|
763 LocalizationInfo* locinfo = NULL; |
|
764 |
|
765 UResourceBundle* nfrb = ures_open(U_ICUDATA_RBNF, locale.getName(), &status); |
|
766 if (U_SUCCESS(status)) { |
|
767 setLocaleIDs(ures_getLocaleByType(nfrb, ULOC_VALID_LOCALE, &status), |
|
768 ures_getLocaleByType(nfrb, ULOC_ACTUAL_LOCALE, &status)); |
|
769 |
|
770 UResourceBundle* rbnfRules = ures_getByKeyWithFallback(nfrb, rules_tag, NULL, &status); |
|
771 if (U_FAILURE(status)) { |
|
772 ures_close(nfrb); |
|
773 } |
|
774 UResourceBundle* ruleSets = ures_getByKeyWithFallback(rbnfRules, fmt_tag, NULL, &status); |
|
775 if (U_FAILURE(status)) { |
|
776 ures_close(rbnfRules); |
|
777 ures_close(nfrb); |
|
778 return; |
|
779 } |
|
780 |
|
781 UnicodeString desc; |
|
782 while (ures_hasNext(ruleSets)) { |
|
783 desc.append(ures_getNextUnicodeString(ruleSets,NULL,&status)); |
|
784 } |
|
785 UParseError perror; |
|
786 |
|
787 init (desc, locinfo, perror, status); |
|
788 |
|
789 ures_close(ruleSets); |
|
790 ures_close(rbnfRules); |
|
791 } |
|
792 ures_close(nfrb); |
|
793 } |
|
794 |
|
795 RuleBasedNumberFormat::RuleBasedNumberFormat(const RuleBasedNumberFormat& rhs) |
|
796 : NumberFormat(rhs) |
|
797 , ruleSets(NULL) |
|
798 , ruleSetDescriptions(NULL) |
|
799 , numRuleSets(0) |
|
800 , defaultRuleSet(NULL) |
|
801 , locale(rhs.locale) |
|
802 , collator(NULL) |
|
803 , decimalFormatSymbols(NULL) |
|
804 , lenient(FALSE) |
|
805 , lenientParseRules(NULL) |
|
806 , localizations(NULL) |
|
807 { |
|
808 this->operator=(rhs); |
|
809 } |
|
810 |
|
811 // -------- |
|
812 |
|
813 RuleBasedNumberFormat& |
|
814 RuleBasedNumberFormat::operator=(const RuleBasedNumberFormat& rhs) |
|
815 { |
|
816 UErrorCode status = U_ZERO_ERROR; |
|
817 dispose(); |
|
818 locale = rhs.locale; |
|
819 lenient = rhs.lenient; |
|
820 |
|
821 UnicodeString rules = rhs.getRules(); |
|
822 UParseError perror; |
|
823 init(rules, rhs.localizations ? rhs.localizations->ref() : NULL, perror, status); |
|
824 |
|
825 return *this; |
|
826 } |
|
827 |
|
828 RuleBasedNumberFormat::~RuleBasedNumberFormat() |
|
829 { |
|
830 dispose(); |
|
831 } |
|
832 |
|
833 Format* |
|
834 RuleBasedNumberFormat::clone(void) const |
|
835 { |
|
836 RuleBasedNumberFormat * result = NULL; |
|
837 UnicodeString rules = getRules(); |
|
838 UErrorCode status = U_ZERO_ERROR; |
|
839 UParseError perror; |
|
840 result = new RuleBasedNumberFormat(rules, localizations, locale, perror, status); |
|
841 /* test for NULL */ |
|
842 if (result == 0) { |
|
843 status = U_MEMORY_ALLOCATION_ERROR; |
|
844 return 0; |
|
845 } |
|
846 if (U_FAILURE(status)) { |
|
847 delete result; |
|
848 result = 0; |
|
849 } else { |
|
850 result->lenient = lenient; |
|
851 } |
|
852 return result; |
|
853 } |
|
854 |
|
855 UBool |
|
856 RuleBasedNumberFormat::operator==(const Format& other) const |
|
857 { |
|
858 if (this == &other) { |
|
859 return TRUE; |
|
860 } |
|
861 |
|
862 if (typeid(*this) == typeid(other)) { |
|
863 const RuleBasedNumberFormat& rhs = (const RuleBasedNumberFormat&)other; |
|
864 if (locale == rhs.locale && |
|
865 lenient == rhs.lenient && |
|
866 (localizations == NULL |
|
867 ? rhs.localizations == NULL |
|
868 : (rhs.localizations == NULL |
|
869 ? FALSE |
|
870 : *localizations == rhs.localizations))) { |
|
871 |
|
872 NFRuleSet** p = ruleSets; |
|
873 NFRuleSet** q = rhs.ruleSets; |
|
874 if (p == NULL) { |
|
875 return q == NULL; |
|
876 } else if (q == NULL) { |
|
877 return FALSE; |
|
878 } |
|
879 while (*p && *q && (**p == **q)) { |
|
880 ++p; |
|
881 ++q; |
|
882 } |
|
883 return *q == NULL && *p == NULL; |
|
884 } |
|
885 } |
|
886 |
|
887 return FALSE; |
|
888 } |
|
889 |
|
890 UnicodeString |
|
891 RuleBasedNumberFormat::getRules() const |
|
892 { |
|
893 UnicodeString result; |
|
894 if (ruleSets != NULL) { |
|
895 for (NFRuleSet** p = ruleSets; *p; ++p) { |
|
896 (*p)->appendRules(result); |
|
897 } |
|
898 } |
|
899 return result; |
|
900 } |
|
901 |
|
902 UnicodeString |
|
903 RuleBasedNumberFormat::getRuleSetName(int32_t index) const |
|
904 { |
|
905 if (localizations) { |
|
906 UnicodeString string(TRUE, localizations->getRuleSetName(index), (int32_t)-1); |
|
907 return string; |
|
908 } else if (ruleSets) { |
|
909 UnicodeString result; |
|
910 for (NFRuleSet** p = ruleSets; *p; ++p) { |
|
911 NFRuleSet* rs = *p; |
|
912 if (rs->isPublic()) { |
|
913 if (--index == -1) { |
|
914 rs->getName(result); |
|
915 return result; |
|
916 } |
|
917 } |
|
918 } |
|
919 } |
|
920 UnicodeString empty; |
|
921 return empty; |
|
922 } |
|
923 |
|
924 int32_t |
|
925 RuleBasedNumberFormat::getNumberOfRuleSetNames() const |
|
926 { |
|
927 int32_t result = 0; |
|
928 if (localizations) { |
|
929 result = localizations->getNumberOfRuleSets(); |
|
930 } else if (ruleSets) { |
|
931 for (NFRuleSet** p = ruleSets; *p; ++p) { |
|
932 if ((**p).isPublic()) { |
|
933 ++result; |
|
934 } |
|
935 } |
|
936 } |
|
937 return result; |
|
938 } |
|
939 |
|
940 int32_t |
|
941 RuleBasedNumberFormat::getNumberOfRuleSetDisplayNameLocales(void) const { |
|
942 if (localizations) { |
|
943 return localizations->getNumberOfDisplayLocales(); |
|
944 } |
|
945 return 0; |
|
946 } |
|
947 |
|
948 Locale |
|
949 RuleBasedNumberFormat::getRuleSetDisplayNameLocale(int32_t index, UErrorCode& status) const { |
|
950 if (U_FAILURE(status)) { |
|
951 return Locale(""); |
|
952 } |
|
953 if (localizations && index >= 0 && index < localizations->getNumberOfDisplayLocales()) { |
|
954 UnicodeString name(TRUE, localizations->getLocaleName(index), -1); |
|
955 char buffer[64]; |
|
956 int32_t cap = name.length() + 1; |
|
957 char* bp = buffer; |
|
958 if (cap > 64) { |
|
959 bp = (char *)uprv_malloc(cap); |
|
960 if (bp == NULL) { |
|
961 status = U_MEMORY_ALLOCATION_ERROR; |
|
962 return Locale(""); |
|
963 } |
|
964 } |
|
965 name.extract(0, name.length(), bp, cap, UnicodeString::kInvariant); |
|
966 Locale retLocale(bp); |
|
967 if (bp != buffer) { |
|
968 uprv_free(bp); |
|
969 } |
|
970 return retLocale; |
|
971 } |
|
972 status = U_ILLEGAL_ARGUMENT_ERROR; |
|
973 Locale retLocale; |
|
974 return retLocale; |
|
975 } |
|
976 |
|
977 UnicodeString |
|
978 RuleBasedNumberFormat::getRuleSetDisplayName(int32_t index, const Locale& localeParam) { |
|
979 if (localizations && index >= 0 && index < localizations->getNumberOfRuleSets()) { |
|
980 UnicodeString localeName(localeParam.getBaseName(), -1, UnicodeString::kInvariant); |
|
981 int32_t len = localeName.length(); |
|
982 UChar* localeStr = localeName.getBuffer(len + 1); |
|
983 while (len >= 0) { |
|
984 localeStr[len] = 0; |
|
985 int32_t ix = localizations->indexForLocale(localeStr); |
|
986 if (ix >= 0) { |
|
987 UnicodeString name(TRUE, localizations->getDisplayName(ix, index), -1); |
|
988 return name; |
|
989 } |
|
990 |
|
991 // trim trailing portion, skipping over ommitted sections |
|
992 do { --len;} while (len > 0 && localeStr[len] != 0x005f); // underscore |
|
993 while (len > 0 && localeStr[len-1] == 0x005F) --len; |
|
994 } |
|
995 UnicodeString name(TRUE, localizations->getRuleSetName(index), -1); |
|
996 return name; |
|
997 } |
|
998 UnicodeString bogus; |
|
999 bogus.setToBogus(); |
|
1000 return bogus; |
|
1001 } |
|
1002 |
|
1003 UnicodeString |
|
1004 RuleBasedNumberFormat::getRuleSetDisplayName(const UnicodeString& ruleSetName, const Locale& localeParam) { |
|
1005 if (localizations) { |
|
1006 UnicodeString rsn(ruleSetName); |
|
1007 int32_t ix = localizations->indexForRuleSet(rsn.getTerminatedBuffer()); |
|
1008 return getRuleSetDisplayName(ix, localeParam); |
|
1009 } |
|
1010 UnicodeString bogus; |
|
1011 bogus.setToBogus(); |
|
1012 return bogus; |
|
1013 } |
|
1014 |
|
1015 NFRuleSet* |
|
1016 RuleBasedNumberFormat::findRuleSet(const UnicodeString& name, UErrorCode& status) const |
|
1017 { |
|
1018 if (U_SUCCESS(status) && ruleSets) { |
|
1019 for (NFRuleSet** p = ruleSets; *p; ++p) { |
|
1020 NFRuleSet* rs = *p; |
|
1021 if (rs->isNamed(name)) { |
|
1022 return rs; |
|
1023 } |
|
1024 } |
|
1025 status = U_ILLEGAL_ARGUMENT_ERROR; |
|
1026 } |
|
1027 return NULL; |
|
1028 } |
|
1029 |
|
1030 UnicodeString& |
|
1031 RuleBasedNumberFormat::format(int32_t number, |
|
1032 UnicodeString& toAppendTo, |
|
1033 FieldPosition& /* pos */) const |
|
1034 { |
|
1035 if (defaultRuleSet) defaultRuleSet->format((int64_t)number, toAppendTo, toAppendTo.length()); |
|
1036 return toAppendTo; |
|
1037 } |
|
1038 |
|
1039 |
|
1040 UnicodeString& |
|
1041 RuleBasedNumberFormat::format(int64_t number, |
|
1042 UnicodeString& toAppendTo, |
|
1043 FieldPosition& /* pos */) const |
|
1044 { |
|
1045 if (defaultRuleSet) defaultRuleSet->format(number, toAppendTo, toAppendTo.length()); |
|
1046 return toAppendTo; |
|
1047 } |
|
1048 |
|
1049 |
|
1050 UnicodeString& |
|
1051 RuleBasedNumberFormat::format(double number, |
|
1052 UnicodeString& toAppendTo, |
|
1053 FieldPosition& /* pos */) const |
|
1054 { |
|
1055 // Special case for NaN; adapted from what DecimalFormat::_format( double number,...) does. |
|
1056 if (uprv_isNaN(number)) { |
|
1057 DecimalFormatSymbols* decFmtSyms = getDecimalFormatSymbols(); // RuleBasedNumberFormat internal |
|
1058 if (decFmtSyms) { |
|
1059 toAppendTo += decFmtSyms->getConstSymbol(DecimalFormatSymbols::kNaNSymbol); |
|
1060 } |
|
1061 } else if (defaultRuleSet) { |
|
1062 defaultRuleSet->format(number, toAppendTo, toAppendTo.length()); |
|
1063 } |
|
1064 return toAppendTo; |
|
1065 } |
|
1066 |
|
1067 |
|
1068 UnicodeString& |
|
1069 RuleBasedNumberFormat::format(int32_t number, |
|
1070 const UnicodeString& ruleSetName, |
|
1071 UnicodeString& toAppendTo, |
|
1072 FieldPosition& /* pos */, |
|
1073 UErrorCode& status) const |
|
1074 { |
|
1075 // return format((int64_t)number, ruleSetName, toAppendTo, pos, status); |
|
1076 if (U_SUCCESS(status)) { |
|
1077 if (ruleSetName.indexOf(gPercentPercent, 2, 0) == 0) { |
|
1078 // throw new IllegalArgumentException("Can't use internal rule set"); |
|
1079 status = U_ILLEGAL_ARGUMENT_ERROR; |
|
1080 } else { |
|
1081 NFRuleSet *rs = findRuleSet(ruleSetName, status); |
|
1082 if (rs) { |
|
1083 rs->format((int64_t)number, toAppendTo, toAppendTo.length()); |
|
1084 } |
|
1085 } |
|
1086 } |
|
1087 return toAppendTo; |
|
1088 } |
|
1089 |
|
1090 |
|
1091 UnicodeString& |
|
1092 RuleBasedNumberFormat::format(int64_t number, |
|
1093 const UnicodeString& ruleSetName, |
|
1094 UnicodeString& toAppendTo, |
|
1095 FieldPosition& /* pos */, |
|
1096 UErrorCode& status) const |
|
1097 { |
|
1098 if (U_SUCCESS(status)) { |
|
1099 if (ruleSetName.indexOf(gPercentPercent, 2, 0) == 0) { |
|
1100 // throw new IllegalArgumentException("Can't use internal rule set"); |
|
1101 status = U_ILLEGAL_ARGUMENT_ERROR; |
|
1102 } else { |
|
1103 NFRuleSet *rs = findRuleSet(ruleSetName, status); |
|
1104 if (rs) { |
|
1105 rs->format(number, toAppendTo, toAppendTo.length()); |
|
1106 } |
|
1107 } |
|
1108 } |
|
1109 return toAppendTo; |
|
1110 } |
|
1111 |
|
1112 |
|
1113 UnicodeString& |
|
1114 RuleBasedNumberFormat::format(double number, |
|
1115 const UnicodeString& ruleSetName, |
|
1116 UnicodeString& toAppendTo, |
|
1117 FieldPosition& /* pos */, |
|
1118 UErrorCode& status) const |
|
1119 { |
|
1120 if (U_SUCCESS(status)) { |
|
1121 if (ruleSetName.indexOf(gPercentPercent, 2, 0) == 0) { |
|
1122 // throw new IllegalArgumentException("Can't use internal rule set"); |
|
1123 status = U_ILLEGAL_ARGUMENT_ERROR; |
|
1124 } else { |
|
1125 NFRuleSet *rs = findRuleSet(ruleSetName, status); |
|
1126 if (rs) { |
|
1127 rs->format(number, toAppendTo, toAppendTo.length()); |
|
1128 } |
|
1129 } |
|
1130 } |
|
1131 return toAppendTo; |
|
1132 } |
|
1133 |
|
1134 void |
|
1135 RuleBasedNumberFormat::parse(const UnicodeString& text, |
|
1136 Formattable& result, |
|
1137 ParsePosition& parsePosition) const |
|
1138 { |
|
1139 if (!ruleSets) { |
|
1140 parsePosition.setErrorIndex(0); |
|
1141 return; |
|
1142 } |
|
1143 |
|
1144 UnicodeString workingText(text, parsePosition.getIndex()); |
|
1145 ParsePosition workingPos(0); |
|
1146 |
|
1147 ParsePosition high_pp(0); |
|
1148 Formattable high_result; |
|
1149 |
|
1150 for (NFRuleSet** p = ruleSets; *p; ++p) { |
|
1151 NFRuleSet *rp = *p; |
|
1152 if (rp->isPublic() && rp->isParseable()) { |
|
1153 ParsePosition working_pp(0); |
|
1154 Formattable working_result; |
|
1155 |
|
1156 rp->parse(workingText, working_pp, kMaxDouble, working_result); |
|
1157 if (working_pp.getIndex() > high_pp.getIndex()) { |
|
1158 high_pp = working_pp; |
|
1159 high_result = working_result; |
|
1160 |
|
1161 if (high_pp.getIndex() == workingText.length()) { |
|
1162 break; |
|
1163 } |
|
1164 } |
|
1165 } |
|
1166 } |
|
1167 |
|
1168 int32_t startIndex = parsePosition.getIndex(); |
|
1169 parsePosition.setIndex(startIndex + high_pp.getIndex()); |
|
1170 if (high_pp.getIndex() > 0) { |
|
1171 parsePosition.setErrorIndex(-1); |
|
1172 } else { |
|
1173 int32_t errorIndex = (high_pp.getErrorIndex()>0)? high_pp.getErrorIndex(): 0; |
|
1174 parsePosition.setErrorIndex(startIndex + errorIndex); |
|
1175 } |
|
1176 result = high_result; |
|
1177 if (result.getType() == Formattable::kDouble) { |
|
1178 int32_t r = (int32_t)result.getDouble(); |
|
1179 if ((double)r == result.getDouble()) { |
|
1180 result.setLong(r); |
|
1181 } |
|
1182 } |
|
1183 } |
|
1184 |
|
1185 #if !UCONFIG_NO_COLLATION |
|
1186 |
|
1187 void |
|
1188 RuleBasedNumberFormat::setLenient(UBool enabled) |
|
1189 { |
|
1190 lenient = enabled; |
|
1191 if (!enabled && collator) { |
|
1192 delete collator; |
|
1193 collator = NULL; |
|
1194 } |
|
1195 } |
|
1196 |
|
1197 #endif |
|
1198 |
|
1199 void |
|
1200 RuleBasedNumberFormat::setDefaultRuleSet(const UnicodeString& ruleSetName, UErrorCode& status) { |
|
1201 if (U_SUCCESS(status)) { |
|
1202 if (ruleSetName.isEmpty()) { |
|
1203 if (localizations) { |
|
1204 UnicodeString name(TRUE, localizations->getRuleSetName(0), -1); |
|
1205 defaultRuleSet = findRuleSet(name, status); |
|
1206 } else { |
|
1207 initDefaultRuleSet(); |
|
1208 } |
|
1209 } else if (ruleSetName.startsWith(UNICODE_STRING_SIMPLE("%%"))) { |
|
1210 status = U_ILLEGAL_ARGUMENT_ERROR; |
|
1211 } else { |
|
1212 NFRuleSet* result = findRuleSet(ruleSetName, status); |
|
1213 if (result != NULL) { |
|
1214 defaultRuleSet = result; |
|
1215 } |
|
1216 } |
|
1217 } |
|
1218 } |
|
1219 |
|
1220 UnicodeString |
|
1221 RuleBasedNumberFormat::getDefaultRuleSetName() const { |
|
1222 UnicodeString result; |
|
1223 if (defaultRuleSet && defaultRuleSet->isPublic()) { |
|
1224 defaultRuleSet->getName(result); |
|
1225 } else { |
|
1226 result.setToBogus(); |
|
1227 } |
|
1228 return result; |
|
1229 } |
|
1230 |
|
1231 void |
|
1232 RuleBasedNumberFormat::initDefaultRuleSet() |
|
1233 { |
|
1234 defaultRuleSet = NULL; |
|
1235 if (!ruleSets) { |
|
1236 return; |
|
1237 } |
|
1238 |
|
1239 const UnicodeString spellout = UNICODE_STRING_SIMPLE("%spellout-numbering"); |
|
1240 const UnicodeString ordinal = UNICODE_STRING_SIMPLE("%digits-ordinal"); |
|
1241 const UnicodeString duration = UNICODE_STRING_SIMPLE("%duration"); |
|
1242 |
|
1243 NFRuleSet**p = &ruleSets[0]; |
|
1244 while (*p) { |
|
1245 if ((*p)->isNamed(spellout) || (*p)->isNamed(ordinal) || (*p)->isNamed(duration)) { |
|
1246 defaultRuleSet = *p; |
|
1247 return; |
|
1248 } else { |
|
1249 ++p; |
|
1250 } |
|
1251 } |
|
1252 |
|
1253 defaultRuleSet = *--p; |
|
1254 if (!defaultRuleSet->isPublic()) { |
|
1255 while (p != ruleSets) { |
|
1256 if ((*--p)->isPublic()) { |
|
1257 defaultRuleSet = *p; |
|
1258 break; |
|
1259 } |
|
1260 } |
|
1261 } |
|
1262 } |
|
1263 |
|
1264 |
|
1265 void |
|
1266 RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* localizationInfos, |
|
1267 UParseError& pErr, UErrorCode& status) |
|
1268 { |
|
1269 // TODO: implement UParseError |
|
1270 uprv_memset(&pErr, 0, sizeof(UParseError)); |
|
1271 // Note: this can leave ruleSets == NULL, so remaining code should check |
|
1272 if (U_FAILURE(status)) { |
|
1273 return; |
|
1274 } |
|
1275 |
|
1276 this->localizations = localizationInfos == NULL ? NULL : localizationInfos->ref(); |
|
1277 |
|
1278 UnicodeString description(rules); |
|
1279 if (!description.length()) { |
|
1280 status = U_MEMORY_ALLOCATION_ERROR; |
|
1281 return; |
|
1282 } |
|
1283 |
|
1284 // start by stripping the trailing whitespace from all the rules |
|
1285 // (this is all the whitespace follwing each semicolon in the |
|
1286 // description). This allows us to look for rule-set boundaries |
|
1287 // by searching for ";%" without having to worry about whitespace |
|
1288 // between the ; and the % |
|
1289 stripWhitespace(description); |
|
1290 |
|
1291 // check to see if there's a set of lenient-parse rules. If there |
|
1292 // is, pull them out into our temporary holding place for them, |
|
1293 // and delete them from the description before the real desciption- |
|
1294 // parsing code sees them |
|
1295 int32_t lp = description.indexOf(gLenientParse, -1, 0); |
|
1296 if (lp != -1) { |
|
1297 // we've got to make sure we're not in the middle of a rule |
|
1298 // (where "%%lenient-parse" would actually get treated as |
|
1299 // rule text) |
|
1300 if (lp == 0 || description.charAt(lp - 1) == gSemiColon) { |
|
1301 // locate the beginning and end of the actual collation |
|
1302 // rules (there may be whitespace between the name and |
|
1303 // the first token in the description) |
|
1304 int lpEnd = description.indexOf(gSemiPercent, 2, lp); |
|
1305 |
|
1306 if (lpEnd == -1) { |
|
1307 lpEnd = description.length() - 1; |
|
1308 } |
|
1309 int lpStart = lp + u_strlen(gLenientParse); |
|
1310 while (PatternProps::isWhiteSpace(description.charAt(lpStart))) { |
|
1311 ++lpStart; |
|
1312 } |
|
1313 |
|
1314 // copy out the lenient-parse rules and delete them |
|
1315 // from the description |
|
1316 lenientParseRules = new UnicodeString(); |
|
1317 /* test for NULL */ |
|
1318 if (lenientParseRules == 0) { |
|
1319 status = U_MEMORY_ALLOCATION_ERROR; |
|
1320 return; |
|
1321 } |
|
1322 lenientParseRules->setTo(description, lpStart, lpEnd - lpStart); |
|
1323 |
|
1324 description.remove(lp, lpEnd + 1 - lp); |
|
1325 } |
|
1326 } |
|
1327 |
|
1328 // pre-flight parsing the description and count the number of |
|
1329 // rule sets (";%" marks the end of one rule set and the beginning |
|
1330 // of the next) |
|
1331 numRuleSets = 0; |
|
1332 for (int32_t p = description.indexOf(gSemiPercent, 2, 0); p != -1; p = description.indexOf(gSemiPercent, 2, p)) { |
|
1333 ++numRuleSets; |
|
1334 ++p; |
|
1335 } |
|
1336 ++numRuleSets; |
|
1337 |
|
1338 // our rule list is an array of the appropriate size |
|
1339 ruleSets = (NFRuleSet **)uprv_malloc((numRuleSets + 1) * sizeof(NFRuleSet *)); |
|
1340 /* test for NULL */ |
|
1341 if (ruleSets == 0) { |
|
1342 status = U_MEMORY_ALLOCATION_ERROR; |
|
1343 return; |
|
1344 } |
|
1345 |
|
1346 for (int i = 0; i <= numRuleSets; ++i) { |
|
1347 ruleSets[i] = NULL; |
|
1348 } |
|
1349 |
|
1350 // divide up the descriptions into individual rule-set descriptions |
|
1351 // and store them in a temporary array. At each step, we also |
|
1352 // new up a rule set, but all this does is initialize its name |
|
1353 // and remove it from its description. We can't actually parse |
|
1354 // the rest of the descriptions and finish initializing everything |
|
1355 // because we have to know the names and locations of all the rule |
|
1356 // sets before we can actually set everything up |
|
1357 if(!numRuleSets) { |
|
1358 status = U_ILLEGAL_ARGUMENT_ERROR; |
|
1359 return; |
|
1360 } |
|
1361 |
|
1362 ruleSetDescriptions = new UnicodeString[numRuleSets]; |
|
1363 if (ruleSetDescriptions == 0) { |
|
1364 status = U_MEMORY_ALLOCATION_ERROR; |
|
1365 return; |
|
1366 } |
|
1367 |
|
1368 { |
|
1369 int curRuleSet = 0; |
|
1370 int32_t start = 0; |
|
1371 for (int32_t p = description.indexOf(gSemiPercent, 2, 0); p != -1; p = description.indexOf(gSemiPercent, 2, start)) { |
|
1372 ruleSetDescriptions[curRuleSet].setTo(description, start, p + 1 - start); |
|
1373 ruleSets[curRuleSet] = new NFRuleSet(ruleSetDescriptions, curRuleSet, status); |
|
1374 if (ruleSets[curRuleSet] == 0) { |
|
1375 status = U_MEMORY_ALLOCATION_ERROR; |
|
1376 return; |
|
1377 } |
|
1378 ++curRuleSet; |
|
1379 start = p + 1; |
|
1380 } |
|
1381 ruleSetDescriptions[curRuleSet].setTo(description, start, description.length() - start); |
|
1382 ruleSets[curRuleSet] = new NFRuleSet(ruleSetDescriptions, curRuleSet, status); |
|
1383 if (ruleSets[curRuleSet] == 0) { |
|
1384 status = U_MEMORY_ALLOCATION_ERROR; |
|
1385 return; |
|
1386 } |
|
1387 } |
|
1388 |
|
1389 // now we can take note of the formatter's default rule set, which |
|
1390 // is the last public rule set in the description (it's the last |
|
1391 // rather than the first so that a user can create a new formatter |
|
1392 // from an existing formatter and change its default behavior just |
|
1393 // by appending more rule sets to the end) |
|
1394 |
|
1395 // {dlf} Initialization of a fraction rule set requires the default rule |
|
1396 // set to be known. For purposes of initialization, this is always the |
|
1397 // last public rule set, no matter what the localization data says. |
|
1398 initDefaultRuleSet(); |
|
1399 |
|
1400 // finally, we can go back through the temporary descriptions |
|
1401 // list and finish seting up the substructure (and we throw |
|
1402 // away the temporary descriptions as we go) |
|
1403 { |
|
1404 for (int i = 0; i < numRuleSets; i++) { |
|
1405 ruleSets[i]->parseRules(ruleSetDescriptions[i], this, status); |
|
1406 } |
|
1407 } |
|
1408 |
|
1409 // Now that the rules are initialized, the 'real' default rule |
|
1410 // set can be adjusted by the localization data. |
|
1411 |
|
1412 // The C code keeps the localization array as is, rather than building |
|
1413 // a separate array of the public rule set names, so we have less work |
|
1414 // to do here-- but we still need to check the names. |
|
1415 |
|
1416 if (localizationInfos) { |
|
1417 // confirm the names, if any aren't in the rules, that's an error |
|
1418 // it is ok if the rules contain public rule sets that are not in this list |
|
1419 for (int32_t i = 0; i < localizationInfos->getNumberOfRuleSets(); ++i) { |
|
1420 UnicodeString name(TRUE, localizationInfos->getRuleSetName(i), -1); |
|
1421 NFRuleSet* rs = findRuleSet(name, status); |
|
1422 if (rs == NULL) { |
|
1423 break; // error |
|
1424 } |
|
1425 if (i == 0) { |
|
1426 defaultRuleSet = rs; |
|
1427 } |
|
1428 } |
|
1429 } else { |
|
1430 defaultRuleSet = getDefaultRuleSet(); |
|
1431 } |
|
1432 } |
|
1433 |
|
1434 void |
|
1435 RuleBasedNumberFormat::stripWhitespace(UnicodeString& description) |
|
1436 { |
|
1437 // iterate through the characters... |
|
1438 UnicodeString result; |
|
1439 |
|
1440 int start = 0; |
|
1441 while (start != -1 && start < description.length()) { |
|
1442 // seek to the first non-whitespace character... |
|
1443 while (start < description.length() |
|
1444 && PatternProps::isWhiteSpace(description.charAt(start))) { |
|
1445 ++start; |
|
1446 } |
|
1447 |
|
1448 // locate the next semicolon in the text and copy the text from |
|
1449 // our current position up to that semicolon into the result |
|
1450 int32_t p = description.indexOf(gSemiColon, start); |
|
1451 if (p == -1) { |
|
1452 // or if we don't find a semicolon, just copy the rest of |
|
1453 // the string into the result |
|
1454 result.append(description, start, description.length() - start); |
|
1455 start = -1; |
|
1456 } |
|
1457 else if (p < description.length()) { |
|
1458 result.append(description, start, p + 1 - start); |
|
1459 start = p + 1; |
|
1460 } |
|
1461 |
|
1462 // when we get here, we've seeked off the end of the sring, and |
|
1463 // we terminate the loop (we continue until *start* is -1 rather |
|
1464 // than until *p* is -1, because otherwise we'd miss the last |
|
1465 // rule in the description) |
|
1466 else { |
|
1467 start = -1; |
|
1468 } |
|
1469 } |
|
1470 |
|
1471 description.setTo(result); |
|
1472 } |
|
1473 |
|
1474 |
|
1475 void |
|
1476 RuleBasedNumberFormat::dispose() |
|
1477 { |
|
1478 if (ruleSets) { |
|
1479 for (NFRuleSet** p = ruleSets; *p; ++p) { |
|
1480 delete *p; |
|
1481 } |
|
1482 uprv_free(ruleSets); |
|
1483 ruleSets = NULL; |
|
1484 } |
|
1485 |
|
1486 if (ruleSetDescriptions) { |
|
1487 delete [] ruleSetDescriptions; |
|
1488 } |
|
1489 |
|
1490 #if !UCONFIG_NO_COLLATION |
|
1491 delete collator; |
|
1492 #endif |
|
1493 collator = NULL; |
|
1494 |
|
1495 delete decimalFormatSymbols; |
|
1496 decimalFormatSymbols = NULL; |
|
1497 |
|
1498 delete lenientParseRules; |
|
1499 lenientParseRules = NULL; |
|
1500 |
|
1501 if (localizations) localizations = localizations->unref(); |
|
1502 } |
|
1503 |
|
1504 |
|
1505 //----------------------------------------------------------------------- |
|
1506 // package-internal API |
|
1507 //----------------------------------------------------------------------- |
|
1508 |
|
1509 /** |
|
1510 * Returns the collator to use for lenient parsing. The collator is lazily created: |
|
1511 * this function creates it the first time it's called. |
|
1512 * @return The collator to use for lenient parsing, or null if lenient parsing |
|
1513 * is turned off. |
|
1514 */ |
|
1515 Collator* |
|
1516 RuleBasedNumberFormat::getCollator() const |
|
1517 { |
|
1518 #if !UCONFIG_NO_COLLATION |
|
1519 if (!ruleSets) { |
|
1520 return NULL; |
|
1521 } |
|
1522 |
|
1523 // lazy-evaulate the collator |
|
1524 if (collator == NULL && lenient) { |
|
1525 // create a default collator based on the formatter's locale, |
|
1526 // then pull out that collator's rules, append any additional |
|
1527 // rules specified in the description, and create a _new_ |
|
1528 // collator based on the combinaiton of those rules |
|
1529 |
|
1530 UErrorCode status = U_ZERO_ERROR; |
|
1531 |
|
1532 Collator* temp = Collator::createInstance(locale, status); |
|
1533 RuleBasedCollator* newCollator; |
|
1534 if (U_SUCCESS(status) && (newCollator = dynamic_cast<RuleBasedCollator*>(temp)) != NULL) { |
|
1535 if (lenientParseRules) { |
|
1536 UnicodeString rules(newCollator->getRules()); |
|
1537 rules.append(*lenientParseRules); |
|
1538 |
|
1539 newCollator = new RuleBasedCollator(rules, status); |
|
1540 // Exit if newCollator could not be created. |
|
1541 if (newCollator == NULL) { |
|
1542 return NULL; |
|
1543 } |
|
1544 } else { |
|
1545 temp = NULL; |
|
1546 } |
|
1547 if (U_SUCCESS(status)) { |
|
1548 newCollator->setAttribute(UCOL_DECOMPOSITION_MODE, UCOL_ON, status); |
|
1549 // cast away const |
|
1550 ((RuleBasedNumberFormat*)this)->collator = newCollator; |
|
1551 } else { |
|
1552 delete newCollator; |
|
1553 } |
|
1554 } |
|
1555 delete temp; |
|
1556 } |
|
1557 #endif |
|
1558 |
|
1559 // if lenient-parse mode is off, this will be null |
|
1560 // (see setLenientParseMode()) |
|
1561 return collator; |
|
1562 } |
|
1563 |
|
1564 |
|
1565 /** |
|
1566 * Returns the DecimalFormatSymbols object that should be used by all DecimalFormat |
|
1567 * instances owned by this formatter. This object is lazily created: this function |
|
1568 * creates it the first time it's called. |
|
1569 * @return The DecimalFormatSymbols object that should be used by all DecimalFormat |
|
1570 * instances owned by this formatter. |
|
1571 */ |
|
1572 DecimalFormatSymbols* |
|
1573 RuleBasedNumberFormat::getDecimalFormatSymbols() const |
|
1574 { |
|
1575 // lazy-evaluate the DecimalFormatSymbols object. This object |
|
1576 // is shared by all DecimalFormat instances belonging to this |
|
1577 // formatter |
|
1578 if (decimalFormatSymbols == NULL) { |
|
1579 UErrorCode status = U_ZERO_ERROR; |
|
1580 DecimalFormatSymbols* temp = new DecimalFormatSymbols(locale, status); |
|
1581 if (U_SUCCESS(status)) { |
|
1582 ((RuleBasedNumberFormat*)this)->decimalFormatSymbols = temp; |
|
1583 } else { |
|
1584 delete temp; |
|
1585 } |
|
1586 } |
|
1587 return decimalFormatSymbols; |
|
1588 } |
|
1589 |
|
1590 // De-owning the current localized symbols and adopt the new symbols. |
|
1591 void |
|
1592 RuleBasedNumberFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt) |
|
1593 { |
|
1594 if (symbolsToAdopt == NULL) { |
|
1595 return; // do not allow caller to set decimalFormatSymbols to NULL |
|
1596 } |
|
1597 |
|
1598 if (decimalFormatSymbols != NULL) { |
|
1599 delete decimalFormatSymbols; |
|
1600 } |
|
1601 |
|
1602 decimalFormatSymbols = symbolsToAdopt; |
|
1603 |
|
1604 { |
|
1605 // Apply the new decimalFormatSymbols by reparsing the rulesets |
|
1606 UErrorCode status = U_ZERO_ERROR; |
|
1607 |
|
1608 for (int32_t i = 0; i < numRuleSets; i++) { |
|
1609 ruleSets[i]->parseRules(ruleSetDescriptions[i], this, status); |
|
1610 } |
|
1611 } |
|
1612 } |
|
1613 |
|
1614 // Setting the symbols is equlivalent to adopting a newly created localized symbols. |
|
1615 void |
|
1616 RuleBasedNumberFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols) |
|
1617 { |
|
1618 adoptDecimalFormatSymbols(new DecimalFormatSymbols(symbols)); |
|
1619 } |
|
1620 |
|
1621 U_NAMESPACE_END |
|
1622 |
|
1623 /* U_HAVE_RBNF */ |
|
1624 #endif |