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 #include "number_decnum.h"
16
17 using namespace icu;
18 using namespace icu::number;
19 using 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.
touchRangeLocales(RangeMacroProps & macros)24 void icu::number::impl::touchRangeLocales(RangeMacroProps& macros) {
25 macros.formatter1.fMacros.locale = macros.locale;
26 macros.formatter2.fMacros.locale = macros.locale;
27 }
28
29
30 template<typename Derived>
numberFormatterBoth(const UnlocalizedNumberFormatter & formatter) const31 Derived 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
39 template<typename Derived>
numberFormatterBoth(const UnlocalizedNumberFormatter & formatter)40 Derived 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
48 template<typename Derived>
numberFormatterBoth(UnlocalizedNumberFormatter && formatter) const49 Derived 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
57 template<typename Derived>
numberFormatterBoth(UnlocalizedNumberFormatter && formatter)58 Derived 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
66 template<typename Derived>
numberFormatterFirst(const UnlocalizedNumberFormatter & formatter) const67 Derived 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
75 template<typename Derived>
numberFormatterFirst(const UnlocalizedNumberFormatter & formatter)76 Derived 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
84 template<typename Derived>
numberFormatterFirst(UnlocalizedNumberFormatter && formatter) const85 Derived 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
93 template<typename Derived>
numberFormatterFirst(UnlocalizedNumberFormatter && formatter)94 Derived 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
102 template<typename Derived>
numberFormatterSecond(const UnlocalizedNumberFormatter & formatter) const103 Derived 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
111 template<typename Derived>
numberFormatterSecond(const UnlocalizedNumberFormatter & formatter)112 Derived 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
120 template<typename Derived>
numberFormatterSecond(UnlocalizedNumberFormatter && formatter) const121 Derived 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
129 template<typename Derived>
numberFormatterSecond(UnlocalizedNumberFormatter && formatter)130 Derived 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
138 template<typename Derived>
collapse(UNumberRangeCollapse collapse) const139 Derived NumberRangeFormatterSettings<Derived>::collapse(UNumberRangeCollapse collapse) const& {
140 Derived copy(*this);
141 copy.fMacros.collapse = collapse;
142 return copy;
143 }
144
145 template<typename Derived>
collapse(UNumberRangeCollapse collapse)146 Derived NumberRangeFormatterSettings<Derived>::collapse(UNumberRangeCollapse collapse) && {
147 Derived move(std::move(*this));
148 move.fMacros.collapse = collapse;
149 return move;
150 }
151
152 template<typename Derived>
identityFallback(UNumberRangeIdentityFallback identityFallback) const153 Derived NumberRangeFormatterSettings<Derived>::identityFallback(UNumberRangeIdentityFallback identityFallback) const& {
154 Derived copy(*this);
155 copy.fMacros.identityFallback = identityFallback;
156 return copy;
157 }
158
159 template<typename Derived>
identityFallback(UNumberRangeIdentityFallback identityFallback)160 Derived NumberRangeFormatterSettings<Derived>::identityFallback(UNumberRangeIdentityFallback identityFallback) && {
161 Derived move(std::move(*this));
162 move.fMacros.identityFallback = identityFallback;
163 return move;
164 }
165
166 template<typename Derived>
clone() const167 LocalPointer<Derived> NumberRangeFormatterSettings<Derived>::clone() const & {
168 return LocalPointer<Derived>(new Derived(*this));
169 }
170
171 template<typename Derived>
clone()172 LocalPointer<Derived> NumberRangeFormatterSettings<Derived>::clone() && {
173 return LocalPointer<Derived>(new Derived(std::move(*this)));
174 }
175
176 // Declare all classes that implement NumberRangeFormatterSettings
177 // See https://stackoverflow.com/a/495056/1407170
178 template
179 class icu::number::NumberRangeFormatterSettings<icu::number::UnlocalizedNumberRangeFormatter>;
180 template
181 class icu::number::NumberRangeFormatterSettings<icu::number::LocalizedNumberRangeFormatter>;
182
183
with()184 UnlocalizedNumberRangeFormatter NumberRangeFormatter::with() {
185 UnlocalizedNumberRangeFormatter result;
186 return result;
187 }
188
withLocale(const Locale & locale)189 LocalizedNumberRangeFormatter NumberRangeFormatter::withLocale(const Locale& locale) {
190 return with().locale(locale);
191 }
192
193
194 template<typename T> using NFS = NumberRangeFormatterSettings<T>;
195 using LNF = LocalizedNumberRangeFormatter;
196 using UNF = UnlocalizedNumberRangeFormatter;
197
UnlocalizedNumberRangeFormatter(const UNF & other)198 UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(const UNF& other)
199 : UNF(static_cast<const NFS<UNF>&>(other)) {}
200
UnlocalizedNumberRangeFormatter(const NFS<UNF> & other)201 UnlocalizedNumberRangeFormatter::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.
UnlocalizedNumberRangeFormatter(UNF && src)207 UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(UNF&& src) noexcept
208 : UNF(static_cast<NFS<UNF>&&>(src)) {}
209
UnlocalizedNumberRangeFormatter(NFS<UNF> && src)210 UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(NFS<UNF>&& src) noexcept
211 : NFS<UNF>(std::move(src)) {
212 // No additional fields to assign
213 }
214
UnlocalizedNumberRangeFormatter(const impl::RangeMacroProps & macros)215 UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(const impl::RangeMacroProps ¯os) {
216 fMacros = macros;
217 }
218
UnlocalizedNumberRangeFormatter(impl::RangeMacroProps && macros)219 UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(impl::RangeMacroProps &¯os) {
220 fMacros = macros;
221 }
222
operator =(const UNF & other)223 UnlocalizedNumberRangeFormatter& UnlocalizedNumberRangeFormatter::operator=(const UNF& other) {
224 NFS<UNF>::operator=(static_cast<const NFS<UNF>&>(other));
225 // No additional fields to assign
226 return *this;
227 }
228
operator =(UNF && src)229 UnlocalizedNumberRangeFormatter& UnlocalizedNumberRangeFormatter::operator=(UNF&& src) noexcept {
230 NFS<UNF>::operator=(static_cast<NFS<UNF>&&>(src));
231 // No additional fields to assign
232 return *this;
233 }
234
235 // Make default copy constructor call the NumberRangeFormatterSettings copy constructor.
LocalizedNumberRangeFormatter(const LNF & other)236 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const LNF& other)
237 : LNF(static_cast<const NFS<LNF>&>(other)) {}
238
LocalizedNumberRangeFormatter(const NFS<LNF> & other)239 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const NFS<LNF>& other)
240 : NFS<LNF>(other) {
241 // No additional fields to assign
242 }
243
LocalizedNumberRangeFormatter(LocalizedNumberRangeFormatter && src)244 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(LocalizedNumberRangeFormatter&& src) noexcept
245 : LNF(static_cast<NFS<LNF>&&>(src)) {}
246
LocalizedNumberRangeFormatter(NFS<LNF> && src)247 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(NFS<LNF>&& src) noexcept
248 : NFS<LNF>(std::move(src)) {
249 // Steal the compiled formatter
250 LNF&& _src = static_cast<LNF&&>(src);
251 auto* stolen = _src.fAtomicFormatter.exchange(nullptr);
252 delete fAtomicFormatter.exchange(stolen);
253 }
254
operator =(const LNF & other)255 LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(const LNF& other) {
256 if (this == &other) { return *this; } // self-assignment: no-op
257 NFS<LNF>::operator=(static_cast<const NFS<LNF>&>(other));
258 // Do not steal; just clear
259 delete fAtomicFormatter.exchange(nullptr);
260 return *this;
261 }
262
operator =(LNF && src)263 LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(LNF&& src) noexcept {
264 NFS<LNF>::operator=(static_cast<NFS<LNF>&&>(src));
265 // Steal the compiled formatter
266 auto* stolen = src.fAtomicFormatter.exchange(nullptr);
267 delete fAtomicFormatter.exchange(stolen);
268 return *this;
269 }
270
271
~LocalizedNumberRangeFormatter()272 LocalizedNumberRangeFormatter::~LocalizedNumberRangeFormatter() {
273 delete fAtomicFormatter.exchange(nullptr);
274 }
275
LocalizedNumberRangeFormatter(const RangeMacroProps & macros,const Locale & locale)276 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const RangeMacroProps& macros, const Locale& locale) {
277 fMacros = macros;
278 fMacros.locale = locale;
279 touchRangeLocales(fMacros);
280 }
281
LocalizedNumberRangeFormatter(RangeMacroProps && macros,const Locale & locale)282 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(RangeMacroProps&& macros, const Locale& locale) {
283 fMacros = std::move(macros);
284 fMacros.locale = locale;
285 touchRangeLocales(fMacros);
286 }
287
locale(const Locale & locale) const288 LocalizedNumberRangeFormatter UnlocalizedNumberRangeFormatter::locale(const Locale& locale) const& {
289 return LocalizedNumberRangeFormatter(fMacros, locale);
290 }
291
locale(const Locale & locale)292 LocalizedNumberRangeFormatter UnlocalizedNumberRangeFormatter::locale(const Locale& locale)&& {
293 return LocalizedNumberRangeFormatter(std::move(fMacros), locale);
294 }
295
296
withoutLocale() const297 UnlocalizedNumberRangeFormatter LocalizedNumberRangeFormatter::withoutLocale() const & {
298 RangeMacroProps macros(fMacros);
299 macros.locale = Locale();
300 return UnlocalizedNumberRangeFormatter(macros);
301 }
302
withoutLocale()303 UnlocalizedNumberRangeFormatter LocalizedNumberRangeFormatter::withoutLocale() && {
304 RangeMacroProps macros(std::move(fMacros));
305 macros.locale = Locale();
306 return UnlocalizedNumberRangeFormatter(std::move(macros));
307 }
308
309
formatFormattableRange(const Formattable & first,const Formattable & second,UErrorCode & status) const310 FormattedNumberRange LocalizedNumberRangeFormatter::formatFormattableRange(
311 const Formattable& first, const Formattable& second, UErrorCode& status) const {
312 if (U_FAILURE(status)) {
313 return FormattedNumberRange(U_ILLEGAL_ARGUMENT_ERROR);
314 }
315
316 auto* results = new UFormattedNumberRangeData();
317 if (results == nullptr) {
318 status = U_MEMORY_ALLOCATION_ERROR;
319 return FormattedNumberRange(status);
320 }
321
322 first.populateDecimalQuantity(results->quantity1, status);
323 if (U_FAILURE(status)) {
324 return FormattedNumberRange(status);
325 }
326
327 second.populateDecimalQuantity(results->quantity2, status);
328 if (U_FAILURE(status)) {
329 return FormattedNumberRange(status);
330 }
331
332 formatImpl(*results, first == second, status);
333
334 // Do not save the results object if we encountered a failure.
335 if (U_SUCCESS(status)) {
336 return FormattedNumberRange(results);
337 } else {
338 delete results;
339 return FormattedNumberRange(status);
340 }
341 }
342
formatImpl(UFormattedNumberRangeData & results,bool equalBeforeRounding,UErrorCode & status) const343 void LocalizedNumberRangeFormatter::formatImpl(
344 UFormattedNumberRangeData& results, bool equalBeforeRounding, UErrorCode& status) const {
345 const auto* impl = getFormatter(status);
346 if (U_FAILURE(status)) {
347 return;
348 }
349 if (impl == nullptr) {
350 status = U_INTERNAL_PROGRAM_ERROR;
351 return;
352 }
353 impl->format(results, equalBeforeRounding, status);
354 if (U_FAILURE(status)) {
355 return;
356 }
357 results.getStringRef().writeTerminator(status);
358 }
359
360 const impl::NumberRangeFormatterImpl*
getFormatter(UErrorCode & status) const361 LocalizedNumberRangeFormatter::getFormatter(UErrorCode& status) const {
362 // TODO: Move this into umutex.h? (similar logic also in decimfmt.cpp)
363 // See ICU-20146
364
365 if (U_FAILURE(status)) {
366 return nullptr;
367 }
368
369 // First try to get the pre-computed formatter
370 auto* ptr = fAtomicFormatter.load();
371 if (ptr != nullptr) {
372 return ptr;
373 }
374
375 // Try computing the formatter on our own
376 auto* temp = new NumberRangeFormatterImpl(fMacros, status);
377 if (U_FAILURE(status)) {
378 delete temp;
379 return nullptr;
380 }
381 if (temp == nullptr) {
382 status = U_MEMORY_ALLOCATION_ERROR;
383 return nullptr;
384 }
385
386 // Note: ptr starts as nullptr; during compare_exchange,
387 // it is set to what is actually stored in the atomic
388 // if another thread beat us to computing the formatter object.
389 auto* nonConstThis = const_cast<LocalizedNumberRangeFormatter*>(this);
390 if (!nonConstThis->fAtomicFormatter.compare_exchange_strong(ptr, temp)) {
391 // Another thread beat us to computing the formatter
392 delete temp;
393 return ptr;
394 } else {
395 // Our copy of the formatter got stored in the atomic
396 return temp;
397 }
398
399 }
400
401
402 #endif /* #if !UCONFIG_NO_FORMATTING */
403