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