1 /*
2 ******************************************************************************
3 * Copyright (C) 2014, International Business Machines
4 * Corporation and others.  All Rights Reserved.
5 ******************************************************************************
6 * quantityformatter.cpp
7 */
8 #include "quantityformatter.h"
9 #include "simplepatternformatter.h"
10 #include "uassert.h"
11 #include "unicode/unistr.h"
12 #include "unicode/decimfmt.h"
13 #include "cstring.h"
14 #include "plurrule_impl.h"
15 #include "unicode/plurrule.h"
16 #include "charstr.h"
17 #include "unicode/fmtable.h"
18 #include "unicode/fieldpos.h"
19 
20 #if !UCONFIG_NO_FORMATTING
21 
22 U_NAMESPACE_BEGIN
23 
24 // other must always be first.
25 static const char * const gPluralForms[] = {
26         "other", "zero", "one", "two", "few", "many"};
27 
getPluralIndex(const char * pluralForm)28 static int32_t getPluralIndex(const char *pluralForm) {
29     int32_t len = UPRV_LENGTHOF(gPluralForms);
30     for (int32_t i = 0; i < len; ++i) {
31         if (uprv_strcmp(pluralForm, gPluralForms[i]) == 0) {
32             return i;
33         }
34     }
35     return -1;
36 }
37 
QuantityFormatter()38 QuantityFormatter::QuantityFormatter() {
39     for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
40         formatters[i] = NULL;
41     }
42 }
43 
QuantityFormatter(const QuantityFormatter & other)44 QuantityFormatter::QuantityFormatter(const QuantityFormatter &other) {
45     for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
46         if (other.formatters[i] == NULL) {
47             formatters[i] = NULL;
48         } else {
49             formatters[i] = new SimplePatternFormatter(*other.formatters[i]);
50         }
51     }
52 }
53 
operator =(const QuantityFormatter & other)54 QuantityFormatter &QuantityFormatter::operator=(
55         const QuantityFormatter& other) {
56     if (this == &other) {
57         return *this;
58     }
59     for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
60         delete formatters[i];
61         if (other.formatters[i] == NULL) {
62             formatters[i] = NULL;
63         } else {
64             formatters[i] = new SimplePatternFormatter(*other.formatters[i]);
65         }
66     }
67     return *this;
68 }
69 
~QuantityFormatter()70 QuantityFormatter::~QuantityFormatter() {
71     for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
72         delete formatters[i];
73     }
74 }
75 
reset()76 void QuantityFormatter::reset() {
77     for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
78         delete formatters[i];
79         formatters[i] = NULL;
80     }
81 }
82 
add(const char * variant,const UnicodeString & rawPattern,UErrorCode & status)83 UBool QuantityFormatter::add(
84         const char *variant,
85         const UnicodeString &rawPattern,
86         UErrorCode &status) {
87     if (U_FAILURE(status)) {
88         return FALSE;
89     }
90     int32_t pluralIndex = getPluralIndex(variant);
91     if (pluralIndex == -1) {
92         status = U_ILLEGAL_ARGUMENT_ERROR;
93         return FALSE;
94     }
95     SimplePatternFormatter *newFmt =
96             new SimplePatternFormatter(rawPattern);
97     if (newFmt == NULL) {
98         status = U_MEMORY_ALLOCATION_ERROR;
99         return FALSE;
100     }
101     if (newFmt->getPlaceholderCount() > 1) {
102         delete newFmt;
103         status = U_ILLEGAL_ARGUMENT_ERROR;
104         return FALSE;
105     }
106     delete formatters[pluralIndex];
107     formatters[pluralIndex] = newFmt;
108     return TRUE;
109 }
110 
isValid() const111 UBool QuantityFormatter::isValid() const {
112     return formatters[0] != NULL;
113 }
114 
getByVariant(const char * variant) const115 const SimplePatternFormatter *QuantityFormatter::getByVariant(
116         const char *variant) const {
117     int32_t pluralIndex = getPluralIndex(variant);
118     if (pluralIndex == -1) {
119         pluralIndex = 0;
120     }
121     const SimplePatternFormatter *pattern = formatters[pluralIndex];
122     if (pattern == NULL) {
123         pattern = formatters[0];
124     }
125     return pattern;
126 }
127 
format(const Formattable & quantity,const NumberFormat & fmt,const PluralRules & rules,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const128 UnicodeString &QuantityFormatter::format(
129             const Formattable& quantity,
130             const NumberFormat &fmt,
131             const PluralRules &rules,
132             UnicodeString &appendTo,
133             FieldPosition &pos,
134             UErrorCode &status) const {
135     if (U_FAILURE(status)) {
136         return appendTo;
137     }
138     UnicodeString count;
139     const DecimalFormat *decFmt = dynamic_cast<const DecimalFormat *>(&fmt);
140     if (decFmt != NULL) {
141         FixedDecimal fd = decFmt->getFixedDecimal(quantity, status);
142         if (U_FAILURE(status)) {
143             return appendTo;
144         }
145         count = rules.select(fd);
146     } else {
147         if (quantity.getType() == Formattable::kDouble) {
148             count = rules.select(quantity.getDouble());
149         } else if (quantity.getType() == Formattable::kLong) {
150             count = rules.select(quantity.getLong());
151         } else if (quantity.getType() == Formattable::kInt64) {
152             count = rules.select((double) quantity.getInt64());
153         } else {
154             status = U_ILLEGAL_ARGUMENT_ERROR;
155             return appendTo;
156         }
157     }
158     CharString buffer;
159     buffer.appendInvariantChars(count, status);
160     if (U_FAILURE(status)) {
161         return appendTo;
162     }
163     const SimplePatternFormatter *pattern = getByVariant(buffer.data());
164     if (pattern == NULL) {
165         status = U_INVALID_STATE_ERROR;
166         return appendTo;
167     }
168     UnicodeString formattedNumber;
169     FieldPosition fpos(pos.getField());
170     fmt.format(quantity, formattedNumber, fpos, status);
171     const UnicodeString *params[1] = {&formattedNumber};
172     int32_t offsets[1];
173     pattern->formatAndAppend(
174             params,
175             UPRV_LENGTHOF(params),
176             appendTo,
177             offsets,
178             UPRV_LENGTHOF(offsets),
179             status);
180     if (offsets[0] != -1) {
181         if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
182             pos.setBeginIndex(fpos.getBeginIndex() + offsets[0]);
183             pos.setEndIndex(fpos.getEndIndex() + offsets[0]);
184         }
185     }
186     return appendTo;
187 }
188 
189 U_NAMESPACE_END
190 
191 #endif /* #if !UCONFIG_NO_FORMATTING */
192