2 *******************************************************************************
3 * Copyright (C) 2009-2015, 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"
19 #include "plurrule_impl.h"
23 #if !UCONFIG_NO_FORMATTING
27 static const UChar OTHER_STRING
[] = {
28 0x6F, 0x74, 0x68, 0x65, 0x72, 0 // "other"
31 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralFormat
)
33 PluralFormat::PluralFormat(UErrorCode
& status
)
34 : locale(Locale::getDefault()),
38 init(NULL
, UPLURAL_TYPE_CARDINAL
, status
);
41 PluralFormat::PluralFormat(const Locale
& loc
, UErrorCode
& status
)
46 init(NULL
, UPLURAL_TYPE_CARDINAL
, status
);
49 PluralFormat::PluralFormat(const PluralRules
& rules
, UErrorCode
& status
)
50 : locale(Locale::getDefault()),
54 init(&rules
, UPLURAL_TYPE_COUNT
, status
);
57 PluralFormat::PluralFormat(const Locale
& loc
,
58 const PluralRules
& rules
,
64 init(&rules
, UPLURAL_TYPE_COUNT
, status
);
67 PluralFormat::PluralFormat(const Locale
& loc
,
74 init(NULL
, type
, status
);
77 PluralFormat::PluralFormat(const UnicodeString
& pat
,
79 : locale(Locale::getDefault()),
83 init(NULL
, UPLURAL_TYPE_CARDINAL
, status
);
84 applyPattern(pat
, status
);
87 PluralFormat::PluralFormat(const Locale
& loc
,
88 const UnicodeString
& pat
,
94 init(NULL
, UPLURAL_TYPE_CARDINAL
, status
);
95 applyPattern(pat
, status
);
98 PluralFormat::PluralFormat(const PluralRules
& rules
,
99 const UnicodeString
& pat
,
101 : locale(Locale::getDefault()),
105 init(&rules
, UPLURAL_TYPE_COUNT
, status
);
106 applyPattern(pat
, status
);
109 PluralFormat::PluralFormat(const Locale
& loc
,
110 const PluralRules
& rules
,
111 const UnicodeString
& pat
,
117 init(&rules
, UPLURAL_TYPE_COUNT
, status
);
118 applyPattern(pat
, status
);
121 PluralFormat::PluralFormat(const Locale
& loc
,
123 const UnicodeString
& pat
,
129 init(NULL
, type
, status
);
130 applyPattern(pat
, status
);
133 PluralFormat::PluralFormat(const PluralFormat
& other
)
135 locale(other
.locale
),
136 msgPattern(other
.msgPattern
),
138 offset(other
.offset
) {
143 PluralFormat::copyObjects(const PluralFormat
& other
) {
144 UErrorCode status
= U_ZERO_ERROR
;
145 if (numberFormat
!= NULL
) {
148 if (pluralRulesWrapper
.pluralRules
!= NULL
) {
149 delete pluralRulesWrapper
.pluralRules
;
152 if (other
.numberFormat
== NULL
) {
153 numberFormat
= NumberFormat::createInstance(locale
, status
);
155 numberFormat
= (NumberFormat
*)other
.numberFormat
->clone();
157 if (other
.pluralRulesWrapper
.pluralRules
== NULL
) {
158 pluralRulesWrapper
.pluralRules
= PluralRules::forLocale(locale
, status
);
160 pluralRulesWrapper
.pluralRules
= other
.pluralRulesWrapper
.pluralRules
->clone();
165 PluralFormat::~PluralFormat() {
170 PluralFormat::init(const PluralRules
* rules
, UPluralType type
, UErrorCode
& status
) {
171 if (U_FAILURE(status
)) {
176 pluralRulesWrapper
.pluralRules
= PluralRules::forLocale(locale
, type
, status
);
178 pluralRulesWrapper
.pluralRules
= rules
->clone();
179 if (pluralRulesWrapper
.pluralRules
== NULL
) {
180 status
= U_MEMORY_ALLOCATION_ERROR
;
185 numberFormat
= NumberFormat::createInstance(locale
, status
);
189 PluralFormat::applyPattern(const UnicodeString
& newPattern
, UErrorCode
& status
) {
190 msgPattern
.parsePluralStyle(newPattern
, NULL
, status
);
191 if (U_FAILURE(status
)) {
196 offset
= msgPattern
.getPluralOffset(0);
200 PluralFormat::format(const Formattable
& obj
,
201 UnicodeString
& appendTo
,
203 UErrorCode
& status
) const
205 if (U_FAILURE(status
)) return appendTo
;
207 if (obj
.isNumeric()) {
208 return format(obj
, obj
.getDouble(), appendTo
, pos
, status
);
210 status
= U_ILLEGAL_ARGUMENT_ERROR
;
216 PluralFormat::format(int32_t number
, UErrorCode
& status
) const {
217 FieldPosition
fpos(0);
218 UnicodeString result
;
219 return format(Formattable(number
), number
, result
, fpos
, status
);
223 PluralFormat::format(double number
, UErrorCode
& status
) const {
224 FieldPosition
fpos(0);
225 UnicodeString result
;
226 return format(Formattable(number
), number
, result
, fpos
, status
);
231 PluralFormat::format(int32_t number
,
232 UnicodeString
& appendTo
,
234 UErrorCode
& status
) const {
235 return format(Formattable(number
), (double)number
, appendTo
, pos
, status
);
239 PluralFormat::format(double number
,
240 UnicodeString
& appendTo
,
242 UErrorCode
& status
) const {
243 return format(Formattable(number
), (double)number
, appendTo
, pos
, status
);
247 PluralFormat::format(const Formattable
& numberObject
, double number
,
248 UnicodeString
& appendTo
,
250 UErrorCode
& status
) const {
251 if (U_FAILURE(status
)) {
254 if (msgPattern
.countParts() == 0) {
255 return numberFormat
->format(numberObject
, appendTo
, pos
, status
);
257 // Get the appropriate sub-message.
258 // Select it based on the formatted number-offset.
259 double numberMinusOffset
= number
- offset
;
260 UnicodeString numberString
;
261 FieldPosition ignorePos
;
262 FixedDecimal
dec(numberMinusOffset
);
264 numberFormat
->format(numberObject
, numberString
, ignorePos
, status
); // could be BigDecimal etc.
265 DecimalFormat
*decFmt
= dynamic_cast<DecimalFormat
*>(numberFormat
);
267 dec
= decFmt
->getFixedDecimal(numberObject
, status
);
270 numberFormat
->format(numberMinusOffset
, numberString
, ignorePos
, status
);
271 DecimalFormat
*decFmt
= dynamic_cast<DecimalFormat
*>(numberFormat
);
273 dec
= decFmt
->getFixedDecimal(numberMinusOffset
, status
);
276 int32_t partIndex
= findSubMessage(msgPattern
, 0, pluralRulesWrapper
, &dec
, number
, status
);
277 if (U_FAILURE(status
)) { return appendTo
; }
278 // Replace syntactic # signs in the top level of this sub-message
279 // (not in nested arguments) with the formatted number-offset.
280 const UnicodeString
& pattern
= msgPattern
.getPatternString();
281 int32_t prevIndex
= msgPattern
.getPart(partIndex
).getLimit();
283 const MessagePattern::Part
& part
= msgPattern
.getPart(++partIndex
);
284 const UMessagePatternPartType type
= part
.getType();
285 int32_t index
= part
.getIndex();
286 if (type
== UMSGPAT_PART_TYPE_MSG_LIMIT
) {
287 return appendTo
.append(pattern
, prevIndex
, index
- prevIndex
);
288 } else if ((type
== UMSGPAT_PART_TYPE_REPLACE_NUMBER
) ||
289 (type
== UMSGPAT_PART_TYPE_SKIP_SYNTAX
&& MessageImpl::jdkAposMode(msgPattern
))) {
290 appendTo
.append(pattern
, prevIndex
, index
- prevIndex
);
291 if (type
== UMSGPAT_PART_TYPE_REPLACE_NUMBER
) {
292 appendTo
.append(numberString
);
294 prevIndex
= part
.getLimit();
295 } else if (type
== UMSGPAT_PART_TYPE_ARG_START
) {
296 appendTo
.append(pattern
, prevIndex
, index
- prevIndex
);
298 partIndex
= msgPattern
.getLimitPartIndex(partIndex
);
299 index
= msgPattern
.getPart(partIndex
).getLimit();
300 MessageImpl::appendReducedApostrophes(pattern
, prevIndex
, index
, appendTo
);
307 PluralFormat::toPattern(UnicodeString
& appendTo
) {
308 if (0 == msgPattern
.countParts()) {
309 appendTo
.setToBogus();
311 appendTo
.append(msgPattern
.getPatternString());
317 PluralFormat::setLocale(const Locale
& loc
, UErrorCode
& status
) {
318 if (U_FAILURE(status
)) {
326 pluralRulesWrapper
.reset();
327 init(NULL
, UPLURAL_TYPE_CARDINAL
, status
);
331 PluralFormat::setNumberFormat(const NumberFormat
* format
, UErrorCode
& status
) {
332 if (U_FAILURE(status
)) {
335 NumberFormat
* nf
= (NumberFormat
*)format
->clone();
340 status
= U_MEMORY_ALLOCATION_ERROR
;
345 PluralFormat::clone() const
347 return new PluralFormat(*this);
352 PluralFormat::operator=(const PluralFormat
& other
) {
353 if (this != &other
) {
354 locale
= other
.locale
;
355 msgPattern
= other
.msgPattern
;
356 offset
= other
.offset
;
364 PluralFormat::operator==(const Format
& other
) const {
365 if (this == &other
) {
368 if (!Format::operator==(other
)) {
371 const PluralFormat
& o
= (const PluralFormat
&)other
;
373 locale
== o
.locale
&&
374 msgPattern
== o
.msgPattern
&& // implies same offset
375 (numberFormat
== NULL
) == (o
.numberFormat
== NULL
) &&
376 (numberFormat
== NULL
|| *numberFormat
== *o
.numberFormat
) &&
377 (pluralRulesWrapper
.pluralRules
== NULL
) == (o
.pluralRulesWrapper
.pluralRules
== NULL
) &&
378 (pluralRulesWrapper
.pluralRules
== NULL
||
379 *pluralRulesWrapper
.pluralRules
== *o
.pluralRulesWrapper
.pluralRules
);
383 PluralFormat::operator!=(const Format
& other
) const {
384 return !operator==(other
);
388 PluralFormat::parseObject(const UnicodeString
& /*source*/,
389 Formattable
& /*result*/,
390 ParsePosition
& pos
) const
392 // Parsing not supported.
393 pos
.setErrorIndex(pos
.getIndex());
396 int32_t PluralFormat::findSubMessage(const MessagePattern
& pattern
, int32_t partIndex
,
397 const PluralSelector
& selector
, void *context
,
398 double number
, UErrorCode
& ec
) {
402 int32_t count
=pattern
.countParts();
404 const MessagePattern::Part
* part
=&pattern
.getPart(partIndex
);
405 if (MessagePattern::Part::hasNumericValue(part
->getType())) {
406 offset
=pattern
.getNumericValue(*part
);
411 // The keyword is empty until we need to match against a non-explicit, not-"other" value.
412 // Then we get the keyword from the selector.
413 // (In other words, we never call the selector if we match against an explicit value,
414 // or if the only non-explicit keyword is "other".)
415 UnicodeString keyword
;
416 UnicodeString
other(FALSE
, OTHER_STRING
, 5);
417 // When we find a match, we set msgStart>0 and also set this boolean to true
418 // to avoid matching the keyword again (duplicates are allowed)
419 // while we continue to look for an explicit-value match.
420 UBool haveKeywordMatch
=FALSE
;
421 // msgStart is 0 until we find any appropriate sub-message.
422 // We remember the first "other" sub-message if we have not seen any
423 // appropriate sub-message before.
424 // We remember the first matching-keyword sub-message if we have not seen
425 // one of those before.
426 // (The parser allows [does not check for] duplicate keywords.
427 // We just have to make sure to take the first one.)
428 // We avoid matching the keyword twice by also setting haveKeywordMatch=true
429 // at the first keyword match.
430 // We keep going until we find an explicit-value match or reach the end of the plural style.
432 // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples
433 // until ARG_LIMIT or end of plural-only pattern.
435 part
=&pattern
.getPart(partIndex
++);
436 const UMessagePatternPartType type
= part
->getType();
437 if(type
==UMSGPAT_PART_TYPE_ARG_LIMIT
) {
440 U_ASSERT (type
==UMSGPAT_PART_TYPE_ARG_SELECTOR
);
441 // part is an ARG_SELECTOR followed by an optional explicit value, and then a message
442 if(MessagePattern::Part::hasNumericValue(pattern
.getPartType(partIndex
))) {
443 // explicit value like "=2"
444 part
=&pattern
.getPart(partIndex
++);
445 if(number
==pattern
.getNumericValue(*part
)) {
446 // matches explicit value
449 } else if(!haveKeywordMatch
) {
450 // plural keyword like "few" or "other"
451 // Compare "other" first and call the selector if this is not "other".
452 if(pattern
.partSubstringMatches(*part
, other
)) {
455 if(0 == keyword
.compare(other
)) {
456 // This is the first "other" sub-message,
457 // and the selected keyword is also "other".
458 // Do not match "other" again.
459 haveKeywordMatch
=TRUE
;
463 if(keyword
.isEmpty()) {
464 keyword
=selector
.select(context
, number
-offset
, ec
);
465 if(msgStart
!=0 && (0 == keyword
.compare(other
))) {
466 // We have already seen an "other" sub-message.
467 // Do not match "other" again.
468 haveKeywordMatch
=TRUE
;
469 // Skip keyword matching but do getLimitPartIndex().
472 if(!haveKeywordMatch
&& pattern
.partSubstringMatches(*part
, keyword
)) {
475 // Do not match this keyword again.
476 haveKeywordMatch
=TRUE
;
480 partIndex
=pattern
.getLimitPartIndex(partIndex
);
481 } while(++partIndex
<count
);
485 void PluralFormat::parseType(const UnicodeString
& source
, const NFRule
*rbnfLenientScanner
, Formattable
& result
, FieldPosition
& pos
) const {
486 // If no pattern was applied, return null.
487 if (msgPattern
.countParts() == 0) {
488 pos
.setBeginIndex(-1);
494 int count
=msgPattern
.countParts();
495 int startingAt
= pos
.getBeginIndex();
496 if (startingAt
< 0) {
500 // The keyword is null until we need to match against a non-explicit, not-"other" value.
501 // Then we get the keyword from the selector.
502 // (In other words, we never call the selector if we match against an explicit value,
503 // or if the only non-explicit keyword is "other".)
504 UnicodeString keyword
;
505 UnicodeString matchedWord
;
506 const UnicodeString
& pattern
= msgPattern
.getPatternString();
507 int matchedIndex
= -1;
508 // Iterate over (ARG_SELECTOR ARG_START message ARG_LIMIT) tuples
509 // until the end of the plural-only pattern.
510 while (partIndex
< count
) {
511 const MessagePattern::Part
* partSelector
= &msgPattern
.getPart(partIndex
++);
512 if (partSelector
->getType() != UMSGPAT_PART_TYPE_ARG_SELECTOR
) {
517 const MessagePattern::Part
* partStart
= &msgPattern
.getPart(partIndex
++);
518 if (partStart
->getType() != UMSGPAT_PART_TYPE_MSG_START
) {
523 const MessagePattern::Part
* partLimit
= &msgPattern
.getPart(partIndex
++);
524 if (partLimit
->getType() != UMSGPAT_PART_TYPE_MSG_LIMIT
) {
529 UnicodeString currArg
= pattern
.tempSubString(partStart
->getLimit(), partLimit
->getIndex() - partStart
->getLimit());
530 if (rbnfLenientScanner
!= NULL
) {
531 // If lenient parsing is turned ON, we've got some time consuming parsing ahead of us.
533 currMatchIndex
= rbnfLenientScanner
->findTextLenient(source
, currArg
, startingAt
, &length
);
536 currMatchIndex
= source
.indexOf(currArg
, startingAt
);
538 if (currMatchIndex
>= 0 && currMatchIndex
>= matchedIndex
&& currArg
.length() > matchedWord
.length()) {
539 matchedIndex
= currMatchIndex
;
540 matchedWord
= currArg
;
541 keyword
= pattern
.tempSubString(partStart
->getLimit(), partLimit
->getIndex() - partStart
->getLimit());
544 if (matchedIndex
>= 0) {
545 pos
.setBeginIndex(matchedIndex
);
546 pos
.setEndIndex(matchedIndex
+ matchedWord
.length());
547 result
.setString(keyword
);
552 pos
.setBeginIndex(-1);
556 PluralFormat::PluralSelector::~PluralSelector() {}
558 PluralFormat::PluralSelectorAdapter::~PluralSelectorAdapter() {
562 UnicodeString
PluralFormat::PluralSelectorAdapter::select(void *context
, double number
,
563 UErrorCode
& /*ec*/) const {
564 (void)number
; // unused except in the assertion
565 FixedDecimal
*dec
=static_cast<FixedDecimal
*>(context
);
566 U_ASSERT(dec
->source
==number
);
567 return pluralRules
->select(*dec
);
570 void PluralFormat::PluralSelectorAdapter::reset() {
579 #endif /* #if !UCONFIG_NO_FORMATTING */