1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 ******************************************************************************
5 * Copyright (C) 2014-2016, International Business Machines
6 * Corporation and others. All Rights Reserved.
7 ******************************************************************************
8 * quantityformatter.cpp
9 */
10
11 #include "unicode/utypes.h"
12
13 #if !UCONFIG_NO_FORMATTING
14
15 #include "unicode/simpleformatter.h"
16 #include "quantityformatter.h"
17 #include "uassert.h"
18 #include "unicode/unistr.h"
19 #include "unicode/decimfmt.h"
20 #include "cstring.h"
21 #include "unicode/plurrule.h"
22 #include "charstr.h"
23 #include "unicode/fmtable.h"
24 #include "unicode/fieldpos.h"
25 #include "standardplural.h"
26 #include "uassert.h"
27 #include "number_decimalquantity.h"
28 #include "number_utypes.h"
29 #include "formatted_string_builder.h"
30
31 U_NAMESPACE_BEGIN
32
QuantityFormatter()33 QuantityFormatter::QuantityFormatter() {
34 for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
35 formatters[i] = NULL;
36 }
37 }
38
QuantityFormatter(const QuantityFormatter & other)39 QuantityFormatter::QuantityFormatter(const QuantityFormatter &other) {
40 for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
41 if (other.formatters[i] == NULL) {
42 formatters[i] = NULL;
43 } else {
44 formatters[i] = new SimpleFormatter(*other.formatters[i]);
45 }
46 }
47 }
48
operator =(const QuantityFormatter & other)49 QuantityFormatter &QuantityFormatter::operator=(
50 const QuantityFormatter& other) {
51 if (this == &other) {
52 return *this;
53 }
54 for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
55 delete formatters[i];
56 if (other.formatters[i] == NULL) {
57 formatters[i] = NULL;
58 } else {
59 formatters[i] = new SimpleFormatter(*other.formatters[i]);
60 }
61 }
62 return *this;
63 }
64
~QuantityFormatter()65 QuantityFormatter::~QuantityFormatter() {
66 for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
67 delete formatters[i];
68 }
69 }
70
reset()71 void QuantityFormatter::reset() {
72 for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
73 delete formatters[i];
74 formatters[i] = NULL;
75 }
76 }
77
addIfAbsent(const char * variant,const UnicodeString & rawPattern,UErrorCode & status)78 UBool QuantityFormatter::addIfAbsent(
79 const char *variant,
80 const UnicodeString &rawPattern,
81 UErrorCode &status) {
82 int32_t pluralIndex = StandardPlural::indexFromString(variant, status);
83 if (U_FAILURE(status)) {
84 return FALSE;
85 }
86 if (formatters[pluralIndex] != NULL) {
87 return TRUE;
88 }
89 SimpleFormatter *newFmt = new SimpleFormatter(rawPattern, 0, 1, status);
90 if (newFmt == NULL) {
91 status = U_MEMORY_ALLOCATION_ERROR;
92 return FALSE;
93 }
94 if (U_FAILURE(status)) {
95 delete newFmt;
96 return FALSE;
97 }
98 formatters[pluralIndex] = newFmt;
99 return TRUE;
100 }
101
isValid() const102 UBool QuantityFormatter::isValid() const {
103 return formatters[StandardPlural::OTHER] != NULL;
104 }
105
getByVariant(const char * variant) const106 const SimpleFormatter *QuantityFormatter::getByVariant(
107 const char *variant) const {
108 U_ASSERT(isValid());
109 int32_t pluralIndex = StandardPlural::indexOrOtherIndexFromString(variant);
110 const SimpleFormatter *pattern = formatters[pluralIndex];
111 if (pattern == NULL) {
112 pattern = formatters[StandardPlural::OTHER];
113 }
114 return pattern;
115 }
116
format(const Formattable & number,const NumberFormat & fmt,const PluralRules & rules,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const117 UnicodeString &QuantityFormatter::format(
118 const Formattable &number,
119 const NumberFormat &fmt,
120 const PluralRules &rules,
121 UnicodeString &appendTo,
122 FieldPosition &pos,
123 UErrorCode &status) const {
124 UnicodeString formattedNumber;
125 StandardPlural::Form p = selectPlural(number, fmt, rules, formattedNumber, pos, status);
126 if (U_FAILURE(status)) {
127 return appendTo;
128 }
129 const SimpleFormatter *pattern = formatters[p];
130 if (pattern == NULL) {
131 pattern = formatters[StandardPlural::OTHER];
132 if (pattern == NULL) {
133 status = U_INVALID_STATE_ERROR;
134 return appendTo;
135 }
136 }
137 return format(*pattern, formattedNumber, appendTo, pos, status);
138 }
139
140 // The following methods live here so that class PluralRules does not depend on number formatting,
141 // and the SimpleFormatter does not depend on FieldPosition.
142
selectPlural(const Formattable & number,const NumberFormat & fmt,const PluralRules & rules,UnicodeString & formattedNumber,FieldPosition & pos,UErrorCode & status)143 StandardPlural::Form QuantityFormatter::selectPlural(
144 const Formattable &number,
145 const NumberFormat &fmt,
146 const PluralRules &rules,
147 UnicodeString &formattedNumber,
148 FieldPosition &pos,
149 UErrorCode &status) {
150 if (U_FAILURE(status)) {
151 return StandardPlural::OTHER;
152 }
153 UnicodeString pluralKeyword;
154 const DecimalFormat *decFmt = dynamic_cast<const DecimalFormat *>(&fmt);
155 if (decFmt != NULL) {
156 number::impl::DecimalQuantity dq;
157 decFmt->formatToDecimalQuantity(number, dq, status);
158 if (U_FAILURE(status)) {
159 return StandardPlural::OTHER;
160 }
161 pluralKeyword = rules.select(dq);
162 decFmt->format(number, formattedNumber, pos, status);
163 } else {
164 if (number.getType() == Formattable::kDouble) {
165 pluralKeyword = rules.select(number.getDouble());
166 } else if (number.getType() == Formattable::kLong) {
167 pluralKeyword = rules.select(number.getLong());
168 } else if (number.getType() == Formattable::kInt64) {
169 pluralKeyword = rules.select((double) number.getInt64());
170 } else {
171 status = U_ILLEGAL_ARGUMENT_ERROR;
172 return StandardPlural::OTHER;
173 }
174 fmt.format(number, formattedNumber, pos, status);
175 }
176 return StandardPlural::orOtherFromString(pluralKeyword);
177 }
178
formatAndSelect(double quantity,const NumberFormat & fmt,const PluralRules & rules,FormattedStringBuilder & output,StandardPlural::Form & pluralForm,UErrorCode & status)179 void QuantityFormatter::formatAndSelect(
180 double quantity,
181 const NumberFormat& fmt,
182 const PluralRules& rules,
183 FormattedStringBuilder& output,
184 StandardPlural::Form& pluralForm,
185 UErrorCode& status) {
186 UnicodeString pluralKeyword;
187 const DecimalFormat* df = dynamic_cast<const DecimalFormat*>(&fmt);
188 if (df != nullptr) {
189 number::impl::UFormattedNumberData fn;
190 fn.quantity.setToDouble(quantity);
191 const number::LocalizedNumberFormatter* lnf = df->toNumberFormatter(status);
192 if (U_FAILURE(status)) {
193 return;
194 }
195 lnf->formatImpl(&fn, status);
196 if (U_FAILURE(status)) {
197 return;
198 }
199 output = std::move(fn.getStringRef());
200 pluralKeyword = rules.select(fn.quantity);
201 } else {
202 UnicodeString result;
203 fmt.format(quantity, result, status);
204 if (U_FAILURE(status)) {
205 return;
206 }
207 // This code path is probably RBNF. Use the generic numeric field.
208 output.append(result, kGeneralNumericField, status);
209 if (U_FAILURE(status)) {
210 return;
211 }
212 pluralKeyword = rules.select(quantity);
213 }
214 pluralForm = StandardPlural::orOtherFromString(pluralKeyword);
215 }
216
format(const SimpleFormatter & pattern,const UnicodeString & value,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status)217 UnicodeString &QuantityFormatter::format(
218 const SimpleFormatter &pattern,
219 const UnicodeString &value,
220 UnicodeString &appendTo,
221 FieldPosition &pos,
222 UErrorCode &status) {
223 if (U_FAILURE(status)) {
224 return appendTo;
225 }
226 const UnicodeString *param = &value;
227 int32_t offset;
228 pattern.formatAndAppend(¶m, 1, appendTo, &offset, 1, status);
229 if (pos.getBeginIndex() != 0 || pos.getEndIndex() != 0) {
230 if (offset >= 0) {
231 pos.setBeginIndex(pos.getBeginIndex() + offset);
232 pos.setEndIndex(pos.getEndIndex() + offset);
233 } else {
234 pos.setBeginIndex(0);
235 pos.setEndIndex(0);
236 }
237 }
238 return appendTo;
239 }
240
241 U_NAMESPACE_END
242
243 #endif /* #if !UCONFIG_NO_FORMATTING */
244