blob: f1060b3c21d45ea9239690daaf5a42ca5f11e86b [file] [log] [blame]
Victor Chang4578a1c2018-10-22 04:26:58 +01001// © 2018 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
7
8// Allow implicit conversion from char16_t* to UnicodeString for this file:
9// Helpful in toString methods and elsewhere.
10#define UNISTR_FROM_STRING_EXPLICIT
11
12#include "numrange_impl.h"
13#include "util.h"
14#include "number_utypes.h"
Victor Chang978167a2021-01-18 17:56:33 +000015#include "number_decnum.h"
Victor Chang4578a1c2018-10-22 04:26:58 +010016
17using namespace icu;
18using namespace icu::number;
19using namespace icu::number::impl;
20
21
22// This function needs to be declared in this namespace so it can be friended.
23// NOTE: In Java, this logic is handled in the resolve() function.
24void icu::number::impl::touchRangeLocales(RangeMacroProps& macros) {
25 macros.formatter1.fMacros.locale = macros.locale;
26 macros.formatter2.fMacros.locale = macros.locale;
27}
28
29
30template<typename Derived>
31Derived NumberRangeFormatterSettings<Derived>::numberFormatterBoth(const UnlocalizedNumberFormatter& formatter) const& {
32 Derived copy(*this);
33 copy.fMacros.formatter1 = formatter;
34 copy.fMacros.singleFormatter = true;
35 touchRangeLocales(copy.fMacros);
36 return copy;
37}
38
39template<typename Derived>
40Derived NumberRangeFormatterSettings<Derived>::numberFormatterBoth(const UnlocalizedNumberFormatter& formatter) && {
41 Derived move(std::move(*this));
42 move.fMacros.formatter1 = formatter;
43 move.fMacros.singleFormatter = true;
44 touchRangeLocales(move.fMacros);
45 return move;
46}
47
48template<typename Derived>
49Derived NumberRangeFormatterSettings<Derived>::numberFormatterBoth(UnlocalizedNumberFormatter&& formatter) const& {
50 Derived copy(*this);
51 copy.fMacros.formatter1 = std::move(formatter);
52 copy.fMacros.singleFormatter = true;
53 touchRangeLocales(copy.fMacros);
54 return copy;
55}
56
57template<typename Derived>
58Derived NumberRangeFormatterSettings<Derived>::numberFormatterBoth(UnlocalizedNumberFormatter&& formatter) && {
59 Derived move(std::move(*this));
60 move.fMacros.formatter1 = std::move(formatter);
61 move.fMacros.singleFormatter = true;
62 touchRangeLocales(move.fMacros);
63 return move;
64}
65
66template<typename Derived>
67Derived NumberRangeFormatterSettings<Derived>::numberFormatterFirst(const UnlocalizedNumberFormatter& formatter) const& {
68 Derived copy(*this);
69 copy.fMacros.formatter1 = formatter;
70 copy.fMacros.singleFormatter = false;
71 touchRangeLocales(copy.fMacros);
72 return copy;
73}
74
75template<typename Derived>
76Derived NumberRangeFormatterSettings<Derived>::numberFormatterFirst(const UnlocalizedNumberFormatter& formatter) && {
77 Derived move(std::move(*this));
78 move.fMacros.formatter1 = formatter;
79 move.fMacros.singleFormatter = false;
80 touchRangeLocales(move.fMacros);
81 return move;
82}
83
84template<typename Derived>
85Derived NumberRangeFormatterSettings<Derived>::numberFormatterFirst(UnlocalizedNumberFormatter&& formatter) const& {
86 Derived copy(*this);
87 copy.fMacros.formatter1 = std::move(formatter);
88 copy.fMacros.singleFormatter = false;
89 touchRangeLocales(copy.fMacros);
90 return copy;
91}
92
93template<typename Derived>
94Derived NumberRangeFormatterSettings<Derived>::numberFormatterFirst(UnlocalizedNumberFormatter&& formatter) && {
95 Derived move(std::move(*this));
96 move.fMacros.formatter1 = std::move(formatter);
97 move.fMacros.singleFormatter = false;
98 touchRangeLocales(move.fMacros);
99 return move;
100}
101
102template<typename Derived>
103Derived NumberRangeFormatterSettings<Derived>::numberFormatterSecond(const UnlocalizedNumberFormatter& formatter) const& {
104 Derived copy(*this);
105 copy.fMacros.formatter2 = formatter;
106 copy.fMacros.singleFormatter = false;
107 touchRangeLocales(copy.fMacros);
108 return copy;
109}
110
111template<typename Derived>
112Derived NumberRangeFormatterSettings<Derived>::numberFormatterSecond(const UnlocalizedNumberFormatter& formatter) && {
113 Derived move(std::move(*this));
114 move.fMacros.formatter2 = formatter;
115 move.fMacros.singleFormatter = false;
116 touchRangeLocales(move.fMacros);
117 return move;
118}
119
120template<typename Derived>
121Derived NumberRangeFormatterSettings<Derived>::numberFormatterSecond(UnlocalizedNumberFormatter&& formatter) const& {
122 Derived copy(*this);
123 copy.fMacros.formatter2 = std::move(formatter);
124 copy.fMacros.singleFormatter = false;
125 touchRangeLocales(copy.fMacros);
126 return copy;
127}
128
129template<typename Derived>
130Derived NumberRangeFormatterSettings<Derived>::numberFormatterSecond(UnlocalizedNumberFormatter&& formatter) && {
131 Derived move(std::move(*this));
132 move.fMacros.formatter2 = std::move(formatter);
133 move.fMacros.singleFormatter = false;
134 touchRangeLocales(move.fMacros);
135 return move;
136}
137
138template<typename Derived>
139Derived NumberRangeFormatterSettings<Derived>::collapse(UNumberRangeCollapse collapse) const& {
140 Derived copy(*this);
141 copy.fMacros.collapse = collapse;
142 return copy;
143}
144
145template<typename Derived>
146Derived NumberRangeFormatterSettings<Derived>::collapse(UNumberRangeCollapse collapse) && {
147 Derived move(std::move(*this));
148 move.fMacros.collapse = collapse;
149 return move;
150}
151
152template<typename Derived>
153Derived NumberRangeFormatterSettings<Derived>::identityFallback(UNumberRangeIdentityFallback identityFallback) const& {
154 Derived copy(*this);
155 copy.fMacros.identityFallback = identityFallback;
156 return copy;
157}
158
159template<typename Derived>
160Derived NumberRangeFormatterSettings<Derived>::identityFallback(UNumberRangeIdentityFallback identityFallback) && {
161 Derived move(std::move(*this));
162 move.fMacros.identityFallback = identityFallback;
163 return move;
164}
165
Nikita Iashchenkoda0990f2019-06-13 19:36:45 +0100166template<typename Derived>
167LocalPointer<Derived> NumberRangeFormatterSettings<Derived>::clone() const & {
168 return LocalPointer<Derived>(new Derived(*this));
169}
170
171template<typename Derived>
172LocalPointer<Derived> NumberRangeFormatterSettings<Derived>::clone() && {
173 return LocalPointer<Derived>(new Derived(std::move(*this)));
174}
175
Victor Chang4578a1c2018-10-22 04:26:58 +0100176// Declare all classes that implement NumberRangeFormatterSettings
177// See https://stackoverflow.com/a/495056/1407170
178template
179class icu::number::NumberRangeFormatterSettings<icu::number::UnlocalizedNumberRangeFormatter>;
180template
181class icu::number::NumberRangeFormatterSettings<icu::number::LocalizedNumberRangeFormatter>;
182
183
184UnlocalizedNumberRangeFormatter NumberRangeFormatter::with() {
185 UnlocalizedNumberRangeFormatter result;
186 return result;
187}
188
189LocalizedNumberRangeFormatter NumberRangeFormatter::withLocale(const Locale& locale) {
190 return with().locale(locale);
191}
192
193
194template<typename T> using NFS = NumberRangeFormatterSettings<T>;
195using LNF = LocalizedNumberRangeFormatter;
196using UNF = UnlocalizedNumberRangeFormatter;
197
198UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(const UNF& other)
199 : UNF(static_cast<const NFS<UNF>&>(other)) {}
200
201UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(const NFS<UNF>& other)
202 : NFS<UNF>(other) {
203 // No additional fields to assign
204}
205
206// Make default copy constructor call the NumberRangeFormatterSettings copy constructor.
207UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(UNF&& src) U_NOEXCEPT
208 : UNF(static_cast<NFS<UNF>&&>(src)) {}
209
210UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(NFS<UNF>&& src) U_NOEXCEPT
211 : NFS<UNF>(std::move(src)) {
212 // No additional fields to assign
213}
214
215UnlocalizedNumberRangeFormatter& UnlocalizedNumberRangeFormatter::operator=(const UNF& other) {
216 NFS<UNF>::operator=(static_cast<const NFS<UNF>&>(other));
217 // No additional fields to assign
218 return *this;
219}
220
221UnlocalizedNumberRangeFormatter& UnlocalizedNumberRangeFormatter::operator=(UNF&& src) U_NOEXCEPT {
222 NFS<UNF>::operator=(static_cast<NFS<UNF>&&>(src));
223 // No additional fields to assign
224 return *this;
225}
226
227// Make default copy constructor call the NumberRangeFormatterSettings copy constructor.
228LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const LNF& other)
229 : LNF(static_cast<const NFS<LNF>&>(other)) {}
230
231LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const NFS<LNF>& other)
232 : NFS<LNF>(other) {
233 // No additional fields to assign
234}
235
236LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(LocalizedNumberRangeFormatter&& src) U_NOEXCEPT
237 : LNF(static_cast<NFS<LNF>&&>(src)) {}
238
239LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(NFS<LNF>&& src) U_NOEXCEPT
240 : NFS<LNF>(std::move(src)) {
241 // Steal the compiled formatter
242 LNF&& _src = static_cast<LNF&&>(src);
243 auto* stolen = _src.fAtomicFormatter.exchange(nullptr);
244 delete fAtomicFormatter.exchange(stolen);
245}
246
247LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(const LNF& other) {
Victor Chang92c98b52021-04-27 16:37:23 +0100248 if (this == &other) { return *this; } // self-assignment: no-op
Victor Chang4578a1c2018-10-22 04:26:58 +0100249 NFS<LNF>::operator=(static_cast<const NFS<LNF>&>(other));
250 // Do not steal; just clear
251 delete fAtomicFormatter.exchange(nullptr);
252 return *this;
253}
254
255LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(LNF&& src) U_NOEXCEPT {
256 NFS<LNF>::operator=(static_cast<NFS<LNF>&&>(src));
257 // Steal the compiled formatter
258 auto* stolen = src.fAtomicFormatter.exchange(nullptr);
259 delete fAtomicFormatter.exchange(stolen);
260 return *this;
261}
262
263
264LocalizedNumberRangeFormatter::~LocalizedNumberRangeFormatter() {
265 delete fAtomicFormatter.exchange(nullptr);
266}
267
268LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const RangeMacroProps& macros, const Locale& locale) {
269 fMacros = macros;
270 fMacros.locale = locale;
271 touchRangeLocales(fMacros);
272}
273
274LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(RangeMacroProps&& macros, const Locale& locale) {
275 fMacros = std::move(macros);
276 fMacros.locale = locale;
277 touchRangeLocales(fMacros);
278}
279
280LocalizedNumberRangeFormatter UnlocalizedNumberRangeFormatter::locale(const Locale& locale) const& {
281 return LocalizedNumberRangeFormatter(fMacros, locale);
282}
283
284LocalizedNumberRangeFormatter UnlocalizedNumberRangeFormatter::locale(const Locale& locale)&& {
285 return LocalizedNumberRangeFormatter(std::move(fMacros), locale);
286}
287
288
289FormattedNumberRange LocalizedNumberRangeFormatter::formatFormattableRange(
290 const Formattable& first, const Formattable& second, UErrorCode& status) const {
291 if (U_FAILURE(status)) {
292 return FormattedNumberRange(U_ILLEGAL_ARGUMENT_ERROR);
293 }
294
295 auto results = new UFormattedNumberRangeData();
296 if (results == nullptr) {
297 status = U_MEMORY_ALLOCATION_ERROR;
298 return FormattedNumberRange(status);
299 }
300
301 first.populateDecimalQuantity(results->quantity1, status);
302 if (U_FAILURE(status)) {
303 return FormattedNumberRange(status);
304 }
305
306 second.populateDecimalQuantity(results->quantity2, status);
307 if (U_FAILURE(status)) {
308 return FormattedNumberRange(status);
309 }
310
311 formatImpl(*results, first == second, status);
312
313 // Do not save the results object if we encountered a failure.
314 if (U_SUCCESS(status)) {
315 return FormattedNumberRange(results);
316 } else {
317 delete results;
318 return FormattedNumberRange(status);
319 }
320}
321
322void LocalizedNumberRangeFormatter::formatImpl(
323 UFormattedNumberRangeData& results, bool equalBeforeRounding, UErrorCode& status) const {
324 auto* impl = getFormatter(status);
325 if (U_FAILURE(status)) {
326 return;
327 }
328 if (impl == nullptr) {
329 status = U_INTERNAL_PROGRAM_ERROR;
330 return;
331 }
332 impl->format(results, equalBeforeRounding, status);
Nikita Iashchenkoda0990f2019-06-13 19:36:45 +0100333 if (U_FAILURE(status)) {
334 return;
335 }
336 results.getStringRef().writeTerminator(status);
Victor Chang4578a1c2018-10-22 04:26:58 +0100337}
338
339const impl::NumberRangeFormatterImpl*
340LocalizedNumberRangeFormatter::getFormatter(UErrorCode& status) const {
341 // TODO: Move this into umutex.h? (similar logic also in decimfmt.cpp)
342 // See ICU-20146
343
344 if (U_FAILURE(status)) {
345 return nullptr;
346 }
347
348 // First try to get the pre-computed formatter
349 auto* ptr = fAtomicFormatter.load();
350 if (ptr != nullptr) {
351 return ptr;
352 }
353
354 // Try computing the formatter on our own
355 auto* temp = new NumberRangeFormatterImpl(fMacros, status);
356 if (U_FAILURE(status)) {
357 return nullptr;
358 }
359 if (temp == nullptr) {
360 status = U_MEMORY_ALLOCATION_ERROR;
361 return nullptr;
362 }
363
364 // Note: ptr starts as nullptr; during compare_exchange,
365 // it is set to what is actually stored in the atomic
366 // if another thread beat us to computing the formatter object.
367 auto* nonConstThis = const_cast<LocalizedNumberRangeFormatter*>(this);
368 if (!nonConstThis->fAtomicFormatter.compare_exchange_strong(ptr, temp)) {
369 // Another thread beat us to computing the formatter
370 delete temp;
371 return ptr;
372 } else {
373 // Our copy of the formatter got stored in the atomic
374 return temp;
375 }
376
377}
378
379
Victor Chang4578a1c2018-10-22 04:26:58 +0100380#endif /* #if !UCONFIG_NO_FORMATTING */