Bug Summary

File:root/firefox-clang/intl/icu/source/i18n/rbnf.cpp
Warning:line 833, column 17
Value stored to 'fmt_tag' during its initialization is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

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