2 *******************************************************************************
3 * Copyright (C) 2009, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
9 * Modification History:
11 * Date Name Description
12 *******************************************************************************
16 #include "unicode/utypes.h"
17 #include "unicode/plurfmt.h"
18 #include "unicode/plurrule.h"
19 #include "plurrule_impl.h"
21 #if !UCONFIG_NO_FORMATTING
26 static void U_CALLCONV
27 deleteHashStrings(void *obj
) {
28 delete (UnicodeString
*)obj
;
32 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralFormat
)
34 #define MAX_KEYWORD_SIZE 30
36 PluralFormat::PluralFormat(UErrorCode
& status
) {
37 init(NULL
, Locale::getDefault(), status
);
40 PluralFormat::PluralFormat(const Locale
& loc
, UErrorCode
& status
) {
41 init(NULL
, loc
, status
);
44 PluralFormat::PluralFormat(const PluralRules
& rules
, UErrorCode
& status
) {
45 init(&rules
, Locale::getDefault(), status
);
48 PluralFormat::PluralFormat(const Locale
& loc
, const PluralRules
& rules
, UErrorCode
& status
) {
49 init(&rules
, loc
, status
);
52 PluralFormat::PluralFormat(const UnicodeString
& pat
, UErrorCode
& status
) {
53 init(NULL
, Locale::getDefault(), status
);
54 applyPattern(pat
, status
);
57 PluralFormat::PluralFormat(const Locale
& loc
, const UnicodeString
& pat
, UErrorCode
& status
) {
58 init(NULL
, loc
, status
);
59 applyPattern(pat
, status
);
62 PluralFormat::PluralFormat(const PluralRules
& rules
, const UnicodeString
& pat
, UErrorCode
& status
) {
63 init(&rules
, Locale::getDefault(), status
);
64 applyPattern(pat
, status
);
67 PluralFormat::PluralFormat(const Locale
& loc
, const PluralRules
& rules
, const UnicodeString
& pat
, UErrorCode
& status
) {
68 init(&rules
, loc
, status
);
69 applyPattern(pat
, status
);
72 PluralFormat::PluralFormat(const PluralFormat
& other
) : Format(other
) {
73 UErrorCode status
= U_ZERO_ERROR
;
74 locale
= other
.locale
;
75 pluralRules
= other
.pluralRules
->clone();
76 pattern
= other
.pattern
;
77 copyHashtable(other
.fParsedValuesHash
, status
);
78 if (U_FAILURE(status
)) {
83 numberFormat
=NumberFormat::createInstance(locale
, status
);
84 if (U_FAILURE(status
)) {
87 delete fParsedValuesHash
;
88 fParsedValuesHash
= NULL
;
91 replacedNumberFormat
=other
.replacedNumberFormat
;
94 PluralFormat::~PluralFormat() {
96 delete fParsedValuesHash
;
101 PluralFormat::init(const PluralRules
* rules
, const Locale
& curLocale
, UErrorCode
& status
) {
102 if (U_FAILURE(status
)) {
107 pluralRules
= PluralRules::forLocale(locale
, status
);
108 if (U_FAILURE(status
)) {
113 pluralRules
= rules
->clone();
115 fParsedValuesHash
=NULL
;
117 numberFormat
= NumberFormat::createInstance(curLocale
, status
);
118 if (U_FAILURE(status
)) {
123 replacedNumberFormat
=NULL
;
127 PluralFormat::applyPattern(const UnicodeString
& newPattern
, UErrorCode
& status
) {
128 if (U_FAILURE(status
)) {
131 this->pattern
= newPattern
;
133 int32_t braceCount
=0;
135 UBool spaceIncluded
=FALSE
;
137 if (fParsedValuesHash
==NULL
) {
138 fParsedValuesHash
= new Hashtable(TRUE
, status
);
139 if (U_FAILURE(status
)) {
142 fParsedValuesHash
->setValueDeleter(deleteHashStrings
);
145 UBool getKeyword
=TRUE
;
146 UnicodeString hashKeyword
;
147 UnicodeString
*hashPattern
;
149 for (int32_t i
=0; i
<pattern
.length(); ++i
) {
150 UChar ch
=pattern
.charAt(i
);
152 if ( !inRange(ch
, type
) ) {
154 status
= U_ILLEGAL_CHARACTER
;
164 if (token
.length()==0) {
168 // space after keyword
169 spaceIncluded
= TRUE
;
177 if (fParsedValuesHash
->get(token
)!= NULL
) {
178 status
= U_DUPLICATE_KEYWORD
;
181 if (token
.length()==0) {
182 status
= U_PATTERN_SYNTAX_ERROR
;
185 if (!pluralRules
->isKeyword(token
)) {
186 status
= U_UNDEFINED_KEYWORD
;
195 status
= U_UNEXPECTED_TOKEN
;
203 spaceIncluded
= FALSE
;
207 status
= U_UNEXPECTED_TOKEN
;
211 hashPattern
= new UnicodeString(token
);
212 fParsedValuesHash
->put(hashKeyword
, hashPattern
, status
);
213 if (U_FAILURE(status
)) {
217 if ( braceCount
==0 ) {
219 hashKeyword
.remove();
227 spaceIncluded
= FALSE
;
232 status
= U_PATTERN_SYNTAX_ERROR
;
240 if ( checkSufficientDefinition() ) {
244 status
= U_DEFAULT_KEYWORD_MISSING
;
250 PluralFormat::format(const Formattable
& obj
,
251 UnicodeString
& appendTo
,
253 UErrorCode
& status
) const
255 if (U_FAILURE(status
)) return appendTo
;
258 switch (obj
.getType())
260 case Formattable::kDouble
:
261 return format((int32_t)obj
.getDouble(), appendTo
, pos
, status
);
263 case Formattable::kLong
:
264 number
= (int32_t)obj
.getLong();
265 return format(number
, appendTo
, pos
, status
);
267 case Formattable::kInt64
:
268 return format((int32_t)obj
.getInt64(), appendTo
, pos
, status
);
270 status
= U_ILLEGAL_ARGUMENT_ERROR
;
276 PluralFormat::format(int32_t number
, UErrorCode
& status
) const {
277 if (U_FAILURE(status
)) {
278 return UnicodeString();
280 FieldPosition
fpos(0);
281 UnicodeString result
;
283 return format(number
, result
, fpos
, status
);
287 PluralFormat::format(double number
, UErrorCode
& status
) const {
288 if (U_FAILURE(status
)) {
289 return UnicodeString();
291 FieldPosition
fpos(0);
292 UnicodeString result
;
294 return format(number
, result
, fpos
, status
);
299 PluralFormat::format(int32_t number
,
300 UnicodeString
& appendTo
,
302 UErrorCode
& status
) const {
303 return format((double)number
, appendTo
, pos
, status
);
307 PluralFormat::format(double number
,
308 UnicodeString
& appendTo
,
310 UErrorCode
& /*status*/) const {
312 if (fParsedValuesHash
==NULL
) {
313 if ( replacedNumberFormat
== NULL
) {
314 return numberFormat
->format(number
, appendTo
, pos
);
317 replacedNumberFormat
->format(number
, appendTo
, pos
);
320 UnicodeString selectedRule
= pluralRules
->select(number
);
321 UnicodeString
*selectedPattern
= (UnicodeString
*)fParsedValuesHash
->get(selectedRule
);
322 if (selectedPattern
==NULL
) {
323 selectedPattern
= (UnicodeString
*)fParsedValuesHash
->get(pluralRules
->getKeywordOther());
325 appendTo
= insertFormattedNumber(number
, *selectedPattern
, appendTo
, pos
);
331 PluralFormat::toPattern(UnicodeString
& appendTo
) {
337 PluralFormat::inRange(UChar ch
, fmtToken
& type
) {
338 if ((ch
>=CAP_A
) && (ch
<=CAP_Z
)) {
339 // we assume all characters are in lower case already.
342 if ((ch
>=LOW_A
) && (ch
<=LOW_Z
)) {
366 PluralFormat::checkSufficientDefinition() {
367 // Check that at least the default rule is defined.
368 if (fParsedValuesHash
==NULL
) return FALSE
;
369 if (fParsedValuesHash
->get(pluralRules
->getKeywordOther()) == NULL
) {
378 PluralFormat::setLocale(const Locale
& loc
, UErrorCode
& status
) {
379 if (U_FAILURE(status
)) {
382 if (pluralRules
!=NULL
) {
386 if (fParsedValuesHash
!= NULL
) {
387 delete fParsedValuesHash
;
388 fParsedValuesHash
= NULL
;
390 if (numberFormat
!=NULL
) {
393 replacedNumberFormat
=NULL
;
395 init(NULL
, loc
, status
);
399 PluralFormat::setNumberFormat(const NumberFormat
* format
, UErrorCode
& /*status*/) {
400 // TODO: The copy constructor and assignment op of NumberFormat class are protected.
401 // create a pointer as the workaround.
402 replacedNumberFormat
= (NumberFormat
*)format
;
406 PluralFormat::clone() const
408 return new PluralFormat(*this);
412 PluralFormat::operator=(const PluralFormat
& other
) {
413 if (this != &other
) {
414 UErrorCode status
= U_ZERO_ERROR
;
416 delete fParsedValuesHash
;
418 locale
= other
.locale
;
419 pluralRules
= other
.pluralRules
->clone();
420 pattern
= other
.pattern
;
421 copyHashtable(other
.fParsedValuesHash
, status
);
422 if (U_FAILURE(status
)) {
425 fParsedValuesHash
= NULL
;
429 numberFormat
=NumberFormat::createInstance(locale
, status
);
430 if (U_FAILURE(status
)) {
432 delete fParsedValuesHash
;
434 fParsedValuesHash
= NULL
;
438 replacedNumberFormat
=other
.replacedNumberFormat
;
445 PluralFormat::operator==(const Format
& other
) const {
446 // This protected comparison operator should only be called by subclasses
447 // which have confirmed that the other object being compared against is
448 // an instance of a sublcass of PluralFormat. THIS IS IMPORTANT.
449 // Format::operator== guarantees that this cast is safe
450 PluralFormat
* fmt
= (PluralFormat
*)&other
;
451 return ((*pluralRules
== *(fmt
->pluralRules
)) &&
452 (*numberFormat
== *(fmt
->numberFormat
)));
456 PluralFormat::operator!=(const Format
& other
) const {
457 return !operator==(other
);
461 PluralFormat::parseObject(const UnicodeString
& /*source*/,
462 Formattable
& /*result*/,
463 ParsePosition
& /*pos*/) const
465 // TODO: not yet supported in icu4j and icu4c
469 PluralFormat::insertFormattedNumber(double number
,
470 UnicodeString
& message
,
471 UnicodeString
& appendTo
,
472 FieldPosition
& pos
) const {
473 UnicodeString result
;
474 int32_t braceStack
=0;
475 int32_t startIndex
=0;
477 if (message
.length()==0) {
480 appendTo
= numberFormat
->format(number
, appendTo
, pos
);
481 for(int32_t i
=0; i
<message
.length(); ++i
) {
482 switch(message
.charAt(i
)) {
491 result
+= UnicodeString(message
, startIndex
, i
);
498 if ( startIndex
< message
.length() ) {
499 result
+= UnicodeString(message
, startIndex
, message
.length()-startIndex
);
506 PluralFormat::copyHashtable(Hashtable
*other
, UErrorCode
& status
) {
507 if (other
== NULL
|| U_FAILURE(status
)) {
508 fParsedValuesHash
= NULL
;
511 fParsedValuesHash
= new Hashtable(TRUE
, status
);
512 if(U_FAILURE(status
)){
515 fParsedValuesHash
->setValueDeleter(deleteHashStrings
);
517 const UHashElement
* elem
= NULL
;
518 // walk through the hash table and create a deep clone
519 while((elem
= other
->nextElement(pos
))!= NULL
){
520 const UHashTok otherKeyTok
= elem
->key
;
521 UnicodeString
* otherKey
= (UnicodeString
*)otherKeyTok
.pointer
;
522 const UHashTok otherKeyToVal
= elem
->value
;
523 UnicodeString
* otherValue
= (UnicodeString
*)otherKeyToVal
.pointer
;
524 fParsedValuesHash
->put(*otherKey
, new UnicodeString(*otherValue
), status
);
525 if(U_FAILURE(status
)){
535 #endif /* #if !UCONFIG_NO_FORMATTING */