• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 *******************************************************************************
3 * Copyright (C) 2009-2013, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
6 *
7 * File PLURFMT.CPP
8 *******************************************************************************
9 */
10 
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"
16 #include "cmemory.h"
17 #include "messageimpl.h"
18 #include "plurrule_impl.h"
19 #include "uassert.h"
20 #include "uhash.h"
21 
22 #if !UCONFIG_NO_FORMATTING
23 
24 U_NAMESPACE_BEGIN
25 
26 static const UChar OTHER_STRING[] = {
27     0x6F, 0x74, 0x68, 0x65, 0x72, 0  // "other"
28 };
29 
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralFormat)30 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralFormat)
31 
32 PluralFormat::PluralFormat(UErrorCode& status)
33         : locale(Locale::getDefault()),
34           msgPattern(status),
35           numberFormat(NULL),
36           offset(0) {
37     init(NULL, UPLURAL_TYPE_CARDINAL, status);
38 }
39 
PluralFormat(const Locale & loc,UErrorCode & status)40 PluralFormat::PluralFormat(const Locale& loc, UErrorCode& status)
41         : locale(loc),
42           msgPattern(status),
43           numberFormat(NULL),
44           offset(0) {
45     init(NULL, UPLURAL_TYPE_CARDINAL, status);
46 }
47 
PluralFormat(const PluralRules & rules,UErrorCode & status)48 PluralFormat::PluralFormat(const PluralRules& rules, UErrorCode& status)
49         : locale(Locale::getDefault()),
50           msgPattern(status),
51           numberFormat(NULL),
52           offset(0) {
53     init(&rules, UPLURAL_TYPE_COUNT, status);
54 }
55 
PluralFormat(const Locale & loc,const PluralRules & rules,UErrorCode & status)56 PluralFormat::PluralFormat(const Locale& loc,
57                            const PluralRules& rules,
58                            UErrorCode& status)
59         : locale(loc),
60           msgPattern(status),
61           numberFormat(NULL),
62           offset(0) {
63     init(&rules, UPLURAL_TYPE_COUNT, status);
64 }
65 
PluralFormat(const Locale & loc,UPluralType type,UErrorCode & status)66 PluralFormat::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);
74 }
75 
PluralFormat(const UnicodeString & pat,UErrorCode & status)76 PluralFormat::PluralFormat(const UnicodeString& pat,
77                            UErrorCode& status)
78         : locale(Locale::getDefault()),
79           msgPattern(status),
80           numberFormat(NULL),
81           offset(0) {
82     init(NULL, UPLURAL_TYPE_CARDINAL, status);
83     applyPattern(pat, status);
84 }
85 
PluralFormat(const Locale & loc,const UnicodeString & pat,UErrorCode & status)86 PluralFormat::PluralFormat(const Locale& loc,
87                            const UnicodeString& pat,
88                            UErrorCode& status)
89         : locale(loc),
90           msgPattern(status),
91           numberFormat(NULL),
92           offset(0) {
93     init(NULL, UPLURAL_TYPE_CARDINAL, status);
94     applyPattern(pat, status);
95 }
96 
PluralFormat(const PluralRules & rules,const UnicodeString & pat,UErrorCode & status)97 PluralFormat::PluralFormat(const PluralRules& rules,
98                            const UnicodeString& pat,
99                            UErrorCode& status)
100         : locale(Locale::getDefault()),
101           msgPattern(status),
102           numberFormat(NULL),
103           offset(0) {
104     init(&rules, UPLURAL_TYPE_COUNT, status);
105     applyPattern(pat, status);
106 }
107 
PluralFormat(const Locale & loc,const PluralRules & rules,const UnicodeString & pat,UErrorCode & status)108 PluralFormat::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) {
116     init(&rules, UPLURAL_TYPE_COUNT, status);
117     applyPattern(pat, status);
118 }
119 
PluralFormat(const Locale & loc,UPluralType type,const UnicodeString & pat,UErrorCode & status)120 PluralFormat::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);
129     applyPattern(pat, status);
130 }
131 
PluralFormat(const PluralFormat & other)132 PluralFormat::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 
141 void
copyObjects(const PluralFormat & other)142 PluralFormat::copyObjects(const PluralFormat& other) {
143     UErrorCode status = U_ZERO_ERROR;
144     if (numberFormat != NULL) {
145         delete numberFormat;
146     }
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();
160     }
161 }
162 
163 
~PluralFormat()164 PluralFormat::~PluralFormat() {
165     delete numberFormat;
166 }
167 
168 void
init(const PluralRules * rules,UPluralType type,UErrorCode & status)169 PluralFormat::init(const PluralRules* rules, UPluralType type, UErrorCode& status) {
170     if (U_FAILURE(status)) {
171         return;
172     }
173 
174     if (rules==NULL) {
175         pluralRulesWrapper.pluralRules = PluralRules::forLocale(locale, type, status);
176     } else {
177         pluralRulesWrapper.pluralRules = rules->clone();
178         if (pluralRulesWrapper.pluralRules == NULL) {
179             status = U_MEMORY_ALLOCATION_ERROR;
180             return;
181         }
182     }
183 
184     numberFormat= NumberFormat::createInstance(locale, status);
185 }
186 
187 void
applyPattern(const UnicodeString & newPattern,UErrorCode & status)188 PluralFormat::applyPattern(const UnicodeString& newPattern, UErrorCode& status) {
189     msgPattern.parsePluralStyle(newPattern, NULL, status);
190     if (U_FAILURE(status)) {
191         msgPattern.clear();
192         offset = 0;
193         return;
194     }
195     offset = msgPattern.getPluralOffset(0);
196 }
197 
198 UnicodeString&
format(const Formattable & obj,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const199 PluralFormat::format(const Formattable& obj,
200                    UnicodeString& appendTo,
201                    FieldPosition& pos,
202                    UErrorCode& status) const
203 {
204     if (U_FAILURE(status)) return appendTo;
205 
206     if (obj.isNumeric()) {
207         return format(obj, obj.getDouble(), appendTo, pos, status);
208     } else {
209         status = U_ILLEGAL_ARGUMENT_ERROR;
210         return appendTo;
211     }
212 }
213 
214 UnicodeString
format(int32_t number,UErrorCode & status) const215 PluralFormat::format(int32_t number, UErrorCode& status) const {
216     FieldPosition fpos(0);
217     UnicodeString result;
218     return format(Formattable(number), number, result, fpos, status);
219 }
220 
221 UnicodeString
format(double number,UErrorCode & status) const222 PluralFormat::format(double number, UErrorCode& status) const {
223     FieldPosition fpos(0);
224     UnicodeString result;
225     return format(Formattable(number), number, result, fpos, status);
226 }
227 
228 
229 UnicodeString&
format(int32_t number,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const230 PluralFormat::format(int32_t number,
231                      UnicodeString& appendTo,
232                      FieldPosition& pos,
233                      UErrorCode& status) const {
234     return format(Formattable(number), (double)number, appendTo, pos, status);
235 }
236 
237 UnicodeString&
format(double number,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const238 PluralFormat::format(double number,
239                      UnicodeString& appendTo,
240                      FieldPosition& pos,
241                      UErrorCode& status) const {
242     return format(Formattable(number), (double)number, appendTo, pos, status);
243 }
244 
245 UnicodeString&
format(const Formattable & numberObject,double number,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const246 PluralFormat::format(const Formattable& numberObject, double number,
247                      UnicodeString& appendTo,
248                      FieldPosition& pos,
249                      UErrorCode& status) const {
250     if (U_FAILURE(status)) {
251         return appendTo;
252     }
253     if (msgPattern.countParts() == 0) {
254         return numberFormat->format(numberObject, appendTo, pos, status);
255     }
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);
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; }
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();
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) {
291                 appendTo.append(numberString);
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         }
302     }
303 }
304 
305 UnicodeString&
toPattern(UnicodeString & appendTo)306 PluralFormat::toPattern(UnicodeString& appendTo) {
307     if (0 == msgPattern.countParts()) {
308         appendTo.setToBogus();
309     } else {
310         appendTo.append(msgPattern.getPatternString());
311     }
312     return appendTo;
313 }
314 
315 void
setLocale(const Locale & loc,UErrorCode & status)316 PluralFormat::setLocale(const Locale& loc, UErrorCode& status) {
317     if (U_FAILURE(status)) {
318         return;
319     }
320     locale = loc;
321     msgPattern.clear();
322     delete numberFormat;
323     offset = 0;
324     numberFormat = NULL;
325     pluralRulesWrapper.reset();
326     init(NULL, UPLURAL_TYPE_CARDINAL, status);
327 }
328 
329 void
setNumberFormat(const NumberFormat * format,UErrorCode & status)330 PluralFormat::setNumberFormat(const NumberFormat* format, UErrorCode& status) {
331     if (U_FAILURE(status)) {
332         return;
333     }
334     NumberFormat* nf = (NumberFormat*)format->clone();
335     if (nf != NULL) {
336         delete numberFormat;
337         numberFormat = nf;
338     } else {
339         status = U_MEMORY_ALLOCATION_ERROR;
340     }
341 }
342 
343 Format*
clone() const344 PluralFormat::clone() const
345 {
346     return new PluralFormat(*this);
347 }
348 
349 
350 PluralFormat&
operator =(const PluralFormat & other)351 PluralFormat::operator=(const PluralFormat& other) {
352     if (this != &other) {
353         locale = other.locale;
354         msgPattern = other.msgPattern;
355         offset = other.offset;
356         copyObjects(other);
357     }
358 
359     return *this;
360 }
361 
362 UBool
operator ==(const Format & other) const363 PluralFormat::operator==(const Format& other) const {
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);
379 }
380 
381 UBool
operator !=(const Format & other) const382 PluralFormat::operator!=(const Format& other) const {
383     return  !operator==(other);
384 }
385 
386 void
parseObject(const UnicodeString &,Formattable &,ParsePosition & pos) const387 PluralFormat::parseObject(const UnicodeString& /*source*/,
388                         Formattable& /*result*/,
389                         ParsePosition& pos) const
390 {
391     // Parsing not supported.
392     pos.setErrorIndex(pos.getIndex());
393 }
394 
findSubMessage(const MessagePattern & pattern,int32_t partIndex,const PluralSelector & selector,void * context,double number,UErrorCode & ec)395 int32_t PluralFormat::findSubMessage(const MessagePattern& pattern, int32_t partIndex,
396                                      const PluralSelector& selector, void *context,
397                                      double number, UErrorCode& ec) {
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     }
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.
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) {
437             break;
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()) {
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().
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                 }
477             }
478         }
479         partIndex=pattern.getLimitPartIndex(partIndex);
480     } while(++partIndex<count);
481     return msgStart;
482 }
483 
~PluralSelector()484 PluralFormat::PluralSelector::~PluralSelector() {}
485 
~PluralSelectorAdapter()486 PluralFormat::PluralSelectorAdapter::~PluralSelectorAdapter() {
487     delete pluralRules;
488 }
489 
select(void * context,double number,UErrorCode &) const490 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);
496 }
497 
reset()498 void PluralFormat::PluralSelectorAdapter::reset() {
499     delete pluralRules;
500     pluralRules = NULL;
501 }
502 
503 
504 U_NAMESPACE_END
505 
506 
507 #endif /* #if !UCONFIG_NO_FORMATTING */
508 
509 //eof
510