]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/plurfmt.cpp
ICU-531.48.tar.gz
[apple/icu.git] / icuSources / i18n / plurfmt.cpp
CommitLineData
46f4442e
A
1/*
2*******************************************************************************
57a6839d 3* Copyright (C) 2009-2013, International Business Machines Corporation and
46f4442e
A
4* others. All Rights Reserved.
5*******************************************************************************
6*
7* File PLURFMT.CPP
46f4442e
A
8*******************************************************************************
9*/
10
57a6839d 11#include "unicode/decimfmt.h"
4388f060 12#include "unicode/messagepattern.h"
46f4442e
A
13#include "unicode/plurfmt.h"
14#include "unicode/plurrule.h"
4388f060
A
15#include "unicode/utypes.h"
16#include "cmemory.h"
17#include "messageimpl.h"
46f4442e 18#include "plurrule_impl.h"
4388f060
A
19#include "uassert.h"
20#include "uhash.h"
46f4442e
A
21
22#if !UCONFIG_NO_FORMATTING
23
24U_NAMESPACE_BEGIN
25
4388f060
A
26static const UChar OTHER_STRING[] = {
27 0x6F, 0x74, 0x68, 0x65, 0x72, 0 // "other"
28};
46f4442e
A
29
30UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralFormat)
31
4388f060
A
32PluralFormat::PluralFormat(UErrorCode& status)
33 : locale(Locale::getDefault()),
34 msgPattern(status),
35 numberFormat(NULL),
36 offset(0) {
51004dcb 37 init(NULL, UPLURAL_TYPE_CARDINAL, status);
46f4442e
A
38}
39
4388f060
A
40PluralFormat::PluralFormat(const Locale& loc, UErrorCode& status)
41 : locale(loc),
42 msgPattern(status),
43 numberFormat(NULL),
44 offset(0) {
51004dcb 45 init(NULL, UPLURAL_TYPE_CARDINAL, status);
46f4442e
A
46}
47
4388f060
A
48PluralFormat::PluralFormat(const PluralRules& rules, UErrorCode& status)
49 : locale(Locale::getDefault()),
50 msgPattern(status),
51 numberFormat(NULL),
52 offset(0) {
51004dcb 53 init(&rules, UPLURAL_TYPE_COUNT, status);
46f4442e
A
54}
55
4388f060
A
56PluralFormat::PluralFormat(const Locale& loc,
57 const PluralRules& rules,
58 UErrorCode& status)
59 : locale(loc),
60 msgPattern(status),
61 numberFormat(NULL),
62 offset(0) {
51004dcb
A
63 init(&rules, UPLURAL_TYPE_COUNT, status);
64}
65
66PluralFormat::PluralFormat(const Locale& loc,
67 UPluralType type,
68 UErrorCode& status)
69 : locale(loc),
70 msgPattern(status),
71 numberFormat(NULL),
72 offset(0) {
73 init(NULL, type, status);
46f4442e
A
74}
75
4388f060
A
76PluralFormat::PluralFormat(const UnicodeString& pat,
77 UErrorCode& status)
78 : locale(Locale::getDefault()),
79 msgPattern(status),
80 numberFormat(NULL),
81 offset(0) {
51004dcb 82 init(NULL, UPLURAL_TYPE_CARDINAL, status);
46f4442e
A
83 applyPattern(pat, status);
84}
85
4388f060
A
86PluralFormat::PluralFormat(const Locale& loc,
87 const UnicodeString& pat,
88 UErrorCode& status)
89 : locale(loc),
90 msgPattern(status),
91 numberFormat(NULL),
92 offset(0) {
51004dcb 93 init(NULL, UPLURAL_TYPE_CARDINAL, status);
46f4442e
A
94 applyPattern(pat, status);
95}
96
4388f060
A
97PluralFormat::PluralFormat(const PluralRules& rules,
98 const UnicodeString& pat,
99 UErrorCode& status)
100 : locale(Locale::getDefault()),
101 msgPattern(status),
102 numberFormat(NULL),
103 offset(0) {
51004dcb 104 init(&rules, UPLURAL_TYPE_COUNT, status);
46f4442e
A
105 applyPattern(pat, status);
106}
107
4388f060
A
108PluralFormat::PluralFormat(const Locale& loc,
109 const PluralRules& rules,
110 const UnicodeString& pat,
111 UErrorCode& status)
112 : locale(loc),
113 msgPattern(status),
114 numberFormat(NULL),
115 offset(0) {
51004dcb
A
116 init(&rules, UPLURAL_TYPE_COUNT, status);
117 applyPattern(pat, status);
118}
119
120PluralFormat::PluralFormat(const Locale& loc,
121 UPluralType type,
122 const UnicodeString& pat,
123 UErrorCode& status)
124 : locale(loc),
125 msgPattern(status),
126 numberFormat(NULL),
127 offset(0) {
128 init(NULL, type, status);
46f4442e
A
129 applyPattern(pat, status);
130}
131
4388f060
A
132PluralFormat::PluralFormat(const PluralFormat& other)
133 : Format(other),
134 locale(other.locale),
135 msgPattern(other.msgPattern),
136 numberFormat(NULL),
137 offset(other.offset) {
138 copyObjects(other);
139}
140
141void
142PluralFormat::copyObjects(const PluralFormat& other) {
46f4442e 143 UErrorCode status = U_ZERO_ERROR;
4388f060
A
144 if (numberFormat != NULL) {
145 delete numberFormat;
729e4ab9 146 }
4388f060
A
147 if (pluralRulesWrapper.pluralRules != NULL) {
148 delete pluralRulesWrapper.pluralRules;
149 }
150
151 if (other.numberFormat == NULL) {
152 numberFormat = NumberFormat::createInstance(locale, status);
153 } else {
154 numberFormat = (NumberFormat*)other.numberFormat->clone();
155 }
156 if (other.pluralRulesWrapper.pluralRules == NULL) {
157 pluralRulesWrapper.pluralRules = PluralRules::forLocale(locale, status);
158 } else {
159 pluralRulesWrapper.pluralRules = other.pluralRulesWrapper.pluralRules->clone();
729e4ab9 160 }
46f4442e
A
161}
162
4388f060 163
46f4442e 164PluralFormat::~PluralFormat() {
46f4442e
A
165 delete numberFormat;
166}
167
168void
51004dcb 169PluralFormat::init(const PluralRules* rules, UPluralType type, UErrorCode& status) {
729e4ab9
A
170 if (U_FAILURE(status)) {
171 return;
172 }
4388f060
A
173
174 if (rules==NULL) {
51004dcb 175 pluralRulesWrapper.pluralRules = PluralRules::forLocale(locale, type, status);
4388f060
A
176 } else {
177 pluralRulesWrapper.pluralRules = rules->clone();
178 if (pluralRulesWrapper.pluralRules == NULL) {
179 status = U_MEMORY_ALLOCATION_ERROR;
729e4ab9
A
180 return;
181 }
46f4442e 182 }
4388f060
A
183
184 numberFormat= NumberFormat::createInstance(locale, status);
46f4442e
A
185}
186
187void
188PluralFormat::applyPattern(const UnicodeString& newPattern, UErrorCode& status) {
4388f060 189 msgPattern.parsePluralStyle(newPattern, NULL, status);
729e4ab9 190 if (U_FAILURE(status)) {
4388f060
A
191 msgPattern.clear();
192 offset = 0;
729e4ab9
A
193 return;
194 }
4388f060 195 offset = msgPattern.getPluralOffset(0);
46f4442e
A
196}
197
198UnicodeString&
199PluralFormat::format(const Formattable& obj,
200 UnicodeString& appendTo,
201 FieldPosition& pos,
202 UErrorCode& status) const
203{
204 if (U_FAILURE(status)) return appendTo;
4388f060
A
205
206 if (obj.isNumeric()) {
57a6839d 207 return format(obj, obj.getDouble(), appendTo, pos, status);
4388f060 208 } else {
46f4442e
A
209 status = U_ILLEGAL_ARGUMENT_ERROR;
210 return appendTo;
211 }
212}
213
214UnicodeString
215PluralFormat::format(int32_t number, UErrorCode& status) const {
216 FieldPosition fpos(0);
217 UnicodeString result;
57a6839d 218 return format(Formattable(number), number, result, fpos, status);
46f4442e
A
219}
220
221UnicodeString
222PluralFormat::format(double number, UErrorCode& status) const {
223 FieldPosition fpos(0);
224 UnicodeString result;
57a6839d 225 return format(Formattable(number), number, result, fpos, status);
46f4442e
A
226}
227
228
229UnicodeString&
230PluralFormat::format(int32_t number,
4388f060 231 UnicodeString& appendTo,
46f4442e
A
232 FieldPosition& pos,
233 UErrorCode& status) const {
57a6839d 234 return format(Formattable(number), (double)number, appendTo, pos, status);
46f4442e
A
235}
236
237UnicodeString&
238PluralFormat::format(double number,
4388f060 239 UnicodeString& appendTo,
46f4442e 240 FieldPosition& pos,
4388f060 241 UErrorCode& status) const {
57a6839d
A
242 return format(Formattable(number), (double)number, appendTo, pos, status);
243}
244
245UnicodeString&
246PluralFormat::format(const Formattable& numberObject, double number,
247 UnicodeString& appendTo,
248 FieldPosition& pos,
249 UErrorCode& status) const {
4388f060
A
250 if (U_FAILURE(status)) {
251 return appendTo;
46f4442e 252 }
4388f060 253 if (msgPattern.countParts() == 0) {
57a6839d 254 return numberFormat->format(numberObject, appendTo, pos, status);
4388f060
A
255 }
256 // Get the appropriate sub-message.
57a6839d
A
257 // Select it based on the formatted number-offset.
258 double numberMinusOffset = number - offset;
259 UnicodeString numberString;
260 FieldPosition ignorePos;
261 FixedDecimal dec(numberMinusOffset);
262 if (offset == 0) {
263 numberFormat->format(numberObject, numberString, ignorePos, status); // could be BigDecimal etc.
264 DecimalFormat *decFmt = dynamic_cast<DecimalFormat *>(numberFormat);
265 if(decFmt != NULL) {
266 dec = decFmt->getFixedDecimal(numberObject, status);
267 }
268 } else {
269 numberFormat->format(numberMinusOffset, numberString, ignorePos, status);
270 DecimalFormat *decFmt = dynamic_cast<DecimalFormat *>(numberFormat);
271 if(decFmt != NULL) {
272 dec = decFmt->getFixedDecimal(numberMinusOffset, status);
273 }
274 }
275 int32_t partIndex = findSubMessage(msgPattern, 0, pluralRulesWrapper, &dec, number, status);
276 if (U_FAILURE(status)) { return appendTo; }
4388f060
A
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();
4388f060
A
280 int32_t prevIndex = msgPattern.getPart(partIndex).getLimit();
281 for (;;) {
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) {
57a6839d 291 appendTo.append(numberString);
4388f060
A
292 }
293 prevIndex = part.getLimit();
294 } else if (type == UMSGPAT_PART_TYPE_ARG_START) {
295 appendTo.append(pattern, prevIndex, index - prevIndex);
296 prevIndex = index;
297 partIndex = msgPattern.getLimitPartIndex(partIndex);
298 index = msgPattern.getPart(partIndex).getLimit();
299 MessageImpl::appendReducedApostrophes(pattern, prevIndex, index, appendTo);
300 prevIndex = index;
301 }
46f4442e 302 }
46f4442e
A
303}
304
305UnicodeString&
306PluralFormat::toPattern(UnicodeString& appendTo) {
4388f060
A
307 if (0 == msgPattern.countParts()) {
308 appendTo.setToBogus();
309 } else {
310 appendTo.append(msgPattern.getPatternString());
46f4442e 311 }
4388f060 312 return appendTo;
46f4442e
A
313}
314
4388f060
A
315void
316PluralFormat::setLocale(const Locale& loc, UErrorCode& status) {
317 if (U_FAILURE(status)) {
318 return;
46f4442e 319 }
4388f060
A
320 locale = loc;
321 msgPattern.clear();
322 delete numberFormat;
323 offset = 0;
324 numberFormat = NULL;
325 pluralRulesWrapper.reset();
51004dcb 326 init(NULL, UPLURAL_TYPE_CARDINAL, status);
46f4442e
A
327}
328
329void
4388f060 330PluralFormat::setNumberFormat(const NumberFormat* format, UErrorCode& status) {
729e4ab9
A
331 if (U_FAILURE(status)) {
332 return;
333 }
4388f060
A
334 NumberFormat* nf = (NumberFormat*)format->clone();
335 if (nf != NULL) {
46f4442e 336 delete numberFormat;
4388f060
A
337 numberFormat = nf;
338 } else {
339 status = U_MEMORY_ALLOCATION_ERROR;
46f4442e 340 }
46f4442e
A
341}
342
343Format*
344PluralFormat::clone() const
345{
346 return new PluralFormat(*this);
347}
348
4388f060 349
46f4442e
A
350PluralFormat&
351PluralFormat::operator=(const PluralFormat& other) {
352 if (this != &other) {
46f4442e 353 locale = other.locale;
4388f060
A
354 msgPattern = other.msgPattern;
355 offset = other.offset;
356 copyObjects(other);
46f4442e
A
357 }
358
359 return *this;
360}
361
362UBool
363PluralFormat::operator==(const Format& other) const {
4388f060
A
364 if (this == &other) {
365 return TRUE;
366 }
367 if (!Format::operator==(other)) {
368 return FALSE;
369 }
370 const PluralFormat& o = (const PluralFormat&)other;
371 return
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);
46f4442e
A
379}
380
381UBool
382PluralFormat::operator!=(const Format& other) const {
383 return !operator==(other);
384}
385
386void
387PluralFormat::parseObject(const UnicodeString& /*source*/,
388 Formattable& /*result*/,
4388f060 389 ParsePosition& pos) const
46f4442e 390{
4388f060
A
391 // Parsing not supported.
392 pos.setErrorIndex(pos.getIndex());
46f4442e
A
393}
394
4388f060 395int32_t PluralFormat::findSubMessage(const MessagePattern& pattern, int32_t partIndex,
57a6839d
A
396 const PluralSelector& selector, void *context,
397 double number, UErrorCode& ec) {
4388f060
A
398 if (U_FAILURE(ec)) {
399 return 0;
400 }
401 int32_t count=pattern.countParts();
402 double offset;
403 const MessagePattern::Part* part=&pattern.getPart(partIndex);
404 if (MessagePattern::Part::hasNumericValue(part->getType())) {
405 offset=pattern.getNumericValue(*part);
406 ++partIndex;
407 } else {
408 offset=0;
409 }
57a6839d 410 // The keyword is empty until we need to match against a non-explicit, not-"other" value.
4388f060
A
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.
430 int32_t msgStart=0;
431 // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples
432 // until ARG_LIMIT or end of plural-only pattern.
433 do {
434 part=&pattern.getPart(partIndex++);
435 const UMessagePatternPartType type = part->getType();
436 if(type==UMSGPAT_PART_TYPE_ARG_LIMIT) {
46f4442e 437 break;
4388f060
A
438 }
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
446 return partIndex;
447 }
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)) {
452 if(msgStart==0) {
453 msgStart=partIndex;
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;
459 }
460 }
461 } else {
462 if(keyword.isEmpty()) {
57a6839d 463 keyword=selector.select(context, number-offset, ec);
4388f060
A
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().
469 }
470 }
471 if(!haveKeywordMatch && pattern.partSubstringMatches(*part, keyword)) {
472 // keyword matches
473 msgStart=partIndex;
474 // Do not match this keyword again.
475 haveKeywordMatch=TRUE;
476 }
46f4442e 477 }
46f4442e 478 }
4388f060
A
479 partIndex=pattern.getLimitPartIndex(partIndex);
480 } while(++partIndex<count);
481 return msgStart;
46f4442e
A
482}
483
4388f060
A
484PluralFormat::PluralSelector::~PluralSelector() {}
485
486PluralFormat::PluralSelectorAdapter::~PluralSelectorAdapter() {
487 delete pluralRules;
488}
489
57a6839d 490UnicodeString PluralFormat::PluralSelectorAdapter::select(void *context, double number,
4388f060 491 UErrorCode& /*ec*/) const {
57a6839d
A
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);
4388f060
A
496}
497
498void PluralFormat::PluralSelectorAdapter::reset() {
499 delete pluralRules;
500 pluralRules = NULL;
46f4442e
A
501}
502
503
504U_NAMESPACE_END
505
506
507#endif /* #if !UCONFIG_NO_FORMATTING */
508
509//eof