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