]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/rbnf.cpp
ICU-64243.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / rbnf.cpp
CommitLineData
f3c0d7a5
A
1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
b75a7d8f
A
3/*
4*******************************************************************************
2ca993e8 5* Copyright (C) 1997-2015, International Business Machines Corporation
73c04bcf 6* and others. All Rights Reserved.
b75a7d8f
A
7*******************************************************************************
8*/
9
57a6839d 10#include "unicode/utypes.h"
51004dcb 11#include "utypeinfo.h" // for 'typeid' to work
729e4ab9 12
b75a7d8f
A
13#include "unicode/rbnf.h"
14
15#if U_HAVE_RBNF
16
17#include "unicode/normlzr.h"
b331163b 18#include "unicode/plurfmt.h"
b75a7d8f
A
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"
374ca955 27#include "unicode/udata.h"
57a6839d
A
28#include "unicode/udisplaycontext.h"
29#include "unicode/brkiter.h"
f3c0d7a5 30#include "unicode/ucasemap.h"
b75a7d8f
A
31
32#include "cmemory.h"
33#include "cstring.h"
4388f060 34#include "patternprops.h"
729e4ab9 35#include "uresimp.h"
f3c0d7a5 36#include "nfrs.h"
3d1f044b 37#include "number_decimalquantity.h"
374ca955
A
38
39// debugging
b331163b 40// #define RBNF_DEBUG
374ca955 41
b331163b 42#ifdef RBNF_DEBUG
2ca993e8 43#include <stdio.h>
374ca955
A
44#endif
45
46#define U_ICUDATA_RBNF U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "rbnf"
b75a7d8f
A
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
374ca955
A
69U_NAMESPACE_BEGIN
70
3d1f044b
A
71using number::impl::DecimalQuantity;
72
374ca955
A
73UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedNumberFormat)
74
73c04bcf
A
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 {
374ca955 83protected:
4388f060 84 virtual ~LocalizationInfo();
374ca955
A
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
73c04bcf
A
114// virtual UClassID getDynamicClassID() const = 0;
115// static UClassID getStaticClassID(void);
374ca955
A
116};
117
4388f060
A
118LocalizationInfo::~LocalizationInfo() {}
119
73c04bcf 120//UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(LocalizationInfo)
374ca955
A
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
73c04bcf
A
280// virtual UClassID getDynamicClassID() const;
281// static UClassID getStaticClassID(void);
374ca955
A
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
0f5d89e8
A
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;
374ca955
A
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
b331163b 357#ifdef RBNF_DEBUG
374ca955 358#define ERROR(msg) parseError(msg); return NULL;
57a6839d 359#define EXPLANATION_ARG explanationArg
374ca955
A
360#else
361#define ERROR(msg) parseError(NULL); return NULL;
57a6839d 362#define EXPLANATION_ARG
374ca955
A
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
57a6839d
A
560void LocDataParser::parseError(const char* EXPLANATION_ARG)
561{
374ca955
A
562 if (!data) {
563 return;
564 }
b75a7d8f 565
374ca955 566 const UChar* start = p - U_PARSE_CONTEXT_LEN - 1;
73c04bcf 567 if (start < data) {
374ca955 568 start = data;
73c04bcf
A
569 }
570 for (UChar* x = p; --x >= start;) {
374ca955
A
571 if (!*x) {
572 start = x+1;
573 break;
574 }
73c04bcf 575 }
374ca955 576 const UChar* limit = p + U_PARSE_CONTEXT_LEN - 1;
73c04bcf 577 if (limit > e) {
374ca955 578 limit = e;
73c04bcf
A
579 }
580 u_strncpy(pe.preContext, start, (int32_t)(p-start));
374ca955 581 pe.preContext[p-start] = 0;
73c04bcf 582 u_strncpy(pe.postContext, p, (int32_t)(limit-p));
374ca955 583 pe.postContext[limit-p] = 0;
73c04bcf 584 pe.offset = (int32_t)(p - data);
374ca955 585
b331163b 586#ifdef RBNF_DEBUG
57a6839d 587 fprintf(stderr, "%s at or near character %ld: ", EXPLANATION_ARG, p-data);
374ca955
A
588
589 UnicodeString msg;
590 msg.append(start, p - start);
591 msg.append((UChar)0x002f); /* SOLIDUS/SLASH */
592 msg.append(p, limit-p);
57a6839d 593 msg.append(UNICODE_STRING_SIMPLE("'"));
374ca955
A
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
73c04bcf 616//UOBJECT_DEFINE_RTTI_IMPLEMENTATION(StringLocalizationInfo)
374ca955
A
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)
3d1f044b 683 : fRuleSets(NULL)
4388f060
A
684 , ruleSetDescriptions(NULL)
685 , numRuleSets(0)
b75a7d8f
A
686 , defaultRuleSet(NULL)
687 , locale(alocale)
688 , collator(NULL)
689 , decimalFormatSymbols(NULL)
2ca993e8
A
690 , defaultInfinityRule(NULL)
691 , defaultNaNRule(NULL)
3d1f044b 692 , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
b75a7d8f
A
693 , lenient(FALSE)
694 , lenientParseRules(NULL)
374ca955 695 , localizations(NULL)
57a6839d
A
696 , capitalizationInfoSet(FALSE)
697 , capitalizationForUIListMenu(FALSE)
698 , capitalizationForStandAlone(FALSE)
699 , capitalizationBrkIter(NULL)
374ca955
A
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)
3d1f044b 708 : fRuleSets(NULL)
4388f060
A
709 , ruleSetDescriptions(NULL)
710 , numRuleSets(0)
374ca955
A
711 , defaultRuleSet(NULL)
712 , locale(Locale::getDefault())
713 , collator(NULL)
714 , decimalFormatSymbols(NULL)
2ca993e8
A
715 , defaultInfinityRule(NULL)
716 , defaultNaNRule(NULL)
3d1f044b 717 , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
374ca955
A
718 , lenient(FALSE)
719 , lenientParseRules(NULL)
720 , localizations(NULL)
57a6839d
A
721 , capitalizationInfoSet(FALSE)
722 , capitalizationForUIListMenu(FALSE)
723 , capitalizationForStandAlone(FALSE)
724 , capitalizationBrkIter(NULL)
374ca955
A
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)
3d1f044b 733 : fRuleSets(NULL)
4388f060
A
734 , ruleSetDescriptions(NULL)
735 , numRuleSets(0)
374ca955
A
736 , defaultRuleSet(NULL)
737 , locale(alocale)
738 , collator(NULL)
739 , decimalFormatSymbols(NULL)
2ca993e8
A
740 , defaultInfinityRule(NULL)
741 , defaultNaNRule(NULL)
3d1f044b 742 , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
374ca955
A
743 , lenient(FALSE)
744 , lenientParseRules(NULL)
745 , localizations(NULL)
57a6839d
A
746 , capitalizationInfoSet(FALSE)
747 , capitalizationForUIListMenu(FALSE)
748 , capitalizationForStandAlone(FALSE)
749 , capitalizationBrkIter(NULL)
374ca955
A
750{
751 init(description, info, perror, status);
752}
753
754RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,
755 UParseError& perror,
756 UErrorCode& status)
3d1f044b 757 : fRuleSets(NULL)
4388f060
A
758 , ruleSetDescriptions(NULL)
759 , numRuleSets(0)
374ca955
A
760 , defaultRuleSet(NULL)
761 , locale(Locale::getDefault())
762 , collator(NULL)
763 , decimalFormatSymbols(NULL)
2ca993e8
A
764 , defaultInfinityRule(NULL)
765 , defaultNaNRule(NULL)
3d1f044b 766 , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
374ca955
A
767 , lenient(FALSE)
768 , lenientParseRules(NULL)
769 , localizations(NULL)
57a6839d
A
770 , capitalizationInfoSet(FALSE)
771 , capitalizationForUIListMenu(FALSE)
772 , capitalizationForStandAlone(FALSE)
773 , capitalizationBrkIter(NULL)
374ca955
A
774{
775 init(description, NULL, perror, status);
776}
777
778RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,
779 const Locale& aLocale,
780 UParseError& perror,
781 UErrorCode& status)
3d1f044b 782 : fRuleSets(NULL)
4388f060
A
783 , ruleSetDescriptions(NULL)
784 , numRuleSets(0)
374ca955
A
785 , defaultRuleSet(NULL)
786 , locale(aLocale)
787 , collator(NULL)
788 , decimalFormatSymbols(NULL)
2ca993e8
A
789 , defaultInfinityRule(NULL)
790 , defaultNaNRule(NULL)
3d1f044b 791 , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
374ca955
A
792 , lenient(FALSE)
793 , lenientParseRules(NULL)
794 , localizations(NULL)
57a6839d
A
795 , capitalizationInfoSet(FALSE)
796 , capitalizationForUIListMenu(FALSE)
797 , capitalizationForStandAlone(FALSE)
798 , capitalizationBrkIter(NULL)
b75a7d8f 799{
374ca955 800 init(description, NULL, perror, status);
b75a7d8f
A
801}
802
803RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag, const Locale& alocale, UErrorCode& status)
3d1f044b 804 : fRuleSets(NULL)
4388f060
A
805 , ruleSetDescriptions(NULL)
806 , numRuleSets(0)
b75a7d8f
A
807 , defaultRuleSet(NULL)
808 , locale(alocale)
809 , collator(NULL)
810 , decimalFormatSymbols(NULL)
2ca993e8
A
811 , defaultInfinityRule(NULL)
812 , defaultNaNRule(NULL)
3d1f044b 813 , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
b75a7d8f
A
814 , lenient(FALSE)
815 , lenientParseRules(NULL)
374ca955 816 , localizations(NULL)
57a6839d
A
817 , capitalizationInfoSet(FALSE)
818 , capitalizationForUIListMenu(FALSE)
819 , capitalizationForStandAlone(FALSE)
820 , capitalizationBrkIter(NULL)
b75a7d8f
A
821{
822 if (U_FAILURE(status)) {
823 return;
824 }
825
729e4ab9 826 const char* rules_tag = "RBNFRules";
b75a7d8f
A
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;
729e4ab9 832 case URBNF_NUMBERING_SYSTEM: fmt_tag = "NumberingSystemRules"; break;
b75a7d8f
A
833 default: status = U_ILLEGAL_ARGUMENT_ERROR; return;
834 }
835
374ca955
A
836 // TODO: read localization info from resource
837 LocalizationInfo* locinfo = NULL;
838
374ca955 839 UResourceBundle* nfrb = ures_open(U_ICUDATA_RBNF, locale.getName(), &status);
b75a7d8f 840 if (U_SUCCESS(status)) {
374ca955
A
841 setLocaleIDs(ures_getLocaleByType(nfrb, ULOC_VALID_LOCALE, &status),
842 ures_getLocaleByType(nfrb, ULOC_ACTUAL_LOCALE, &status));
729e4ab9
A
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 }
4388f060 854
729e4ab9
A
855 UnicodeString desc;
856 while (ures_hasNext(ruleSets)) {
4388f060 857 desc.append(ures_getNextUnicodeString(ruleSets,NULL,&status));
729e4ab9 858 }
b75a7d8f 859 UParseError perror;
729e4ab9 860
2ca993e8 861 init(desc, locinfo, perror, status);
729e4ab9 862
729e4ab9
A
863 ures_close(ruleSets);
864 ures_close(rbnfRules);
b75a7d8f 865 }
b75a7d8f
A
866 ures_close(nfrb);
867}
868
869RuleBasedNumberFormat::RuleBasedNumberFormat(const RuleBasedNumberFormat& rhs)
870 : NumberFormat(rhs)
3d1f044b 871 , fRuleSets(NULL)
4388f060
A
872 , ruleSetDescriptions(NULL)
873 , numRuleSets(0)
b75a7d8f
A
874 , defaultRuleSet(NULL)
875 , locale(rhs.locale)
876 , collator(NULL)
877 , decimalFormatSymbols(NULL)
2ca993e8
A
878 , defaultInfinityRule(NULL)
879 , defaultNaNRule(NULL)
3d1f044b 880 , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
b75a7d8f
A
881 , lenient(FALSE)
882 , lenientParseRules(NULL)
374ca955 883 , localizations(NULL)
57a6839d
A
884 , capitalizationInfoSet(FALSE)
885 , capitalizationForUIListMenu(FALSE)
886 , capitalizationForStandAlone(FALSE)
887 , capitalizationBrkIter(NULL)
b75a7d8f
A
888{
889 this->operator=(rhs);
890}
891
374ca955
A
892// --------
893
b75a7d8f
A
894RuleBasedNumberFormat&
895RuleBasedNumberFormat::operator=(const RuleBasedNumberFormat& rhs)
896{
57a6839d
A
897 if (this == &rhs) {
898 return *this;
899 }
900 NumberFormat::operator=(rhs);
b75a7d8f
A
901 UErrorCode status = U_ZERO_ERROR;
902 dispose();
903 locale = rhs.locale;
374ca955
A
904 lenient = rhs.lenient;
905
b75a7d8f 906 UParseError perror;
57a6839d 907 setDecimalFormatSymbols(*rhs.getDecimalFormatSymbols());
2ca993e8 908 init(rhs.originalDescription, rhs.localizations ? rhs.localizations->ref() : NULL, perror, status);
57a6839d 909 setDefaultRuleSet(rhs.getDefaultRuleSetName(), status);
0f5d89e8 910 setRoundingMode(rhs.getRoundingMode());
57a6839d
A
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
374ca955 918
b75a7d8f
A
919 return *this;
920}
921
922RuleBasedNumberFormat::~RuleBasedNumberFormat()
923{
924 dispose();
925}
926
927Format*
928RuleBasedNumberFormat::clone(void) const
929{
57a6839d 930 return new RuleBasedNumberFormat(*this);
b75a7d8f
A
931}
932
933UBool
934RuleBasedNumberFormat::operator==(const Format& other) const
935{
936 if (this == &other) {
937 return TRUE;
938 }
939
729e4ab9 940 if (typeid(*this) == typeid(other)) {
b75a7d8f 941 const RuleBasedNumberFormat& rhs = (const RuleBasedNumberFormat&)other;
57a6839d
A
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.
b75a7d8f 945 if (locale == rhs.locale &&
374ca955
A
946 lenient == rhs.lenient &&
947 (localizations == NULL
948 ? rhs.localizations == NULL
949 : (rhs.localizations == NULL
950 ? FALSE
951 : *localizations == rhs.localizations))) {
952
3d1f044b
A
953 NFRuleSet** p = fRuleSets;
954 NFRuleSet** q = rhs.fRuleSets;
b75a7d8f 955 if (p == NULL) {
374ca955
A
956 return q == NULL;
957 } else if (q == NULL) {
b75a7d8f
A
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;
3d1f044b
A
975 if (fRuleSets != NULL) {
976 for (NFRuleSet** p = fRuleSets; *p; ++p) {
b75a7d8f
A
977 (*p)->appendRules(result);
978 }
979 }
980 return result;
981}
982
983UnicodeString
984RuleBasedNumberFormat::getRuleSetName(int32_t index) const
985{
374ca955 986 if (localizations) {
2ca993e8
A
987 UnicodeString string(TRUE, localizations->getRuleSetName(index), (int32_t)-1);
988 return string;
989 }
3d1f044b 990 else if (fRuleSets) {
374ca955 991 UnicodeString result;
3d1f044b 992 for (NFRuleSet** p = fRuleSets; *p; ++p) {
b75a7d8f
A
993 NFRuleSet* rs = *p;
994 if (rs->isPublic()) {
995 if (--index == -1) {
996 rs->getName(result);
997 return result;
998 }
999 }
1000 }
1001 }
374ca955
A
1002 UnicodeString empty;
1003 return empty;
b75a7d8f
A
1004}
1005
1006int32_t
1007RuleBasedNumberFormat::getNumberOfRuleSetNames() const
1008{
1009 int32_t result = 0;
374ca955 1010 if (localizations) {
2ca993e8
A
1011 result = localizations->getNumberOfRuleSets();
1012 }
3d1f044b
A
1013 else if (fRuleSets) {
1014 for (NFRuleSet** p = fRuleSets; *p; ++p) {
b75a7d8f
A
1015 if ((**p).isPublic()) {
1016 ++result;
1017 }
1018 }
1019 }
1020 return result;
1021}
1022
374ca955
A
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)) {
73c04bcf 1034 return Locale("");
374ca955
A
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;
73c04bcf 1045 return Locale("");
374ca955
A
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
b75a7d8f
A
1098NFRuleSet*
1099RuleBasedNumberFormat::findRuleSet(const UnicodeString& name, UErrorCode& status) const
1100{
3d1f044b
A
1101 if (U_SUCCESS(status) && fRuleSets) {
1102 for (NFRuleSet** p = fRuleSets; *p; ++p) {
b75a7d8f
A
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
f3c0d7a5 1113UnicodeString&
3d1f044b 1114RuleBasedNumberFormat::format(const DecimalQuantity &number,
f3c0d7a5
A
1115 UnicodeString &appendTo,
1116 FieldPositionIterator *posIter,
1117 UErrorCode &status) const {
1118 if (U_FAILURE(status)) {
1119 return appendTo;
1120 }
3d1f044b
A
1121 DecimalQuantity copy(number);
1122 if (copy.fitsInLong()) {
1123 format(number.toLong(), appendTo, posIter, status);
f3c0d7a5
A
1124 }
1125 else {
3d1f044b
A
1126 copy.roundToMagnitude(0, number::impl::RoundingMode::UNUM_ROUND_HALFEVEN, status);
1127 if (copy.fitsInLong()) {
1128 format(number.toDouble(), appendTo, posIter, status);
f3c0d7a5
A
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.
3d1f044b
A
1135 LocalPointer<NumberFormat> decimalFormat(NumberFormat::createInstance(locale, UNUM_DECIMAL, status), status);
1136 if (decimalFormat.isNull()) {
1137 return appendTo;
1138 }
f3c0d7a5 1139 Formattable f;
3d1f044b
A
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.
f3c0d7a5 1145 decimalFormat->format(f, appendTo, posIter, status);
f3c0d7a5
A
1146 }
1147 }
1148 return appendTo;
1149}
1150
1151
1152UnicodeString&
3d1f044b 1153RuleBasedNumberFormat::format(const DecimalQuantity &number,
f3c0d7a5
A
1154 UnicodeString& appendTo,
1155 FieldPosition& pos,
1156 UErrorCode &status) const {
1157 if (U_FAILURE(status)) {
1158 return appendTo;
1159 }
3d1f044b
A
1160 DecimalQuantity copy(number);
1161 if (copy.fitsInLong()) {
1162 format(number.toLong(), appendTo, pos, status);
f3c0d7a5
A
1163 }
1164 else {
3d1f044b
A
1165 copy.roundToMagnitude(0, number::impl::RoundingMode::UNUM_ROUND_HALFEVEN, status);
1166 if (copy.fitsInLong()) {
1167 format(number.toDouble(), appendTo, pos, status);
f3c0d7a5
A
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.
3d1f044b
A
1174 LocalPointer<NumberFormat> decimalFormat(NumberFormat::createInstance(locale, UNUM_DECIMAL, status), status);
1175 if (decimalFormat.isNull()) {
1176 return appendTo;
1177 }
f3c0d7a5 1178 Formattable f;
3d1f044b
A
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.
f3c0d7a5 1184 decimalFormat->format(f, appendTo, pos, status);
f3c0d7a5
A
1185 }
1186 }
1187 return appendTo;
1188}
1189
b75a7d8f
A
1190UnicodeString&
1191RuleBasedNumberFormat::format(int32_t number,
1192 UnicodeString& toAppendTo,
f3c0d7a5 1193 FieldPosition& pos) const
b75a7d8f 1194{
f3c0d7a5 1195 return format((int64_t)number, toAppendTo, pos);
b75a7d8f
A
1196}
1197
1198
1199UnicodeString&
1200RuleBasedNumberFormat::format(int64_t number,
1201 UnicodeString& toAppendTo,
374ca955 1202 FieldPosition& /* pos */) const
b75a7d8f 1203{
57a6839d 1204 if (defaultRuleSet) {
b331163b 1205 UErrorCode status = U_ZERO_ERROR;
f3c0d7a5 1206 format(number, defaultRuleSet, toAppendTo, status);
57a6839d 1207 }
b75a7d8f
A
1208 return toAppendTo;
1209}
1210
1211
1212UnicodeString&
1213RuleBasedNumberFormat::format(double number,
1214 UnicodeString& toAppendTo,
374ca955 1215 FieldPosition& /* pos */) const
b75a7d8f 1216{
f3c0d7a5 1217 UErrorCode status = U_ZERO_ERROR;
2ca993e8 1218 if (defaultRuleSet) {
0f5d89e8 1219 format(number, *defaultRuleSet, toAppendTo, status);
729e4ab9 1220 }
0f5d89e8 1221 return toAppendTo;
b75a7d8f
A
1222}
1223
1224
1225UnicodeString&
1226RuleBasedNumberFormat::format(int32_t number,
1227 const UnicodeString& ruleSetName,
1228 UnicodeString& toAppendTo,
f3c0d7a5 1229 FieldPosition& pos,
b75a7d8f
A
1230 UErrorCode& status) const
1231{
f3c0d7a5 1232 return format((int64_t)number, ruleSetName, toAppendTo, pos, status);
b75a7d8f
A
1233}
1234
1235
1236UnicodeString&
1237RuleBasedNumberFormat::format(int64_t number,
1238 const UnicodeString& ruleSetName,
1239 UnicodeString& toAppendTo,
374ca955 1240 FieldPosition& /* pos */,
b75a7d8f
A
1241 UErrorCode& status) const
1242{
1243 if (U_SUCCESS(status)) {
4388f060 1244 if (ruleSetName.indexOf(gPercentPercent, 2, 0) == 0) {
b75a7d8f
A
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) {
f3c0d7a5 1250 format(number, rs, toAppendTo, status);
b75a7d8f
A
1251 }
1252 }
1253 }
1254 return toAppendTo;
1255}
1256
1257
b75a7d8f
A
1258UnicodeString&
1259RuleBasedNumberFormat::format(double number,
1260 const UnicodeString& ruleSetName,
1261 UnicodeString& toAppendTo,
374ca955 1262 FieldPosition& /* pos */,
b75a7d8f
A
1263 UErrorCode& status) const
1264{
1265 if (U_SUCCESS(status)) {
4388f060 1266 if (ruleSetName.indexOf(gPercentPercent, 2, 0) == 0) {
b75a7d8f
A
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) {
0f5d89e8 1272 format(number, *rs, toAppendTo, status);
b75a7d8f
A
1273 }
1274 }
1275 }
1276 return toAppendTo;
1277}
1278
0f5d89e8
A
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)) {
3d1f044b
A
1287 DecimalQuantity digitList;
1288 digitList.setToDouble(number);
1289 digitList.roundToMagnitude(
1290 -getMaximumFractionDigits(),
1291 static_cast<UNumberFormatRoundingMode>(getRoundingMode()),
1292 status);
1293 number = digitList.toDouble();
0f5d89e8
A
1294 }
1295 rs.format(number, toAppendTo, toAppendTo.length(), 0, status);
1296 adjustForCapitalizationContext(startPos, toAppendTo, status);
1297}
1298
f3c0d7a5
A
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);
3d1f044b
A
1327 if (decimalFormat == nullptr) {
1328 return toAppendTo;
1329 }
f3c0d7a5
A
1330 Formattable f;
1331 FieldPosition pos(FieldPosition::DONT_CARE);
3d1f044b
A
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.
f3c0d7a5
A
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
57a6839d
A
1352UnicodeString&
1353RuleBasedNumberFormat::adjustForCapitalizationContext(int32_t startPos,
f3c0d7a5
A
1354 UnicodeString& currentResult,
1355 UErrorCode& status) const
57a6839d
A
1356{
1357#if !UCONFIG_NO_BREAK_ITERATION
f3c0d7a5
A
1358 UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status);
1359 if (capitalizationContext != UDISPCTX_CAPITALIZATION_NONE && startPos == 0 && currentResult.length() > 0) {
57a6839d
A
1360 // capitalize currentResult according to context
1361 UChar32 ch = currentResult.char32At(0);
f3c0d7a5
A
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)) ) {
57a6839d
A
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
b75a7d8f
A
1376void
1377RuleBasedNumberFormat::parse(const UnicodeString& text,
1378 Formattable& result,
1379 ParsePosition& parsePosition) const
1380{
3d1f044b 1381 if (!fRuleSets) {
b75a7d8f
A
1382 parsePosition.setErrorIndex(0);
1383 return;
1384 }
1385
374ca955
A
1386 UnicodeString workingText(text, parsePosition.getIndex());
1387 ParsePosition workingPos(0);
1388
1389 ParsePosition high_pp(0);
b75a7d8f
A
1390 Formattable high_result;
1391
3d1f044b 1392 for (NFRuleSet** p = fRuleSets; *p; ++p) {
b75a7d8f 1393 NFRuleSet *rp = *p;
729e4ab9 1394 if (rp->isPublic() && rp->isParseable()) {
374ca955 1395 ParsePosition working_pp(0);
b75a7d8f
A
1396 Formattable working_result;
1397
0f5d89e8 1398 rp->parse(workingText, working_pp, kMaxDouble, 0, working_result, lenient);
b75a7d8f
A
1399 if (working_pp.getIndex() > high_pp.getIndex()) {
1400 high_pp = working_pp;
1401 high_result = working_result;
1402
374ca955 1403 if (high_pp.getIndex() == workingText.length()) {
b75a7d8f
A
1404 break;
1405 }
1406 }
1407 }
1408 }
1409
46f4442e
A
1410 int32_t startIndex = parsePosition.getIndex();
1411 parsePosition.setIndex(startIndex + high_pp.getIndex());
374ca955
A
1412 if (high_pp.getIndex() > 0) {
1413 parsePosition.setErrorIndex(-1);
46f4442e
A
1414 } else {
1415 int32_t errorIndex = (high_pp.getErrorIndex()>0)? high_pp.getErrorIndex(): 0;
1416 parsePosition.setErrorIndex(startIndex + errorIndex);
b75a7d8f 1417 }
b75a7d8f
A
1418 result = high_result;
1419 if (result.getType() == Formattable::kDouble) {
2ca993e8
A
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));
b75a7d8f
A
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()) {
374ca955
A
1448 if (localizations) {
1449 UnicodeString name(TRUE, localizations->getRuleSetName(0), -1);
1450 defaultRuleSet = findRuleSet(name, status);
1451 } else {
b75a7d8f 1452 initDefaultRuleSet();
374ca955
A
1453 }
1454 } else if (ruleSetName.startsWith(UNICODE_STRING_SIMPLE("%%"))) {
b75a7d8f
A
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
374ca955
A
1465UnicodeString
1466RuleBasedNumberFormat::getDefaultRuleSetName() const {
2ca993e8
A
1467 UnicodeString result;
1468 if (defaultRuleSet && defaultRuleSet->isPublic()) {
1469 defaultRuleSet->getName(result);
1470 } else {
1471 result.setToBogus();
1472 }
1473 return result;
374ca955
A
1474}
1475
b75a7d8f
A
1476void
1477RuleBasedNumberFormat::initDefaultRuleSet()
1478{
374ca955 1479 defaultRuleSet = NULL;
3d1f044b 1480 if (!fRuleSets) {
2ca993e8 1481 return;
b75a7d8f 1482 }
374ca955 1483
2ca993e8
A
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"));
729e4ab9 1487
3d1f044b 1488 NFRuleSet**p = &fRuleSets[0];
b75a7d8f 1489 while (*p) {
729e4ab9
A
1490 if ((*p)->isNamed(spellout) || (*p)->isNamed(ordinal) || (*p)->isNamed(duration)) {
1491 defaultRuleSet = *p;
1492 return;
1493 } else {
1494 ++p;
1495 }
b75a7d8f
A
1496 }
1497
1498 defaultRuleSet = *--p;
1499 if (!defaultRuleSet->isPublic()) {
3d1f044b 1500 while (p != fRuleSets) {
b75a7d8f
A
1501 if ((*--p)->isPublic()) {
1502 defaultRuleSet = *p;
1503 break;
1504 }
1505 }
1506 }
1507}
1508
1509
1510void
374ca955 1511RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* localizationInfos,
73c04bcf 1512 UParseError& pErr, UErrorCode& status)
b75a7d8f
A
1513{
1514 // TODO: implement UParseError
73c04bcf 1515 uprv_memset(&pErr, 0, sizeof(UParseError));
b75a7d8f
A
1516 // Note: this can leave ruleSets == NULL, so remaining code should check
1517 if (U_FAILURE(status)) {
1518 return;
1519 }
1520
2ca993e8
A
1521 initializeDecimalFormatSymbols(status);
1522 initializeDefaultInfinityRule(status);
1523 initializeDefaultNaNRule(status);
1524 if (U_FAILURE(status)) {
1525 return;
1526 }
1527
374ca955
A
1528 this->localizations = localizationInfos == NULL ? NULL : localizationInfos->ref();
1529
b75a7d8f
A
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
4388f060 1547 int32_t lp = description.indexOf(gLenientParse, -1, 0);
b75a7d8f
A
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)
4388f060 1556 int lpEnd = description.indexOf(gSemiPercent, 2, lp);
b75a7d8f
A
1557
1558 if (lpEnd == -1) {
1559 lpEnd = description.length() - 1;
1560 }
1561 int lpStart = lp + u_strlen(gLenientParse);
4388f060 1562 while (PatternProps::isWhiteSpace(description.charAt(lpStart))) {
b75a7d8f
A
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 */
3d1f044b 1570 if (lenientParseRules == nullptr) {
b75a7d8f
A
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)
4388f060
A
1583 numRuleSets = 0;
1584 for (int32_t p = description.indexOf(gSemiPercent, 2, 0); p != -1; p = description.indexOf(gSemiPercent, 2, p)) {
b75a7d8f
A
1585 ++numRuleSets;
1586 ++p;
1587 }
1588 ++numRuleSets;
1589
1590 // our rule list is an array of the appropriate size
3d1f044b 1591 fRuleSets = (NFRuleSet **)uprv_malloc((numRuleSets + 1) * sizeof(NFRuleSet *));
b75a7d8f 1592 /* test for NULL */
3d1f044b 1593 if (fRuleSets == 0) {
b75a7d8f
A
1594 status = U_MEMORY_ALLOCATION_ERROR;
1595 return;
1596 }
1597
1598 for (int i = 0; i <= numRuleSets; ++i) {
3d1f044b 1599 fRuleSets[i] = NULL;
b75a7d8f
A
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) {
46f4442e
A
1610 status = U_ILLEGAL_ARGUMENT_ERROR;
1611 return;
b75a7d8f 1612 }
4388f060
A
1613
1614 ruleSetDescriptions = new UnicodeString[numRuleSets];
3d1f044b 1615 if (ruleSetDescriptions == nullptr) {
b75a7d8f
A
1616 status = U_MEMORY_ALLOCATION_ERROR;
1617 return;
1618 }
1619
1620 {
1621 int curRuleSet = 0;
1622 int32_t start = 0;
4388f060 1623 for (int32_t p = description.indexOf(gSemiPercent, 2, 0); p != -1; p = description.indexOf(gSemiPercent, 2, start)) {
b75a7d8f 1624 ruleSetDescriptions[curRuleSet].setTo(description, start, p + 1 - start);
3d1f044b
A
1625 fRuleSets[curRuleSet] = new NFRuleSet(this, ruleSetDescriptions, curRuleSet, status);
1626 if (fRuleSets[curRuleSet] == nullptr) {
b75a7d8f 1627 status = U_MEMORY_ALLOCATION_ERROR;
4388f060 1628 return;
b75a7d8f
A
1629 }
1630 ++curRuleSet;
1631 start = p + 1;
1632 }
1633 ruleSetDescriptions[curRuleSet].setTo(description, start, description.length() - start);
3d1f044b
A
1634 fRuleSets[curRuleSet] = new NFRuleSet(this, ruleSetDescriptions, curRuleSet, status);
1635 if (fRuleSets[curRuleSet] == nullptr) {
b75a7d8f 1636 status = U_MEMORY_ALLOCATION_ERROR;
4388f060 1637 return;
b75a7d8f
A
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)
374ca955
A
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();
b75a7d8f
A
1651
1652 // finally, we can go back through the temporary descriptions
3d1f044b 1653 // list and finish setting up the substructure (and we throw
b75a7d8f
A
1654 // away the temporary descriptions as we go)
1655 {
1656 for (int i = 0; i < numRuleSets; i++) {
3d1f044b 1657 fRuleSets[i]->parseRules(ruleSetDescriptions[i], status);
b75a7d8f
A
1658 }
1659 }
1660
374ca955
A
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 }
57a6839d
A
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)) ) {
3d1f044b 1703 status = U_ZERO_ERROR;
57a6839d
A
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) {
3d1f044b
A
1727 capitalizationForUIListMenu = static_cast<UBool>(intVector[0]);
1728 capitalizationForStandAlone = static_cast<UBool>(intVector[1]);
57a6839d
A
1729 }
1730 }
1731 ures_close(rb);
1732#endif
b75a7d8f
A
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()
4388f060 1745 && PatternProps::isWhiteSpace(description.charAt(start))) {
b75a7d8f
A
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
3d1f044b 1763 // when we get here, we've seeked off the end of the string, and
b75a7d8f
A
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{
3d1f044b
A
1779 if (fRuleSets) {
1780 for (NFRuleSet** p = fRuleSets; *p; ++p) {
b75a7d8f
A
1781 delete *p;
1782 }
3d1f044b
A
1783 uprv_free(fRuleSets);
1784 fRuleSets = NULL;
b75a7d8f
A
1785 }
1786
4388f060
A
1787 if (ruleSetDescriptions) {
1788 delete [] ruleSetDescriptions;
2ca993e8 1789 ruleSetDescriptions = NULL;
4388f060
A
1790 }
1791
b75a7d8f
A
1792#if !UCONFIG_NO_COLLATION
1793 delete collator;
1794#endif
1795 collator = NULL;
1796
1797 delete decimalFormatSymbols;
1798 decimalFormatSymbols = NULL;
1799
2ca993e8
A
1800 delete defaultInfinityRule;
1801 defaultInfinityRule = NULL;
1802
1803 delete defaultNaNRule;
1804 defaultNaNRule = NULL;
1805
b75a7d8f
A
1806 delete lenientParseRules;
1807 lenientParseRules = NULL;
374ca955 1808
57a6839d 1809#if !UCONFIG_NO_BREAK_ITERATION
2ca993e8
A
1810 delete capitalizationBrkIter;
1811 capitalizationBrkIter = NULL;
57a6839d
A
1812#endif
1813
2ca993e8
A
1814 if (localizations) {
1815 localizations = localizations->unref();
1816 }
b75a7d8f
A
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*/
57a6839d 1830const RuleBasedCollator*
b75a7d8f
A
1831RuleBasedNumberFormat::getCollator() const
1832{
1833#if !UCONFIG_NO_COLLATION
3d1f044b 1834 if (!fRuleSets) {
b75a7d8f
A
1835 return NULL;
1836 }
1837
57a6839d 1838 // lazy-evaluate the collator
b75a7d8f
A
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_
3d1f044b 1843 // collator based on the combination of those rules
b75a7d8f
A
1844
1845 UErrorCode status = U_ZERO_ERROR;
1846
1847 Collator* temp = Collator::createInstance(locale, status);
729e4ab9
A
1848 RuleBasedCollator* newCollator;
1849 if (U_SUCCESS(status) && (newCollator = dynamic_cast<RuleBasedCollator*>(temp)) != NULL) {
b75a7d8f
A
1850 if (lenientParseRules) {
1851 UnicodeString rules(newCollator->getRules());
1852 rules.append(*lenientParseRules);
1853
1854 newCollator = new RuleBasedCollator(rules, status);
46f4442e
A
1855 // Exit if newCollator could not be created.
1856 if (newCollator == NULL) {
57a6839d 1857 return NULL;
46f4442e 1858 }
b75a7d8f
A
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
b75a7d8f 1880DecimalFormatSymbols*
2ca993e8 1881RuleBasedNumberFormat::initializeDecimalFormatSymbols(UErrorCode &status)
b75a7d8f
A
1882{
1883 // lazy-evaluate the DecimalFormatSymbols object. This object
1884 // is shared by all DecimalFormat instances belonging to this
1885 // formatter
3d1f044b
A
1886 if (decimalFormatSymbols == nullptr) {
1887 LocalPointer<DecimalFormatSymbols> temp(new DecimalFormatSymbols(locale, status), status);
b75a7d8f 1888 if (U_SUCCESS(status)) {
3d1f044b 1889 decimalFormatSymbols = temp.orphan();
b75a7d8f
A
1890 }
1891 }
1892 return decimalFormatSymbols;
1893}
1894
2ca993e8
A
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)) {
3d1f044b 1909 return nullptr;
2ca993e8
A
1910 }
1911 if (defaultInfinityRule == NULL) {
1912 UnicodeString rule(UNICODE_STRING_SIMPLE("Inf: "));
1913 rule.append(getDecimalFormatSymbols()->getSymbol(DecimalFormatSymbols::kInfinitySymbol));
3d1f044b 1914 LocalPointer<NFRule> temp(new NFRule(this, rule, status), status);
2ca993e8 1915 if (U_SUCCESS(status)) {
3d1f044b 1916 defaultInfinityRule = temp.orphan();
2ca993e8
A
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)) {
3d1f044b 1932 return nullptr;
2ca993e8 1933 }
3d1f044b 1934 if (defaultNaNRule == nullptr) {
2ca993e8
A
1935 UnicodeString rule(UNICODE_STRING_SIMPLE("NaN: "));
1936 rule.append(getDecimalFormatSymbols()->getSymbol(DecimalFormatSymbols::kNaNSymbol));
3d1f044b 1937 LocalPointer<NFRule> temp(new NFRule(this, rule, status), status);
2ca993e8 1938 if (U_SUCCESS(status)) {
3d1f044b 1939 defaultNaNRule = temp.orphan();
2ca993e8
A
1940 }
1941 }
1942 return defaultNaNRule;
1943}
1944
1945const NFRule*
1946RuleBasedNumberFormat::getDefaultNaNRule() const
1947{
1948 return defaultNaNRule;
1949}
1950
4388f060
A
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
2ca993e8
A
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
3d1f044b 1977 if (fRuleSets) {
2ca993e8 1978 for (int32_t i = 0; i < numRuleSets; i++) {
3d1f044b 1979 fRuleSets[i]->setDecimalFormatSymbols(*symbolsToAdopt, status);
2ca993e8 1980 }
4388f060
A
1981 }
1982 }
1983}
1984
3d1f044b 1985// Setting the symbols is equivalent to adopting a newly created localized symbols.
4388f060
A
1986void
1987RuleBasedNumberFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols)
1988{
1989 adoptDecimalFormatSymbols(new DecimalFormatSymbols(symbols));
1990}
1991
b331163b
A
1992PluralFormat *
1993RuleBasedNumberFormat::createPluralFormat(UPluralType pluralType,
1994 const UnicodeString &pattern,
1995 UErrorCode& status) const
1996{
3d1f044b
A
1997 auto *pf = new PluralFormat(locale, pluralType, pattern, status);
1998 if (pf == nullptr) {
1999 status = U_MEMORY_ALLOCATION_ERROR;
2000 }
2001 return pf;
b331163b
A
2002}
2003
0f5d89e8
A
2004/**
2005 * Get the rounding mode.
2006 * @return A rounding mode
2007 */
2008DecimalFormat::ERoundingMode RuleBasedNumberFormat::getRoundingMode() const {
3d1f044b 2009 return fRoundingMode;
0f5d89e8
A
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) {
3d1f044b 2018 fRoundingMode = roundingMode;
0f5d89e8
A
2019}
2020
374ca955
A
2021U_NAMESPACE_END
2022
b75a7d8f
A
2023/* U_HAVE_RBNF */
2024#endif