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"
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 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedNumberFormat
)
74 This is a utility class. It does not use ICU's RTTI.
75 If ICU's RTTI is needed again, you can uncomment the RTTI code and derive from UObject.
76 Please make sure that intltest passes on Windows in Release mode,
77 since the string pooling per compilation unit will mess up how RTTI works.
78 The RTTI code was also removed due to lack of code coverage.
80 class LocalizationInfo
: public UMemory
{
82 virtual ~LocalizationInfo();
86 LocalizationInfo() : refcount(0) {}
88 LocalizationInfo
* ref(void) {
93 LocalizationInfo
* unref(void) {
94 if (refcount
&& --refcount
== 0) {
100 virtual UBool
operator==(const LocalizationInfo
* rhs
) const;
101 inline UBool
operator!=(const LocalizationInfo
* rhs
) const { return !operator==(rhs
); }
103 virtual int32_t getNumberOfRuleSets(void) const = 0;
104 virtual const UChar
* getRuleSetName(int32_t index
) const = 0;
105 virtual int32_t getNumberOfDisplayLocales(void) const = 0;
106 virtual const UChar
* getLocaleName(int32_t index
) const = 0;
107 virtual const UChar
* getDisplayName(int32_t localeIndex
, int32_t ruleIndex
) const = 0;
109 virtual int32_t indexForLocale(const UChar
* locale
) const;
110 virtual int32_t indexForRuleSet(const UChar
* ruleset
) const;
112 // virtual UClassID getDynamicClassID() const = 0;
113 // static UClassID getStaticClassID(void);
116 LocalizationInfo::~LocalizationInfo() {}
118 //UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(LocalizationInfo)
120 // if both strings are NULL, this returns TRUE
122 streq(const UChar
* lhs
, const UChar
* rhs
) {
127 return u_strcmp(lhs
, rhs
) == 0;
133 LocalizationInfo::operator==(const LocalizationInfo
* rhs
) const {
139 int32_t rsc
= getNumberOfRuleSets();
140 if (rsc
== rhs
->getNumberOfRuleSets()) {
141 for (int i
= 0; i
< rsc
; ++i
) {
142 if (!streq(getRuleSetName(i
), rhs
->getRuleSetName(i
))) {
146 int32_t dlc
= getNumberOfDisplayLocales();
147 if (dlc
== rhs
->getNumberOfDisplayLocales()) {
148 for (int i
= 0; i
< dlc
; ++i
) {
149 const UChar
* locale
= getLocaleName(i
);
150 int32_t ix
= rhs
->indexForLocale(locale
);
151 // if no locale, ix is -1, getLocaleName returns null, so streq returns false
152 if (!streq(locale
, rhs
->getLocaleName(ix
))) {
155 for (int j
= 0; j
< rsc
; ++j
) {
156 if (!streq(getDisplayName(i
, j
), rhs
->getDisplayName(ix
, j
))) {
169 LocalizationInfo::indexForLocale(const UChar
* locale
) const {
170 for (int i
= 0; i
< getNumberOfDisplayLocales(); ++i
) {
171 if (streq(locale
, getLocaleName(i
))) {
179 LocalizationInfo::indexForRuleSet(const UChar
* ruleset
) const {
181 for (int i
= 0; i
< getNumberOfRuleSets(); ++i
) {
182 if (streq(ruleset
, getRuleSetName(i
))) {
191 typedef void (*Fn_Deleter
)(void*);
199 VArray() : buf(NULL
), cap(0), size(0), deleter(NULL
) {}
201 VArray(Fn_Deleter del
) : buf(NULL
), cap(0), size(0), deleter(del
) {}
205 for (int i
= 0; i
< size
; ++i
) {
216 void add(void* elem
, UErrorCode
& status
) {
217 if (U_SUCCESS(status
)) {
221 } else if (cap
< 256) {
227 buf
= (void**)uprv_malloc(cap
* sizeof(void*));
229 buf
= (void**)uprv_realloc(buf
, cap
* sizeof(void*));
232 // if we couldn't realloc, we leak the memory we've already allocated, but we're in deep trouble anyway
233 status
= U_MEMORY_ALLOCATION_ERROR
;
236 void* start
= &buf
[size
];
237 size_t count
= (cap
- size
) * sizeof(void*);
238 uprv_memset(start
, 0, count
); // fill with nulls, just because
244 void** release(void) {
255 class StringLocalizationInfo
: public LocalizationInfo
{
261 friend class LocDataParser
;
263 StringLocalizationInfo(UChar
* i
, UChar
*** d
, int32_t numRS
, int32_t numLocs
)
264 : info(i
), data(d
), numRuleSets(numRS
), numLocales(numLocs
)
269 static StringLocalizationInfo
* create(const UnicodeString
& info
, UParseError
& perror
, UErrorCode
& status
);
271 virtual ~StringLocalizationInfo();
272 virtual int32_t getNumberOfRuleSets(void) const { return numRuleSets
; }
273 virtual const UChar
* getRuleSetName(int32_t index
) const;
274 virtual int32_t getNumberOfDisplayLocales(void) const { return numLocales
; }
275 virtual const UChar
* getLocaleName(int32_t index
) const;
276 virtual const UChar
* getDisplayName(int32_t localeIndex
, int32_t ruleIndex
) const;
278 // virtual UClassID getDynamicClassID() const;
279 // static UClassID getStaticClassID(void);
282 void init(UErrorCode
& status
) const;
287 OPEN_ANGLE
= 0x003c, /* '<' */
288 CLOSE_ANGLE
= 0x003e, /* '>' */
296 * Utility for parsing a localization string and returning a StringLocalizationInfo*.
298 class LocDataParser
{
307 LocDataParser(UParseError
& parseError
, UErrorCode
& status
)
308 : data(NULL
), e(NULL
), p(NULL
), ch(0xffff), pe(parseError
), ec(status
) {}
312 * On a successful parse, return a StringLocalizationInfo*, otherwise delete locData, set perror and status,
313 * and return NULL. The StringLocalizationInfo will adopt locData if it is created.
315 StringLocalizationInfo
* parse(UChar
* data
, int32_t len
);
319 inline void inc(void) {
323 inline UBool
checkInc(UChar c
) {
324 if (p
< e
&& (ch
== c
|| *p
== c
)) {
330 inline UBool
check(UChar c
) {
331 return p
< e
&& (ch
== c
|| *p
== c
);
333 inline void skipWhitespace(void) {
334 while (p
< e
&& PatternProps::isWhiteSpace(ch
!= 0xffff ? ch
: *p
)) {
338 inline UBool
inList(UChar c
, const UChar
* list
) const {
339 if (*list
== SPACE
&& PatternProps::isWhiteSpace(c
)) {
342 while (*list
&& *list
!= c
) {
347 void parseError(const char* msg
);
349 StringLocalizationInfo
* doParse(void);
351 UChar
** nextArray(int32_t& requiredLength
);
352 UChar
* nextString(void);
356 #define ERROR(msg) parseError(msg); return NULL;
357 #define EXPLANATION_ARG explanationArg
359 #define ERROR(msg) parseError(NULL); return NULL;
360 #define EXPLANATION_ARG
364 static const UChar DQUOTE_STOPLIST
[] = {
368 static const UChar SQUOTE_STOPLIST
[] = {
372 static const UChar NOQUOTE_STOPLIST
[] = {
373 SPACE
, COMMA
, CLOSE_ANGLE
, OPEN_ANGLE
, TICK
, QUOTE
, 0
381 StringLocalizationInfo
*
382 LocDataParser::parse(UChar
* _data
, int32_t len
) {
384 if (_data
) uprv_free(_data
);
390 pe
.postContext
[0] = 0;
391 pe
.preContext
[0] = 0;
394 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
399 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
413 StringLocalizationInfo
*
414 LocDataParser::doParse(void) {
416 if (!checkInc(OPEN_ANGLE
)) {
417 ERROR("Missing open angle");
419 VArray
array(DeleteFn
);
420 UBool mightHaveNext
= TRUE
;
421 int32_t requiredLength
= -1;
422 while (mightHaveNext
) {
423 mightHaveNext
= FALSE
;
424 UChar
** elem
= nextArray(requiredLength
);
426 UBool haveComma
= check(COMMA
);
431 mightHaveNext
= TRUE
;
433 } else if (haveComma
) {
434 ERROR("Unexpected character");
439 if (!checkInc(CLOSE_ANGLE
)) {
440 if (check(OPEN_ANGLE
)) {
441 ERROR("Missing comma in outer array");
443 ERROR("Missing close angle bracket in outer array");
449 ERROR("Extra text after close of localization data");
454 int32_t numLocs
= array
.length() - 2; // subtract first, NULL
455 UChar
*** result
= (UChar
***)array
.release();
457 return new StringLocalizationInfo(data
, result
, requiredLength
-2, numLocs
); // subtract first, NULL
461 ERROR("Unknown error");
465 LocDataParser::nextArray(int32_t& requiredLength
) {
471 if (!checkInc(OPEN_ANGLE
)) {
472 ERROR("Missing open angle");
476 UBool mightHaveNext
= TRUE
;
477 while (mightHaveNext
) {
478 mightHaveNext
= FALSE
;
479 UChar
* elem
= nextString();
481 UBool haveComma
= check(COMMA
);
486 mightHaveNext
= TRUE
;
488 } else if (haveComma
) {
489 ERROR("Unexpected comma");
493 if (!checkInc(CLOSE_ANGLE
)) {
494 if (check(OPEN_ANGLE
)) {
495 ERROR("Missing close angle bracket in inner array");
497 ERROR("Missing comma in inner array");
503 if (requiredLength
== -1) {
504 requiredLength
= array
.length() + 1;
505 } else if (array
.length() != requiredLength
) {
506 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
507 ERROR("Array not of required length");
510 return (UChar
**)array
.release();
512 ERROR("Unknown Error");
516 LocDataParser::nextString() {
517 UChar
* result
= NULL
;
521 const UChar
* terminators
;
523 UBool haveQuote
= c
== QUOTE
|| c
== TICK
;
526 terminators
= c
== QUOTE
? DQUOTE_STOPLIST
: SQUOTE_STOPLIST
;
528 terminators
= NOQUOTE_STOPLIST
;
531 while (p
< e
&& !inList(*p
, terminators
)) ++p
;
533 ERROR("Unexpected end of data");
539 *p
= 0x0; // terminate by writing to data
540 result
= start
; // just point into data
544 ERROR("Missing matching quote");
545 } else if (p
== start
) {
546 ERROR("Empty string");
549 } else if (x
== OPEN_ANGLE
|| x
== TICK
|| x
== QUOTE
) {
550 ERROR("Unexpected character in string");
554 // ok for there to be no next string
558 void LocDataParser::parseError(const char* EXPLANATION_ARG
)
564 const UChar
* start
= p
- U_PARSE_CONTEXT_LEN
- 1;
568 for (UChar
* x
= p
; --x
>= start
;) {
574 const UChar
* limit
= p
+ U_PARSE_CONTEXT_LEN
- 1;
578 u_strncpy(pe
.preContext
, start
, (int32_t)(p
-start
));
579 pe
.preContext
[p
-start
] = 0;
580 u_strncpy(pe
.postContext
, p
, (int32_t)(limit
-p
));
581 pe
.postContext
[limit
-p
] = 0;
582 pe
.offset
= (int32_t)(p
- data
);
585 fprintf(stderr
, "%s at or near character %ld: ", EXPLANATION_ARG
, p
-data
);
588 msg
.append(start
, p
- start
);
589 msg
.append((UChar
)0x002f); /* SOLIDUS/SLASH */
590 msg
.append(p
, limit
-p
);
591 msg
.append(UNICODE_STRING_SIMPLE("'"));
594 int32_t len
= msg
.extract(0, msg
.length(), buf
, 128);
600 fprintf(stderr
, "%s\n", buf
);
614 //UOBJECT_DEFINE_RTTI_IMPLEMENTATION(StringLocalizationInfo)
616 StringLocalizationInfo
*
617 StringLocalizationInfo::create(const UnicodeString
& info
, UParseError
& perror
, UErrorCode
& status
) {
618 if (U_FAILURE(status
)) {
622 int32_t len
= info
.length();
624 return NULL
; // no error;
627 UChar
* p
= (UChar
*)uprv_malloc(len
* sizeof(UChar
));
629 status
= U_MEMORY_ALLOCATION_ERROR
;
632 info
.extract(p
, len
, status
);
633 if (!U_FAILURE(status
)) {
634 status
= U_ZERO_ERROR
; // clear warning about non-termination
637 LocDataParser
parser(perror
, status
);
638 return parser
.parse(p
, len
);
641 StringLocalizationInfo::~StringLocalizationInfo() {
642 for (UChar
*** p
= (UChar
***)data
; *p
; ++p
) {
643 // remaining data is simply pointer into our unicode string data.
644 if (*p
) uprv_free(*p
);
646 if (data
) uprv_free(data
);
647 if (info
) uprv_free(info
);
652 StringLocalizationInfo::getRuleSetName(int32_t index
) const {
653 if (index
>= 0 && index
< getNumberOfRuleSets()) {
654 return data
[0][index
];
660 StringLocalizationInfo::getLocaleName(int32_t index
) const {
661 if (index
>= 0 && index
< getNumberOfDisplayLocales()) {
662 return data
[index
+1][0];
668 StringLocalizationInfo::getDisplayName(int32_t localeIndex
, int32_t ruleIndex
) const {
669 if (localeIndex
>= 0 && localeIndex
< getNumberOfDisplayLocales() &&
670 ruleIndex
>= 0 && ruleIndex
< getNumberOfRuleSets()) {
671 return data
[localeIndex
+1][ruleIndex
+1];
678 RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString
& description
,
679 const UnicodeString
& locs
,
680 const Locale
& alocale
, UParseError
& perror
, UErrorCode
& status
)
682 , ruleSetDescriptions(NULL
)
684 , defaultRuleSet(NULL
)
687 , decimalFormatSymbols(NULL
)
688 , defaultInfinityRule(NULL
)
689 , defaultNaNRule(NULL
)
690 , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary
)
692 , lenientParseRules(NULL
)
693 , localizations(NULL
)
694 , capitalizationInfoSet(FALSE
)
695 , capitalizationForUIListMenu(FALSE
)
696 , capitalizationForStandAlone(FALSE
)
697 , capitalizationBrkIter(NULL
)
699 LocalizationInfo
* locinfo
= StringLocalizationInfo::create(locs
, perror
, status
);
700 init(description
, locinfo
, perror
, status
);
703 RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString
& description
,
704 const UnicodeString
& locs
,
705 UParseError
& perror
, UErrorCode
& status
)
707 , ruleSetDescriptions(NULL
)
709 , defaultRuleSet(NULL
)
710 , locale(Locale::getDefault())
712 , decimalFormatSymbols(NULL
)
713 , defaultInfinityRule(NULL
)
714 , defaultNaNRule(NULL
)
715 , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary
)
717 , lenientParseRules(NULL
)
718 , localizations(NULL
)
719 , capitalizationInfoSet(FALSE
)
720 , capitalizationForUIListMenu(FALSE
)
721 , capitalizationForStandAlone(FALSE
)
722 , capitalizationBrkIter(NULL
)
724 LocalizationInfo
* locinfo
= StringLocalizationInfo::create(locs
, perror
, status
);
725 init(description
, locinfo
, perror
, status
);
728 RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString
& description
,
729 LocalizationInfo
* info
,
730 const Locale
& alocale
, UParseError
& perror
, UErrorCode
& status
)
732 , ruleSetDescriptions(NULL
)
734 , defaultRuleSet(NULL
)
737 , decimalFormatSymbols(NULL
)
738 , defaultInfinityRule(NULL
)
739 , defaultNaNRule(NULL
)
740 , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary
)
742 , lenientParseRules(NULL
)
743 , localizations(NULL
)
744 , capitalizationInfoSet(FALSE
)
745 , capitalizationForUIListMenu(FALSE
)
746 , capitalizationForStandAlone(FALSE
)
747 , capitalizationBrkIter(NULL
)
749 init(description
, info
, perror
, status
);
752 RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString
& description
,
756 , ruleSetDescriptions(NULL
)
758 , defaultRuleSet(NULL
)
759 , locale(Locale::getDefault())
761 , decimalFormatSymbols(NULL
)
762 , defaultInfinityRule(NULL
)
763 , defaultNaNRule(NULL
)
764 , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary
)
766 , lenientParseRules(NULL
)
767 , localizations(NULL
)
768 , capitalizationInfoSet(FALSE
)
769 , capitalizationForUIListMenu(FALSE
)
770 , capitalizationForStandAlone(FALSE
)
771 , capitalizationBrkIter(NULL
)
773 init(description
, NULL
, perror
, status
);
776 RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString
& description
,
777 const Locale
& aLocale
,
781 , ruleSetDescriptions(NULL
)
783 , defaultRuleSet(NULL
)
786 , decimalFormatSymbols(NULL
)
787 , defaultInfinityRule(NULL
)
788 , defaultNaNRule(NULL
)
789 , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary
)
791 , lenientParseRules(NULL
)
792 , localizations(NULL
)
793 , capitalizationInfoSet(FALSE
)
794 , capitalizationForUIListMenu(FALSE
)
795 , capitalizationForStandAlone(FALSE
)
796 , capitalizationBrkIter(NULL
)
798 init(description
, NULL
, perror
, status
);
801 RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag
, const Locale
& alocale
, UErrorCode
& status
)
803 , ruleSetDescriptions(NULL
)
805 , defaultRuleSet(NULL
)
808 , decimalFormatSymbols(NULL
)
809 , defaultInfinityRule(NULL
)
810 , defaultNaNRule(NULL
)
811 , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary
)
813 , lenientParseRules(NULL
)
814 , localizations(NULL
)
815 , capitalizationInfoSet(FALSE
)
816 , capitalizationForUIListMenu(FALSE
)
817 , capitalizationForStandAlone(FALSE
)
818 , capitalizationBrkIter(NULL
)
820 if (U_FAILURE(status
)) {
824 const char* rules_tag
= "RBNFRules";
825 const char* fmt_tag
= "";
827 case URBNF_SPELLOUT
: fmt_tag
= "SpelloutRules"; break;
828 case URBNF_ORDINAL
: fmt_tag
= "OrdinalRules"; break;
829 case URBNF_DURATION
: fmt_tag
= "DurationRules"; break;
830 case URBNF_NUMBERING_SYSTEM
: fmt_tag
= "NumberingSystemRules"; break;
831 default: status
= U_ILLEGAL_ARGUMENT_ERROR
; return;
834 // TODO: read localization info from resource
835 LocalizationInfo
* locinfo
= NULL
;
837 UResourceBundle
* nfrb
= ures_open(U_ICUDATA_RBNF
, locale
.getName(), &status
);
838 if (U_SUCCESS(status
)) {
839 setLocaleIDs(ures_getLocaleByType(nfrb
, ULOC_VALID_LOCALE
, &status
),
840 ures_getLocaleByType(nfrb
, ULOC_ACTUAL_LOCALE
, &status
));
842 UResourceBundle
* rbnfRules
= ures_getByKeyWithFallback(nfrb
, rules_tag
, NULL
, &status
);
843 if (U_FAILURE(status
)) {
846 UResourceBundle
* ruleSets
= ures_getByKeyWithFallback(rbnfRules
, fmt_tag
, NULL
, &status
);
847 if (U_FAILURE(status
)) {
848 ures_close(rbnfRules
);
854 while (ures_hasNext(ruleSets
)) {
855 desc
.append(ures_getNextUnicodeString(ruleSets
,NULL
,&status
));
859 init(desc
, locinfo
, perror
, status
);
861 ures_close(ruleSets
);
862 ures_close(rbnfRules
);
867 RuleBasedNumberFormat::RuleBasedNumberFormat(const RuleBasedNumberFormat
& rhs
)
870 , ruleSetDescriptions(NULL
)
872 , defaultRuleSet(NULL
)
875 , decimalFormatSymbols(NULL
)
876 , defaultInfinityRule(NULL
)
877 , defaultNaNRule(NULL
)
878 , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary
)
880 , lenientParseRules(NULL
)
881 , localizations(NULL
)
882 , capitalizationInfoSet(FALSE
)
883 , capitalizationForUIListMenu(FALSE
)
884 , capitalizationForStandAlone(FALSE
)
885 , capitalizationBrkIter(NULL
)
887 this->operator=(rhs
);
892 RuleBasedNumberFormat
&
893 RuleBasedNumberFormat::operator=(const RuleBasedNumberFormat
& rhs
)
898 NumberFormat::operator=(rhs
);
899 UErrorCode status
= U_ZERO_ERROR
;
902 lenient
= rhs
.lenient
;
905 setDecimalFormatSymbols(*rhs
.getDecimalFormatSymbols());
906 init(rhs
.originalDescription
, rhs
.localizations
? rhs
.localizations
->ref() : NULL
, perror
, status
);
907 setDefaultRuleSet(rhs
.getDefaultRuleSetName(), status
);
908 setRoundingMode(rhs
.getRoundingMode());
910 capitalizationInfoSet
= rhs
.capitalizationInfoSet
;
911 capitalizationForUIListMenu
= rhs
.capitalizationForUIListMenu
;
912 capitalizationForStandAlone
= rhs
.capitalizationForStandAlone
;
913 #if !UCONFIG_NO_BREAK_ITERATION
914 capitalizationBrkIter
= (rhs
.capitalizationBrkIter
!=NULL
)? rhs
.capitalizationBrkIter
->clone(): NULL
;
920 RuleBasedNumberFormat::~RuleBasedNumberFormat()
926 RuleBasedNumberFormat::clone(void) const
928 return new RuleBasedNumberFormat(*this);
932 RuleBasedNumberFormat::operator==(const Format
& other
) const
934 if (this == &other
) {
938 if (typeid(*this) == typeid(other
)) {
939 const RuleBasedNumberFormat
& rhs
= (const RuleBasedNumberFormat
&)other
;
940 // test for capitalization info equality is adequately handled
941 // by the NumberFormat test for fCapitalizationContext equality;
942 // the info here is just derived from that.
943 if (locale
== rhs
.locale
&&
944 lenient
== rhs
.lenient
&&
945 (localizations
== NULL
946 ? rhs
.localizations
== NULL
947 : (rhs
.localizations
== NULL
949 : *localizations
== rhs
.localizations
))) {
951 NFRuleSet
** p
= ruleSets
;
952 NFRuleSet
** q
= rhs
.ruleSets
;
955 } else if (q
== NULL
) {
958 while (*p
&& *q
&& (**p
== **q
)) {
962 return *q
== NULL
&& *p
== NULL
;
970 RuleBasedNumberFormat::getRules() const
972 UnicodeString result
;
973 if (ruleSets
!= NULL
) {
974 for (NFRuleSet
** p
= ruleSets
; *p
; ++p
) {
975 (*p
)->appendRules(result
);
982 RuleBasedNumberFormat::getRuleSetName(int32_t index
) const
985 UnicodeString
string(TRUE
, localizations
->getRuleSetName(index
), (int32_t)-1);
989 UnicodeString result
;
990 for (NFRuleSet
** p
= ruleSets
; *p
; ++p
) {
992 if (rs
->isPublic()) {
1000 UnicodeString empty
;
1005 RuleBasedNumberFormat::getNumberOfRuleSetNames() const
1008 if (localizations
) {
1009 result
= localizations
->getNumberOfRuleSets();
1011 else if (ruleSets
) {
1012 for (NFRuleSet
** p
= ruleSets
; *p
; ++p
) {
1013 if ((**p
).isPublic()) {
1022 RuleBasedNumberFormat::getNumberOfRuleSetDisplayNameLocales(void) const {
1023 if (localizations
) {
1024 return localizations
->getNumberOfDisplayLocales();
1030 RuleBasedNumberFormat::getRuleSetDisplayNameLocale(int32_t index
, UErrorCode
& status
) const {
1031 if (U_FAILURE(status
)) {
1034 if (localizations
&& index
>= 0 && index
< localizations
->getNumberOfDisplayLocales()) {
1035 UnicodeString
name(TRUE
, localizations
->getLocaleName(index
), -1);
1037 int32_t cap
= name
.length() + 1;
1040 bp
= (char *)uprv_malloc(cap
);
1042 status
= U_MEMORY_ALLOCATION_ERROR
;
1046 name
.extract(0, name
.length(), bp
, cap
, UnicodeString::kInvariant
);
1047 Locale
retLocale(bp
);
1053 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1059 RuleBasedNumberFormat::getRuleSetDisplayName(int32_t index
, const Locale
& localeParam
) {
1060 if (localizations
&& index
>= 0 && index
< localizations
->getNumberOfRuleSets()) {
1061 UnicodeString
localeName(localeParam
.getBaseName(), -1, UnicodeString::kInvariant
);
1062 int32_t len
= localeName
.length();
1063 UChar
* localeStr
= localeName
.getBuffer(len
+ 1);
1066 int32_t ix
= localizations
->indexForLocale(localeStr
);
1068 UnicodeString
name(TRUE
, localizations
->getDisplayName(ix
, index
), -1);
1072 // trim trailing portion, skipping over ommitted sections
1073 do { --len
;} while (len
> 0 && localeStr
[len
] != 0x005f); // underscore
1074 while (len
> 0 && localeStr
[len
-1] == 0x005F) --len
;
1076 UnicodeString
name(TRUE
, localizations
->getRuleSetName(index
), -1);
1079 UnicodeString bogus
;
1085 RuleBasedNumberFormat::getRuleSetDisplayName(const UnicodeString
& ruleSetName
, const Locale
& localeParam
) {
1086 if (localizations
) {
1087 UnicodeString
rsn(ruleSetName
);
1088 int32_t ix
= localizations
->indexForRuleSet(rsn
.getTerminatedBuffer());
1089 return getRuleSetDisplayName(ix
, localeParam
);
1091 UnicodeString bogus
;
1097 RuleBasedNumberFormat::findRuleSet(const UnicodeString
& name
, UErrorCode
& status
) const
1099 if (U_SUCCESS(status
) && ruleSets
) {
1100 for (NFRuleSet
** p
= ruleSets
; *p
; ++p
) {
1102 if (rs
->isNamed(name
)) {
1106 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1112 RuleBasedNumberFormat::format(const DigitList
&number
,
1113 UnicodeString
&appendTo
,
1114 FieldPositionIterator
*posIter
,
1115 UErrorCode
&status
) const {
1116 if (U_FAILURE(status
)) {
1119 DigitList
copy(number
);
1120 if (copy
.fitsIntoInt64(false)) {
1121 format(((DigitList
&)number
).getInt64(), appendTo
, posIter
, status
);
1124 copy
.roundAtExponent(0);
1125 if (copy
.fitsIntoInt64(false)) {
1126 format(number
.getDouble(), appendTo
, posIter
, status
);
1129 // We're outside of our normal range that this framework can handle.
1130 // The DecimalFormat will provide more accurate results.
1132 // TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J.
1133 NumberFormat
*decimalFormat
= NumberFormat::createInstance(locale
, UNUM_DECIMAL
, status
);
1135 f
.adoptDigitList(new DigitList(number
));
1136 decimalFormat
->format(f
, appendTo
, posIter
, status
);
1137 delete decimalFormat
;
1145 RuleBasedNumberFormat::format(const DigitList
&number
,
1146 UnicodeString
& appendTo
,
1148 UErrorCode
&status
) const {
1149 if (U_FAILURE(status
)) {
1152 DigitList
copy(number
);
1153 if (copy
.fitsIntoInt64(false)) {
1154 format(((DigitList
&)number
).getInt64(), appendTo
, pos
, status
);
1157 copy
.roundAtExponent(0);
1158 if (copy
.fitsIntoInt64(false)) {
1159 format(number
.getDouble(), appendTo
, pos
, status
);
1162 // We're outside of our normal range that this framework can handle.
1163 // The DecimalFormat will provide more accurate results.
1165 // TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J.
1166 NumberFormat
*decimalFormat
= NumberFormat::createInstance(locale
, UNUM_DECIMAL
, status
);
1168 f
.adoptDigitList(new DigitList(number
));
1169 decimalFormat
->format(f
, appendTo
, pos
, status
);
1170 delete decimalFormat
;
1177 RuleBasedNumberFormat::format(int32_t number
,
1178 UnicodeString
& toAppendTo
,
1179 FieldPosition
& pos
) const
1181 return format((int64_t)number
, toAppendTo
, pos
);
1186 RuleBasedNumberFormat::format(int64_t number
,
1187 UnicodeString
& toAppendTo
,
1188 FieldPosition
& /* pos */) const
1190 if (defaultRuleSet
) {
1191 UErrorCode status
= U_ZERO_ERROR
;
1192 format(number
, defaultRuleSet
, toAppendTo
, status
);
1199 RuleBasedNumberFormat::format(double number
,
1200 UnicodeString
& toAppendTo
,
1201 FieldPosition
& /* pos */) const
1203 UErrorCode status
= U_ZERO_ERROR
;
1204 if (defaultRuleSet
) {
1205 format(number
, *defaultRuleSet
, toAppendTo
, status
);
1212 RuleBasedNumberFormat::format(int32_t number
,
1213 const UnicodeString
& ruleSetName
,
1214 UnicodeString
& toAppendTo
,
1216 UErrorCode
& status
) const
1218 return format((int64_t)number
, ruleSetName
, toAppendTo
, pos
, status
);
1223 RuleBasedNumberFormat::format(int64_t number
,
1224 const UnicodeString
& ruleSetName
,
1225 UnicodeString
& toAppendTo
,
1226 FieldPosition
& /* pos */,
1227 UErrorCode
& status
) const
1229 if (U_SUCCESS(status
)) {
1230 if (ruleSetName
.indexOf(gPercentPercent
, 2, 0) == 0) {
1231 // throw new IllegalArgumentException("Can't use internal rule set");
1232 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1234 NFRuleSet
*rs
= findRuleSet(ruleSetName
, status
);
1236 format(number
, rs
, toAppendTo
, status
);
1245 RuleBasedNumberFormat::format(double number
,
1246 const UnicodeString
& ruleSetName
,
1247 UnicodeString
& toAppendTo
,
1248 FieldPosition
& /* pos */,
1249 UErrorCode
& status
) const
1251 if (U_SUCCESS(status
)) {
1252 if (ruleSetName
.indexOf(gPercentPercent
, 2, 0) == 0) {
1253 // throw new IllegalArgumentException("Can't use internal rule set");
1254 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1256 NFRuleSet
*rs
= findRuleSet(ruleSetName
, status
);
1258 format(number
, *rs
, toAppendTo
, status
);
1266 RuleBasedNumberFormat::format(double number
,
1268 UnicodeString
& toAppendTo
,
1269 UErrorCode
& status
) const
1271 int32_t startPos
= toAppendTo
.length();
1272 if (getRoundingMode() != DecimalFormat::ERoundingMode::kRoundUnnecessary
&& !uprv_isNaN(number
) && !uprv_isInfinite(number
)) {
1273 DigitList digitList
;
1274 digitList
.set(number
);
1275 digitList
.setRoundingMode(getRoundingMode());
1276 digitList
.roundFixedPoint(getMaximumFractionDigits());
1277 number
= digitList
.getDouble();
1279 rs
.format(number
, toAppendTo
, toAppendTo
.length(), 0, status
);
1280 adjustForCapitalizationContext(startPos
, toAppendTo
, status
);
1284 * Bottleneck through which all the public format() methods
1285 * that take a long pass. By the time we get here, we know
1286 * which rule set we're using to do the formatting.
1287 * @param number The number to format
1288 * @param ruleSet The rule set to use to format the number
1289 * @return The text that resulted from formatting the number
1292 RuleBasedNumberFormat::format(int64_t number
, NFRuleSet
*ruleSet
, UnicodeString
& toAppendTo
, UErrorCode
& status
) const
1294 // all API format() routines that take a double vector through
1295 // here. We have these two identical functions-- one taking a
1296 // double and one taking a long-- the couple digits of precision
1297 // that long has but double doesn't (both types are 8 bytes long,
1298 // but double has to borrow some of the mantissa bits to hold
1300 // Create an empty string buffer where the result will
1301 // be built, and pass it to the rule set (along with an insertion
1302 // position of 0 and the number being formatted) to the rule set
1305 if (U_SUCCESS(status
)) {
1306 if (number
== U_INT64_MIN
) {
1307 // We can't handle this value right now. Provide an accurate default value.
1309 // TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J.
1310 NumberFormat
*decimalFormat
= NumberFormat::createInstance(locale
, UNUM_DECIMAL
, status
);
1312 FieldPosition
pos(FieldPosition::DONT_CARE
);
1313 DigitList
*digitList
= new DigitList();
1314 digitList
->set(number
);
1315 f
.adoptDigitList(digitList
);
1316 decimalFormat
->format(f
, toAppendTo
, pos
, status
);
1317 delete decimalFormat
;
1320 int32_t startPos
= toAppendTo
.length();
1321 ruleSet
->format(number
, toAppendTo
, toAppendTo
.length(), 0, status
);
1322 adjustForCapitalizationContext(startPos
, toAppendTo
, status
);
1329 RuleBasedNumberFormat::adjustForCapitalizationContext(int32_t startPos
,
1330 UnicodeString
& currentResult
,
1331 UErrorCode
& status
) const
1333 #if !UCONFIG_NO_BREAK_ITERATION
1334 UDisplayContext capitalizationContext
= getContext(UDISPCTX_TYPE_CAPITALIZATION
, status
);
1335 if (capitalizationContext
!= UDISPCTX_CAPITALIZATION_NONE
&& startPos
== 0 && currentResult
.length() > 0) {
1336 // capitalize currentResult according to context
1337 UChar32 ch
= currentResult
.char32At(0);
1338 if (u_islower(ch
) && U_SUCCESS(status
) && capitalizationBrkIter
!= NULL
&&
1339 ( capitalizationContext
== UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
||
1340 (capitalizationContext
== UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
&& capitalizationForUIListMenu
) ||
1341 (capitalizationContext
== UDISPCTX_CAPITALIZATION_FOR_STANDALONE
&& capitalizationForStandAlone
)) ) {
1342 // titlecase first word of currentResult, here use sentence iterator unlike current implementations
1343 // in LocaleDisplayNamesImpl::adjustForUsageAndContext and RelativeDateFormat::format
1344 currentResult
.toTitle(capitalizationBrkIter
, locale
, U_TITLECASE_NO_LOWERCASE
| U_TITLECASE_NO_BREAK_ADJUSTMENT
);
1348 return currentResult
;
1353 RuleBasedNumberFormat::parse(const UnicodeString
& text
,
1354 Formattable
& result
,
1355 ParsePosition
& parsePosition
) const
1358 parsePosition
.setErrorIndex(0);
1362 UnicodeString
workingText(text
, parsePosition
.getIndex());
1363 ParsePosition
workingPos(0);
1365 ParsePosition
high_pp(0);
1366 Formattable high_result
;
1368 for (NFRuleSet
** p
= ruleSets
; *p
; ++p
) {
1370 if (rp
->isPublic() && rp
->isParseable()) {
1371 ParsePosition
working_pp(0);
1372 Formattable working_result
;
1374 rp
->parse(workingText
, working_pp
, kMaxDouble
, 0, working_result
, lenient
);
1375 if (working_pp
.getIndex() > high_pp
.getIndex()) {
1376 high_pp
= working_pp
;
1377 high_result
= working_result
;
1379 if (high_pp
.getIndex() == workingText
.length()) {
1386 int32_t startIndex
= parsePosition
.getIndex();
1387 parsePosition
.setIndex(startIndex
+ high_pp
.getIndex());
1388 if (high_pp
.getIndex() > 0) {
1389 parsePosition
.setErrorIndex(-1);
1391 int32_t errorIndex
= (high_pp
.getErrorIndex()>0)? high_pp
.getErrorIndex(): 0;
1392 parsePosition
.setErrorIndex(startIndex
+ errorIndex
);
1394 result
= high_result
;
1395 if (result
.getType() == Formattable::kDouble
) {
1396 double d
= result
.getDouble();
1397 if (!uprv_isNaN(d
) && d
== uprv_trunc(d
) && INT32_MIN
<= d
&& d
<= INT32_MAX
) {
1398 // Note: casting a double to an int when the double is too large or small
1399 // to fit the destination is undefined behavior. The explicit range checks,
1400 // above, are required. Just casting and checking the result value is undefined.
1401 result
.setLong(static_cast<int32_t>(d
));
1406 #if !UCONFIG_NO_COLLATION
1409 RuleBasedNumberFormat::setLenient(UBool enabled
)
1412 if (!enabled
&& collator
) {
1421 RuleBasedNumberFormat::setDefaultRuleSet(const UnicodeString
& ruleSetName
, UErrorCode
& status
) {
1422 if (U_SUCCESS(status
)) {
1423 if (ruleSetName
.isEmpty()) {
1424 if (localizations
) {
1425 UnicodeString
name(TRUE
, localizations
->getRuleSetName(0), -1);
1426 defaultRuleSet
= findRuleSet(name
, status
);
1428 initDefaultRuleSet();
1430 } else if (ruleSetName
.startsWith(UNICODE_STRING_SIMPLE("%%"))) {
1431 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1433 NFRuleSet
* result
= findRuleSet(ruleSetName
, status
);
1434 if (result
!= NULL
) {
1435 defaultRuleSet
= result
;
1442 RuleBasedNumberFormat::getDefaultRuleSetName() const {
1443 UnicodeString result
;
1444 if (defaultRuleSet
&& defaultRuleSet
->isPublic()) {
1445 defaultRuleSet
->getName(result
);
1447 result
.setToBogus();
1453 RuleBasedNumberFormat::initDefaultRuleSet()
1455 defaultRuleSet
= NULL
;
1460 const UnicodeString
spellout(UNICODE_STRING_SIMPLE("%spellout-numbering"));
1461 const UnicodeString
ordinal(UNICODE_STRING_SIMPLE("%digits-ordinal"));
1462 const UnicodeString
duration(UNICODE_STRING_SIMPLE("%duration"));
1464 NFRuleSet
**p
= &ruleSets
[0];
1466 if ((*p
)->isNamed(spellout
) || (*p
)->isNamed(ordinal
) || (*p
)->isNamed(duration
)) {
1467 defaultRuleSet
= *p
;
1474 defaultRuleSet
= *--p
;
1475 if (!defaultRuleSet
->isPublic()) {
1476 while (p
!= ruleSets
) {
1477 if ((*--p
)->isPublic()) {
1478 defaultRuleSet
= *p
;
1487 RuleBasedNumberFormat::init(const UnicodeString
& rules
, LocalizationInfo
* localizationInfos
,
1488 UParseError
& pErr
, UErrorCode
& status
)
1490 // TODO: implement UParseError
1491 uprv_memset(&pErr
, 0, sizeof(UParseError
));
1492 // Note: this can leave ruleSets == NULL, so remaining code should check
1493 if (U_FAILURE(status
)) {
1497 initializeDecimalFormatSymbols(status
);
1498 initializeDefaultInfinityRule(status
);
1499 initializeDefaultNaNRule(status
);
1500 if (U_FAILURE(status
)) {
1504 this->localizations
= localizationInfos
== NULL
? NULL
: localizationInfos
->ref();
1506 UnicodeString
description(rules
);
1507 if (!description
.length()) {
1508 status
= U_MEMORY_ALLOCATION_ERROR
;
1512 // start by stripping the trailing whitespace from all the rules
1513 // (this is all the whitespace follwing each semicolon in the
1514 // description). This allows us to look for rule-set boundaries
1515 // by searching for ";%" without having to worry about whitespace
1516 // between the ; and the %
1517 stripWhitespace(description
);
1519 // check to see if there's a set of lenient-parse rules. If there
1520 // is, pull them out into our temporary holding place for them,
1521 // and delete them from the description before the real desciption-
1522 // parsing code sees them
1523 int32_t lp
= description
.indexOf(gLenientParse
, -1, 0);
1525 // we've got to make sure we're not in the middle of a rule
1526 // (where "%%lenient-parse" would actually get treated as
1528 if (lp
== 0 || description
.charAt(lp
- 1) == gSemiColon
) {
1529 // locate the beginning and end of the actual collation
1530 // rules (there may be whitespace between the name and
1531 // the first token in the description)
1532 int lpEnd
= description
.indexOf(gSemiPercent
, 2, lp
);
1535 lpEnd
= description
.length() - 1;
1537 int lpStart
= lp
+ u_strlen(gLenientParse
);
1538 while (PatternProps::isWhiteSpace(description
.charAt(lpStart
))) {
1542 // copy out the lenient-parse rules and delete them
1543 // from the description
1544 lenientParseRules
= new UnicodeString();
1546 if (lenientParseRules
== 0) {
1547 status
= U_MEMORY_ALLOCATION_ERROR
;
1550 lenientParseRules
->setTo(description
, lpStart
, lpEnd
- lpStart
);
1552 description
.remove(lp
, lpEnd
+ 1 - lp
);
1556 // pre-flight parsing the description and count the number of
1557 // rule sets (";%" marks the end of one rule set and the beginning
1560 for (int32_t p
= description
.indexOf(gSemiPercent
, 2, 0); p
!= -1; p
= description
.indexOf(gSemiPercent
, 2, p
)) {
1566 // our rule list is an array of the appropriate size
1567 ruleSets
= (NFRuleSet
**)uprv_malloc((numRuleSets
+ 1) * sizeof(NFRuleSet
*));
1569 if (ruleSets
== 0) {
1570 status
= U_MEMORY_ALLOCATION_ERROR
;
1574 for (int i
= 0; i
<= numRuleSets
; ++i
) {
1578 // divide up the descriptions into individual rule-set descriptions
1579 // and store them in a temporary array. At each step, we also
1580 // new up a rule set, but all this does is initialize its name
1581 // and remove it from its description. We can't actually parse
1582 // the rest of the descriptions and finish initializing everything
1583 // because we have to know the names and locations of all the rule
1584 // sets before we can actually set everything up
1586 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1590 ruleSetDescriptions
= new UnicodeString
[numRuleSets
];
1591 if (ruleSetDescriptions
== 0) {
1592 status
= U_MEMORY_ALLOCATION_ERROR
;
1599 for (int32_t p
= description
.indexOf(gSemiPercent
, 2, 0); p
!= -1; p
= description
.indexOf(gSemiPercent
, 2, start
)) {
1600 ruleSetDescriptions
[curRuleSet
].setTo(description
, start
, p
+ 1 - start
);
1601 ruleSets
[curRuleSet
] = new NFRuleSet(this, ruleSetDescriptions
, curRuleSet
, status
);
1602 if (ruleSets
[curRuleSet
] == 0) {
1603 status
= U_MEMORY_ALLOCATION_ERROR
;
1609 ruleSetDescriptions
[curRuleSet
].setTo(description
, start
, description
.length() - start
);
1610 ruleSets
[curRuleSet
] = new NFRuleSet(this, ruleSetDescriptions
, curRuleSet
, status
);
1611 if (ruleSets
[curRuleSet
] == 0) {
1612 status
= U_MEMORY_ALLOCATION_ERROR
;
1617 // now we can take note of the formatter's default rule set, which
1618 // is the last public rule set in the description (it's the last
1619 // rather than the first so that a user can create a new formatter
1620 // from an existing formatter and change its default behavior just
1621 // by appending more rule sets to the end)
1623 // {dlf} Initialization of a fraction rule set requires the default rule
1624 // set to be known. For purposes of initialization, this is always the
1625 // last public rule set, no matter what the localization data says.
1626 initDefaultRuleSet();
1628 // finally, we can go back through the temporary descriptions
1629 // list and finish seting up the substructure (and we throw
1630 // away the temporary descriptions as we go)
1632 for (int i
= 0; i
< numRuleSets
; i
++) {
1633 ruleSets
[i
]->parseRules(ruleSetDescriptions
[i
], status
);
1637 // Now that the rules are initialized, the 'real' default rule
1638 // set can be adjusted by the localization data.
1640 // The C code keeps the localization array as is, rather than building
1641 // a separate array of the public rule set names, so we have less work
1642 // to do here-- but we still need to check the names.
1644 if (localizationInfos
) {
1645 // confirm the names, if any aren't in the rules, that's an error
1646 // it is ok if the rules contain public rule sets that are not in this list
1647 for (int32_t i
= 0; i
< localizationInfos
->getNumberOfRuleSets(); ++i
) {
1648 UnicodeString
name(TRUE
, localizationInfos
->getRuleSetName(i
), -1);
1649 NFRuleSet
* rs
= findRuleSet(name
, status
);
1654 defaultRuleSet
= rs
;
1658 defaultRuleSet
= getDefaultRuleSet();
1660 originalDescription
= rules
;
1663 // override the NumberFormat implementation in order to
1664 // lazily initialize relevant items
1666 RuleBasedNumberFormat::setContext(UDisplayContext value
, UErrorCode
& status
)
1668 NumberFormat::setContext(value
, status
);
1669 if (U_SUCCESS(status
)) {
1670 if (!capitalizationInfoSet
&&
1671 (value
==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
|| value
==UDISPCTX_CAPITALIZATION_FOR_STANDALONE
)) {
1672 initCapitalizationContextInfo(locale
);
1673 capitalizationInfoSet
= TRUE
;
1675 #if !UCONFIG_NO_BREAK_ITERATION
1676 if ( capitalizationBrkIter
== NULL
&& (value
==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
||
1677 (value
==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
&& capitalizationForUIListMenu
) ||
1678 (value
==UDISPCTX_CAPITALIZATION_FOR_STANDALONE
&& capitalizationForStandAlone
)) ) {
1679 UErrorCode status
= U_ZERO_ERROR
;
1680 capitalizationBrkIter
= BreakIterator::createSentenceInstance(locale
, status
);
1681 if (U_FAILURE(status
)) {
1682 delete capitalizationBrkIter
;
1683 capitalizationBrkIter
= NULL
;
1691 RuleBasedNumberFormat::initCapitalizationContextInfo(const Locale
& thelocale
)
1693 #if !UCONFIG_NO_BREAK_ITERATION
1694 const char * localeID
= (thelocale
!= NULL
)? thelocale
.getBaseName(): NULL
;
1695 UErrorCode status
= U_ZERO_ERROR
;
1696 UResourceBundle
*rb
= ures_open(NULL
, localeID
, &status
);
1697 rb
= ures_getByKeyWithFallback(rb
, "contextTransforms", rb
, &status
);
1698 rb
= ures_getByKeyWithFallback(rb
, "number-spellout", rb
, &status
);
1699 if (U_SUCCESS(status
) && rb
!= NULL
) {
1701 const int32_t * intVector
= ures_getIntVector(rb
, &len
, &status
);
1702 if (U_SUCCESS(status
) && intVector
!= NULL
&& len
>= 2) {
1703 capitalizationForUIListMenu
= intVector
[0];
1704 capitalizationForStandAlone
= intVector
[1];
1712 RuleBasedNumberFormat::stripWhitespace(UnicodeString
& description
)
1714 // iterate through the characters...
1715 UnicodeString result
;
1718 while (start
!= -1 && start
< description
.length()) {
1719 // seek to the first non-whitespace character...
1720 while (start
< description
.length()
1721 && PatternProps::isWhiteSpace(description
.charAt(start
))) {
1725 // locate the next semicolon in the text and copy the text from
1726 // our current position up to that semicolon into the result
1727 int32_t p
= description
.indexOf(gSemiColon
, start
);
1729 // or if we don't find a semicolon, just copy the rest of
1730 // the string into the result
1731 result
.append(description
, start
, description
.length() - start
);
1734 else if (p
< description
.length()) {
1735 result
.append(description
, start
, p
+ 1 - start
);
1739 // when we get here, we've seeked off the end of the sring, and
1740 // we terminate the loop (we continue until *start* is -1 rather
1741 // than until *p* is -1, because otherwise we'd miss the last
1742 // rule in the description)
1748 description
.setTo(result
);
1753 RuleBasedNumberFormat::dispose()
1756 for (NFRuleSet
** p
= ruleSets
; *p
; ++p
) {
1759 uprv_free(ruleSets
);
1763 if (ruleSetDescriptions
) {
1764 delete [] ruleSetDescriptions
;
1765 ruleSetDescriptions
= NULL
;
1768 #if !UCONFIG_NO_COLLATION
1773 delete decimalFormatSymbols
;
1774 decimalFormatSymbols
= NULL
;
1776 delete defaultInfinityRule
;
1777 defaultInfinityRule
= NULL
;
1779 delete defaultNaNRule
;
1780 defaultNaNRule
= NULL
;
1782 delete lenientParseRules
;
1783 lenientParseRules
= NULL
;
1785 #if !UCONFIG_NO_BREAK_ITERATION
1786 delete capitalizationBrkIter
;
1787 capitalizationBrkIter
= NULL
;
1790 if (localizations
) {
1791 localizations
= localizations
->unref();
1796 //-----------------------------------------------------------------------
1797 // package-internal API
1798 //-----------------------------------------------------------------------
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
1806 const RuleBasedCollator
*
1807 RuleBasedNumberFormat::getCollator() const
1809 #if !UCONFIG_NO_COLLATION
1814 // lazy-evaluate the collator
1815 if (collator
== NULL
&& 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 combinaiton of those rules
1821 UErrorCode status
= U_ZERO_ERROR
;
1823 Collator
* temp
= Collator::createInstance(locale
, status
);
1824 RuleBasedCollator
* newCollator
;
1825 if (U_SUCCESS(status
) && (newCollator
= dynamic_cast<RuleBasedCollator
*>(temp
)) != NULL
) {
1826 if (lenientParseRules
) {
1827 UnicodeString
rules(newCollator
->getRules());
1828 rules
.append(*lenientParseRules
);
1830 newCollator
= new RuleBasedCollator(rules
, status
);
1831 // Exit if newCollator could not be created.
1832 if (newCollator
== NULL
) {
1838 if (U_SUCCESS(status
)) {
1839 newCollator
->setAttribute(UCOL_DECOMPOSITION_MODE
, UCOL_ON
, status
);
1841 ((RuleBasedNumberFormat
*)this)->collator
= newCollator
;
1850 // if lenient-parse mode is off, this will be null
1851 // (see setLenientParseMode())
1856 DecimalFormatSymbols
*
1857 RuleBasedNumberFormat::initializeDecimalFormatSymbols(UErrorCode
&status
)
1859 // lazy-evaluate the DecimalFormatSymbols object. This object
1860 // is shared by all DecimalFormat instances belonging to this
1862 if (decimalFormatSymbols
== NULL
) {
1863 DecimalFormatSymbols
* temp
= new DecimalFormatSymbols(locale
, status
);
1864 if (U_SUCCESS(status
)) {
1865 decimalFormatSymbols
= temp
;
1871 return decimalFormatSymbols
;
1875 * Returns the DecimalFormatSymbols object that should be used by all DecimalFormat
1876 * instances owned by this formatter.
1878 const DecimalFormatSymbols
*
1879 RuleBasedNumberFormat::getDecimalFormatSymbols() const
1881 return decimalFormatSymbols
;
1885 RuleBasedNumberFormat::initializeDefaultInfinityRule(UErrorCode
&status
)
1887 if (U_FAILURE(status
)) {
1890 if (defaultInfinityRule
== NULL
) {
1891 UnicodeString
rule(UNICODE_STRING_SIMPLE("Inf: "));
1892 rule
.append(getDecimalFormatSymbols()->getSymbol(DecimalFormatSymbols::kInfinitySymbol
));
1893 NFRule
* temp
= new NFRule(this, rule
, status
);
1894 if (U_SUCCESS(status
)) {
1895 defaultInfinityRule
= temp
;
1901 return defaultInfinityRule
;
1905 RuleBasedNumberFormat::getDefaultInfinityRule() const
1907 return defaultInfinityRule
;
1911 RuleBasedNumberFormat::initializeDefaultNaNRule(UErrorCode
&status
)
1913 if (U_FAILURE(status
)) {
1916 if (defaultNaNRule
== NULL
) {
1917 UnicodeString
rule(UNICODE_STRING_SIMPLE("NaN: "));
1918 rule
.append(getDecimalFormatSymbols()->getSymbol(DecimalFormatSymbols::kNaNSymbol
));
1919 NFRule
* temp
= new NFRule(this, rule
, status
);
1920 if (U_SUCCESS(status
)) {
1921 defaultNaNRule
= temp
;
1927 return defaultNaNRule
;
1931 RuleBasedNumberFormat::getDefaultNaNRule() const
1933 return defaultNaNRule
;
1936 // De-owning the current localized symbols and adopt the new symbols.
1938 RuleBasedNumberFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols
* symbolsToAdopt
)
1940 if (symbolsToAdopt
== NULL
) {
1941 return; // do not allow caller to set decimalFormatSymbols to NULL
1944 if (decimalFormatSymbols
!= NULL
) {
1945 delete decimalFormatSymbols
;
1948 decimalFormatSymbols
= symbolsToAdopt
;
1951 // Apply the new decimalFormatSymbols by reparsing the rulesets
1952 UErrorCode status
= U_ZERO_ERROR
;
1954 delete defaultInfinityRule
;
1955 defaultInfinityRule
= NULL
;
1956 initializeDefaultInfinityRule(status
); // Reset with the new DecimalFormatSymbols
1958 delete defaultNaNRule
;
1959 defaultNaNRule
= NULL
;
1960 initializeDefaultNaNRule(status
); // Reset with the new DecimalFormatSymbols
1963 for (int32_t i
= 0; i
< numRuleSets
; i
++) {
1964 ruleSets
[i
]->setDecimalFormatSymbols(*symbolsToAdopt
, status
);
1970 // Setting the symbols is equlivalent to adopting a newly created localized symbols.
1972 RuleBasedNumberFormat::setDecimalFormatSymbols(const DecimalFormatSymbols
& symbols
)
1974 adoptDecimalFormatSymbols(new DecimalFormatSymbols(symbols
));
1978 RuleBasedNumberFormat::createPluralFormat(UPluralType pluralType
,
1979 const UnicodeString
&pattern
,
1980 UErrorCode
& status
) const
1982 return new PluralFormat(locale
, pluralType
, pattern
, status
);
1986 * Get the rounding mode.
1987 * @return A rounding mode
1989 DecimalFormat::ERoundingMode
RuleBasedNumberFormat::getRoundingMode() const {
1990 return roundingMode
;
1994 * Set the rounding mode. This has no effect unless the rounding
1995 * increment is greater than zero.
1996 * @param roundingMode A rounding mode
1998 void RuleBasedNumberFormat::setRoundingMode(DecimalFormat::ERoundingMode roundingMode
) {
1999 this->roundingMode
= roundingMode
;