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) U_NOEXCEPT
208         : UNF(static_cast<NFS<UNF>&&>(src)) {}
209 
UnlocalizedNumberRangeFormatter(NFS<UNF> && src)210 UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(NFS<UNF>&& src) U_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) 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.
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) U_NOEXCEPT
237         : LNF(static_cast<NFS<LNF>&&>(src)) {}
238 
LocalizedNumberRangeFormatter(NFS<LNF> && src)239 LocalizedNumberRangeFormatter::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 
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) 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 
~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         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 
380 #endif /* #if !UCONFIG_NO_FORMATTING */
381