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