1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 1997-2015, International Business Machines Corporation
6 * and others. All Rights Reserved.
7 *******************************************************************************
10 #include "unicode/utypes.h"
11 #include "utypeinfo.h" // for 'typeid' to work
13 #include "unicode/rbnf.h"
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"
34 #include "patternprops.h"
37 #include "number_decimalquantity.h"
46 #define U_ICUDATA_RBNF U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "rbnf"
48 static const UChar gPercentPercent
[] =
53 // All urbnf objects are created through openRules, so we init all of the
54 // Unicode string constants required by rbnf, nfrs, or nfr here.
55 static const UChar gLenientParse
[] =
57 0x25, 0x25, 0x6C, 0x65, 0x6E, 0x69, 0x65, 0x6E, 0x74, 0x2D, 0x70, 0x61, 0x72, 0x73, 0x65, 0x3A, 0
58 }; /* "%%lenient-parse:" */
59 static const UChar gSemiColon
= 0x003B;
60 static const UChar gSemiPercent
[] =
65 #define kSomeNumberOfBitsDiv2 22
66 #define kHalfMaxDouble (double)(1 << kSomeNumberOfBitsDiv2)
67 #define kMaxDouble (kHalfMaxDouble * kHalfMaxDouble)
71 using number::impl::DecimalQuantity
;
73 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedNumberFormat
)
76 This is a utility class. It does not use ICU's RTTI.
77 If ICU's RTTI is needed again, you can uncomment the RTTI code and derive from UObject.
78 Please make sure that intltest passes on Windows in Release mode,
79 since the string pooling per compilation unit will mess up how RTTI works.
80 The RTTI code was also removed due to lack of code coverage.
82 class LocalizationInfo
: public UMemory
{
84 virtual ~LocalizationInfo();
88 LocalizationInfo() : refcount(0) {}
90 LocalizationInfo
* ref(void) {
95 LocalizationInfo
* unref(void) {
96 if (refcount
&& --refcount
== 0) {
102 virtual UBool
operator==(const LocalizationInfo
* rhs
) const;
103 inline UBool
operator!=(const LocalizationInfo
* rhs
) const { return !operator==(rhs
); }
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;
111 virtual int32_t indexForLocale(const UChar
* locale
) const;
112 virtual int32_t indexForRuleSet(const UChar
* ruleset
) const;
114 // virtual UClassID getDynamicClassID() const = 0;
115 // static UClassID getStaticClassID(void);
118 LocalizationInfo::~LocalizationInfo() {}
120 //UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(LocalizationInfo)
122 // if both strings are NULL, this returns TRUE
124 streq(const UChar
* lhs
, const UChar
* rhs
) {
129 return u_strcmp(lhs
, rhs
) == 0;
135 LocalizationInfo::operator==(const LocalizationInfo
* rhs
) const {
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
))) {
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
))) {
157 for (int j
= 0; j
< rsc
; ++j
) {
158 if (!streq(getDisplayName(i
, j
), rhs
->getDisplayName(ix
, j
))) {
171 LocalizationInfo::indexForLocale(const UChar
* locale
) const {
172 for (int i
= 0; i
< getNumberOfDisplayLocales(); ++i
) {
173 if (streq(locale
, getLocaleName(i
))) {
181 LocalizationInfo::indexForRuleSet(const UChar
* ruleset
) const {
183 for (int i
= 0; i
< getNumberOfRuleSets(); ++i
) {
184 if (streq(ruleset
, getRuleSetName(i
))) {
193 typedef void (*Fn_Deleter
)(void*);
201 VArray() : buf(NULL
), cap(0), size(0), deleter(NULL
) {}
203 VArray(Fn_Deleter del
) : buf(NULL
), cap(0), size(0), deleter(del
) {}
207 for (int i
= 0; i
< size
; ++i
) {
218 void add(void* elem
, UErrorCode
& status
) {
219 if (U_SUCCESS(status
)) {
223 } else if (cap
< 256) {
229 buf
= (void**)uprv_malloc(cap
* sizeof(void*));
231 buf
= (void**)uprv_realloc(buf
, cap
* sizeof(void*));
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
;
238 void* start
= &buf
[size
];
239 size_t count
= (cap
- size
) * sizeof(void*);
240 uprv_memset(start
, 0, count
); // fill with nulls, just because
246 void** release(void) {
257 class StringLocalizationInfo
: public LocalizationInfo
{
263 friend class LocDataParser
;
265 StringLocalizationInfo(UChar
* i
, UChar
*** d
, int32_t numRS
, int32_t numLocs
)
266 : info(i
), data(d
), numRuleSets(numRS
), numLocales(numLocs
)
271 static StringLocalizationInfo
* create(const UnicodeString
& info
, UParseError
& perror
, UErrorCode
& status
);
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;
280 // virtual UClassID getDynamicClassID() const;
281 // static UClassID getStaticClassID(void);
284 void init(UErrorCode
& status
) const;
289 OPEN_ANGLE
= 0x003c, /* '<' */
290 CLOSE_ANGLE
= 0x003e, /* '>' */
298 * Utility for parsing a localization string and returning a StringLocalizationInfo*.
300 class LocDataParser
{
309 LocDataParser(UParseError
& parseError
, UErrorCode
& status
)
310 : data(NULL
), e(NULL
), p(NULL
), ch(0xffff), pe(parseError
), ec(status
) {}
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.
317 StringLocalizationInfo
* parse(UChar
* data
, int32_t len
);
321 inline void inc(void) {
325 inline UBool
checkInc(UChar c
) {
326 if (p
< e
&& (ch
== c
|| *p
== c
)) {
332 inline UBool
check(UChar c
) {
333 return p
< e
&& (ch
== c
|| *p
== c
);
335 inline void skipWhitespace(void) {
336 while (p
< e
&& PatternProps::isWhiteSpace(ch
!= 0xffff ? ch
: *p
)) {
340 inline UBool
inList(UChar c
, const UChar
* list
) const {
341 if (*list
== SPACE
&& PatternProps::isWhiteSpace(c
)) {
344 while (*list
&& *list
!= c
) {
349 void parseError(const char* msg
);
351 StringLocalizationInfo
* doParse(void);
353 UChar
** nextArray(int32_t& requiredLength
);
354 UChar
* nextString(void);
358 #define ERROR(msg) parseError(msg); return NULL;
359 #define EXPLANATION_ARG explanationArg
361 #define ERROR(msg) parseError(NULL); return NULL;
362 #define EXPLANATION_ARG
366 static const UChar DQUOTE_STOPLIST
[] = {
370 static const UChar SQUOTE_STOPLIST
[] = {
374 static const UChar NOQUOTE_STOPLIST
[] = {
375 SPACE
, COMMA
, CLOSE_ANGLE
, OPEN_ANGLE
, TICK
, QUOTE
, 0
383 StringLocalizationInfo
*
384 LocDataParser::parse(UChar
* _data
, int32_t len
) {
386 if (_data
) uprv_free(_data
);
392 pe
.postContext
[0] = 0;
393 pe
.preContext
[0] = 0;
396 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
401 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
415 StringLocalizationInfo
*
416 LocDataParser::doParse(void) {
418 if (!checkInc(OPEN_ANGLE
)) {
419 ERROR("Missing open angle");
421 VArray
array(DeleteFn
);
422 UBool mightHaveNext
= TRUE
;
423 int32_t requiredLength
= -1;
424 while (mightHaveNext
) {
425 mightHaveNext
= FALSE
;
426 UChar
** elem
= nextArray(requiredLength
);
428 UBool haveComma
= check(COMMA
);
433 mightHaveNext
= TRUE
;
435 } else if (haveComma
) {
436 ERROR("Unexpected character");
441 if (!checkInc(CLOSE_ANGLE
)) {
442 if (check(OPEN_ANGLE
)) {
443 ERROR("Missing comma in outer array");
445 ERROR("Missing close angle bracket in outer array");
451 ERROR("Extra text after close of localization data");
456 int32_t numLocs
= array
.length() - 2; // subtract first, NULL
457 UChar
*** result
= (UChar
***)array
.release();
459 return new StringLocalizationInfo(data
, result
, requiredLength
-2, numLocs
); // subtract first, NULL
463 ERROR("Unknown error");
467 LocDataParser::nextArray(int32_t& requiredLength
) {
473 if (!checkInc(OPEN_ANGLE
)) {
474 ERROR("Missing open angle");
478 UBool mightHaveNext
= TRUE
;
479 while (mightHaveNext
) {
480 mightHaveNext
= FALSE
;
481 UChar
* elem
= nextString();
483 UBool haveComma
= check(COMMA
);
488 mightHaveNext
= TRUE
;
490 } else if (haveComma
) {
491 ERROR("Unexpected comma");
495 if (!checkInc(CLOSE_ANGLE
)) {
496 if (check(OPEN_ANGLE
)) {
497 ERROR("Missing close angle bracket in inner array");
499 ERROR("Missing comma in inner array");
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");
512 return (UChar
**)array
.release();
514 ERROR("Unknown Error");
518 LocDataParser::nextString() {
519 UChar
* result
= NULL
;
523 const UChar
* terminators
;
525 UBool haveQuote
= c
== QUOTE
|| c
== TICK
;
528 terminators
= c
== QUOTE
? DQUOTE_STOPLIST
: SQUOTE_STOPLIST
;
530 terminators
= NOQUOTE_STOPLIST
;
533 while (p
< e
&& !inList(*p
, terminators
)) ++p
;
535 ERROR("Unexpected end of data");
541 *p
= 0x0; // terminate by writing to data
542 result
= start
; // just point into data
546 ERROR("Missing matching quote");
547 } else if (p
== start
) {
548 ERROR("Empty string");
551 } else if (x
== OPEN_ANGLE
|| x
== TICK
|| x
== QUOTE
) {
552 ERROR("Unexpected character in string");
556 // ok for there to be no next string
560 void LocDataParser::parseError(const char* EXPLANATION_ARG
)
566 const UChar
* start
= p
- U_PARSE_CONTEXT_LEN
- 1;
570 for (UChar
* x
= p
; --x
>= start
;) {
576 const UChar
* limit
= p
+ U_PARSE_CONTEXT_LEN
- 1;
580 u_strncpy(pe
.preContext
, start
, (int32_t)(p
-start
));
581 pe
.preContext
[p
-start
] = 0;
582 u_strncpy(pe
.postContext
, p
, (int32_t)(limit
-p
));
583 pe
.postContext
[limit
-p
] = 0;
584 pe
.offset
= (int32_t)(p
- data
);
587 fprintf(stderr
, "%s at or near character %ld: ", EXPLANATION_ARG
, p
-data
);
590 msg
.append(start
, p
- start
);
591 msg
.append((UChar
)0x002f); /* SOLIDUS/SLASH */
592 msg
.append(p
, limit
-p
);
593 msg
.append(UNICODE_STRING_SIMPLE("'"));
596 int32_t len
= msg
.extract(0, msg
.length(), buf
, 128);
602 fprintf(stderr
, "%s\n", buf
);
616 //UOBJECT_DEFINE_RTTI_IMPLEMENTATION(StringLocalizationInfo)
618 StringLocalizationInfo
*
619 StringLocalizationInfo::create(const UnicodeString
& info
, UParseError
& perror
, UErrorCode
& status
) {
620 if (U_FAILURE(status
)) {
624 int32_t len
= info
.length();
626 return NULL
; // no error;
629 UChar
* p
= (UChar
*)uprv_malloc(len
* sizeof(UChar
));
631 status
= U_MEMORY_ALLOCATION_ERROR
;
634 info
.extract(p
, len
, status
);
635 if (!U_FAILURE(status
)) {
636 status
= U_ZERO_ERROR
; // clear warning about non-termination
639 LocDataParser
parser(perror
, status
);
640 return parser
.parse(p
, len
);
643 StringLocalizationInfo::~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
);
648 if (data
) uprv_free(data
);
649 if (info
) uprv_free(info
);
654 StringLocalizationInfo::getRuleSetName(int32_t index
) const {
655 if (index
>= 0 && index
< getNumberOfRuleSets()) {
656 return data
[0][index
];
662 StringLocalizationInfo::getLocaleName(int32_t index
) const {
663 if (index
>= 0 && index
< getNumberOfDisplayLocales()) {
664 return data
[index
+1][0];
670 StringLocalizationInfo::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];
680 RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString
& description
,
681 const UnicodeString
& locs
,
682 const Locale
& alocale
, UParseError
& perror
, UErrorCode
& status
)
684 , ruleSetDescriptions(NULL
)
686 , defaultRuleSet(NULL
)
689 , decimalFormatSymbols(NULL
)
690 , defaultInfinityRule(NULL
)
691 , defaultNaNRule(NULL
)
692 , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary
)
694 , lenientParseRules(NULL
)
695 , localizations(NULL
)
696 , capitalizationInfoSet(FALSE
)
697 , capitalizationForUIListMenu(FALSE
)
698 , capitalizationForStandAlone(FALSE
)
699 , capitalizationBrkIter(NULL
)
701 LocalizationInfo
* locinfo
= StringLocalizationInfo::create(locs
, perror
, status
);
702 init(description
, locinfo
, perror
, status
);
705 RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString
& description
,
706 const UnicodeString
& locs
,
707 UParseError
& perror
, UErrorCode
& status
)
709 , ruleSetDescriptions(NULL
)
711 , defaultRuleSet(NULL
)
712 , locale(Locale::getDefault())
714 , decimalFormatSymbols(NULL
)
715 , defaultInfinityRule(NULL
)
716 , defaultNaNRule(NULL
)
717 , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary
)
719 , lenientParseRules(NULL
)
720 , localizations(NULL
)
721 , capitalizationInfoSet(FALSE
)
722 , capitalizationForUIListMenu(FALSE
)
723 , capitalizationForStandAlone(FALSE
)
724 , capitalizationBrkIter(NULL
)
726 LocalizationInfo
* locinfo
= StringLocalizationInfo::create(locs
, perror
, status
);
727 init(description
, locinfo
, perror
, status
);
730 RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString
& description
,
731 LocalizationInfo
* info
,
732 const Locale
& alocale
, UParseError
& perror
, UErrorCode
& status
)
734 , ruleSetDescriptions(NULL
)
736 , defaultRuleSet(NULL
)
739 , decimalFormatSymbols(NULL
)
740 , defaultInfinityRule(NULL
)
741 , defaultNaNRule(NULL
)
742 , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary
)
744 , lenientParseRules(NULL
)
745 , localizations(NULL
)
746 , capitalizationInfoSet(FALSE
)
747 , capitalizationForUIListMenu(FALSE
)
748 , capitalizationForStandAlone(FALSE
)
749 , capitalizationBrkIter(NULL
)
751 init(description
, info
, perror
, status
);
754 RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString
& description
,
758 , ruleSetDescriptions(NULL
)
760 , defaultRuleSet(NULL
)
761 , locale(Locale::getDefault())
763 , decimalFormatSymbols(NULL
)
764 , defaultInfinityRule(NULL
)
765 , defaultNaNRule(NULL
)
766 , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary
)
768 , lenientParseRules(NULL
)
769 , localizations(NULL
)
770 , capitalizationInfoSet(FALSE
)
771 , capitalizationForUIListMenu(FALSE
)
772 , capitalizationForStandAlone(FALSE
)
773 , capitalizationBrkIter(NULL
)
775 init(description
, NULL
, perror
, status
);
778 RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString
& description
,
779 const Locale
& aLocale
,
783 , ruleSetDescriptions(NULL
)
785 , defaultRuleSet(NULL
)
788 , decimalFormatSymbols(NULL
)
789 , defaultInfinityRule(NULL
)
790 , defaultNaNRule(NULL
)
791 , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary
)
793 , lenientParseRules(NULL
)
794 , localizations(NULL
)
795 , capitalizationInfoSet(FALSE
)
796 , capitalizationForUIListMenu(FALSE
)
797 , capitalizationForStandAlone(FALSE
)
798 , capitalizationBrkIter(NULL
)
800 init(description
, NULL
, perror
, status
);
803 RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag
, const Locale
& alocale
, UErrorCode
& status
)
805 , ruleSetDescriptions(NULL
)
807 , defaultRuleSet(NULL
)
810 , decimalFormatSymbols(NULL
)
811 , defaultInfinityRule(NULL
)
812 , defaultNaNRule(NULL
)
813 , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary
)
815 , lenientParseRules(NULL
)
816 , localizations(NULL
)
817 , capitalizationInfoSet(FALSE
)
818 , capitalizationForUIListMenu(FALSE
)
819 , capitalizationForStandAlone(FALSE
)
820 , capitalizationBrkIter(NULL
)
822 if (U_FAILURE(status
)) {
826 const char* rules_tag
= "RBNFRules";
827 const char* fmt_tag
= "";
829 case URBNF_SPELLOUT
: fmt_tag
= "SpelloutRules"; break;
830 case URBNF_ORDINAL
: fmt_tag
= "OrdinalRules"; break;
831 case URBNF_DURATION
: fmt_tag
= "DurationRules"; break;
832 case URBNF_NUMBERING_SYSTEM
: fmt_tag
= "NumberingSystemRules"; break;
833 default: status
= U_ILLEGAL_ARGUMENT_ERROR
; return;
836 // TODO: read localization info from resource
837 LocalizationInfo
* locinfo
= NULL
;
839 UResourceBundle
* nfrb
= ures_open(U_ICUDATA_RBNF
, locale
.getName(), &status
);
840 if (U_SUCCESS(status
)) {
841 setLocaleIDs(ures_getLocaleByType(nfrb
, ULOC_VALID_LOCALE
, &status
),
842 ures_getLocaleByType(nfrb
, ULOC_ACTUAL_LOCALE
, &status
));
844 UResourceBundle
* rbnfRules
= ures_getByKeyWithFallback(nfrb
, rules_tag
, NULL
, &status
);
845 if (U_FAILURE(status
)) {
848 UResourceBundle
* ruleSets
= ures_getByKeyWithFallback(rbnfRules
, fmt_tag
, NULL
, &status
);
849 if (U_FAILURE(status
)) {
850 ures_close(rbnfRules
);
856 while (ures_hasNext(ruleSets
)) {
857 desc
.append(ures_getNextUnicodeString(ruleSets
,NULL
,&status
));
861 init(desc
, locinfo
, perror
, status
);
863 ures_close(ruleSets
);
864 ures_close(rbnfRules
);
869 RuleBasedNumberFormat::RuleBasedNumberFormat(const RuleBasedNumberFormat
& rhs
)
872 , ruleSetDescriptions(NULL
)
874 , defaultRuleSet(NULL
)
877 , decimalFormatSymbols(NULL
)
878 , defaultInfinityRule(NULL
)
879 , defaultNaNRule(NULL
)
880 , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary
)
882 , lenientParseRules(NULL
)
883 , localizations(NULL
)
884 , capitalizationInfoSet(FALSE
)
885 , capitalizationForUIListMenu(FALSE
)
886 , capitalizationForStandAlone(FALSE
)
887 , capitalizationBrkIter(NULL
)
889 this->operator=(rhs
);
894 RuleBasedNumberFormat
&
895 RuleBasedNumberFormat::operator=(const RuleBasedNumberFormat
& rhs
)
900 NumberFormat::operator=(rhs
);
901 UErrorCode status
= U_ZERO_ERROR
;
904 lenient
= rhs
.lenient
;
907 setDecimalFormatSymbols(*rhs
.getDecimalFormatSymbols());
908 init(rhs
.originalDescription
, rhs
.localizations
? rhs
.localizations
->ref() : NULL
, perror
, status
);
909 setDefaultRuleSet(rhs
.getDefaultRuleSetName(), status
);
910 setRoundingMode(rhs
.getRoundingMode());
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
;
922 RuleBasedNumberFormat::~RuleBasedNumberFormat()
928 RuleBasedNumberFormat::clone(void) const
930 return new RuleBasedNumberFormat(*this);
934 RuleBasedNumberFormat::operator==(const Format
& other
) const
936 if (this == &other
) {
940 if (typeid(*this) == typeid(other
)) {
941 const RuleBasedNumberFormat
& rhs
= (const RuleBasedNumberFormat
&)other
;
942 // test for capitalization info equality is adequately handled
943 // by the NumberFormat test for fCapitalizationContext equality;
944 // the info here is just derived from that.
945 if (locale
== rhs
.locale
&&
946 lenient
== rhs
.lenient
&&
947 (localizations
== NULL
948 ? rhs
.localizations
== NULL
949 : (rhs
.localizations
== NULL
951 : *localizations
== rhs
.localizations
))) {
953 NFRuleSet
** p
= fRuleSets
;
954 NFRuleSet
** q
= rhs
.fRuleSets
;
957 } else if (q
== NULL
) {
960 while (*p
&& *q
&& (**p
== **q
)) {
964 return *q
== NULL
&& *p
== NULL
;
972 RuleBasedNumberFormat::getRules() const
974 UnicodeString result
;
975 if (fRuleSets
!= NULL
) {
976 for (NFRuleSet
** p
= fRuleSets
; *p
; ++p
) {
977 (*p
)->appendRules(result
);
984 RuleBasedNumberFormat::getRuleSetName(int32_t index
) const
987 UnicodeString
string(TRUE
, localizations
->getRuleSetName(index
), (int32_t)-1);
990 else if (fRuleSets
) {
991 UnicodeString result
;
992 for (NFRuleSet
** p
= fRuleSets
; *p
; ++p
) {
994 if (rs
->isPublic()) {
1002 UnicodeString empty
;
1007 RuleBasedNumberFormat::getNumberOfRuleSetNames() const
1010 if (localizations
) {
1011 result
= localizations
->getNumberOfRuleSets();
1013 else if (fRuleSets
) {
1014 for (NFRuleSet
** p
= fRuleSets
; *p
; ++p
) {
1015 if ((**p
).isPublic()) {
1024 RuleBasedNumberFormat::getNumberOfRuleSetDisplayNameLocales(void) const {
1025 if (localizations
) {
1026 return localizations
->getNumberOfDisplayLocales();
1032 RuleBasedNumberFormat::getRuleSetDisplayNameLocale(int32_t index
, UErrorCode
& status
) const {
1033 if (U_FAILURE(status
)) {
1036 if (localizations
&& index
>= 0 && index
< localizations
->getNumberOfDisplayLocales()) {
1037 UnicodeString
name(TRUE
, localizations
->getLocaleName(index
), -1);
1039 int32_t cap
= name
.length() + 1;
1042 bp
= (char *)uprv_malloc(cap
);
1044 status
= U_MEMORY_ALLOCATION_ERROR
;
1048 name
.extract(0, name
.length(), bp
, cap
, UnicodeString::kInvariant
);
1049 Locale
retLocale(bp
);
1055 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1061 RuleBasedNumberFormat::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);
1068 int32_t ix
= localizations
->indexForLocale(localeStr
);
1070 UnicodeString
name(TRUE
, localizations
->getDisplayName(ix
, index
), -1);
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
;
1078 UnicodeString
name(TRUE
, localizations
->getRuleSetName(index
), -1);
1081 UnicodeString bogus
;
1087 RuleBasedNumberFormat::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
);
1093 UnicodeString bogus
;
1099 RuleBasedNumberFormat::findRuleSet(const UnicodeString
& name
, UErrorCode
& status
) const
1101 if (U_SUCCESS(status
) && fRuleSets
) {
1102 for (NFRuleSet
** p
= fRuleSets
; *p
; ++p
) {
1104 if (rs
->isNamed(name
)) {
1108 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1114 RuleBasedNumberFormat::format(const DecimalQuantity
&number
,
1115 UnicodeString
&appendTo
,
1116 FieldPositionIterator
*posIter
,
1117 UErrorCode
&status
) const {
1118 if (U_FAILURE(status
)) {
1121 DecimalQuantity
copy(number
);
1122 if (copy
.fitsInLong()) {
1123 format(number
.toLong(), appendTo
, posIter
, status
);
1126 copy
.roundToMagnitude(0, number::impl::RoundingMode::UNUM_ROUND_HALFEVEN
, status
);
1127 if (copy
.fitsInLong()) {
1128 format(number
.toDouble(), appendTo
, posIter
, status
);
1131 // We're outside of our normal range that this framework can handle.
1132 // The DecimalFormat will provide more accurate results.
1134 // TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J.
1135 LocalPointer
<NumberFormat
> decimalFormat(NumberFormat::createInstance(locale
, UNUM_DECIMAL
, status
), status
);
1136 if (decimalFormat
.isNull()) {
1140 LocalPointer
<DecimalQuantity
> decimalQuantity(new DecimalQuantity(number
), status
);
1141 if (decimalQuantity
.isNull()) {
1144 f
.adoptDecimalQuantity(decimalQuantity
.orphan()); // f now owns decimalQuantity.
1145 decimalFormat
->format(f
, appendTo
, posIter
, status
);
1153 RuleBasedNumberFormat::format(const DecimalQuantity
&number
,
1154 UnicodeString
& appendTo
,
1156 UErrorCode
&status
) const {
1157 if (U_FAILURE(status
)) {
1160 DecimalQuantity
copy(number
);
1161 if (copy
.fitsInLong()) {
1162 format(number
.toLong(), appendTo
, pos
, status
);
1165 copy
.roundToMagnitude(0, number::impl::RoundingMode::UNUM_ROUND_HALFEVEN
, status
);
1166 if (copy
.fitsInLong()) {
1167 format(number
.toDouble(), appendTo
, pos
, status
);
1170 // We're outside of our normal range that this framework can handle.
1171 // The DecimalFormat will provide more accurate results.
1173 // TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J.
1174 LocalPointer
<NumberFormat
> decimalFormat(NumberFormat::createInstance(locale
, UNUM_DECIMAL
, status
), status
);
1175 if (decimalFormat
.isNull()) {
1179 LocalPointer
<DecimalQuantity
> decimalQuantity(new DecimalQuantity(number
), status
);
1180 if (decimalQuantity
.isNull()) {
1183 f
.adoptDecimalQuantity(decimalQuantity
.orphan()); // f now owns decimalQuantity.
1184 decimalFormat
->format(f
, appendTo
, pos
, status
);
1191 RuleBasedNumberFormat::format(int32_t number
,
1192 UnicodeString
& toAppendTo
,
1193 FieldPosition
& pos
) const
1195 return format((int64_t)number
, toAppendTo
, pos
);
1200 RuleBasedNumberFormat::format(int64_t number
,
1201 UnicodeString
& toAppendTo
,
1202 FieldPosition
& /* pos */) const
1204 if (defaultRuleSet
) {
1205 UErrorCode status
= U_ZERO_ERROR
;
1206 format(number
, defaultRuleSet
, toAppendTo
, status
);
1213 RuleBasedNumberFormat::format(double number
,
1214 UnicodeString
& toAppendTo
,
1215 FieldPosition
& /* pos */) const
1217 UErrorCode status
= U_ZERO_ERROR
;
1218 if (defaultRuleSet
) {
1219 format(number
, *defaultRuleSet
, toAppendTo
, status
);
1226 RuleBasedNumberFormat::format(int32_t number
,
1227 const UnicodeString
& ruleSetName
,
1228 UnicodeString
& toAppendTo
,
1230 UErrorCode
& status
) const
1232 return format((int64_t)number
, ruleSetName
, toAppendTo
, pos
, status
);
1237 RuleBasedNumberFormat::format(int64_t number
,
1238 const UnicodeString
& ruleSetName
,
1239 UnicodeString
& toAppendTo
,
1240 FieldPosition
& /* pos */,
1241 UErrorCode
& status
) const
1243 if (U_SUCCESS(status
)) {
1244 if (ruleSetName
.indexOf(gPercentPercent
, 2, 0) == 0) {
1245 // throw new IllegalArgumentException("Can't use internal rule set");
1246 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1248 NFRuleSet
*rs
= findRuleSet(ruleSetName
, status
);
1250 format(number
, rs
, toAppendTo
, status
);
1259 RuleBasedNumberFormat::format(double number
,
1260 const UnicodeString
& ruleSetName
,
1261 UnicodeString
& toAppendTo
,
1262 FieldPosition
& /* pos */,
1263 UErrorCode
& status
) const
1265 if (U_SUCCESS(status
)) {
1266 if (ruleSetName
.indexOf(gPercentPercent
, 2, 0) == 0) {
1267 // throw new IllegalArgumentException("Can't use internal rule set");
1268 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1270 NFRuleSet
*rs
= findRuleSet(ruleSetName
, status
);
1272 format(number
, *rs
, toAppendTo
, status
);
1280 RuleBasedNumberFormat::format(double number
,
1282 UnicodeString
& toAppendTo
,
1283 UErrorCode
& status
) const
1285 int32_t startPos
= toAppendTo
.length();
1286 if (getRoundingMode() != DecimalFormat::ERoundingMode::kRoundUnnecessary
&& !uprv_isNaN(number
) && !uprv_isInfinite(number
)) {
1287 DecimalQuantity digitList
;
1288 digitList
.setToDouble(number
);
1289 digitList
.roundToMagnitude(
1290 -getMaximumFractionDigits(),
1291 static_cast<UNumberFormatRoundingMode
>(getRoundingMode()),
1293 number
= digitList
.toDouble();
1295 rs
.format(number
, toAppendTo
, toAppendTo
.length(), 0, status
);
1296 adjustForCapitalizationContext(startPos
, toAppendTo
, status
);
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
1308 RuleBasedNumberFormat::format(int64_t number
, NFRuleSet
*ruleSet
, UnicodeString
& toAppendTo
, UErrorCode
& status
) const
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
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
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.
1325 // TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J.
1326 NumberFormat
*decimalFormat
= NumberFormat::createInstance(locale
, UNUM_DECIMAL
, status
);
1327 if (decimalFormat
== nullptr) {
1331 FieldPosition
pos(FieldPosition::DONT_CARE
);
1332 DecimalQuantity
*decimalQuantity
= new DecimalQuantity();
1333 if (decimalQuantity
== nullptr) {
1334 status
= U_MEMORY_ALLOCATION_ERROR
;
1335 delete decimalFormat
;
1338 decimalQuantity
->setToLong(number
);
1339 f
.adoptDecimalQuantity(decimalQuantity
); // f now owns decimalQuantity.
1340 decimalFormat
->format(f
, toAppendTo
, pos
, status
);
1341 delete decimalFormat
;
1344 int32_t startPos
= toAppendTo
.length();
1345 ruleSet
->format(number
, toAppendTo
, toAppendTo
.length(), 0, status
);
1346 adjustForCapitalizationContext(startPos
, toAppendTo
, status
);
1353 RuleBasedNumberFormat::adjustForCapitalizationContext(int32_t startPos
,
1354 UnicodeString
& currentResult
,
1355 UErrorCode
& status
) const
1357 #if !UCONFIG_NO_BREAK_ITERATION
1358 UDisplayContext capitalizationContext
= getContext(UDISPCTX_TYPE_CAPITALIZATION
, status
);
1359 if (capitalizationContext
!= UDISPCTX_CAPITALIZATION_NONE
&& startPos
== 0 && currentResult
.length() > 0) {
1360 // capitalize currentResult according to context
1361 UChar32 ch
= currentResult
.char32At(0);
1362 if (u_islower(ch
) && U_SUCCESS(status
) && capitalizationBrkIter
!= NULL
&&
1363 ( capitalizationContext
== UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
||
1364 (capitalizationContext
== UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
&& capitalizationForUIListMenu
) ||
1365 (capitalizationContext
== UDISPCTX_CAPITALIZATION_FOR_STANDALONE
&& capitalizationForStandAlone
)) ) {
1366 // titlecase first word of currentResult, here use sentence iterator unlike current implementations
1367 // in LocaleDisplayNamesImpl::adjustForUsageAndContext and RelativeDateFormat::format
1368 currentResult
.toTitle(capitalizationBrkIter
, locale
, U_TITLECASE_NO_LOWERCASE
| U_TITLECASE_NO_BREAK_ADJUSTMENT
);
1372 return currentResult
;
1377 RuleBasedNumberFormat::parse(const UnicodeString
& text
,
1378 Formattable
& result
,
1379 ParsePosition
& parsePosition
) const
1382 parsePosition
.setErrorIndex(0);
1386 UnicodeString
workingText(text
, parsePosition
.getIndex());
1387 ParsePosition
workingPos(0);
1389 ParsePosition
high_pp(0);
1390 Formattable high_result
;
1392 for (NFRuleSet
** p
= fRuleSets
; *p
; ++p
) {
1394 if (rp
->isPublic() && rp
->isParseable()) {
1395 ParsePosition
working_pp(0);
1396 Formattable working_result
;
1398 rp
->parse(workingText
, working_pp
, kMaxDouble
, 0, working_result
, lenient
);
1399 if (working_pp
.getIndex() > high_pp
.getIndex()) {
1400 high_pp
= working_pp
;
1401 high_result
= working_result
;
1403 if (high_pp
.getIndex() == workingText
.length()) {
1410 int32_t startIndex
= parsePosition
.getIndex();
1411 parsePosition
.setIndex(startIndex
+ high_pp
.getIndex());
1412 if (high_pp
.getIndex() > 0) {
1413 parsePosition
.setErrorIndex(-1);
1415 int32_t errorIndex
= (high_pp
.getErrorIndex()>0)? high_pp
.getErrorIndex(): 0;
1416 parsePosition
.setErrorIndex(startIndex
+ errorIndex
);
1418 result
= high_result
;
1419 if (result
.getType() == Formattable::kDouble
) {
1420 double d
= result
.getDouble();
1421 if (!uprv_isNaN(d
) && d
== uprv_trunc(d
) && INT32_MIN
<= d
&& d
<= INT32_MAX
) {
1422 // Note: casting a double to an int when the double is too large or small
1423 // to fit the destination is undefined behavior. The explicit range checks,
1424 // above, are required. Just casting and checking the result value is undefined.
1425 result
.setLong(static_cast<int32_t>(d
));
1430 #if !UCONFIG_NO_COLLATION
1433 RuleBasedNumberFormat::setLenient(UBool enabled
)
1436 if (!enabled
&& collator
) {
1445 RuleBasedNumberFormat::setDefaultRuleSet(const UnicodeString
& ruleSetName
, UErrorCode
& status
) {
1446 if (U_SUCCESS(status
)) {
1447 if (ruleSetName
.isEmpty()) {
1448 if (localizations
) {
1449 UnicodeString
name(TRUE
, localizations
->getRuleSetName(0), -1);
1450 defaultRuleSet
= findRuleSet(name
, status
);
1452 initDefaultRuleSet();
1454 } else if (ruleSetName
.startsWith(UNICODE_STRING_SIMPLE("%%"))) {
1455 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1457 NFRuleSet
* result
= findRuleSet(ruleSetName
, status
);
1458 if (result
!= NULL
) {
1459 defaultRuleSet
= result
;
1466 RuleBasedNumberFormat::getDefaultRuleSetName() const {
1467 UnicodeString result
;
1468 if (defaultRuleSet
&& defaultRuleSet
->isPublic()) {
1469 defaultRuleSet
->getName(result
);
1471 result
.setToBogus();
1477 RuleBasedNumberFormat::initDefaultRuleSet()
1479 defaultRuleSet
= NULL
;
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"));
1488 NFRuleSet
**p
= &fRuleSets
[0];
1490 if ((*p
)->isNamed(spellout
) || (*p
)->isNamed(ordinal
) || (*p
)->isNamed(duration
)) {
1491 defaultRuleSet
= *p
;
1498 defaultRuleSet
= *--p
;
1499 if (!defaultRuleSet
->isPublic()) {
1500 while (p
!= fRuleSets
) {
1501 if ((*--p
)->isPublic()) {
1502 defaultRuleSet
= *p
;
1511 RuleBasedNumberFormat::init(const UnicodeString
& rules
, LocalizationInfo
* localizationInfos
,
1512 UParseError
& pErr
, UErrorCode
& status
)
1514 // TODO: implement UParseError
1515 uprv_memset(&pErr
, 0, sizeof(UParseError
));
1516 // Note: this can leave ruleSets == NULL, so remaining code should check
1517 if (U_FAILURE(status
)) {
1521 initializeDecimalFormatSymbols(status
);
1522 initializeDefaultInfinityRule(status
);
1523 initializeDefaultNaNRule(status
);
1524 if (U_FAILURE(status
)) {
1528 this->localizations
= localizationInfos
== NULL
? NULL
: localizationInfos
->ref();
1530 UnicodeString
description(rules
);
1531 if (!description
.length()) {
1532 status
= U_MEMORY_ALLOCATION_ERROR
;
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
);
1543 // check to see if there's a set of lenient-parse rules. If there
1544 // is, pull them out into our temporary holding place for them,
1545 // and delete them from the description before the real desciption-
1546 // parsing code sees them
1547 int32_t lp
= description
.indexOf(gLenientParse
, -1, 0);
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
1552 if (lp
== 0 || description
.charAt(lp
- 1) == gSemiColon
) {
1553 // locate the beginning and end of the actual collation
1554 // rules (there may be whitespace between the name and
1555 // the first token in the description)
1556 int lpEnd
= description
.indexOf(gSemiPercent
, 2, lp
);
1559 lpEnd
= description
.length() - 1;
1561 int lpStart
= lp
+ u_strlen(gLenientParse
);
1562 while (PatternProps::isWhiteSpace(description
.charAt(lpStart
))) {
1566 // copy out the lenient-parse rules and delete them
1567 // from the description
1568 lenientParseRules
= new UnicodeString();
1570 if (lenientParseRules
== nullptr) {
1571 status
= U_MEMORY_ALLOCATION_ERROR
;
1574 lenientParseRules
->setTo(description
, lpStart
, lpEnd
- lpStart
);
1576 description
.remove(lp
, lpEnd
+ 1 - lp
);
1580 // pre-flight parsing the description and count the number of
1581 // rule sets (";%" marks the end of one rule set and the beginning
1584 for (int32_t p
= description
.indexOf(gSemiPercent
, 2, 0); p
!= -1; p
= description
.indexOf(gSemiPercent
, 2, p
)) {
1590 // our rule list is an array of the appropriate size
1591 fRuleSets
= (NFRuleSet
**)uprv_malloc((numRuleSets
+ 1) * sizeof(NFRuleSet
*));
1593 if (fRuleSets
== 0) {
1594 status
= U_MEMORY_ALLOCATION_ERROR
;
1598 for (int i
= 0; i
<= numRuleSets
; ++i
) {
1599 fRuleSets
[i
] = NULL
;
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
1610 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1614 ruleSetDescriptions
= new UnicodeString
[numRuleSets
];
1615 if (ruleSetDescriptions
== nullptr) {
1616 status
= U_MEMORY_ALLOCATION_ERROR
;
1623 for (int32_t p
= description
.indexOf(gSemiPercent
, 2, 0); p
!= -1; p
= description
.indexOf(gSemiPercent
, 2, start
)) {
1624 ruleSetDescriptions
[curRuleSet
].setTo(description
, start
, p
+ 1 - start
);
1625 fRuleSets
[curRuleSet
] = new NFRuleSet(this, ruleSetDescriptions
, curRuleSet
, status
);
1626 if (fRuleSets
[curRuleSet
] == nullptr) {
1627 status
= U_MEMORY_ALLOCATION_ERROR
;
1633 ruleSetDescriptions
[curRuleSet
].setTo(description
, start
, description
.length() - start
);
1634 fRuleSets
[curRuleSet
] = new NFRuleSet(this, ruleSetDescriptions
, curRuleSet
, status
);
1635 if (fRuleSets
[curRuleSet
] == nullptr) {
1636 status
= U_MEMORY_ALLOCATION_ERROR
;
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)
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();
1652 // finally, we can go back through the temporary descriptions
1653 // list and finish setting up the substructure (and we throw
1654 // away the temporary descriptions as we go)
1656 for (int i
= 0; i
< numRuleSets
; i
++) {
1657 fRuleSets
[i
]->parseRules(ruleSetDescriptions
[i
], status
);
1661 // Now that the rules are initialized, the 'real' default rule
1662 // set can be adjusted by the localization data.
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.
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
);
1678 defaultRuleSet
= rs
;
1682 defaultRuleSet
= getDefaultRuleSet();
1684 originalDescription
= rules
;
1687 // override the NumberFormat implementation in order to
1688 // lazily initialize relevant items
1690 RuleBasedNumberFormat::setContext(UDisplayContext value
, UErrorCode
& status
)
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
;
1699 #if !UCONFIG_NO_BREAK_ITERATION
1700 if ( capitalizationBrkIter
== NULL
&& (value
==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
||
1701 (value
==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
&& capitalizationForUIListMenu
) ||
1702 (value
==UDISPCTX_CAPITALIZATION_FOR_STANDALONE
&& capitalizationForStandAlone
)) ) {
1703 status
= U_ZERO_ERROR
;
1704 capitalizationBrkIter
= BreakIterator::createSentenceInstance(locale
, status
);
1705 if (U_FAILURE(status
)) {
1706 delete capitalizationBrkIter
;
1707 capitalizationBrkIter
= NULL
;
1715 RuleBasedNumberFormat::initCapitalizationContextInfo(const Locale
& thelocale
)
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
) {
1725 const int32_t * intVector
= ures_getIntVector(rb
, &len
, &status
);
1726 if (U_SUCCESS(status
) && intVector
!= NULL
&& len
>= 2) {
1727 capitalizationForUIListMenu
= static_cast<UBool
>(intVector
[0]);
1728 capitalizationForStandAlone
= static_cast<UBool
>(intVector
[1]);
1736 RuleBasedNumberFormat::stripWhitespace(UnicodeString
& description
)
1738 // iterate through the characters...
1739 UnicodeString result
;
1742 while (start
!= -1 && start
< description
.length()) {
1743 // seek to the first non-whitespace character...
1744 while (start
< description
.length()
1745 && PatternProps::isWhiteSpace(description
.charAt(start
))) {
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
);
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
);
1758 else if (p
< description
.length()) {
1759 result
.append(description
, start
, p
+ 1 - start
);
1763 // when we get here, we've seeked off the end of the string, and
1764 // we terminate the loop (we continue until *start* is -1 rather
1765 // than until *p* is -1, because otherwise we'd miss the last
1766 // rule in the description)
1772 description
.setTo(result
);
1777 RuleBasedNumberFormat::dispose()
1780 for (NFRuleSet
** p
= fRuleSets
; *p
; ++p
) {
1783 uprv_free(fRuleSets
);
1787 if (ruleSetDescriptions
) {
1788 delete [] ruleSetDescriptions
;
1789 ruleSetDescriptions
= NULL
;
1792 #if !UCONFIG_NO_COLLATION
1797 delete decimalFormatSymbols
;
1798 decimalFormatSymbols
= NULL
;
1800 delete defaultInfinityRule
;
1801 defaultInfinityRule
= NULL
;
1803 delete defaultNaNRule
;
1804 defaultNaNRule
= NULL
;
1806 delete lenientParseRules
;
1807 lenientParseRules
= NULL
;
1809 #if !UCONFIG_NO_BREAK_ITERATION
1810 delete capitalizationBrkIter
;
1811 capitalizationBrkIter
= NULL
;
1814 if (localizations
) {
1815 localizations
= localizations
->unref();
1820 //-----------------------------------------------------------------------
1821 // package-internal API
1822 //-----------------------------------------------------------------------
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
1830 const RuleBasedCollator
*
1831 RuleBasedNumberFormat::getCollator() const
1833 #if !UCONFIG_NO_COLLATION
1838 // lazy-evaluate the collator
1839 if (collator
== NULL
&& lenient
) {
1840 // create a default collator based on the formatter's locale,
1841 // then pull out that collator's rules, append any additional
1842 // rules specified in the description, and create a _new_
1843 // collator based on the combination of those rules
1845 UErrorCode status
= U_ZERO_ERROR
;
1847 Collator
* temp
= Collator::createInstance(locale
, status
);
1848 RuleBasedCollator
* newCollator
;
1849 if (U_SUCCESS(status
) && (newCollator
= dynamic_cast<RuleBasedCollator
*>(temp
)) != NULL
) {
1850 if (lenientParseRules
) {
1851 UnicodeString
rules(newCollator
->getRules());
1852 rules
.append(*lenientParseRules
);
1854 newCollator
= new RuleBasedCollator(rules
, status
);
1855 // Exit if newCollator could not be created.
1856 if (newCollator
== NULL
) {
1862 if (U_SUCCESS(status
)) {
1863 newCollator
->setAttribute(UCOL_DECOMPOSITION_MODE
, UCOL_ON
, status
);
1865 ((RuleBasedNumberFormat
*)this)->collator
= newCollator
;
1874 // if lenient-parse mode is off, this will be null
1875 // (see setLenientParseMode())
1880 DecimalFormatSymbols
*
1881 RuleBasedNumberFormat::initializeDecimalFormatSymbols(UErrorCode
&status
)
1883 // lazy-evaluate the DecimalFormatSymbols object. This object
1884 // is shared by all DecimalFormat instances belonging to this
1886 if (decimalFormatSymbols
== nullptr) {
1887 LocalPointer
<DecimalFormatSymbols
> temp(new DecimalFormatSymbols(locale
, status
), status
);
1888 if (U_SUCCESS(status
)) {
1889 decimalFormatSymbols
= temp
.orphan();
1892 return decimalFormatSymbols
;
1896 * Returns the DecimalFormatSymbols object that should be used by all DecimalFormat
1897 * instances owned by this formatter.
1899 const DecimalFormatSymbols
*
1900 RuleBasedNumberFormat::getDecimalFormatSymbols() const
1902 return decimalFormatSymbols
;
1906 RuleBasedNumberFormat::initializeDefaultInfinityRule(UErrorCode
&status
)
1908 if (U_FAILURE(status
)) {
1911 if (defaultInfinityRule
== NULL
) {
1912 UnicodeString
rule(UNICODE_STRING_SIMPLE("Inf: "));
1913 rule
.append(getDecimalFormatSymbols()->getSymbol(DecimalFormatSymbols::kInfinitySymbol
));
1914 LocalPointer
<NFRule
> temp(new NFRule(this, rule
, status
), status
);
1915 if (U_SUCCESS(status
)) {
1916 defaultInfinityRule
= temp
.orphan();
1919 return defaultInfinityRule
;
1923 RuleBasedNumberFormat::getDefaultInfinityRule() const
1925 return defaultInfinityRule
;
1929 RuleBasedNumberFormat::initializeDefaultNaNRule(UErrorCode
&status
)
1931 if (U_FAILURE(status
)) {
1934 if (defaultNaNRule
== nullptr) {
1935 UnicodeString
rule(UNICODE_STRING_SIMPLE("NaN: "));
1936 rule
.append(getDecimalFormatSymbols()->getSymbol(DecimalFormatSymbols::kNaNSymbol
));
1937 LocalPointer
<NFRule
> temp(new NFRule(this, rule
, status
), status
);
1938 if (U_SUCCESS(status
)) {
1939 defaultNaNRule
= temp
.orphan();
1942 return defaultNaNRule
;
1946 RuleBasedNumberFormat::getDefaultNaNRule() const
1948 return defaultNaNRule
;
1951 // De-owning the current localized symbols and adopt the new symbols.
1953 RuleBasedNumberFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols
* symbolsToAdopt
)
1955 if (symbolsToAdopt
== NULL
) {
1956 return; // do not allow caller to set decimalFormatSymbols to NULL
1959 if (decimalFormatSymbols
!= NULL
) {
1960 delete decimalFormatSymbols
;
1963 decimalFormatSymbols
= symbolsToAdopt
;
1966 // Apply the new decimalFormatSymbols by reparsing the rulesets
1967 UErrorCode status
= U_ZERO_ERROR
;
1969 delete defaultInfinityRule
;
1970 defaultInfinityRule
= NULL
;
1971 initializeDefaultInfinityRule(status
); // Reset with the new DecimalFormatSymbols
1973 delete defaultNaNRule
;
1974 defaultNaNRule
= NULL
;
1975 initializeDefaultNaNRule(status
); // Reset with the new DecimalFormatSymbols
1978 for (int32_t i
= 0; i
< numRuleSets
; i
++) {
1979 fRuleSets
[i
]->setDecimalFormatSymbols(*symbolsToAdopt
, status
);
1985 // Setting the symbols is equivalent to adopting a newly created localized symbols.
1987 RuleBasedNumberFormat::setDecimalFormatSymbols(const DecimalFormatSymbols
& symbols
)
1989 adoptDecimalFormatSymbols(new DecimalFormatSymbols(symbols
));
1993 RuleBasedNumberFormat::createPluralFormat(UPluralType pluralType
,
1994 const UnicodeString
&pattern
,
1995 UErrorCode
& status
) const
1997 auto *pf
= new PluralFormat(locale
, pluralType
, pattern
, status
);
1998 if (pf
== nullptr) {
1999 status
= U_MEMORY_ALLOCATION_ERROR
;
2005 * Get the rounding mode.
2006 * @return A rounding mode
2008 DecimalFormat::ERoundingMode
RuleBasedNumberFormat::getRoundingMode() const {
2009 return fRoundingMode
;
2013 * Set the rounding mode. This has no effect unless the rounding
2014 * increment is greater than zero.
2015 * @param roundingMode A rounding mode
2017 void RuleBasedNumberFormat::setRoundingMode(DecimalFormat::ERoundingMode roundingMode
) {
2018 fRoundingMode
= roundingMode
;