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 // Declare all classes that implement NumberRangeFormatterSettings
166 // See https://stackoverflow.com/a/495056/1407170
167 template
168 class icu::number::NumberRangeFormatterSettings<icu::number::UnlocalizedNumberRangeFormatter>;
169 template
170 class icu::number::NumberRangeFormatterSettings<icu::number::LocalizedNumberRangeFormatter>;
171
172
with()173 UnlocalizedNumberRangeFormatter NumberRangeFormatter::with() {
174 UnlocalizedNumberRangeFormatter result;
175 return result;
176 }
177
withLocale(const Locale & locale)178 LocalizedNumberRangeFormatter NumberRangeFormatter::withLocale(const Locale& locale) {
179 return with().locale(locale);
180 }
181
182
183 template<typename T> using NFS = NumberRangeFormatterSettings<T>;
184 using LNF = LocalizedNumberRangeFormatter;
185 using UNF = UnlocalizedNumberRangeFormatter;
186
UnlocalizedNumberRangeFormatter(const UNF & other)187 UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(const UNF& other)
188 : UNF(static_cast<const NFS<UNF>&>(other)) {}
189
UnlocalizedNumberRangeFormatter(const NFS<UNF> & other)190 UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(const NFS<UNF>& other)
191 : NFS<UNF>(other) {
192 // No additional fields to assign
193 }
194
195 // Make default copy constructor call the NumberRangeFormatterSettings copy constructor.
UnlocalizedNumberRangeFormatter(UNF && src)196 UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(UNF&& src) U_NOEXCEPT
197 : UNF(static_cast<NFS<UNF>&&>(src)) {}
198
UnlocalizedNumberRangeFormatter(NFS<UNF> && src)199 UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(NFS<UNF>&& src) U_NOEXCEPT
200 : NFS<UNF>(std::move(src)) {
201 // No additional fields to assign
202 }
203
operator =(const UNF & other)204 UnlocalizedNumberRangeFormatter& UnlocalizedNumberRangeFormatter::operator=(const UNF& other) {
205 NFS<UNF>::operator=(static_cast<const NFS<UNF>&>(other));
206 // No additional fields to assign
207 return *this;
208 }
209
operator =(UNF && src)210 UnlocalizedNumberRangeFormatter& UnlocalizedNumberRangeFormatter::operator=(UNF&& src) U_NOEXCEPT {
211 NFS<UNF>::operator=(static_cast<NFS<UNF>&&>(src));
212 // No additional fields to assign
213 return *this;
214 }
215
216 // Make default copy constructor call the NumberRangeFormatterSettings copy constructor.
LocalizedNumberRangeFormatter(const LNF & other)217 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const LNF& other)
218 : LNF(static_cast<const NFS<LNF>&>(other)) {}
219
LocalizedNumberRangeFormatter(const NFS<LNF> & other)220 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const NFS<LNF>& other)
221 : NFS<LNF>(other) {
222 // No additional fields to assign
223 }
224
LocalizedNumberRangeFormatter(LocalizedNumberRangeFormatter && src)225 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(LocalizedNumberRangeFormatter&& src) U_NOEXCEPT
226 : LNF(static_cast<NFS<LNF>&&>(src)) {}
227
LocalizedNumberRangeFormatter(NFS<LNF> && src)228 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(NFS<LNF>&& src) U_NOEXCEPT
229 : NFS<LNF>(std::move(src)) {
230 // Steal the compiled formatter
231 LNF&& _src = static_cast<LNF&&>(src);
232 auto* stolen = _src.fAtomicFormatter.exchange(nullptr);
233 delete fAtomicFormatter.exchange(stolen);
234 }
235
operator =(const LNF & other)236 LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(const LNF& other) {
237 NFS<LNF>::operator=(static_cast<const NFS<LNF>&>(other));
238 // Do not steal; just clear
239 delete fAtomicFormatter.exchange(nullptr);
240 return *this;
241 }
242
operator =(LNF && src)243 LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(LNF&& src) U_NOEXCEPT {
244 NFS<LNF>::operator=(static_cast<NFS<LNF>&&>(src));
245 // Steal the compiled formatter
246 auto* stolen = src.fAtomicFormatter.exchange(nullptr);
247 delete fAtomicFormatter.exchange(stolen);
248 return *this;
249 }
250
251
~LocalizedNumberRangeFormatter()252 LocalizedNumberRangeFormatter::~LocalizedNumberRangeFormatter() {
253 delete fAtomicFormatter.exchange(nullptr);
254 }
255
LocalizedNumberRangeFormatter(const RangeMacroProps & macros,const Locale & locale)256 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const RangeMacroProps& macros, const Locale& locale) {
257 fMacros = macros;
258 fMacros.locale = locale;
259 touchRangeLocales(fMacros);
260 }
261
LocalizedNumberRangeFormatter(RangeMacroProps && macros,const Locale & locale)262 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(RangeMacroProps&& macros, const Locale& locale) {
263 fMacros = std::move(macros);
264 fMacros.locale = locale;
265 touchRangeLocales(fMacros);
266 }
267
locale(const Locale & locale) const268 LocalizedNumberRangeFormatter UnlocalizedNumberRangeFormatter::locale(const Locale& locale) const& {
269 return LocalizedNumberRangeFormatter(fMacros, locale);
270 }
271
locale(const Locale & locale)272 LocalizedNumberRangeFormatter UnlocalizedNumberRangeFormatter::locale(const Locale& locale)&& {
273 return LocalizedNumberRangeFormatter(std::move(fMacros), locale);
274 }
275
276
formatFormattableRange(const Formattable & first,const Formattable & second,UErrorCode & status) const277 FormattedNumberRange LocalizedNumberRangeFormatter::formatFormattableRange(
278 const Formattable& first, const Formattable& second, UErrorCode& status) const {
279 if (U_FAILURE(status)) {
280 return FormattedNumberRange(U_ILLEGAL_ARGUMENT_ERROR);
281 }
282
283 auto results = new UFormattedNumberRangeData();
284 if (results == nullptr) {
285 status = U_MEMORY_ALLOCATION_ERROR;
286 return FormattedNumberRange(status);
287 }
288
289 first.populateDecimalQuantity(results->quantity1, status);
290 if (U_FAILURE(status)) {
291 return FormattedNumberRange(status);
292 }
293
294 second.populateDecimalQuantity(results->quantity2, status);
295 if (U_FAILURE(status)) {
296 return FormattedNumberRange(status);
297 }
298
299 formatImpl(*results, first == second, status);
300
301 // Do not save the results object if we encountered a failure.
302 if (U_SUCCESS(status)) {
303 return FormattedNumberRange(results);
304 } else {
305 delete results;
306 return FormattedNumberRange(status);
307 }
308 }
309
formatImpl(UFormattedNumberRangeData & results,bool equalBeforeRounding,UErrorCode & status) const310 void LocalizedNumberRangeFormatter::formatImpl(
311 UFormattedNumberRangeData& results, bool equalBeforeRounding, UErrorCode& status) const {
312 auto* impl = getFormatter(status);
313 if (U_FAILURE(status)) {
314 return;
315 }
316 if (impl == nullptr) {
317 status = U_INTERNAL_PROGRAM_ERROR;
318 return;
319 }
320 impl->format(results, equalBeforeRounding, status);
321 }
322
323 const impl::NumberRangeFormatterImpl*
getFormatter(UErrorCode & status) const324 LocalizedNumberRangeFormatter::getFormatter(UErrorCode& status) const {
325 // TODO: Move this into umutex.h? (similar logic also in decimfmt.cpp)
326 // See ICU-20146
327
328 if (U_FAILURE(status)) {
329 return nullptr;
330 }
331
332 // First try to get the pre-computed formatter
333 auto* ptr = fAtomicFormatter.load();
334 if (ptr != nullptr) {
335 return ptr;
336 }
337
338 // Try computing the formatter on our own
339 auto* temp = new NumberRangeFormatterImpl(fMacros, status);
340 if (U_FAILURE(status)) {
341 return nullptr;
342 }
343 if (temp == nullptr) {
344 status = U_MEMORY_ALLOCATION_ERROR;
345 return nullptr;
346 }
347
348 // Note: ptr starts as nullptr; during compare_exchange,
349 // it is set to what is actually stored in the atomic
350 // if another thread beat us to computing the formatter object.
351 auto* nonConstThis = const_cast<LocalizedNumberRangeFormatter*>(this);
352 if (!nonConstThis->fAtomicFormatter.compare_exchange_strong(ptr, temp)) {
353 // Another thread beat us to computing the formatter
354 delete temp;
355 return ptr;
356 } else {
357 // Our copy of the formatter got stored in the atomic
358 return temp;
359 }
360
361 }
362
363
FormattedNumberRange(FormattedNumberRange && src)364 FormattedNumberRange::FormattedNumberRange(FormattedNumberRange&& src) U_NOEXCEPT
365 : fResults(src.fResults), fErrorCode(src.fErrorCode) {
366 // Disown src.fResults to prevent double-deletion
367 src.fResults = nullptr;
368 src.fErrorCode = U_INVALID_STATE_ERROR;
369 }
370
operator =(FormattedNumberRange && src)371 FormattedNumberRange& FormattedNumberRange::operator=(FormattedNumberRange&& src) U_NOEXCEPT {
372 delete fResults;
373 fResults = src.fResults;
374 fErrorCode = src.fErrorCode;
375 // Disown src.fResults to prevent double-deletion
376 src.fResults = nullptr;
377 src.fErrorCode = U_INVALID_STATE_ERROR;
378 return *this;
379 }
380
toString(UErrorCode & status) const381 UnicodeString FormattedNumberRange::toString(UErrorCode& status) const {
382 if (U_FAILURE(status)) {
383 return ICU_Utility::makeBogusString();
384 }
385 if (fResults == nullptr) {
386 status = fErrorCode;
387 return ICU_Utility::makeBogusString();
388 }
389 return fResults->string.toUnicodeString();
390 }
391
appendTo(Appendable & appendable,UErrorCode & status) const392 Appendable& FormattedNumberRange::appendTo(Appendable& appendable, UErrorCode& status) const {
393 if (U_FAILURE(status)) {
394 return appendable;
395 }
396 if (fResults == nullptr) {
397 status = fErrorCode;
398 return appendable;
399 }
400 appendable.appendString(fResults->string.chars(), fResults->string.length());
401 return appendable;
402 }
403
nextFieldPosition(FieldPosition & fieldPosition,UErrorCode & status) const404 UBool FormattedNumberRange::nextFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) const {
405 if (U_FAILURE(status)) {
406 return FALSE;
407 }
408 if (fResults == nullptr) {
409 status = fErrorCode;
410 return FALSE;
411 }
412 // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
413 return fResults->string.nextFieldPosition(fieldPosition, status) ? TRUE : FALSE;
414 }
415
getAllFieldPositions(FieldPositionIterator & iterator,UErrorCode & status) const416 void FormattedNumberRange::getAllFieldPositions(FieldPositionIterator& iterator, UErrorCode& status) const {
417 FieldPositionIteratorHandler fpih(&iterator, status);
418 getAllFieldPositionsImpl(fpih, status);
419 }
420
getAllFieldPositionsImpl(FieldPositionIteratorHandler & fpih,UErrorCode & status) const421 void FormattedNumberRange::getAllFieldPositionsImpl(
422 FieldPositionIteratorHandler& fpih, UErrorCode& status) const {
423 if (U_FAILURE(status)) {
424 return;
425 }
426 if (fResults == nullptr) {
427 status = fErrorCode;
428 return;
429 }
430 fResults->string.getAllFieldPositions(fpih, status);
431 }
432
getFirstDecimal(UErrorCode & status) const433 UnicodeString FormattedNumberRange::getFirstDecimal(UErrorCode& status) const {
434 if (U_FAILURE(status)) {
435 return ICU_Utility::makeBogusString();
436 }
437 if (fResults == nullptr) {
438 status = fErrorCode;
439 return ICU_Utility::makeBogusString();
440 }
441 return fResults->quantity1.toScientificString();
442 }
443
getSecondDecimal(UErrorCode & status) const444 UnicodeString FormattedNumberRange::getSecondDecimal(UErrorCode& status) const {
445 if (U_FAILURE(status)) {
446 return ICU_Utility::makeBogusString();
447 }
448 if (fResults == nullptr) {
449 status = fErrorCode;
450 return ICU_Utility::makeBogusString();
451 }
452 return fResults->quantity2.toScientificString();
453 }
454
getIdentityResult(UErrorCode & status) const455 UNumberRangeIdentityResult FormattedNumberRange::getIdentityResult(UErrorCode& status) const {
456 if (U_FAILURE(status)) {
457 return UNUM_IDENTITY_RESULT_NOT_EQUAL;
458 }
459 if (fResults == nullptr) {
460 status = fErrorCode;
461 return UNUM_IDENTITY_RESULT_NOT_EQUAL;
462 }
463 return fResults->identityResult;
464 }
465
~FormattedNumberRange()466 FormattedNumberRange::~FormattedNumberRange() {
467 delete fResults;
468 }
469
470
471
472 #endif /* #if !UCONFIG_NO_FORMATTING */
473