2 *******************************************************************************
3 * Copyright (C) 2008, 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 numberFormat
=NumberFormat::createInstance(locale
, status
);
79 replacedNumberFormat
=other
.replacedNumberFormat
;
82 PluralFormat::~PluralFormat() {
84 delete fParsedValuesHash
;
89 PluralFormat::init(const PluralRules
* rules
, const Locale
& curLocale
, UErrorCode
& status
) {
92 pluralRules
= PluralRules::forLocale(locale
, status
);
95 pluralRules
= rules
->clone();
97 fParsedValuesHash
=NULL
;
99 numberFormat
= NumberFormat::createInstance(curLocale
, status
);
100 replacedNumberFormat
=NULL
;
104 PluralFormat::applyPattern(const UnicodeString
& newPattern
, UErrorCode
& status
) {
105 this->pattern
= newPattern
;
107 int32_t braceCount
=0;
109 UBool spaceIncluded
=FALSE
;
111 if (fParsedValuesHash
==NULL
) {
112 fParsedValuesHash
= new Hashtable(TRUE
, status
);
113 if (U_FAILURE(status
)) {
116 fParsedValuesHash
->setValueDeleter(deleteHashStrings
);
119 UBool getKeyword
=TRUE
;
120 UnicodeString hashKeyword
;
121 UnicodeString
*hashPattern
;
123 for (int32_t i
=0; i
<pattern
.length(); ++i
) {
124 UChar ch
=pattern
.charAt(i
);
126 if ( !inRange(ch
, type
) ) {
128 status
= U_ILLEGAL_CHARACTER
;
138 if (token
.length()==0) {
142 // space after keyword
143 spaceIncluded
= TRUE
;
151 if (fParsedValuesHash
->get(token
)!= NULL
) {
152 status
= U_DUPLICATE_KEYWORD
;
155 if (token
.length()==0) {
156 status
= U_PATTERN_SYNTAX_ERROR
;
159 if (!pluralRules
->isKeyword(token
)) {
160 status
= U_UNDEFINED_KEYWORD
;
169 status
= U_UNEXPECTED_TOKEN
;
177 spaceIncluded
= FALSE
;
181 status
= U_UNEXPECTED_TOKEN
;
185 hashPattern
= new UnicodeString(token
);
186 fParsedValuesHash
->put(hashKeyword
, hashPattern
, status
);
188 if ( braceCount
==0 ) {
190 hashKeyword
.remove();
198 spaceIncluded
= FALSE
;
203 status
= U_PATTERN_SYNTAX_ERROR
;
211 if ( checkSufficientDefinition() ) {
215 status
= U_DEFAULT_KEYWORD_MISSING
;
221 PluralFormat::format(const Formattable
& obj
,
222 UnicodeString
& appendTo
,
224 UErrorCode
& status
) const
226 if (U_FAILURE(status
)) return appendTo
;
229 switch (obj
.getType())
231 case Formattable::kDouble
:
232 return format((int32_t)obj
.getDouble(), appendTo
, pos
, status
);
234 case Formattable::kLong
:
235 number
= (int32_t)obj
.getLong();
236 return format(number
, appendTo
, pos
, status
);
238 case Formattable::kInt64
:
239 return format((int32_t)obj
.getInt64(), appendTo
, pos
, status
);
241 status
= U_ILLEGAL_ARGUMENT_ERROR
;
247 PluralFormat::format(int32_t number
, UErrorCode
& status
) const {
248 FieldPosition
fpos(0);
249 UnicodeString result
;
251 return format(number
, result
, fpos
, status
);
255 PluralFormat::format(double number
, UErrorCode
& status
) const {
256 FieldPosition
fpos(0);
257 UnicodeString result
;
259 return format(number
, result
, fpos
, status
);
264 PluralFormat::format(int32_t number
,
265 UnicodeString
& appendTo
,
267 UErrorCode
& status
) const {
268 return format((double)number
, appendTo
, pos
, status
);
272 PluralFormat::format(double number
,
273 UnicodeString
& appendTo
,
275 UErrorCode
& /*status*/) const {
277 if (fParsedValuesHash
==NULL
) {
278 if ( replacedNumberFormat
== NULL
) {
279 return numberFormat
->format(number
, appendTo
, pos
);
282 replacedNumberFormat
->format(number
, appendTo
, pos
);
285 UnicodeString selectedRule
= pluralRules
->select(number
);
286 UnicodeString
*selectedPattern
= (UnicodeString
*)fParsedValuesHash
->get(selectedRule
);
287 if (selectedPattern
==NULL
) {
288 selectedPattern
= (UnicodeString
*)fParsedValuesHash
->get(pluralRules
->getKeywordOther());
290 appendTo
= insertFormattedNumber(number
, *selectedPattern
, appendTo
, pos
);
296 PluralFormat::toPattern(UnicodeString
& appendTo
) {
302 PluralFormat::inRange(UChar ch
, fmtToken
& type
) {
303 if ((ch
>=CAP_A
) && (ch
<=CAP_Z
)) {
304 // we assume all characters are in lower case already.
307 if ((ch
>=LOW_A
) && (ch
<=LOW_Z
)) {
331 PluralFormat::checkSufficientDefinition() {
332 // Check that at least the default rule is defined.
333 if (fParsedValuesHash
==NULL
) return FALSE
;
334 if (fParsedValuesHash
->get(pluralRules
->getKeywordOther()) == NULL
) {
343 PluralFormat::setLocale(const Locale
& loc
, UErrorCode
& status
) {
344 if (pluralRules
!=NULL
) {
348 if (fParsedValuesHash
!= NULL
) {
349 delete fParsedValuesHash
;
350 fParsedValuesHash
= NULL
;
352 if (numberFormat
!=NULL
) {
355 replacedNumberFormat
=NULL
;
357 init(NULL
, loc
, status
);
361 PluralFormat::setNumberFormat(const NumberFormat
* format
, UErrorCode
& /*status*/) {
362 // TODO: The copy constructor and assignment op of NumberFormat class are protected.
363 // create a pointer as the workaround.
364 replacedNumberFormat
= (NumberFormat
*)format
;
368 PluralFormat::clone() const
370 return new PluralFormat(*this);
374 PluralFormat::operator=(const PluralFormat
& other
) {
375 if (this != &other
) {
376 UErrorCode status
= U_ZERO_ERROR
;
378 delete fParsedValuesHash
;
380 locale
= other
.locale
;
381 pluralRules
= other
.pluralRules
->clone();
382 pattern
= other
.pattern
;
383 copyHashtable(other
.fParsedValuesHash
, status
);
384 numberFormat
=NumberFormat::createInstance(locale
, status
);
385 replacedNumberFormat
=other
.replacedNumberFormat
;
392 PluralFormat::operator==(const Format
& other
) const {
393 // This protected comparison operator should only be called by subclasses
394 // which have confirmed that the other object being compared against is
395 // an instance of a sublcass of PluralFormat. THIS IS IMPORTANT.
396 // Format::operator== guarantees that this cast is safe
397 PluralFormat
* fmt
= (PluralFormat
*)&other
;
398 return ((*pluralRules
== *(fmt
->pluralRules
)) &&
399 (*numberFormat
== *(fmt
->numberFormat
)));
403 PluralFormat::operator!=(const Format
& other
) const {
404 return !operator==(other
);
408 PluralFormat::parseObject(const UnicodeString
& /*source*/,
409 Formattable
& /*result*/,
410 ParsePosition
& /*pos*/) const
412 // TODO: not yet supported in icu4j and icu4c
416 PluralFormat::insertFormattedNumber(double number
,
417 UnicodeString
& message
,
418 UnicodeString
& appendTo
,
419 FieldPosition
& pos
) const {
420 UnicodeString result
;
421 int32_t braceStack
=0;
422 int32_t startIndex
=0;
424 if (message
.length()==0) {
427 appendTo
= numberFormat
->format(number
, appendTo
, pos
);
428 for(int32_t i
=0; i
<message
.length(); ++i
) {
429 switch(message
.charAt(i
)) {
438 result
+= UnicodeString(message
, startIndex
, i
);
445 if ( startIndex
< message
.length() ) {
446 result
+= UnicodeString(message
, startIndex
, message
.length()-startIndex
);
453 PluralFormat::copyHashtable(Hashtable
*other
, UErrorCode
& status
) {
455 fParsedValuesHash
= NULL
;
458 fParsedValuesHash
= new Hashtable(TRUE
, status
);
459 if(U_FAILURE(status
)){
462 fParsedValuesHash
->setValueDeleter(deleteHashStrings
);
464 const UHashElement
* elem
= NULL
;
465 // walk through the hash table and create a deep clone
466 while((elem
= other
->nextElement(pos
))!= NULL
){
467 const UHashTok otherKeyTok
= elem
->key
;
468 UnicodeString
* otherKey
= (UnicodeString
*)otherKeyTok
.pointer
;
469 const UHashTok otherKeyToVal
= elem
->value
;
470 UnicodeString
* otherValue
= (UnicodeString
*)otherKeyToVal
.pointer
;
471 fParsedValuesHash
->put(*otherKey
, new UnicodeString(*otherValue
), status
);
472 if(U_FAILURE(status
)){
482 #endif /* #if !UCONFIG_NO_FORMATTING */