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
operator =(const UNF & other)215 UnlocalizedNumberRangeFormatter& 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
operator =(UNF && src)221 UnlocalizedNumberRangeFormatter& UnlocalizedNumberRangeFormatter::operator=(UNF&& src) 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.
LocalizedNumberRangeFormatter(const LNF & other)228 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const LNF& other)
229 : LNF(static_cast<const NFS<LNF>&>(other)) {}
230
LocalizedNumberRangeFormatter(const NFS<LNF> & other)231 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const NFS<LNF>& other)
232 : NFS<LNF>(other) {
233 // No additional fields to assign
234 }
235
LocalizedNumberRangeFormatter(LocalizedNumberRangeFormatter && src)236 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(LocalizedNumberRangeFormatter&& src) noexcept
237 : LNF(static_cast<NFS<LNF>&&>(src)) {}
238
LocalizedNumberRangeFormatter(NFS<LNF> && src)239 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(NFS<LNF>&& src) 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
operator =(const LNF & other)247 LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(const LNF& other) {
248 if (this == &other) { return *this; } // self-assignment: no-op
249 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
operator =(LNF && src)255 LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(LNF&& src) 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
~LocalizedNumberRangeFormatter()264 LocalizedNumberRangeFormatter::~LocalizedNumberRangeFormatter() {
265 delete fAtomicFormatter.exchange(nullptr);
266 }
267
LocalizedNumberRangeFormatter(const RangeMacroProps & macros,const Locale & locale)268 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const RangeMacroProps& macros, const Locale& locale) {
269 fMacros = macros;
270 fMacros.locale = locale;
271 touchRangeLocales(fMacros);
272 }
273
LocalizedNumberRangeFormatter(RangeMacroProps && macros,const Locale & locale)274 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(RangeMacroProps&& macros, const Locale& locale) {
275 fMacros = std::move(macros);
276 fMacros.locale = locale;
277 touchRangeLocales(fMacros);
278 }
279
locale(const Locale & locale) const280 LocalizedNumberRangeFormatter UnlocalizedNumberRangeFormatter::locale(const Locale& locale) const& {
281 return LocalizedNumberRangeFormatter(fMacros, locale);
282 }
283
locale(const Locale & locale)284 LocalizedNumberRangeFormatter UnlocalizedNumberRangeFormatter::locale(const Locale& locale)&& {
285 return LocalizedNumberRangeFormatter(std::move(fMacros), locale);
286 }
287
288
formatFormattableRange(const Formattable & first,const Formattable & second,UErrorCode & status) const289 FormattedNumberRange 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
formatImpl(UFormattedNumberRangeData & results,bool equalBeforeRounding,UErrorCode & status) const322 void 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);
333 if (U_FAILURE(status)) {
334 return;
335 }
336 results.getStringRef().writeTerminator(status);
337 }
338
339 const impl::NumberRangeFormatterImpl*
getFormatter(UErrorCode & status) const340 LocalizedNumberRangeFormatter::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 delete temp;
358 return nullptr;
359 }
360 if (temp == nullptr) {
361 status = U_MEMORY_ALLOCATION_ERROR;
362 return nullptr;
363 }
364
365 // Note: ptr starts as nullptr; during compare_exchange,
366 // it is set to what is actually stored in the atomic
367 // if another thread beat us to computing the formatter object.
368 auto* nonConstThis = const_cast<LocalizedNumberRangeFormatter*>(this);
369 if (!nonConstThis->fAtomicFormatter.compare_exchange_strong(ptr, temp)) {
370 // Another thread beat us to computing the formatter
371 delete temp;
372 return ptr;
373 } else {
374 // Our copy of the formatter got stored in the atomic
375 return temp;
376 }
377
378 }
379
380
381 #endif /* #if !UCONFIG_NO_FORMATTING */
382