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
24U_NAMESPACE_BEGIN
25
26static const UChar OTHER_STRING[] = {
27    0x6F, 0x74, 0x68, 0x65, 0x72, 0  // "other"
28};
29
30UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralFormat)
31
32PluralFormat::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
40PluralFormat::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
48PluralFormat::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
56PluralFormat::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
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);
74}
75
76PluralFormat::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
86PluralFormat::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
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) {
104    init(&rules, UPLURAL_TYPE_COUNT, status);
105    applyPattern(pat, status);
106}
107
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) {
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);
129    applyPattern(pat, status);
130}
131
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) {
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
164PluralFormat::~PluralFormat() {
165    delete numberFormat;
166}
167
168void
169PluralFormat::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
187void
188PluralFormat::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
198UnicodeString&
199PluralFormat::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
214UnicodeString
215PluralFormat::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
221UnicodeString
222PluralFormat::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
229UnicodeString&
230PluralFormat::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
237UnicodeString&
238PluralFormat::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
245UnicodeString&
246PluralFormat::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
305UnicodeString&
306PluralFormat::toPattern(UnicodeString& appendTo) {
307    if (0 == msgPattern.countParts()) {
308        appendTo.setToBogus();
309    } else {
310        appendTo.append(msgPattern.getPatternString());
311    }
312    return appendTo;
313}
314
315void
316PluralFormat::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
329void
330PluralFormat::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
343Format*
344PluralFormat::clone() const
345{
346    return new PluralFormat(*this);
347}
348
349
350PluralFormat&
351PluralFormat::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
362UBool
363PluralFormat::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
381UBool
382PluralFormat::operator!=(const Format& other) const {
383    return  !operator==(other);
384}
385
386void
387PluralFormat::parseObject(const UnicodeString& /*source*/,
388                        Formattable& /*result*/,
389                        ParsePosition& pos) const
390{
391    // Parsing not supported.
392    pos.setErrorIndex(pos.getIndex());
393}
394
395int32_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
484PluralFormat::PluralSelector::~PluralSelector() {}
485
486PluralFormat::PluralSelectorAdapter::~PluralSelectorAdapter() {
487    delete pluralRules;
488}
489
490UnicodeString 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
498void PluralFormat::PluralSelectorAdapter::reset() {
499    delete pluralRules;
500    pluralRules = NULL;
501}
502
503
504U_NAMESPACE_END
505
506
507#endif /* #if !UCONFIG_NO_FORMATTING */
508
509//eof
510