2 *******************************************************************************
3 * Copyright (C) 2009-2013, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
8 *******************************************************************************
11 #include "unicode/decimfmt.h"
12 #include "unicode/messagepattern.h"
13 #include "unicode/plurfmt.h"
14 #include "unicode/plurrule.h"
15 #include "unicode/utypes.h"
17 #include "messageimpl.h"
18 #include "plurrule_impl.h"
22 #if !UCONFIG_NO_FORMATTING
26 static const UChar OTHER_STRING
[] = {
27 0x6F, 0x74, 0x68, 0x65, 0x72, 0 // "other"
30 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralFormat
)
32 PluralFormat::PluralFormat(UErrorCode
& status
)
33 : locale(Locale::getDefault()),
37 init(NULL
, UPLURAL_TYPE_CARDINAL
, status
);
40 PluralFormat::PluralFormat(const Locale
& loc
, UErrorCode
& status
)
45 init(NULL
, UPLURAL_TYPE_CARDINAL
, status
);
48 PluralFormat::PluralFormat(const PluralRules
& rules
, UErrorCode
& status
)
49 : locale(Locale::getDefault()),
53 init(&rules
, UPLURAL_TYPE_COUNT
, status
);
56 PluralFormat::PluralFormat(const Locale
& loc
,
57 const PluralRules
& rules
,
63 init(&rules
, UPLURAL_TYPE_COUNT
, status
);
66 PluralFormat::PluralFormat(const Locale
& loc
,
73 init(NULL
, type
, status
);
76 PluralFormat::PluralFormat(const UnicodeString
& pat
,
78 : locale(Locale::getDefault()),
82 init(NULL
, UPLURAL_TYPE_CARDINAL
, status
);
83 applyPattern(pat
, status
);
86 PluralFormat::PluralFormat(const Locale
& loc
,
87 const UnicodeString
& pat
,
93 init(NULL
, UPLURAL_TYPE_CARDINAL
, status
);
94 applyPattern(pat
, status
);
97 PluralFormat::PluralFormat(const PluralRules
& rules
,
98 const UnicodeString
& pat
,
100 : locale(Locale::getDefault()),
104 init(&rules
, UPLURAL_TYPE_COUNT
, status
);
105 applyPattern(pat
, status
);
108 PluralFormat::PluralFormat(const Locale
& loc
,
109 const PluralRules
& rules
,
110 const UnicodeString
& pat
,
116 init(&rules
, UPLURAL_TYPE_COUNT
, status
);
117 applyPattern(pat
, status
);
120 PluralFormat::PluralFormat(const Locale
& loc
,
122 const UnicodeString
& pat
,
128 init(NULL
, type
, status
);
129 applyPattern(pat
, status
);
132 PluralFormat::PluralFormat(const PluralFormat
& other
)
134 locale(other
.locale
),
135 msgPattern(other
.msgPattern
),
137 offset(other
.offset
) {
142 PluralFormat::copyObjects(const PluralFormat
& other
) {
143 UErrorCode status
= U_ZERO_ERROR
;
144 if (numberFormat
!= NULL
) {
147 if (pluralRulesWrapper
.pluralRules
!= NULL
) {
148 delete pluralRulesWrapper
.pluralRules
;
151 if (other
.numberFormat
== NULL
) {
152 numberFormat
= NumberFormat::createInstance(locale
, status
);
154 numberFormat
= (NumberFormat
*)other
.numberFormat
->clone();
156 if (other
.pluralRulesWrapper
.pluralRules
== NULL
) {
157 pluralRulesWrapper
.pluralRules
= PluralRules::forLocale(locale
, status
);
159 pluralRulesWrapper
.pluralRules
= other
.pluralRulesWrapper
.pluralRules
->clone();
164 PluralFormat::~PluralFormat() {
169 PluralFormat::init(const PluralRules
* rules
, UPluralType type
, UErrorCode
& status
) {
170 if (U_FAILURE(status
)) {
175 pluralRulesWrapper
.pluralRules
= PluralRules::forLocale(locale
, type
, status
);
177 pluralRulesWrapper
.pluralRules
= rules
->clone();
178 if (pluralRulesWrapper
.pluralRules
== NULL
) {
179 status
= U_MEMORY_ALLOCATION_ERROR
;
184 numberFormat
= NumberFormat::createInstance(locale
, status
);
188 PluralFormat::applyPattern(const UnicodeString
& newPattern
, UErrorCode
& status
) {
189 msgPattern
.parsePluralStyle(newPattern
, NULL
, status
);
190 if (U_FAILURE(status
)) {
195 offset
= msgPattern
.getPluralOffset(0);
199 PluralFormat::format(const Formattable
& obj
,
200 UnicodeString
& appendTo
,
202 UErrorCode
& status
) const
204 if (U_FAILURE(status
)) return appendTo
;
206 if (obj
.isNumeric()) {
207 return format(obj
, obj
.getDouble(), appendTo
, pos
, status
);
209 status
= U_ILLEGAL_ARGUMENT_ERROR
;
215 PluralFormat::format(int32_t number
, UErrorCode
& status
) const {
216 FieldPosition
fpos(0);
217 UnicodeString result
;
218 return format(Formattable(number
), number
, result
, fpos
, status
);
222 PluralFormat::format(double number
, UErrorCode
& status
) const {
223 FieldPosition
fpos(0);
224 UnicodeString result
;
225 return format(Formattable(number
), number
, result
, fpos
, status
);
230 PluralFormat::format(int32_t number
,
231 UnicodeString
& appendTo
,
233 UErrorCode
& status
) const {
234 return format(Formattable(number
), (double)number
, appendTo
, pos
, status
);
238 PluralFormat::format(double number
,
239 UnicodeString
& appendTo
,
241 UErrorCode
& status
) const {
242 return format(Formattable(number
), (double)number
, appendTo
, pos
, status
);
246 PluralFormat::format(const Formattable
& numberObject
, double number
,
247 UnicodeString
& appendTo
,
249 UErrorCode
& status
) const {
250 if (U_FAILURE(status
)) {
253 if (msgPattern
.countParts() == 0) {
254 return numberFormat
->format(numberObject
, appendTo
, pos
, status
);
256 // Get the appropriate sub-message.
257 // Select it based on the formatted number-offset.
258 double numberMinusOffset
= number
- offset
;
259 UnicodeString numberString
;
260 FieldPosition ignorePos
;
261 FixedDecimal
dec(numberMinusOffset
);
263 numberFormat
->format(numberObject
, numberString
, ignorePos
, status
); // could be BigDecimal etc.
264 DecimalFormat
*decFmt
= dynamic_cast<DecimalFormat
*>(numberFormat
);
266 dec
= decFmt
->getFixedDecimal(numberObject
, status
);
269 numberFormat
->format(numberMinusOffset
, numberString
, ignorePos
, status
);
270 DecimalFormat
*decFmt
= dynamic_cast<DecimalFormat
*>(numberFormat
);
272 dec
= decFmt
->getFixedDecimal(numberMinusOffset
, status
);
275 int32_t partIndex
= findSubMessage(msgPattern
, 0, pluralRulesWrapper
, &dec
, number
, status
);
276 if (U_FAILURE(status
)) { return appendTo
; }
277 // Replace syntactic # signs in the top level of this sub-message
278 // (not in nested arguments) with the formatted number-offset.
279 const UnicodeString
& pattern
= msgPattern
.getPatternString();
280 int32_t prevIndex
= msgPattern
.getPart(partIndex
).getLimit();
282 const MessagePattern::Part
& part
= msgPattern
.getPart(++partIndex
);
283 const UMessagePatternPartType type
= part
.getType();
284 int32_t index
= part
.getIndex();
285 if (type
== UMSGPAT_PART_TYPE_MSG_LIMIT
) {
286 return appendTo
.append(pattern
, prevIndex
, index
- prevIndex
);
287 } else if ((type
== UMSGPAT_PART_TYPE_REPLACE_NUMBER
) ||
288 (type
== UMSGPAT_PART_TYPE_SKIP_SYNTAX
&& MessageImpl::jdkAposMode(msgPattern
))) {
289 appendTo
.append(pattern
, prevIndex
, index
- prevIndex
);
290 if (type
== UMSGPAT_PART_TYPE_REPLACE_NUMBER
) {
291 appendTo
.append(numberString
);
293 prevIndex
= part
.getLimit();
294 } else if (type
== UMSGPAT_PART_TYPE_ARG_START
) {
295 appendTo
.append(pattern
, prevIndex
, index
- prevIndex
);
297 partIndex
= msgPattern
.getLimitPartIndex(partIndex
);
298 index
= msgPattern
.getPart(partIndex
).getLimit();
299 MessageImpl::appendReducedApostrophes(pattern
, prevIndex
, index
, appendTo
);
306 PluralFormat::toPattern(UnicodeString
& appendTo
) {
307 if (0 == msgPattern
.countParts()) {
308 appendTo
.setToBogus();
310 appendTo
.append(msgPattern
.getPatternString());
316 PluralFormat::setLocale(const Locale
& loc
, UErrorCode
& status
) {
317 if (U_FAILURE(status
)) {
325 pluralRulesWrapper
.reset();
326 init(NULL
, UPLURAL_TYPE_CARDINAL
, status
);
330 PluralFormat::setNumberFormat(const NumberFormat
* format
, UErrorCode
& status
) {
331 if (U_FAILURE(status
)) {
334 NumberFormat
* nf
= (NumberFormat
*)format
->clone();
339 status
= U_MEMORY_ALLOCATION_ERROR
;
344 PluralFormat::clone() const
346 return new PluralFormat(*this);
351 PluralFormat::operator=(const PluralFormat
& other
) {
352 if (this != &other
) {
353 locale
= other
.locale
;
354 msgPattern
= other
.msgPattern
;
355 offset
= other
.offset
;
363 PluralFormat::operator==(const Format
& other
) const {
364 if (this == &other
) {
367 if (!Format::operator==(other
)) {
370 const PluralFormat
& o
= (const PluralFormat
&)other
;
372 locale
== o
.locale
&&
373 msgPattern
== o
.msgPattern
&& // implies same offset
374 (numberFormat
== NULL
) == (o
.numberFormat
== NULL
) &&
375 (numberFormat
== NULL
|| *numberFormat
== *o
.numberFormat
) &&
376 (pluralRulesWrapper
.pluralRules
== NULL
) == (o
.pluralRulesWrapper
.pluralRules
== NULL
) &&
377 (pluralRulesWrapper
.pluralRules
== NULL
||
378 *pluralRulesWrapper
.pluralRules
== *o
.pluralRulesWrapper
.pluralRules
);
382 PluralFormat::operator!=(const Format
& other
) const {
383 return !operator==(other
);
387 PluralFormat::parseObject(const UnicodeString
& /*source*/,
388 Formattable
& /*result*/,
389 ParsePosition
& pos
) const
391 // Parsing not supported.
392 pos
.setErrorIndex(pos
.getIndex());
395 int32_t PluralFormat::findSubMessage(const MessagePattern
& pattern
, int32_t partIndex
,
396 const PluralSelector
& selector
, void *context
,
397 double number
, UErrorCode
& ec
) {
401 int32_t count
=pattern
.countParts();
403 const MessagePattern::Part
* part
=&pattern
.getPart(partIndex
);
404 if (MessagePattern::Part::hasNumericValue(part
->getType())) {
405 offset
=pattern
.getNumericValue(*part
);
410 // The keyword is empty until we need to match against a non-explicit, not-"other" value.
411 // Then we get the keyword from the selector.
412 // (In other words, we never call the selector if we match against an explicit value,
413 // or if the only non-explicit keyword is "other".)
414 UnicodeString keyword
;
415 UnicodeString
other(FALSE
, OTHER_STRING
, 5);
416 // When we find a match, we set msgStart>0 and also set this boolean to true
417 // to avoid matching the keyword again (duplicates are allowed)
418 // while we continue to look for an explicit-value match.
419 UBool haveKeywordMatch
=FALSE
;
420 // msgStart is 0 until we find any appropriate sub-message.
421 // We remember the first "other" sub-message if we have not seen any
422 // appropriate sub-message before.
423 // We remember the first matching-keyword sub-message if we have not seen
424 // one of those before.
425 // (The parser allows [does not check for] duplicate keywords.
426 // We just have to make sure to take the first one.)
427 // We avoid matching the keyword twice by also setting haveKeywordMatch=true
428 // at the first keyword match.
429 // We keep going until we find an explicit-value match or reach the end of the plural style.
431 // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples
432 // until ARG_LIMIT or end of plural-only pattern.
434 part
=&pattern
.getPart(partIndex
++);
435 const UMessagePatternPartType type
= part
->getType();
436 if(type
==UMSGPAT_PART_TYPE_ARG_LIMIT
) {
439 U_ASSERT (type
==UMSGPAT_PART_TYPE_ARG_SELECTOR
);
440 // part is an ARG_SELECTOR followed by an optional explicit value, and then a message
441 if(MessagePattern::Part::hasNumericValue(pattern
.getPartType(partIndex
))) {
442 // explicit value like "=2"
443 part
=&pattern
.getPart(partIndex
++);
444 if(number
==pattern
.getNumericValue(*part
)) {
445 // matches explicit value
448 } else if(!haveKeywordMatch
) {
449 // plural keyword like "few" or "other"
450 // Compare "other" first and call the selector if this is not "other".
451 if(pattern
.partSubstringMatches(*part
, other
)) {
454 if(0 == keyword
.compare(other
)) {
455 // This is the first "other" sub-message,
456 // and the selected keyword is also "other".
457 // Do not match "other" again.
458 haveKeywordMatch
=TRUE
;
462 if(keyword
.isEmpty()) {
463 keyword
=selector
.select(context
, number
-offset
, ec
);
464 if(msgStart
!=0 && (0 == keyword
.compare(other
))) {
465 // We have already seen an "other" sub-message.
466 // Do not match "other" again.
467 haveKeywordMatch
=TRUE
;
468 // Skip keyword matching but do getLimitPartIndex().
471 if(!haveKeywordMatch
&& pattern
.partSubstringMatches(*part
, keyword
)) {
474 // Do not match this keyword again.
475 haveKeywordMatch
=TRUE
;
479 partIndex
=pattern
.getLimitPartIndex(partIndex
);
480 } while(++partIndex
<count
);
484 PluralFormat::PluralSelector::~PluralSelector() {}
486 PluralFormat::PluralSelectorAdapter::~PluralSelectorAdapter() {
490 UnicodeString
PluralFormat::PluralSelectorAdapter::select(void *context
, double number
,
491 UErrorCode
& /*ec*/) const {
492 (void)number
; // unused except in the assertion
493 FixedDecimal
*dec
=static_cast<FixedDecimal
*>(context
);
494 U_ASSERT(dec
->source
==number
);
495 return pluralRules
->select(*dec
);
498 void PluralFormat::PluralSelectorAdapter::reset() {
507 #endif /* #if !UCONFIG_NO_FORMATTING */