2 *******************************************************************************
3 * Copyright (C) 2009-2012, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
9 * Modification History:
11 * Date Name Description
12 *******************************************************************************
15 #include "unicode/messagepattern.h"
16 #include "unicode/plurfmt.h"
17 #include "unicode/plurrule.h"
18 #include "unicode/utypes.h"
20 #include "messageimpl.h"
21 #include "plurrule_impl.h"
25 #if !UCONFIG_NO_FORMATTING
29 static const UChar OTHER_STRING
[] = {
30 0x6F, 0x74, 0x68, 0x65, 0x72, 0 // "other"
33 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralFormat
)
35 PluralFormat::PluralFormat(UErrorCode
& status
)
36 : locale(Locale::getDefault()),
40 init(NULL
, UPLURAL_TYPE_CARDINAL
, status
);
43 PluralFormat::PluralFormat(const Locale
& loc
, UErrorCode
& status
)
48 init(NULL
, UPLURAL_TYPE_CARDINAL
, status
);
51 PluralFormat::PluralFormat(const PluralRules
& rules
, UErrorCode
& status
)
52 : locale(Locale::getDefault()),
56 init(&rules
, UPLURAL_TYPE_COUNT
, status
);
59 PluralFormat::PluralFormat(const Locale
& loc
,
60 const PluralRules
& rules
,
66 init(&rules
, UPLURAL_TYPE_COUNT
, status
);
69 PluralFormat::PluralFormat(const Locale
& loc
,
76 init(NULL
, type
, status
);
79 PluralFormat::PluralFormat(const UnicodeString
& pat
,
81 : locale(Locale::getDefault()),
85 init(NULL
, UPLURAL_TYPE_CARDINAL
, status
);
86 applyPattern(pat
, status
);
89 PluralFormat::PluralFormat(const Locale
& loc
,
90 const UnicodeString
& pat
,
96 init(NULL
, UPLURAL_TYPE_CARDINAL
, status
);
97 applyPattern(pat
, status
);
100 PluralFormat::PluralFormat(const PluralRules
& rules
,
101 const UnicodeString
& pat
,
103 : locale(Locale::getDefault()),
107 init(&rules
, UPLURAL_TYPE_COUNT
, status
);
108 applyPattern(pat
, status
);
111 PluralFormat::PluralFormat(const Locale
& loc
,
112 const PluralRules
& rules
,
113 const UnicodeString
& pat
,
119 init(&rules
, UPLURAL_TYPE_COUNT
, status
);
120 applyPattern(pat
, status
);
123 PluralFormat::PluralFormat(const Locale
& loc
,
125 const UnicodeString
& pat
,
131 init(NULL
, type
, status
);
132 applyPattern(pat
, status
);
135 PluralFormat::PluralFormat(const PluralFormat
& other
)
137 locale(other
.locale
),
138 msgPattern(other
.msgPattern
),
140 offset(other
.offset
) {
145 PluralFormat::copyObjects(const PluralFormat
& other
) {
146 UErrorCode status
= U_ZERO_ERROR
;
147 if (numberFormat
!= NULL
) {
150 if (pluralRulesWrapper
.pluralRules
!= NULL
) {
151 delete pluralRulesWrapper
.pluralRules
;
154 if (other
.numberFormat
== NULL
) {
155 numberFormat
= NumberFormat::createInstance(locale
, status
);
157 numberFormat
= (NumberFormat
*)other
.numberFormat
->clone();
159 if (other
.pluralRulesWrapper
.pluralRules
== NULL
) {
160 pluralRulesWrapper
.pluralRules
= PluralRules::forLocale(locale
, status
);
162 pluralRulesWrapper
.pluralRules
= other
.pluralRulesWrapper
.pluralRules
->clone();
167 PluralFormat::~PluralFormat() {
172 PluralFormat::init(const PluralRules
* rules
, UPluralType type
, UErrorCode
& status
) {
173 if (U_FAILURE(status
)) {
178 pluralRulesWrapper
.pluralRules
= PluralRules::forLocale(locale
, type
, status
);
180 pluralRulesWrapper
.pluralRules
= rules
->clone();
181 if (pluralRulesWrapper
.pluralRules
== NULL
) {
182 status
= U_MEMORY_ALLOCATION_ERROR
;
187 numberFormat
= NumberFormat::createInstance(locale
, status
);
191 PluralFormat::applyPattern(const UnicodeString
& newPattern
, UErrorCode
& status
) {
192 msgPattern
.parsePluralStyle(newPattern
, NULL
, status
);
193 if (U_FAILURE(status
)) {
198 offset
= msgPattern
.getPluralOffset(0);
202 PluralFormat::format(const Formattable
& obj
,
203 UnicodeString
& appendTo
,
205 UErrorCode
& status
) const
207 if (U_FAILURE(status
)) return appendTo
;
209 if (obj
.isNumeric()) {
210 return format(obj
.getDouble(), appendTo
, pos
, status
);
212 status
= U_ILLEGAL_ARGUMENT_ERROR
;
218 PluralFormat::format(int32_t number
, UErrorCode
& status
) const {
219 FieldPosition
fpos(0);
220 UnicodeString result
;
221 return format(number
, result
, fpos
, status
);
225 PluralFormat::format(double number
, UErrorCode
& status
) const {
226 FieldPosition
fpos(0);
227 UnicodeString result
;
228 return format(number
, result
, fpos
, status
);
233 PluralFormat::format(int32_t number
,
234 UnicodeString
& appendTo
,
236 UErrorCode
& status
) const {
237 return format((double)number
, appendTo
, pos
, status
);
241 PluralFormat::format(double number
,
242 UnicodeString
& appendTo
,
244 UErrorCode
& status
) const {
245 if (U_FAILURE(status
)) {
248 if (msgPattern
.countParts() == 0) {
249 return numberFormat
->format(number
, appendTo
, pos
);
251 // Get the appropriate sub-message.
252 int32_t partIndex
= findSubMessage(msgPattern
, 0, pluralRulesWrapper
, number
, status
);
253 // Replace syntactic # signs in the top level of this sub-message
254 // (not in nested arguments) with the formatted number-offset.
255 const UnicodeString
& pattern
= msgPattern
.getPatternString();
257 int32_t prevIndex
= msgPattern
.getPart(partIndex
).getLimit();
259 const MessagePattern::Part
& part
= msgPattern
.getPart(++partIndex
);
260 const UMessagePatternPartType type
= part
.getType();
261 int32_t index
= part
.getIndex();
262 if (type
== UMSGPAT_PART_TYPE_MSG_LIMIT
) {
263 return appendTo
.append(pattern
, prevIndex
, index
- prevIndex
);
264 } else if ((type
== UMSGPAT_PART_TYPE_REPLACE_NUMBER
) ||
265 (type
== UMSGPAT_PART_TYPE_SKIP_SYNTAX
&& MessageImpl::jdkAposMode(msgPattern
))) {
266 appendTo
.append(pattern
, prevIndex
, index
- prevIndex
);
267 if (type
== UMSGPAT_PART_TYPE_REPLACE_NUMBER
) {
268 numberFormat
->format(number
, appendTo
);
270 prevIndex
= part
.getLimit();
271 } else if (type
== UMSGPAT_PART_TYPE_ARG_START
) {
272 appendTo
.append(pattern
, prevIndex
, index
- prevIndex
);
274 partIndex
= msgPattern
.getLimitPartIndex(partIndex
);
275 index
= msgPattern
.getPart(partIndex
).getLimit();
276 MessageImpl::appendReducedApostrophes(pattern
, prevIndex
, index
, appendTo
);
283 PluralFormat::toPattern(UnicodeString
& appendTo
) {
284 if (0 == msgPattern
.countParts()) {
285 appendTo
.setToBogus();
287 appendTo
.append(msgPattern
.getPatternString());
293 PluralFormat::setLocale(const Locale
& loc
, UErrorCode
& status
) {
294 if (U_FAILURE(status
)) {
302 pluralRulesWrapper
.reset();
303 init(NULL
, UPLURAL_TYPE_CARDINAL
, status
);
307 PluralFormat::setNumberFormat(const NumberFormat
* format
, UErrorCode
& status
) {
308 if (U_FAILURE(status
)) {
311 NumberFormat
* nf
= (NumberFormat
*)format
->clone();
316 status
= U_MEMORY_ALLOCATION_ERROR
;
321 PluralFormat::clone() const
323 return new PluralFormat(*this);
328 PluralFormat::operator=(const PluralFormat
& other
) {
329 if (this != &other
) {
330 locale
= other
.locale
;
331 msgPattern
= other
.msgPattern
;
332 offset
= other
.offset
;
340 PluralFormat::operator==(const Format
& other
) const {
341 if (this == &other
) {
344 if (!Format::operator==(other
)) {
347 const PluralFormat
& o
= (const PluralFormat
&)other
;
349 locale
== o
.locale
&&
350 msgPattern
== o
.msgPattern
&& // implies same offset
351 (numberFormat
== NULL
) == (o
.numberFormat
== NULL
) &&
352 (numberFormat
== NULL
|| *numberFormat
== *o
.numberFormat
) &&
353 (pluralRulesWrapper
.pluralRules
== NULL
) == (o
.pluralRulesWrapper
.pluralRules
== NULL
) &&
354 (pluralRulesWrapper
.pluralRules
== NULL
||
355 *pluralRulesWrapper
.pluralRules
== *o
.pluralRulesWrapper
.pluralRules
);
359 PluralFormat::operator!=(const Format
& other
) const {
360 return !operator==(other
);
364 PluralFormat::parseObject(const UnicodeString
& /*source*/,
365 Formattable
& /*result*/,
366 ParsePosition
& pos
) const
368 // Parsing not supported.
369 pos
.setErrorIndex(pos
.getIndex());
372 int32_t PluralFormat::findSubMessage(const MessagePattern
& pattern
, int32_t partIndex
,
373 const PluralSelector
& selector
, double number
, UErrorCode
& ec
) {
377 int32_t count
=pattern
.countParts();
379 const MessagePattern::Part
* part
=&pattern
.getPart(partIndex
);
380 if (MessagePattern::Part::hasNumericValue(part
->getType())) {
381 offset
=pattern
.getNumericValue(*part
);
386 // The keyword is empty until we need to match against non-explicit, not-"other" value.
387 // Then we get the keyword from the selector.
388 // (In other words, we never call the selector if we match against an explicit value,
389 // or if the only non-explicit keyword is "other".)
390 UnicodeString keyword
;
391 UnicodeString
other(FALSE
, OTHER_STRING
, 5);
392 // When we find a match, we set msgStart>0 and also set this boolean to true
393 // to avoid matching the keyword again (duplicates are allowed)
394 // while we continue to look for an explicit-value match.
395 UBool haveKeywordMatch
=FALSE
;
396 // msgStart is 0 until we find any appropriate sub-message.
397 // We remember the first "other" sub-message if we have not seen any
398 // appropriate sub-message before.
399 // We remember the first matching-keyword sub-message if we have not seen
400 // one of those before.
401 // (The parser allows [does not check for] duplicate keywords.
402 // We just have to make sure to take the first one.)
403 // We avoid matching the keyword twice by also setting haveKeywordMatch=true
404 // at the first keyword match.
405 // We keep going until we find an explicit-value match or reach the end of the plural style.
407 // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples
408 // until ARG_LIMIT or end of plural-only pattern.
410 part
=&pattern
.getPart(partIndex
++);
411 const UMessagePatternPartType type
= part
->getType();
412 if(type
==UMSGPAT_PART_TYPE_ARG_LIMIT
) {
415 U_ASSERT (type
==UMSGPAT_PART_TYPE_ARG_SELECTOR
);
416 // part is an ARG_SELECTOR followed by an optional explicit value, and then a message
417 if(MessagePattern::Part::hasNumericValue(pattern
.getPartType(partIndex
))) {
418 // explicit value like "=2"
419 part
=&pattern
.getPart(partIndex
++);
420 if(number
==pattern
.getNumericValue(*part
)) {
421 // matches explicit value
424 } else if(!haveKeywordMatch
) {
425 // plural keyword like "few" or "other"
426 // Compare "other" first and call the selector if this is not "other".
427 if(pattern
.partSubstringMatches(*part
, other
)) {
430 if(0 == keyword
.compare(other
)) {
431 // This is the first "other" sub-message,
432 // and the selected keyword is also "other".
433 // Do not match "other" again.
434 haveKeywordMatch
=TRUE
;
438 if(keyword
.isEmpty()) {
439 keyword
=selector
.select(number
-offset
, ec
);
440 if(msgStart
!=0 && (0 == keyword
.compare(other
))) {
441 // We have already seen an "other" sub-message.
442 // Do not match "other" again.
443 haveKeywordMatch
=TRUE
;
444 // Skip keyword matching but do getLimitPartIndex().
447 if(!haveKeywordMatch
&& pattern
.partSubstringMatches(*part
, keyword
)) {
450 // Do not match this keyword again.
451 haveKeywordMatch
=TRUE
;
455 partIndex
=pattern
.getLimitPartIndex(partIndex
);
456 } while(++partIndex
<count
);
460 PluralFormat::PluralSelector::~PluralSelector() {}
462 PluralFormat::PluralSelectorAdapter::~PluralSelectorAdapter() {
466 UnicodeString
PluralFormat::PluralSelectorAdapter::select(double number
,
467 UErrorCode
& /*ec*/) const {
468 return pluralRules
->select(number
);
471 void PluralFormat::PluralSelectorAdapter::reset() {
480 #endif /* #if !UCONFIG_NO_FORMATTING */