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         output.append(result, UNUM_FIELD_COUNT, status);
208         if (U_FAILURE(status)) {
209             return;
210         }
211         pluralKeyword = rules.select(quantity);
212     }
213     pluralForm = StandardPlural::orOtherFromString(pluralKeyword);
214 }
215 
format(const SimpleFormatter & pattern,const UnicodeString & value,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status)216 UnicodeString &QuantityFormatter::format(
217             const SimpleFormatter &pattern,
218             const UnicodeString &value,
219             UnicodeString &appendTo,
220             FieldPosition &pos,
221             UErrorCode &status) {
222     if (U_FAILURE(status)) {
223         return appendTo;
224     }
225     const UnicodeString *param = &value;
226     int32_t offset;
227     pattern.formatAndAppend(&param, 1, appendTo, &offset, 1, status);
228     if (pos.getBeginIndex() != 0 || pos.getEndIndex() != 0) {
229         if (offset >= 0) {
230             pos.setBeginIndex(pos.getBeginIndex() + offset);
231             pos.setEndIndex(pos.getEndIndex() + offset);
232         } else {
233             pos.setBeginIndex(0);
234             pos.setEndIndex(0);
235         }
236     }
237     return appendTo;
238 }
239 
240 U_NAMESPACE_END
241 
242 #endif /* #if !UCONFIG_NO_FORMATTING */
243