blob: 27113106c5045117efdc9eb1d4a5e3a467df1d94 [file] [log] [blame]
Jungshik Shinb3189662017-11-07 11:18:34 -08001// © 2017 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3
4#include "unicode/utypes.h"
5
6#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
7
8#include "uassert.h"
9#include "unicode/numberformatter.h"
10#include "number_decimalquantity.h"
11#include "number_formatimpl.h"
12#include "umutex.h"
13
14using namespace icu;
15using namespace icu::number;
16using namespace icu::number::impl;
17
18template<typename Derived>
19Derived NumberFormatterSettings<Derived>::notation(const Notation &notation) const {
20 Derived copy(*this);
21 // NOTE: Slicing is OK.
22 copy.fMacros.notation = notation;
23 return copy;
24}
25
26template<typename Derived>
27Derived NumberFormatterSettings<Derived>::unit(const icu::MeasureUnit &unit) const {
28 Derived copy(*this);
29 // NOTE: Slicing occurs here. However, CurrencyUnit can be restored from MeasureUnit.
30 // TimeUnit may be affected, but TimeUnit is not as relevant to number formatting.
31 copy.fMacros.unit = unit;
32 return copy;
33}
34
35template<typename Derived>
Jungshik Shinf61e46d2018-05-04 13:00:45 -070036Derived NumberFormatterSettings<Derived>::adoptUnit(icu::MeasureUnit *unit) const {
Jungshik Shinb3189662017-11-07 11:18:34 -080037 Derived copy(*this);
38 // Just copy the unit into the MacroProps by value, and delete it since we have ownership.
39 // NOTE: Slicing occurs here. However, CurrencyUnit can be restored from MeasureUnit.
40 // TimeUnit may be affected, but TimeUnit is not as relevant to number formatting.
41 if (unit != nullptr) {
Jungshik Shinf61e46d2018-05-04 13:00:45 -070042 // TODO: On nullptr, reset to default value?
Jungshik Shinb3189662017-11-07 11:18:34 -080043 copy.fMacros.unit = *unit;
44 delete unit;
45 }
46 return copy;
47}
48
49template<typename Derived>
Jungshik Shinf61e46d2018-05-04 13:00:45 -070050Derived NumberFormatterSettings<Derived>::perUnit(const icu::MeasureUnit &perUnit) const {
51 Derived copy(*this);
52 // See comments above about slicing.
53 copy.fMacros.perUnit = perUnit;
54 return copy;
55}
56
57template<typename Derived>
58Derived NumberFormatterSettings<Derived>::adoptPerUnit(icu::MeasureUnit *perUnit) const {
59 Derived copy(*this);
60 // See comments above about slicing and ownership.
61 if (perUnit != nullptr) {
62 // TODO: On nullptr, reset to default value?
63 copy.fMacros.perUnit = *perUnit;
64 delete perUnit;
65 }
66 return copy;
67}
68
69template<typename Derived>
Jungshik Shinb3189662017-11-07 11:18:34 -080070Derived NumberFormatterSettings<Derived>::rounding(const Rounder &rounder) const {
71 Derived copy(*this);
72 // NOTE: Slicing is OK.
73 copy.fMacros.rounder = rounder;
74 return copy;
75}
76
77template<typename Derived>
Jungshik Shinf61e46d2018-05-04 13:00:45 -070078Derived NumberFormatterSettings<Derived>::grouping(const UGroupingStrategy &strategy) const {
Jungshik Shinb3189662017-11-07 11:18:34 -080079 Derived copy(*this);
Jungshik Shinf61e46d2018-05-04 13:00:45 -070080 // NOTE: This is slightly different than how the setting is stored in Java
81 // because we want to put it on the stack.
82 copy.fMacros.grouper = Grouper::forStrategy(strategy);
Jungshik Shinb3189662017-11-07 11:18:34 -080083 return copy;
84}
85
86template<typename Derived>
87Derived NumberFormatterSettings<Derived>::integerWidth(const IntegerWidth &style) const {
88 Derived copy(*this);
89 copy.fMacros.integerWidth = style;
90 return copy;
91}
92
93template<typename Derived>
94Derived NumberFormatterSettings<Derived>::symbols(const DecimalFormatSymbols &symbols) const {
95 Derived copy(*this);
96 copy.fMacros.symbols.setTo(symbols);
97 return copy;
98}
99
100template<typename Derived>
Jungshik Shinf61e46d2018-05-04 13:00:45 -0700101Derived NumberFormatterSettings<Derived>::adoptSymbols(NumberingSystem *ns) const {
Jungshik Shinb3189662017-11-07 11:18:34 -0800102 Derived copy(*this);
103 copy.fMacros.symbols.setTo(ns);
104 return copy;
105}
106
107template<typename Derived>
108Derived NumberFormatterSettings<Derived>::unitWidth(const UNumberUnitWidth &width) const {
109 Derived copy(*this);
110 copy.fMacros.unitWidth = width;
111 return copy;
112}
113
114template<typename Derived>
115Derived NumberFormatterSettings<Derived>::sign(const UNumberSignDisplay &style) const {
116 Derived copy(*this);
117 copy.fMacros.sign = style;
118 return copy;
119}
120
121template<typename Derived>
122Derived NumberFormatterSettings<Derived>::decimal(const UNumberDecimalSeparatorDisplay &style) const {
123 Derived copy(*this);
124 copy.fMacros.decimal = style;
125 return copy;
126}
127
128template<typename Derived>
129Derived NumberFormatterSettings<Derived>::padding(const Padder &padder) const {
130 Derived copy(*this);
131 copy.fMacros.padder = padder;
132 return copy;
133}
134
135template<typename Derived>
136Derived NumberFormatterSettings<Derived>::threshold(int32_t threshold) const {
137 Derived copy(*this);
138 copy.fMacros.threshold = threshold;
139 return copy;
140}
141
142// Declare all classes that implement NumberFormatterSettings
143// See https://stackoverflow.com/a/495056/1407170
144template
145class icu::number::NumberFormatterSettings<icu::number::UnlocalizedNumberFormatter>;
146template
147class icu::number::NumberFormatterSettings<icu::number::LocalizedNumberFormatter>;
148
149
150UnlocalizedNumberFormatter NumberFormatter::with() {
151 UnlocalizedNumberFormatter result;
152 return result;
153}
154
155LocalizedNumberFormatter NumberFormatter::withLocale(const Locale &locale) {
156 return with().locale(locale);
157}
158
159// Make the child class constructor that takes the parent class call the parent class's copy constructor
160UnlocalizedNumberFormatter::UnlocalizedNumberFormatter(
161 const NumberFormatterSettings <UnlocalizedNumberFormatter> &other)
162 : NumberFormatterSettings<UnlocalizedNumberFormatter>(other) {
163}
164
165// Make the child class constructor that takes the parent class call the parent class's copy constructor
166// For LocalizedNumberFormatter, also copy over the extra fields
167LocalizedNumberFormatter::LocalizedNumberFormatter(
168 const NumberFormatterSettings <LocalizedNumberFormatter> &other)
169 : NumberFormatterSettings<LocalizedNumberFormatter>(other) {
170 // No additional copies required
171}
172
173LocalizedNumberFormatter::LocalizedNumberFormatter(const MacroProps &macros, const Locale &locale) {
174 fMacros = macros;
175 fMacros.locale = locale;
176}
177
178LocalizedNumberFormatter UnlocalizedNumberFormatter::locale(const Locale &locale) const {
179 return LocalizedNumberFormatter(fMacros, locale);
180}
181
182SymbolsWrapper::SymbolsWrapper(const SymbolsWrapper &other) {
183 doCopyFrom(other);
184}
185
186SymbolsWrapper &SymbolsWrapper::operator=(const SymbolsWrapper &other) {
187 if (this == &other) {
188 return *this;
189 }
190 doCleanup();
191 doCopyFrom(other);
192 return *this;
193}
194
195SymbolsWrapper::~SymbolsWrapper() {
196 doCleanup();
197}
198
199void SymbolsWrapper::setTo(const DecimalFormatSymbols &dfs) {
200 doCleanup();
201 fType = SYMPTR_DFS;
202 fPtr.dfs = new DecimalFormatSymbols(dfs);
203}
204
205void SymbolsWrapper::setTo(const NumberingSystem *ns) {
206 doCleanup();
207 fType = SYMPTR_NS;
208 fPtr.ns = ns;
209}
210
211void SymbolsWrapper::doCopyFrom(const SymbolsWrapper &other) {
212 fType = other.fType;
213 switch (fType) {
214 case SYMPTR_NONE:
215 // No action necessary
216 break;
217 case SYMPTR_DFS:
218 // Memory allocation failures are exposed in copyErrorTo()
219 if (other.fPtr.dfs != nullptr) {
220 fPtr.dfs = new DecimalFormatSymbols(*other.fPtr.dfs);
221 } else {
222 fPtr.dfs = nullptr;
223 }
224 break;
225 case SYMPTR_NS:
226 // Memory allocation failures are exposed in copyErrorTo()
227 if (other.fPtr.ns != nullptr) {
228 fPtr.ns = new NumberingSystem(*other.fPtr.ns);
229 } else {
230 fPtr.ns = nullptr;
231 }
232 break;
233 }
234}
235
236void SymbolsWrapper::doCleanup() {
237 switch (fType) {
238 case SYMPTR_NONE:
239 // No action necessary
240 break;
241 case SYMPTR_DFS:
242 delete fPtr.dfs;
243 break;
244 case SYMPTR_NS:
245 delete fPtr.ns;
246 break;
247 }
248}
249
250bool SymbolsWrapper::isDecimalFormatSymbols() const {
251 return fType == SYMPTR_DFS;
252}
253
254bool SymbolsWrapper::isNumberingSystem() const {
255 return fType == SYMPTR_NS;
256}
257
258const DecimalFormatSymbols* SymbolsWrapper::getDecimalFormatSymbols() const {
259 U_ASSERT(fType == SYMPTR_DFS);
260 return fPtr.dfs;
261}
262
263const NumberingSystem* SymbolsWrapper::getNumberingSystem() const {
264 U_ASSERT(fType == SYMPTR_NS);
265 return fPtr.ns;
266}
267
268LocalizedNumberFormatter::~LocalizedNumberFormatter() {
269 delete fCompiled;
270}
271
272FormattedNumber LocalizedNumberFormatter::formatInt(int64_t value, UErrorCode &status) const {
273 if (U_FAILURE(status)) { return FormattedNumber(U_ILLEGAL_ARGUMENT_ERROR); }
274 auto results = new NumberFormatterResults();
275 if (results == nullptr) {
276 status = U_MEMORY_ALLOCATION_ERROR;
277 return FormattedNumber(status);
278 }
279 results->quantity.setToLong(value);
280 return formatImpl(results, status);
281}
282
283FormattedNumber LocalizedNumberFormatter::formatDouble(double value, UErrorCode &status) const {
284 if (U_FAILURE(status)) { return FormattedNumber(U_ILLEGAL_ARGUMENT_ERROR); }
285 auto results = new NumberFormatterResults();
286 if (results == nullptr) {
287 status = U_MEMORY_ALLOCATION_ERROR;
288 return FormattedNumber(status);
289 }
290 results->quantity.setToDouble(value);
291 return formatImpl(results, status);
292}
293
294FormattedNumber LocalizedNumberFormatter::formatDecimal(StringPiece value, UErrorCode &status) const {
295 if (U_FAILURE(status)) { return FormattedNumber(U_ILLEGAL_ARGUMENT_ERROR); }
296 auto results = new NumberFormatterResults();
297 if (results == nullptr) {
298 status = U_MEMORY_ALLOCATION_ERROR;
299 return FormattedNumber(status);
300 }
301 results->quantity.setToDecNumber(value);
302 return formatImpl(results, status);
303}
304
305FormattedNumber
306LocalizedNumberFormatter::formatImpl(impl::NumberFormatterResults *results, UErrorCode &status) const {
307 // fUnsafeCallCount contains memory to be interpreted as an atomic int, most commonly
308 // std::atomic<int32_t>. Since the type of atomic int is platform-dependent, we cast the
309 // bytes in fUnsafeCallCount to u_atomic_int32_t, a typedef for the platform-dependent
310 // atomic int type defined in umutex.h.
311 static_assert(sizeof(u_atomic_int32_t) <= sizeof(fUnsafeCallCount),
312 "Atomic integer size on this platform exceeds the size allocated by fUnsafeCallCount");
313 u_atomic_int32_t* callCount = reinterpret_cast<u_atomic_int32_t*>(
314 const_cast<LocalizedNumberFormatter*>(this)->fUnsafeCallCount);
315
316 // A positive value in the atomic int indicates that the data structure is not yet ready;
317 // a negative value indicates that it is ready. If, after the increment, the atomic int
318 // is exactly threshold, then it is the current thread's job to build the data structure.
319 // Note: We set the callCount to INT32_MIN so that if another thread proceeds to increment
320 // the atomic int, the value remains below zero.
321 int32_t currentCount = umtx_loadAcquire(*callCount);
322 if (0 <= currentCount && currentCount <= fMacros.threshold && fMacros.threshold > 0) {
323 currentCount = umtx_atomic_inc(callCount);
324 }
325
326 if (currentCount == fMacros.threshold && fMacros.threshold > 0) {
327 // Build the data structure and then use it (slow to fast path).
328 const NumberFormatterImpl* compiled =
329 NumberFormatterImpl::fromMacros(fMacros, status);
330 U_ASSERT(fCompiled == nullptr);
331 const_cast<LocalizedNumberFormatter *>(this)->fCompiled = compiled;
332 umtx_storeRelease(*callCount, INT32_MIN);
333 compiled->apply(results->quantity, results->string, status);
334 } else if (currentCount < 0) {
335 // The data structure is already built; use it (fast path).
336 U_ASSERT(fCompiled != nullptr);
337 fCompiled->apply(results->quantity, results->string, status);
338 } else {
339 // Format the number without building the data structure (slow path).
340 NumberFormatterImpl::applyStatic(fMacros, results->quantity, results->string, status);
341 }
342
343 // Do not save the results object if we encountered a failure.
344 if (U_SUCCESS(status)) {
345 return FormattedNumber(results);
346 } else {
347 delete results;
348 return FormattedNumber(status);
349 }
350}
351
352UnicodeString FormattedNumber::toString() const {
353 if (fResults == nullptr) {
354 // TODO: http://bugs.icu-project.org/trac/ticket/13437
355 return {};
356 }
357 return fResults->string.toUnicodeString();
358}
359
360Appendable &FormattedNumber::appendTo(Appendable &appendable) {
361 if (fResults == nullptr) {
362 // TODO: http://bugs.icu-project.org/trac/ticket/13437
363 return appendable;
364 }
365 appendable.appendString(fResults->string.chars(), fResults->string.length());
366 return appendable;
367}
368
369void FormattedNumber::populateFieldPosition(FieldPosition &fieldPosition, UErrorCode &status) {
370 if (U_FAILURE(status)) { return; }
371 if (fResults == nullptr) {
372 status = fErrorCode;
373 return;
374 }
375 fResults->string.populateFieldPosition(fieldPosition, 0, status);
376}
377
378void
379FormattedNumber::populateFieldPositionIterator(FieldPositionIterator &iterator, UErrorCode &status) {
380 if (U_FAILURE(status)) { return; }
381 if (fResults == nullptr) {
382 status = fErrorCode;
383 return;
384 }
385 fResults->string.populateFieldPositionIterator(iterator, status);
386}
387
388FormattedNumber::~FormattedNumber() {
389 delete fResults;
390}
391
392#endif /* #if !UCONFIG_NO_FORMATTING */