2 *******************************************************************************
3 * Copyright (C) 1997-2014, International Business Machines Corporation
4 * and others. All Rights Reserved.
5 *******************************************************************************
8 #include "unicode/utypes.h"
9 #include "utypeinfo.h" // for 'typeid' to work
11 #include "unicode/rbnf.h"
15 #include "unicode/normlzr.h"
16 #include "unicode/plurfmt.h"
17 #include "unicode/tblcoll.h"
18 #include "unicode/uchar.h"
19 #include "unicode/ucol.h"
20 #include "unicode/uloc.h"
21 #include "unicode/unum.h"
22 #include "unicode/ures.h"
23 #include "unicode/ustring.h"
24 #include "unicode/utf16.h"
25 #include "unicode/udata.h"
26 #include "unicode/udisplaycontext.h"
27 #include "unicode/brkiter.h"
32 #include "patternprops.h"
42 #define U_ICUDATA_RBNF U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "rbnf"
44 static const UChar gPercentPercent
[] =
49 // All urbnf objects are created through openRules, so we init all of the
50 // Unicode string constants required by rbnf, nfrs, or nfr here.
51 static const UChar gLenientParse
[] =
53 0x25, 0x25, 0x6C, 0x65, 0x6E, 0x69, 0x65, 0x6E, 0x74, 0x2D, 0x70, 0x61, 0x72, 0x73, 0x65, 0x3A, 0
54 }; /* "%%lenient-parse:" */
55 static const UChar gSemiColon
= 0x003B;
56 static const UChar gSemiPercent
[] =
61 #define kSomeNumberOfBitsDiv2 22
62 #define kHalfMaxDouble (double)(1 << kSomeNumberOfBitsDiv2)
63 #define kMaxDouble (kHalfMaxDouble * kHalfMaxDouble)
67 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedNumberFormat
)
70 This is a utility class. It does not use ICU's RTTI.
71 If ICU's RTTI is needed again, you can uncomment the RTTI code and derive from UObject.
72 Please make sure that intltest passes on Windows in Release mode,
73 since the string pooling per compilation unit will mess up how RTTI works.
74 The RTTI code was also removed due to lack of code coverage.
76 class LocalizationInfo
: public UMemory
{
78 virtual ~LocalizationInfo();
82 LocalizationInfo() : refcount(0) {}
84 LocalizationInfo
* ref(void) {
89 LocalizationInfo
* unref(void) {
90 if (refcount
&& --refcount
== 0) {
96 virtual UBool
operator==(const LocalizationInfo
* rhs
) const;
97 inline UBool
operator!=(const LocalizationInfo
* rhs
) const { return !operator==(rhs
); }
99 virtual int32_t getNumberOfRuleSets(void) const = 0;
100 virtual const UChar
* getRuleSetName(int32_t index
) const = 0;
101 virtual int32_t getNumberOfDisplayLocales(void) const = 0;
102 virtual const UChar
* getLocaleName(int32_t index
) const = 0;
103 virtual const UChar
* getDisplayName(int32_t localeIndex
, int32_t ruleIndex
) const = 0;
105 virtual int32_t indexForLocale(const UChar
* locale
) const;
106 virtual int32_t indexForRuleSet(const UChar
* ruleset
) const;
108 // virtual UClassID getDynamicClassID() const = 0;
109 // static UClassID getStaticClassID(void);
112 LocalizationInfo::~LocalizationInfo() {}
114 //UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(LocalizationInfo)
116 // if both strings are NULL, this returns TRUE
118 streq(const UChar
* lhs
, const UChar
* rhs
) {
123 return u_strcmp(lhs
, rhs
) == 0;
129 LocalizationInfo::operator==(const LocalizationInfo
* rhs
) const {
135 int32_t rsc
= getNumberOfRuleSets();
136 if (rsc
== rhs
->getNumberOfRuleSets()) {
137 for (int i
= 0; i
< rsc
; ++i
) {
138 if (!streq(getRuleSetName(i
), rhs
->getRuleSetName(i
))) {
142 int32_t dlc
= getNumberOfDisplayLocales();
143 if (dlc
== rhs
->getNumberOfDisplayLocales()) {
144 for (int i
= 0; i
< dlc
; ++i
) {
145 const UChar
* locale
= getLocaleName(i
);
146 int32_t ix
= rhs
->indexForLocale(locale
);
147 // if no locale, ix is -1, getLocaleName returns null, so streq returns false
148 if (!streq(locale
, rhs
->getLocaleName(ix
))) {
151 for (int j
= 0; j
< rsc
; ++j
) {
152 if (!streq(getDisplayName(i
, j
), rhs
->getDisplayName(ix
, j
))) {
165 LocalizationInfo::indexForLocale(const UChar
* locale
) const {
166 for (int i
= 0; i
< getNumberOfDisplayLocales(); ++i
) {
167 if (streq(locale
, getLocaleName(i
))) {
175 LocalizationInfo::indexForRuleSet(const UChar
* ruleset
) const {
177 for (int i
= 0; i
< getNumberOfRuleSets(); ++i
) {
178 if (streq(ruleset
, getRuleSetName(i
))) {
187 typedef void (*Fn_Deleter
)(void*);
195 VArray() : buf(NULL
), cap(0), size(0), deleter(NULL
) {}
197 VArray(Fn_Deleter del
) : buf(NULL
), cap(0), size(0), deleter(del
) {}
201 for (int i
= 0; i
< size
; ++i
) {
212 void add(void* elem
, UErrorCode
& status
) {
213 if (U_SUCCESS(status
)) {
217 } else if (cap
< 256) {
223 buf
= (void**)uprv_malloc(cap
* sizeof(void*));
225 buf
= (void**)uprv_realloc(buf
, cap
* sizeof(void*));
228 // if we couldn't realloc, we leak the memory we've already allocated, but we're in deep trouble anyway
229 status
= U_MEMORY_ALLOCATION_ERROR
;
232 void* start
= &buf
[size
];
233 size_t count
= (cap
- size
) * sizeof(void*);
234 uprv_memset(start
, 0, count
); // fill with nulls, just because
240 void** release(void) {
251 class StringLocalizationInfo
: public LocalizationInfo
{
257 friend class LocDataParser
;
259 StringLocalizationInfo(UChar
* i
, UChar
*** d
, int32_t numRS
, int32_t numLocs
)
260 : info(i
), data(d
), numRuleSets(numRS
), numLocales(numLocs
)
265 static StringLocalizationInfo
* create(const UnicodeString
& info
, UParseError
& perror
, UErrorCode
& status
);
267 virtual ~StringLocalizationInfo();
268 virtual int32_t getNumberOfRuleSets(void) const { return numRuleSets
; }
269 virtual const UChar
* getRuleSetName(int32_t index
) const;
270 virtual int32_t getNumberOfDisplayLocales(void) const { return numLocales
; }
271 virtual const UChar
* getLocaleName(int32_t index
) const;
272 virtual const UChar
* getDisplayName(int32_t localeIndex
, int32_t ruleIndex
) const;
274 // virtual UClassID getDynamicClassID() const;
275 // static UClassID getStaticClassID(void);
278 void init(UErrorCode
& status
) const;
283 OPEN_ANGLE
= 0x003c, /* '<' */
284 CLOSE_ANGLE
= 0x003e, /* '>' */
292 * Utility for parsing a localization string and returning a StringLocalizationInfo*.
294 class LocDataParser
{
303 LocDataParser(UParseError
& parseError
, UErrorCode
& status
)
304 : data(NULL
), e(NULL
), p(NULL
), ch(0xffff), pe(parseError
), ec(status
) {}
308 * On a successful parse, return a StringLocalizationInfo*, otherwise delete locData, set perror and status,
309 * and return NULL. The StringLocalizationInfo will adopt locData if it is created.
311 StringLocalizationInfo
* parse(UChar
* data
, int32_t len
);
315 void inc(void) { ++p
; ch
= 0xffff; }
316 UBool
checkInc(UChar c
) { if (p
< e
&& (ch
== c
|| *p
== c
)) { inc(); return TRUE
; } return FALSE
; }
317 UBool
check(UChar c
) { return p
< e
&& (ch
== c
|| *p
== c
); }
318 void skipWhitespace(void) { while (p
< e
&& PatternProps::isWhiteSpace(ch
!= 0xffff ? ch
: *p
)) inc();}
319 UBool
inList(UChar c
, const UChar
* list
) const {
320 if (*list
== SPACE
&& PatternProps::isWhiteSpace(c
)) return TRUE
;
321 while (*list
&& *list
!= c
) ++list
; return *list
== c
;
323 void parseError(const char* msg
);
325 StringLocalizationInfo
* doParse(void);
327 UChar
** nextArray(int32_t& requiredLength
);
328 UChar
* nextString(void);
332 #define ERROR(msg) parseError(msg); return NULL;
333 #define EXPLANATION_ARG explanationArg
335 #define ERROR(msg) parseError(NULL); return NULL;
336 #define EXPLANATION_ARG
340 static const UChar DQUOTE_STOPLIST
[] = {
344 static const UChar SQUOTE_STOPLIST
[] = {
348 static const UChar NOQUOTE_STOPLIST
[] = {
349 SPACE
, COMMA
, CLOSE_ANGLE
, OPEN_ANGLE
, TICK
, QUOTE
, 0
357 StringLocalizationInfo
*
358 LocDataParser::parse(UChar
* _data
, int32_t len
) {
360 if (_data
) uprv_free(_data
);
366 pe
.postContext
[0] = 0;
367 pe
.preContext
[0] = 0;
370 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
375 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
389 StringLocalizationInfo
*
390 LocDataParser::doParse(void) {
392 if (!checkInc(OPEN_ANGLE
)) {
393 ERROR("Missing open angle");
395 VArray
array(DeleteFn
);
396 UBool mightHaveNext
= TRUE
;
397 int32_t requiredLength
= -1;
398 while (mightHaveNext
) {
399 mightHaveNext
= FALSE
;
400 UChar
** elem
= nextArray(requiredLength
);
402 UBool haveComma
= check(COMMA
);
407 mightHaveNext
= TRUE
;
409 } else if (haveComma
) {
410 ERROR("Unexpected character");
415 if (!checkInc(CLOSE_ANGLE
)) {
416 if (check(OPEN_ANGLE
)) {
417 ERROR("Missing comma in outer array");
419 ERROR("Missing close angle bracket in outer array");
425 ERROR("Extra text after close of localization data");
430 int32_t numLocs
= array
.length() - 2; // subtract first, NULL
431 UChar
*** result
= (UChar
***)array
.release();
433 return new StringLocalizationInfo(data
, result
, requiredLength
-2, numLocs
); // subtract first, NULL
437 ERROR("Unknown error");
441 LocDataParser::nextArray(int32_t& requiredLength
) {
447 if (!checkInc(OPEN_ANGLE
)) {
448 ERROR("Missing open angle");
452 UBool mightHaveNext
= TRUE
;
453 while (mightHaveNext
) {
454 mightHaveNext
= FALSE
;
455 UChar
* elem
= nextString();
457 UBool haveComma
= check(COMMA
);
462 mightHaveNext
= TRUE
;
464 } else if (haveComma
) {
465 ERROR("Unexpected comma");
469 if (!checkInc(CLOSE_ANGLE
)) {
470 if (check(OPEN_ANGLE
)) {
471 ERROR("Missing close angle bracket in inner array");
473 ERROR("Missing comma in inner array");
479 if (requiredLength
== -1) {
480 requiredLength
= array
.length() + 1;
481 } else if (array
.length() != requiredLength
) {
482 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
483 ERROR("Array not of required length");
486 return (UChar
**)array
.release();
488 ERROR("Unknown Error");
492 LocDataParser::nextString() {
493 UChar
* result
= NULL
;
497 const UChar
* terminators
;
499 UBool haveQuote
= c
== QUOTE
|| c
== TICK
;
502 terminators
= c
== QUOTE
? DQUOTE_STOPLIST
: SQUOTE_STOPLIST
;
504 terminators
= NOQUOTE_STOPLIST
;
507 while (p
< e
&& !inList(*p
, terminators
)) ++p
;
509 ERROR("Unexpected end of data");
515 *p
= 0x0; // terminate by writing to data
516 result
= start
; // just point into data
520 ERROR("Missing matching quote");
521 } else if (p
== start
) {
522 ERROR("Empty string");
525 } else if (x
== OPEN_ANGLE
|| x
== TICK
|| x
== QUOTE
) {
526 ERROR("Unexpected character in string");
530 // ok for there to be no next string
534 void LocDataParser::parseError(const char* EXPLANATION_ARG
)
540 const UChar
* start
= p
- U_PARSE_CONTEXT_LEN
- 1;
544 for (UChar
* x
= p
; --x
>= start
;) {
550 const UChar
* limit
= p
+ U_PARSE_CONTEXT_LEN
- 1;
554 u_strncpy(pe
.preContext
, start
, (int32_t)(p
-start
));
555 pe
.preContext
[p
-start
] = 0;
556 u_strncpy(pe
.postContext
, p
, (int32_t)(limit
-p
));
557 pe
.postContext
[limit
-p
] = 0;
558 pe
.offset
= (int32_t)(p
- data
);
561 fprintf(stderr
, "%s at or near character %ld: ", EXPLANATION_ARG
, p
-data
);
564 msg
.append(start
, p
- start
);
565 msg
.append((UChar
)0x002f); /* SOLIDUS/SLASH */
566 msg
.append(p
, limit
-p
);
567 msg
.append(UNICODE_STRING_SIMPLE("'"));
570 int32_t len
= msg
.extract(0, msg
.length(), buf
, 128);
576 fprintf(stderr
, "%s\n", buf
);
590 //UOBJECT_DEFINE_RTTI_IMPLEMENTATION(StringLocalizationInfo)
592 StringLocalizationInfo
*
593 StringLocalizationInfo::create(const UnicodeString
& info
, UParseError
& perror
, UErrorCode
& status
) {
594 if (U_FAILURE(status
)) {
598 int32_t len
= info
.length();
600 return NULL
; // no error;
603 UChar
* p
= (UChar
*)uprv_malloc(len
* sizeof(UChar
));
605 status
= U_MEMORY_ALLOCATION_ERROR
;
608 info
.extract(p
, len
, status
);
609 if (!U_FAILURE(status
)) {
610 status
= U_ZERO_ERROR
; // clear warning about non-termination
613 LocDataParser
parser(perror
, status
);
614 return parser
.parse(p
, len
);
617 StringLocalizationInfo::~StringLocalizationInfo() {
618 for (UChar
*** p
= (UChar
***)data
; *p
; ++p
) {
619 // remaining data is simply pointer into our unicode string data.
620 if (*p
) uprv_free(*p
);
622 if (data
) uprv_free(data
);
623 if (info
) uprv_free(info
);
628 StringLocalizationInfo::getRuleSetName(int32_t index
) const {
629 if (index
>= 0 && index
< getNumberOfRuleSets()) {
630 return data
[0][index
];
636 StringLocalizationInfo::getLocaleName(int32_t index
) const {
637 if (index
>= 0 && index
< getNumberOfDisplayLocales()) {
638 return data
[index
+1][0];
644 StringLocalizationInfo::getDisplayName(int32_t localeIndex
, int32_t ruleIndex
) const {
645 if (localeIndex
>= 0 && localeIndex
< getNumberOfDisplayLocales() &&
646 ruleIndex
>= 0 && ruleIndex
< getNumberOfRuleSets()) {
647 return data
[localeIndex
+1][ruleIndex
+1];
654 RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString
& description
,
655 const UnicodeString
& locs
,
656 const Locale
& alocale
, UParseError
& perror
, UErrorCode
& status
)
658 , ruleSetDescriptions(NULL
)
660 , defaultRuleSet(NULL
)
663 , decimalFormatSymbols(NULL
)
665 , lenientParseRules(NULL
)
666 , localizations(NULL
)
667 , capitalizationInfoSet(FALSE
)
668 , capitalizationForUIListMenu(FALSE
)
669 , capitalizationForStandAlone(FALSE
)
670 , capitalizationBrkIter(NULL
)
672 LocalizationInfo
* locinfo
= StringLocalizationInfo::create(locs
, perror
, status
);
673 init(description
, locinfo
, perror
, status
);
676 RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString
& description
,
677 const UnicodeString
& locs
,
678 UParseError
& perror
, UErrorCode
& status
)
680 , ruleSetDescriptions(NULL
)
682 , defaultRuleSet(NULL
)
683 , locale(Locale::getDefault())
685 , decimalFormatSymbols(NULL
)
687 , lenientParseRules(NULL
)
688 , localizations(NULL
)
689 , capitalizationInfoSet(FALSE
)
690 , capitalizationForUIListMenu(FALSE
)
691 , capitalizationForStandAlone(FALSE
)
692 , capitalizationBrkIter(NULL
)
694 LocalizationInfo
* locinfo
= StringLocalizationInfo::create(locs
, perror
, status
);
695 init(description
, locinfo
, perror
, status
);
698 RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString
& description
,
699 LocalizationInfo
* info
,
700 const Locale
& alocale
, UParseError
& perror
, UErrorCode
& status
)
702 , ruleSetDescriptions(NULL
)
704 , defaultRuleSet(NULL
)
707 , decimalFormatSymbols(NULL
)
709 , lenientParseRules(NULL
)
710 , localizations(NULL
)
711 , capitalizationInfoSet(FALSE
)
712 , capitalizationForUIListMenu(FALSE
)
713 , capitalizationForStandAlone(FALSE
)
714 , capitalizationBrkIter(NULL
)
716 init(description
, info
, perror
, status
);
719 RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString
& description
,
723 , ruleSetDescriptions(NULL
)
725 , defaultRuleSet(NULL
)
726 , locale(Locale::getDefault())
728 , decimalFormatSymbols(NULL
)
730 , lenientParseRules(NULL
)
731 , localizations(NULL
)
732 , capitalizationInfoSet(FALSE
)
733 , capitalizationForUIListMenu(FALSE
)
734 , capitalizationForStandAlone(FALSE
)
735 , capitalizationBrkIter(NULL
)
737 init(description
, NULL
, perror
, status
);
740 RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString
& description
,
741 const Locale
& aLocale
,
745 , ruleSetDescriptions(NULL
)
747 , defaultRuleSet(NULL
)
750 , decimalFormatSymbols(NULL
)
752 , lenientParseRules(NULL
)
753 , localizations(NULL
)
754 , capitalizationInfoSet(FALSE
)
755 , capitalizationForUIListMenu(FALSE
)
756 , capitalizationForStandAlone(FALSE
)
757 , capitalizationBrkIter(NULL
)
759 init(description
, NULL
, perror
, status
);
762 RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag
, const Locale
& alocale
, UErrorCode
& status
)
764 , ruleSetDescriptions(NULL
)
766 , defaultRuleSet(NULL
)
769 , decimalFormatSymbols(NULL
)
771 , lenientParseRules(NULL
)
772 , localizations(NULL
)
773 , capitalizationInfoSet(FALSE
)
774 , capitalizationForUIListMenu(FALSE
)
775 , capitalizationForStandAlone(FALSE
)
776 , capitalizationBrkIter(NULL
)
778 if (U_FAILURE(status
)) {
782 const char* rules_tag
= "RBNFRules";
783 const char* fmt_tag
= "";
785 case URBNF_SPELLOUT
: fmt_tag
= "SpelloutRules"; break;
786 case URBNF_ORDINAL
: fmt_tag
= "OrdinalRules"; break;
787 case URBNF_DURATION
: fmt_tag
= "DurationRules"; break;
788 case URBNF_NUMBERING_SYSTEM
: fmt_tag
= "NumberingSystemRules"; break;
789 default: status
= U_ILLEGAL_ARGUMENT_ERROR
; return;
792 // TODO: read localization info from resource
793 LocalizationInfo
* locinfo
= NULL
;
795 UResourceBundle
* nfrb
= ures_open(U_ICUDATA_RBNF
, locale
.getName(), &status
);
796 if (U_SUCCESS(status
)) {
797 setLocaleIDs(ures_getLocaleByType(nfrb
, ULOC_VALID_LOCALE
, &status
),
798 ures_getLocaleByType(nfrb
, ULOC_ACTUAL_LOCALE
, &status
));
800 UResourceBundle
* rbnfRules
= ures_getByKeyWithFallback(nfrb
, rules_tag
, NULL
, &status
);
801 if (U_FAILURE(status
)) {
804 UResourceBundle
* ruleSets
= ures_getByKeyWithFallback(rbnfRules
, fmt_tag
, NULL
, &status
);
805 if (U_FAILURE(status
)) {
806 ures_close(rbnfRules
);
812 while (ures_hasNext(ruleSets
)) {
813 desc
.append(ures_getNextUnicodeString(ruleSets
,NULL
,&status
));
817 init (desc
, locinfo
, perror
, status
);
819 ures_close(ruleSets
);
820 ures_close(rbnfRules
);
825 RuleBasedNumberFormat::RuleBasedNumberFormat(const RuleBasedNumberFormat
& rhs
)
828 , ruleSetDescriptions(NULL
)
830 , defaultRuleSet(NULL
)
833 , decimalFormatSymbols(NULL
)
835 , lenientParseRules(NULL
)
836 , localizations(NULL
)
837 , capitalizationInfoSet(FALSE
)
838 , capitalizationForUIListMenu(FALSE
)
839 , capitalizationForStandAlone(FALSE
)
840 , capitalizationBrkIter(NULL
)
842 this->operator=(rhs
);
847 RuleBasedNumberFormat
&
848 RuleBasedNumberFormat::operator=(const RuleBasedNumberFormat
& rhs
)
853 NumberFormat::operator=(rhs
);
854 UErrorCode status
= U_ZERO_ERROR
;
857 lenient
= rhs
.lenient
;
860 init(rhs
.originalDescription
, rhs
.localizations
? rhs
.localizations
->ref() : NULL
, perror
, status
);
861 setDecimalFormatSymbols(*rhs
.getDecimalFormatSymbols());
862 setDefaultRuleSet(rhs
.getDefaultRuleSetName(), status
);
864 capitalizationInfoSet
= rhs
.capitalizationInfoSet
;
865 capitalizationForUIListMenu
= rhs
.capitalizationForUIListMenu
;
866 capitalizationForStandAlone
= rhs
.capitalizationForStandAlone
;
867 #if !UCONFIG_NO_BREAK_ITERATION
868 capitalizationBrkIter
= (rhs
.capitalizationBrkIter
!=NULL
)? rhs
.capitalizationBrkIter
->clone(): NULL
;
874 RuleBasedNumberFormat::~RuleBasedNumberFormat()
880 RuleBasedNumberFormat::clone(void) const
882 return new RuleBasedNumberFormat(*this);
886 RuleBasedNumberFormat::operator==(const Format
& other
) const
888 if (this == &other
) {
892 if (typeid(*this) == typeid(other
)) {
893 const RuleBasedNumberFormat
& rhs
= (const RuleBasedNumberFormat
&)other
;
894 // test for capitalization info equality is adequately handled
895 // by the NumberFormat test for fCapitalizationContext equality;
896 // the info here is just derived from that.
897 if (locale
== rhs
.locale
&&
898 lenient
== rhs
.lenient
&&
899 (localizations
== NULL
900 ? rhs
.localizations
== NULL
901 : (rhs
.localizations
== NULL
903 : *localizations
== rhs
.localizations
))) {
905 NFRuleSet
** p
= ruleSets
;
906 NFRuleSet
** q
= rhs
.ruleSets
;
909 } else if (q
== NULL
) {
912 while (*p
&& *q
&& (**p
== **q
)) {
916 return *q
== NULL
&& *p
== NULL
;
924 RuleBasedNumberFormat::getRules() const
926 UnicodeString result
;
927 if (ruleSets
!= NULL
) {
928 for (NFRuleSet
** p
= ruleSets
; *p
; ++p
) {
929 (*p
)->appendRules(result
);
936 RuleBasedNumberFormat::getRuleSetName(int32_t index
) const
939 UnicodeString
string(TRUE
, localizations
->getRuleSetName(index
), (int32_t)-1);
941 } else if (ruleSets
) {
942 UnicodeString result
;
943 for (NFRuleSet
** p
= ruleSets
; *p
; ++p
) {
945 if (rs
->isPublic()) {
958 RuleBasedNumberFormat::getNumberOfRuleSetNames() const
962 result
= localizations
->getNumberOfRuleSets();
963 } else if (ruleSets
) {
964 for (NFRuleSet
** p
= ruleSets
; *p
; ++p
) {
965 if ((**p
).isPublic()) {
974 RuleBasedNumberFormat::getNumberOfRuleSetDisplayNameLocales(void) const {
976 return localizations
->getNumberOfDisplayLocales();
982 RuleBasedNumberFormat::getRuleSetDisplayNameLocale(int32_t index
, UErrorCode
& status
) const {
983 if (U_FAILURE(status
)) {
986 if (localizations
&& index
>= 0 && index
< localizations
->getNumberOfDisplayLocales()) {
987 UnicodeString
name(TRUE
, localizations
->getLocaleName(index
), -1);
989 int32_t cap
= name
.length() + 1;
992 bp
= (char *)uprv_malloc(cap
);
994 status
= U_MEMORY_ALLOCATION_ERROR
;
998 name
.extract(0, name
.length(), bp
, cap
, UnicodeString::kInvariant
);
999 Locale
retLocale(bp
);
1005 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1011 RuleBasedNumberFormat::getRuleSetDisplayName(int32_t index
, const Locale
& localeParam
) {
1012 if (localizations
&& index
>= 0 && index
< localizations
->getNumberOfRuleSets()) {
1013 UnicodeString
localeName(localeParam
.getBaseName(), -1, UnicodeString::kInvariant
);
1014 int32_t len
= localeName
.length();
1015 UChar
* localeStr
= localeName
.getBuffer(len
+ 1);
1018 int32_t ix
= localizations
->indexForLocale(localeStr
);
1020 UnicodeString
name(TRUE
, localizations
->getDisplayName(ix
, index
), -1);
1024 // trim trailing portion, skipping over ommitted sections
1025 do { --len
;} while (len
> 0 && localeStr
[len
] != 0x005f); // underscore
1026 while (len
> 0 && localeStr
[len
-1] == 0x005F) --len
;
1028 UnicodeString
name(TRUE
, localizations
->getRuleSetName(index
), -1);
1031 UnicodeString bogus
;
1037 RuleBasedNumberFormat::getRuleSetDisplayName(const UnicodeString
& ruleSetName
, const Locale
& localeParam
) {
1038 if (localizations
) {
1039 UnicodeString
rsn(ruleSetName
);
1040 int32_t ix
= localizations
->indexForRuleSet(rsn
.getTerminatedBuffer());
1041 return getRuleSetDisplayName(ix
, localeParam
);
1043 UnicodeString bogus
;
1049 RuleBasedNumberFormat::findRuleSet(const UnicodeString
& name
, UErrorCode
& status
) const
1051 if (U_SUCCESS(status
) && ruleSets
) {
1052 for (NFRuleSet
** p
= ruleSets
; *p
; ++p
) {
1054 if (rs
->isNamed(name
)) {
1058 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1064 RuleBasedNumberFormat::format(int32_t number
,
1065 UnicodeString
& toAppendTo
,
1066 FieldPosition
& /* pos */) const
1068 if (defaultRuleSet
) {
1069 UErrorCode status
= U_ZERO_ERROR
;
1070 int32_t startPos
= toAppendTo
.length();
1071 defaultRuleSet
->format((int64_t)number
, toAppendTo
, toAppendTo
.length(), status
);
1072 adjustForCapitalizationContext(startPos
, toAppendTo
);
1079 RuleBasedNumberFormat::format(int64_t number
,
1080 UnicodeString
& toAppendTo
,
1081 FieldPosition
& /* pos */) const
1083 if (defaultRuleSet
) {
1084 UErrorCode status
= U_ZERO_ERROR
;
1085 int32_t startPos
= toAppendTo
.length();
1086 defaultRuleSet
->format(number
, toAppendTo
, toAppendTo
.length(), status
);
1087 adjustForCapitalizationContext(startPos
, toAppendTo
);
1094 RuleBasedNumberFormat::format(double number
,
1095 UnicodeString
& toAppendTo
,
1096 FieldPosition
& /* pos */) const
1098 int32_t startPos
= toAppendTo
.length();
1099 // Special case for NaN; adapted from what DecimalFormat::_format( double number,...) does.
1100 if (uprv_isNaN(number
)) {
1101 DecimalFormatSymbols
* decFmtSyms
= getDecimalFormatSymbols(); // RuleBasedNumberFormat internal
1103 toAppendTo
+= decFmtSyms
->getConstSymbol(DecimalFormatSymbols::kNaNSymbol
);
1105 } else if (defaultRuleSet
) {
1106 UErrorCode status
= U_ZERO_ERROR
;
1107 defaultRuleSet
->format(number
, toAppendTo
, toAppendTo
.length(), status
);
1109 return adjustForCapitalizationContext(startPos
, toAppendTo
);
1114 RuleBasedNumberFormat::format(int32_t number
,
1115 const UnicodeString
& ruleSetName
,
1116 UnicodeString
& toAppendTo
,
1117 FieldPosition
& /* pos */,
1118 UErrorCode
& status
) const
1120 // return format((int64_t)number, ruleSetName, toAppendTo, pos, status);
1121 if (U_SUCCESS(status
)) {
1122 if (ruleSetName
.indexOf(gPercentPercent
, 2, 0) == 0) {
1123 // throw new IllegalArgumentException("Can't use internal rule set");
1124 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1126 NFRuleSet
*rs
= findRuleSet(ruleSetName
, status
);
1128 int32_t startPos
= toAppendTo
.length();
1129 rs
->format((int64_t)number
, toAppendTo
, toAppendTo
.length(), status
);
1130 adjustForCapitalizationContext(startPos
, toAppendTo
);
1139 RuleBasedNumberFormat::format(int64_t number
,
1140 const UnicodeString
& ruleSetName
,
1141 UnicodeString
& toAppendTo
,
1142 FieldPosition
& /* pos */,
1143 UErrorCode
& status
) const
1145 if (U_SUCCESS(status
)) {
1146 if (ruleSetName
.indexOf(gPercentPercent
, 2, 0) == 0) {
1147 // throw new IllegalArgumentException("Can't use internal rule set");
1148 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1150 NFRuleSet
*rs
= findRuleSet(ruleSetName
, status
);
1152 int32_t startPos
= toAppendTo
.length();
1153 rs
->format(number
, toAppendTo
, toAppendTo
.length(), status
);
1154 adjustForCapitalizationContext(startPos
, toAppendTo
);
1163 RuleBasedNumberFormat::format(double number
,
1164 const UnicodeString
& ruleSetName
,
1165 UnicodeString
& toAppendTo
,
1166 FieldPosition
& /* pos */,
1167 UErrorCode
& status
) const
1169 if (U_SUCCESS(status
)) {
1170 if (ruleSetName
.indexOf(gPercentPercent
, 2, 0) == 0) {
1171 // throw new IllegalArgumentException("Can't use internal rule set");
1172 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1174 NFRuleSet
*rs
= findRuleSet(ruleSetName
, status
);
1176 int32_t startPos
= toAppendTo
.length();
1177 rs
->format(number
, toAppendTo
, toAppendTo
.length(), status
);
1178 adjustForCapitalizationContext(startPos
, toAppendTo
);
1186 RuleBasedNumberFormat::adjustForCapitalizationContext(int32_t startPos
,
1187 UnicodeString
& currentResult
) const
1189 #if !UCONFIG_NO_BREAK_ITERATION
1190 if (startPos
==0 && currentResult
.length() > 0) {
1191 // capitalize currentResult according to context
1192 UChar32 ch
= currentResult
.char32At(0);
1193 UErrorCode status
= U_ZERO_ERROR
;
1194 UDisplayContext capitalizationContext
= getContext(UDISPCTX_TYPE_CAPITALIZATION
, status
);
1195 if ( u_islower(ch
) && U_SUCCESS(status
) && capitalizationBrkIter
!= NULL
&&
1196 ( capitalizationContext
==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
||
1197 (capitalizationContext
==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
&& capitalizationForUIListMenu
) ||
1198 (capitalizationContext
==UDISPCTX_CAPITALIZATION_FOR_STANDALONE
&& capitalizationForStandAlone
)) ) {
1199 // titlecase first word of currentResult, here use sentence iterator unlike current implementations
1200 // in LocaleDisplayNamesImpl::adjustForUsageAndContext and RelativeDateFormat::format
1201 currentResult
.toTitle(capitalizationBrkIter
, locale
, U_TITLECASE_NO_LOWERCASE
| U_TITLECASE_NO_BREAK_ADJUSTMENT
);
1205 return currentResult
;
1210 RuleBasedNumberFormat::parse(const UnicodeString
& text
,
1211 Formattable
& result
,
1212 ParsePosition
& parsePosition
) const
1215 parsePosition
.setErrorIndex(0);
1219 UnicodeString
workingText(text
, parsePosition
.getIndex());
1220 ParsePosition
workingPos(0);
1222 ParsePosition
high_pp(0);
1223 Formattable high_result
;
1225 for (NFRuleSet
** p
= ruleSets
; *p
; ++p
) {
1227 if (rp
->isPublic() && rp
->isParseable()) {
1228 ParsePosition
working_pp(0);
1229 Formattable working_result
;
1231 rp
->parse(workingText
, working_pp
, kMaxDouble
, working_result
, lenient
);
1232 if (working_pp
.getIndex() > high_pp
.getIndex()) {
1233 high_pp
= working_pp
;
1234 high_result
= working_result
;
1236 if (high_pp
.getIndex() == workingText
.length()) {
1243 int32_t startIndex
= parsePosition
.getIndex();
1244 parsePosition
.setIndex(startIndex
+ high_pp
.getIndex());
1245 if (high_pp
.getIndex() > 0) {
1246 parsePosition
.setErrorIndex(-1);
1248 int32_t errorIndex
= (high_pp
.getErrorIndex()>0)? high_pp
.getErrorIndex(): 0;
1249 parsePosition
.setErrorIndex(startIndex
+ errorIndex
);
1251 result
= high_result
;
1252 if (result
.getType() == Formattable::kDouble
) {
1253 int32_t r
= (int32_t)result
.getDouble();
1254 if ((double)r
== result
.getDouble()) {
1260 #if !UCONFIG_NO_COLLATION
1263 RuleBasedNumberFormat::setLenient(UBool enabled
)
1266 if (!enabled
&& collator
) {
1275 RuleBasedNumberFormat::setDefaultRuleSet(const UnicodeString
& ruleSetName
, UErrorCode
& status
) {
1276 if (U_SUCCESS(status
)) {
1277 if (ruleSetName
.isEmpty()) {
1278 if (localizations
) {
1279 UnicodeString
name(TRUE
, localizations
->getRuleSetName(0), -1);
1280 defaultRuleSet
= findRuleSet(name
, status
);
1282 initDefaultRuleSet();
1284 } else if (ruleSetName
.startsWith(UNICODE_STRING_SIMPLE("%%"))) {
1285 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1287 NFRuleSet
* result
= findRuleSet(ruleSetName
, status
);
1288 if (result
!= NULL
) {
1289 defaultRuleSet
= result
;
1296 RuleBasedNumberFormat::getDefaultRuleSetName() const {
1297 UnicodeString result
;
1298 if (defaultRuleSet
&& defaultRuleSet
->isPublic()) {
1299 defaultRuleSet
->getName(result
);
1301 result
.setToBogus();
1307 RuleBasedNumberFormat::initDefaultRuleSet()
1309 defaultRuleSet
= NULL
;
1314 const UnicodeString spellout
= UNICODE_STRING_SIMPLE("%spellout-numbering");
1315 const UnicodeString ordinal
= UNICODE_STRING_SIMPLE("%digits-ordinal");
1316 const UnicodeString duration
= UNICODE_STRING_SIMPLE("%duration");
1318 NFRuleSet
**p
= &ruleSets
[0];
1320 if ((*p
)->isNamed(spellout
) || (*p
)->isNamed(ordinal
) || (*p
)->isNamed(duration
)) {
1321 defaultRuleSet
= *p
;
1328 defaultRuleSet
= *--p
;
1329 if (!defaultRuleSet
->isPublic()) {
1330 while (p
!= ruleSets
) {
1331 if ((*--p
)->isPublic()) {
1332 defaultRuleSet
= *p
;
1341 RuleBasedNumberFormat::init(const UnicodeString
& rules
, LocalizationInfo
* localizationInfos
,
1342 UParseError
& pErr
, UErrorCode
& status
)
1344 // TODO: implement UParseError
1345 uprv_memset(&pErr
, 0, sizeof(UParseError
));
1346 // Note: this can leave ruleSets == NULL, so remaining code should check
1347 if (U_FAILURE(status
)) {
1351 this->localizations
= localizationInfos
== NULL
? NULL
: localizationInfos
->ref();
1353 UnicodeString
description(rules
);
1354 if (!description
.length()) {
1355 status
= U_MEMORY_ALLOCATION_ERROR
;
1359 // start by stripping the trailing whitespace from all the rules
1360 // (this is all the whitespace follwing each semicolon in the
1361 // description). This allows us to look for rule-set boundaries
1362 // by searching for ";%" without having to worry about whitespace
1363 // between the ; and the %
1364 stripWhitespace(description
);
1366 // check to see if there's a set of lenient-parse rules. If there
1367 // is, pull them out into our temporary holding place for them,
1368 // and delete them from the description before the real desciption-
1369 // parsing code sees them
1370 int32_t lp
= description
.indexOf(gLenientParse
, -1, 0);
1372 // we've got to make sure we're not in the middle of a rule
1373 // (where "%%lenient-parse" would actually get treated as
1375 if (lp
== 0 || description
.charAt(lp
- 1) == gSemiColon
) {
1376 // locate the beginning and end of the actual collation
1377 // rules (there may be whitespace between the name and
1378 // the first token in the description)
1379 int lpEnd
= description
.indexOf(gSemiPercent
, 2, lp
);
1382 lpEnd
= description
.length() - 1;
1384 int lpStart
= lp
+ u_strlen(gLenientParse
);
1385 while (PatternProps::isWhiteSpace(description
.charAt(lpStart
))) {
1389 // copy out the lenient-parse rules and delete them
1390 // from the description
1391 lenientParseRules
= new UnicodeString();
1393 if (lenientParseRules
== 0) {
1394 status
= U_MEMORY_ALLOCATION_ERROR
;
1397 lenientParseRules
->setTo(description
, lpStart
, lpEnd
- lpStart
);
1399 description
.remove(lp
, lpEnd
+ 1 - lp
);
1403 // pre-flight parsing the description and count the number of
1404 // rule sets (";%" marks the end of one rule set and the beginning
1407 for (int32_t p
= description
.indexOf(gSemiPercent
, 2, 0); p
!= -1; p
= description
.indexOf(gSemiPercent
, 2, p
)) {
1413 // our rule list is an array of the appropriate size
1414 ruleSets
= (NFRuleSet
**)uprv_malloc((numRuleSets
+ 1) * sizeof(NFRuleSet
*));
1416 if (ruleSets
== 0) {
1417 status
= U_MEMORY_ALLOCATION_ERROR
;
1421 for (int i
= 0; i
<= numRuleSets
; ++i
) {
1425 // divide up the descriptions into individual rule-set descriptions
1426 // and store them in a temporary array. At each step, we also
1427 // new up a rule set, but all this does is initialize its name
1428 // and remove it from its description. We can't actually parse
1429 // the rest of the descriptions and finish initializing everything
1430 // because we have to know the names and locations of all the rule
1431 // sets before we can actually set everything up
1433 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1437 ruleSetDescriptions
= new UnicodeString
[numRuleSets
];
1438 if (ruleSetDescriptions
== 0) {
1439 status
= U_MEMORY_ALLOCATION_ERROR
;
1446 for (int32_t p
= description
.indexOf(gSemiPercent
, 2, 0); p
!= -1; p
= description
.indexOf(gSemiPercent
, 2, start
)) {
1447 ruleSetDescriptions
[curRuleSet
].setTo(description
, start
, p
+ 1 - start
);
1448 ruleSets
[curRuleSet
] = new NFRuleSet(ruleSetDescriptions
, curRuleSet
, status
);
1449 if (ruleSets
[curRuleSet
] == 0) {
1450 status
= U_MEMORY_ALLOCATION_ERROR
;
1456 ruleSetDescriptions
[curRuleSet
].setTo(description
, start
, description
.length() - start
);
1457 ruleSets
[curRuleSet
] = new NFRuleSet(ruleSetDescriptions
, curRuleSet
, status
);
1458 if (ruleSets
[curRuleSet
] == 0) {
1459 status
= U_MEMORY_ALLOCATION_ERROR
;
1464 // now we can take note of the formatter's default rule set, which
1465 // is the last public rule set in the description (it's the last
1466 // rather than the first so that a user can create a new formatter
1467 // from an existing formatter and change its default behavior just
1468 // by appending more rule sets to the end)
1470 // {dlf} Initialization of a fraction rule set requires the default rule
1471 // set to be known. For purposes of initialization, this is always the
1472 // last public rule set, no matter what the localization data says.
1473 initDefaultRuleSet();
1475 // finally, we can go back through the temporary descriptions
1476 // list and finish seting up the substructure (and we throw
1477 // away the temporary descriptions as we go)
1479 for (int i
= 0; i
< numRuleSets
; i
++) {
1480 ruleSets
[i
]->parseRules(ruleSetDescriptions
[i
], this, status
);
1484 // Now that the rules are initialized, the 'real' default rule
1485 // set can be adjusted by the localization data.
1487 // The C code keeps the localization array as is, rather than building
1488 // a separate array of the public rule set names, so we have less work
1489 // to do here-- but we still need to check the names.
1491 if (localizationInfos
) {
1492 // confirm the names, if any aren't in the rules, that's an error
1493 // it is ok if the rules contain public rule sets that are not in this list
1494 for (int32_t i
= 0; i
< localizationInfos
->getNumberOfRuleSets(); ++i
) {
1495 UnicodeString
name(TRUE
, localizationInfos
->getRuleSetName(i
), -1);
1496 NFRuleSet
* rs
= findRuleSet(name
, status
);
1501 defaultRuleSet
= rs
;
1505 defaultRuleSet
= getDefaultRuleSet();
1507 originalDescription
= rules
;
1510 // override the NumberFormat implementation in order to
1511 // lazily initialize relevant items
1513 RuleBasedNumberFormat::setContext(UDisplayContext value
, UErrorCode
& status
)
1515 NumberFormat::setContext(value
, status
);
1516 if (U_SUCCESS(status
)) {
1517 if (!capitalizationInfoSet
&&
1518 (value
==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
|| value
==UDISPCTX_CAPITALIZATION_FOR_STANDALONE
)) {
1519 initCapitalizationContextInfo(locale
);
1520 capitalizationInfoSet
= TRUE
;
1522 #if !UCONFIG_NO_BREAK_ITERATION
1523 if ( capitalizationBrkIter
== NULL
&& (value
==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
||
1524 (value
==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
&& capitalizationForUIListMenu
) ||
1525 (value
==UDISPCTX_CAPITALIZATION_FOR_STANDALONE
&& capitalizationForStandAlone
)) ) {
1526 UErrorCode status
= U_ZERO_ERROR
;
1527 capitalizationBrkIter
= BreakIterator::createSentenceInstance(locale
, status
);
1528 if (U_FAILURE(status
)) {
1529 delete capitalizationBrkIter
;
1530 capitalizationBrkIter
= NULL
;
1538 RuleBasedNumberFormat::initCapitalizationContextInfo(const Locale
& thelocale
)
1540 #if !UCONFIG_NO_BREAK_ITERATION
1541 const char * localeID
= (thelocale
!= NULL
)? thelocale
.getBaseName(): NULL
;
1542 UErrorCode status
= U_ZERO_ERROR
;
1543 UResourceBundle
*rb
= ures_open(NULL
, localeID
, &status
);
1544 rb
= ures_getByKeyWithFallback(rb
, "contextTransforms", rb
, &status
);
1545 rb
= ures_getByKeyWithFallback(rb
, "number-spellout", rb
, &status
);
1546 if (U_SUCCESS(status
) && rb
!= NULL
) {
1548 const int32_t * intVector
= ures_getIntVector(rb
, &len
, &status
);
1549 if (U_SUCCESS(status
) && intVector
!= NULL
&& len
>= 2) {
1550 capitalizationForUIListMenu
= intVector
[0];
1551 capitalizationForStandAlone
= intVector
[1];
1559 RuleBasedNumberFormat::stripWhitespace(UnicodeString
& description
)
1561 // iterate through the characters...
1562 UnicodeString result
;
1565 while (start
!= -1 && start
< description
.length()) {
1566 // seek to the first non-whitespace character...
1567 while (start
< description
.length()
1568 && PatternProps::isWhiteSpace(description
.charAt(start
))) {
1572 // locate the next semicolon in the text and copy the text from
1573 // our current position up to that semicolon into the result
1574 int32_t p
= description
.indexOf(gSemiColon
, start
);
1576 // or if we don't find a semicolon, just copy the rest of
1577 // the string into the result
1578 result
.append(description
, start
, description
.length() - start
);
1581 else if (p
< description
.length()) {
1582 result
.append(description
, start
, p
+ 1 - start
);
1586 // when we get here, we've seeked off the end of the sring, and
1587 // we terminate the loop (we continue until *start* is -1 rather
1588 // than until *p* is -1, because otherwise we'd miss the last
1589 // rule in the description)
1595 description
.setTo(result
);
1600 RuleBasedNumberFormat::dispose()
1603 for (NFRuleSet
** p
= ruleSets
; *p
; ++p
) {
1606 uprv_free(ruleSets
);
1610 if (ruleSetDescriptions
) {
1611 delete [] ruleSetDescriptions
;
1614 #if !UCONFIG_NO_COLLATION
1619 delete decimalFormatSymbols
;
1620 decimalFormatSymbols
= NULL
;
1622 delete lenientParseRules
;
1623 lenientParseRules
= NULL
;
1625 #if !UCONFIG_NO_BREAK_ITERATION
1626 delete capitalizationBrkIter
;
1627 capitalizationBrkIter
= NULL
;
1630 if (localizations
) localizations
= localizations
->unref();
1634 //-----------------------------------------------------------------------
1635 // package-internal API
1636 //-----------------------------------------------------------------------
1639 * Returns the collator to use for lenient parsing. The collator is lazily created:
1640 * this function creates it the first time it's called.
1641 * @return The collator to use for lenient parsing, or null if lenient parsing
1644 const RuleBasedCollator
*
1645 RuleBasedNumberFormat::getCollator() const
1647 #if !UCONFIG_NO_COLLATION
1652 // lazy-evaluate the collator
1653 if (collator
== NULL
&& lenient
) {
1654 // create a default collator based on the formatter's locale,
1655 // then pull out that collator's rules, append any additional
1656 // rules specified in the description, and create a _new_
1657 // collator based on the combinaiton of those rules
1659 UErrorCode status
= U_ZERO_ERROR
;
1661 Collator
* temp
= Collator::createInstance(locale
, status
);
1662 RuleBasedCollator
* newCollator
;
1663 if (U_SUCCESS(status
) && (newCollator
= dynamic_cast<RuleBasedCollator
*>(temp
)) != NULL
) {
1664 if (lenientParseRules
) {
1665 UnicodeString
rules(newCollator
->getRules());
1666 rules
.append(*lenientParseRules
);
1668 newCollator
= new RuleBasedCollator(rules
, status
);
1669 // Exit if newCollator could not be created.
1670 if (newCollator
== NULL
) {
1676 if (U_SUCCESS(status
)) {
1677 newCollator
->setAttribute(UCOL_DECOMPOSITION_MODE
, UCOL_ON
, status
);
1679 ((RuleBasedNumberFormat
*)this)->collator
= newCollator
;
1688 // if lenient-parse mode is off, this will be null
1689 // (see setLenientParseMode())
1695 * Returns the DecimalFormatSymbols object that should be used by all DecimalFormat
1696 * instances owned by this formatter. This object is lazily created: this function
1697 * creates it the first time it's called.
1698 * @return The DecimalFormatSymbols object that should be used by all DecimalFormat
1699 * instances owned by this formatter.
1701 DecimalFormatSymbols
*
1702 RuleBasedNumberFormat::getDecimalFormatSymbols() const
1704 // lazy-evaluate the DecimalFormatSymbols object. This object
1705 // is shared by all DecimalFormat instances belonging to this
1707 if (decimalFormatSymbols
== NULL
) {
1708 UErrorCode status
= U_ZERO_ERROR
;
1709 DecimalFormatSymbols
* temp
= new DecimalFormatSymbols(locale
, status
);
1710 if (U_SUCCESS(status
)) {
1711 ((RuleBasedNumberFormat
*)this)->decimalFormatSymbols
= temp
;
1716 return decimalFormatSymbols
;
1719 // De-owning the current localized symbols and adopt the new symbols.
1721 RuleBasedNumberFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols
* symbolsToAdopt
)
1723 if (symbolsToAdopt
== NULL
) {
1724 return; // do not allow caller to set decimalFormatSymbols to NULL
1727 if (decimalFormatSymbols
!= NULL
) {
1728 delete decimalFormatSymbols
;
1731 decimalFormatSymbols
= symbolsToAdopt
;
1734 // Apply the new decimalFormatSymbols by reparsing the rulesets
1735 UErrorCode status
= U_ZERO_ERROR
;
1737 for (int32_t i
= 0; i
< numRuleSets
; i
++) {
1738 ruleSets
[i
]->parseRules(ruleSetDescriptions
[i
], this, status
);
1743 // Setting the symbols is equlivalent to adopting a newly created localized symbols.
1745 RuleBasedNumberFormat::setDecimalFormatSymbols(const DecimalFormatSymbols
& symbols
)
1747 adoptDecimalFormatSymbols(new DecimalFormatSymbols(symbols
));
1751 RuleBasedNumberFormat::createPluralFormat(UPluralType pluralType
,
1752 const UnicodeString
&pattern
,
1753 UErrorCode
& status
) const
1755 return new PluralFormat(locale
, pluralType
, pattern
, status
);