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
16 using namespace icu;
17 using namespace icu::number;
18 using 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.
touchRangeLocales(RangeMacroProps & macros)23 void icu::number::impl::touchRangeLocales(RangeMacroProps& macros) {
24 macros.formatter1.fMacros.locale = macros.locale;
25 macros.formatter2.fMacros.locale = macros.locale;
26 }
27
28
29 template<typename Derived>
numberFormatterBoth(const UnlocalizedNumberFormatter & formatter) const30 Derived 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
38 template<typename Derived>
numberFormatterBoth(const UnlocalizedNumberFormatter & formatter)39 Derived 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
47 template<typename Derived>
numberFormatterBoth(UnlocalizedNumberFormatter && formatter) const48 Derived 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
56 template<typename Derived>
numberFormatterBoth(UnlocalizedNumberFormatter && formatter)57 Derived 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
65 template<typename Derived>
numberFormatterFirst(const UnlocalizedNumberFormatter & formatter) const66 Derived 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
74 template<typename Derived>
numberFormatterFirst(const UnlocalizedNumberFormatter & formatter)75 Derived 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
83 template<typename Derived>
numberFormatterFirst(UnlocalizedNumberFormatter && formatter) const84 Derived 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
92 template<typename Derived>
numberFormatterFirst(UnlocalizedNumberFormatter && formatter)93 Derived 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
101 template<typename Derived>
numberFormatterSecond(const UnlocalizedNumberFormatter & formatter) const102 Derived 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
110 template<typename Derived>
numberFormatterSecond(const UnlocalizedNumberFormatter & formatter)111 Derived 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
119 template<typename Derived>
numberFormatterSecond(UnlocalizedNumberFormatter && formatter) const120 Derived 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
128 template<typename Derived>
numberFormatterSecond(UnlocalizedNumberFormatter && formatter)129 Derived 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
137 template<typename Derived>
collapse(UNumberRangeCollapse collapse) const138 Derived NumberRangeFormatterSettings<Derived>::collapse(UNumberRangeCollapse collapse) const& {
139 Derived copy(*this);
140 copy.fMacros.collapse = collapse;
141 return copy;
142 }
143
144 template<typename Derived>
collapse(UNumberRangeCollapse collapse)145 Derived NumberRangeFormatterSettings<Derived>::collapse(UNumberRangeCollapse collapse) && {
146 Derived move(std::move(*this));
147 move.fMacros.collapse = collapse;
148 return move;
149 }
150
151 template<typename Derived>
identityFallback(UNumberRangeIdentityFallback identityFallback) const152 Derived NumberRangeFormatterSettings<Derived>::identityFallback(UNumberRangeIdentityFallback identityFallback) const& {
153 Derived copy(*this);
154 copy.fMacros.identityFallback = identityFallback;
155 return copy;
156 }
157
158 template<typename Derived>
identityFallback(UNumberRangeIdentityFallback identityFallback)159 Derived NumberRangeFormatterSettings<Derived>::identityFallback(UNumberRangeIdentityFallback identityFallback) && {
160 Derived move(std::move(*this));
161 move.fMacros.identityFallback = identityFallback;
162 return move;
163 }
164
165 template<typename Derived>
clone() const166 LocalPointer<Derived> NumberRangeFormatterSettings<Derived>::clone() const & {
167 return LocalPointer<Derived>(new Derived(*this));
168 }
169
170 template<typename Derived>
clone()171 LocalPointer<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
177 template
178 class icu::number::NumberRangeFormatterSettings<icu::number::UnlocalizedNumberRangeFormatter>;
179 template
180 class icu::number::NumberRangeFormatterSettings<icu::number::LocalizedNumberRangeFormatter>;
181
182
with()183 UnlocalizedNumberRangeFormatter NumberRangeFormatter::with() {
184 UnlocalizedNumberRangeFormatter result;
185 return result;
186 }
187
withLocale(const Locale & locale)188 LocalizedNumberRangeFormatter NumberRangeFormatter::withLocale(const Locale& locale) {
189 return with().locale(locale);
190 }
191
192
193 template<typename T> using NFS = NumberRangeFormatterSettings<T>;
194 using LNF = LocalizedNumberRangeFormatter;
195 using UNF = UnlocalizedNumberRangeFormatter;
196
UnlocalizedNumberRangeFormatter(const UNF & other)197 UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(const UNF& other)
198 : UNF(static_cast<const NFS<UNF>&>(other)) {}
199
UnlocalizedNumberRangeFormatter(const NFS<UNF> & other)200 UnlocalizedNumberRangeFormatter::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.
UnlocalizedNumberRangeFormatter(UNF && src)206 UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(UNF&& src) U_NOEXCEPT
207 : UNF(static_cast<NFS<UNF>&&>(src)) {}
208
UnlocalizedNumberRangeFormatter(NFS<UNF> && src)209 UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(NFS<UNF>&& src) U_NOEXCEPT
210 : NFS<UNF>(std::move(src)) {
211 // No additional fields to assign
212 }
213
operator =(const UNF & other)214 UnlocalizedNumberRangeFormatter& 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
operator =(UNF && src)220 UnlocalizedNumberRangeFormatter& 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.
LocalizedNumberRangeFormatter(const LNF & other)227 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const LNF& other)
228 : LNF(static_cast<const NFS<LNF>&>(other)) {}
229
LocalizedNumberRangeFormatter(const NFS<LNF> & other)230 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const NFS<LNF>& other)
231 : NFS<LNF>(other) {
232 // No additional fields to assign
233 }
234
LocalizedNumberRangeFormatter(LocalizedNumberRangeFormatter && src)235 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(LocalizedNumberRangeFormatter&& src) U_NOEXCEPT
236 : LNF(static_cast<NFS<LNF>&&>(src)) {}
237
LocalizedNumberRangeFormatter(NFS<LNF> && src)238 LocalizedNumberRangeFormatter::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
operator =(const LNF & other)246 LocalizedNumberRangeFormatter& 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
operator =(LNF && src)253 LocalizedNumberRangeFormatter& 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
~LocalizedNumberRangeFormatter()262 LocalizedNumberRangeFormatter::~LocalizedNumberRangeFormatter() {
263 delete fAtomicFormatter.exchange(nullptr);
264 }
265
LocalizedNumberRangeFormatter(const RangeMacroProps & macros,const Locale & locale)266 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const RangeMacroProps& macros, const Locale& locale) {
267 fMacros = macros;
268 fMacros.locale = locale;
269 touchRangeLocales(fMacros);
270 }
271
LocalizedNumberRangeFormatter(RangeMacroProps && macros,const Locale & locale)272 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(RangeMacroProps&& macros, const Locale& locale) {
273 fMacros = std::move(macros);
274 fMacros.locale = locale;
275 touchRangeLocales(fMacros);
276 }
277
locale(const Locale & locale) const278 LocalizedNumberRangeFormatter UnlocalizedNumberRangeFormatter::locale(const Locale& locale) const& {
279 return LocalizedNumberRangeFormatter(fMacros, locale);
280 }
281
locale(const Locale & locale)282 LocalizedNumberRangeFormatter UnlocalizedNumberRangeFormatter::locale(const Locale& locale)&& {
283 return LocalizedNumberRangeFormatter(std::move(fMacros), locale);
284 }
285
286
formatFormattableRange(const Formattable & first,const Formattable & second,UErrorCode & status) const287 FormattedNumberRange 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
formatImpl(UFormattedNumberRangeData & results,bool equalBeforeRounding,UErrorCode & status) const320 void 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
337 const impl::NumberRangeFormatterImpl*
getFormatter(UErrorCode & status) const338 LocalizedNumberRangeFormatter::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
UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedNumberRange)378 UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedNumberRange)
379
380 #define UPRV_NOARG
381
382 UBool FormattedNumberRange::nextFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) const {
383 UPRV_FORMATTED_VALUE_METHOD_GUARD(FALSE)
384 // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
385 return fData->nextFieldPosition(fieldPosition, status);
386 }
387
getAllFieldPositions(FieldPositionIterator & iterator,UErrorCode & status) const388 void FormattedNumberRange::getAllFieldPositions(FieldPositionIterator& iterator, UErrorCode& status) const {
389 FieldPositionIteratorHandler fpih(&iterator, status);
390 getAllFieldPositionsImpl(fpih, status);
391 }
392
getAllFieldPositionsImpl(FieldPositionIteratorHandler & fpih,UErrorCode & status) const393 void FormattedNumberRange::getAllFieldPositionsImpl(
394 FieldPositionIteratorHandler& fpih, UErrorCode& status) const {
395 UPRV_FORMATTED_VALUE_METHOD_GUARD(UPRV_NOARG)
396 fData->getAllFieldPositions(fpih, status);
397 }
398
getFirstDecimal(UErrorCode & status) const399 UnicodeString FormattedNumberRange::getFirstDecimal(UErrorCode& status) const {
400 UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString())
401 return fData->quantity1.toScientificString();
402 }
403
getSecondDecimal(UErrorCode & status) const404 UnicodeString FormattedNumberRange::getSecondDecimal(UErrorCode& status) const {
405 UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString())
406 return fData->quantity2.toScientificString();
407 }
408
getIdentityResult(UErrorCode & status) const409 UNumberRangeIdentityResult FormattedNumberRange::getIdentityResult(UErrorCode& status) const {
410 UPRV_FORMATTED_VALUE_METHOD_GUARD(UNUM_IDENTITY_RESULT_NOT_EQUAL)
411 return fData->identityResult;
412 }
413
414
415 UFormattedNumberRangeData::~UFormattedNumberRangeData() = default;
416
417
418
419 #endif /* #if !UCONFIG_NO_FORMATTING */
420