numrange_fluent.cpp (14090B)
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. 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> 31 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> 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> 49 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> 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> 67 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> 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> 85 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> 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> 103 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> 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> 121 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> 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> 139 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> 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> 153 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> 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> 167 LocalPointer<Derived> NumberRangeFormatterSettings<Derived>::clone() const & { 168 return LocalPointer<Derived>(new Derived(*this)); 169 } 170 171 template<typename Derived> 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 184 UnlocalizedNumberRangeFormatter NumberRangeFormatter::with() { 185 UnlocalizedNumberRangeFormatter result; 186 return result; 187 } 188 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 198 UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(const UNF& other) 199 : UNF(static_cast<const NFS<UNF>&>(other)) {} 200 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. 207 UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(UNF&& src) noexcept 208 : UNF(static_cast<NFS<UNF>&&>(src)) {} 209 210 UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(NFS<UNF>&& src) noexcept 211 : NFS<UNF>(std::move(src)) { 212 // No additional fields to assign 213 } 214 215 UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(const impl::RangeMacroProps ¯os) { 216 fMacros = macros; 217 } 218 219 UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(impl::RangeMacroProps &¯os) { 220 fMacros = macros; 221 } 222 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 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. 236 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const LNF& other) 237 : LNF(static_cast<const NFS<LNF>&>(other)) {} 238 239 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const NFS<LNF>& other) 240 : NFS<LNF>(other) { 241 // No additional fields to assign 242 } 243 244 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(LocalizedNumberRangeFormatter&& src) noexcept 245 : LNF(static_cast<NFS<LNF>&&>(src)) {} 246 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 #ifndef __wasi__ 252 auto* stolen = _src.fAtomicFormatter.exchange(nullptr); 253 delete fAtomicFormatter.exchange(stolen); 254 #else 255 delete fAtomicFormatter; 256 fAtomicFormatter = _src.fAtomicFormatter; 257 _src.fAtomicFormatter = nullptr; 258 #endif 259 } 260 261 LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(const LNF& other) { 262 if (this == &other) { return *this; } // self-assignment: no-op 263 NFS<LNF>::operator=(static_cast<const NFS<LNF>&>(other)); 264 // Do not steal; just clear 265 #ifndef __wasi__ 266 delete fAtomicFormatter.exchange(nullptr); 267 #else 268 delete fAtomicFormatter; 269 #endif 270 return *this; 271 } 272 273 LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(LNF&& src) noexcept { 274 NFS<LNF>::operator=(static_cast<NFS<LNF>&&>(src)); 275 // Steal the compiled formatter 276 #ifndef __wasi__ 277 auto* stolen = src.fAtomicFormatter.exchange(nullptr); 278 delete fAtomicFormatter.exchange(stolen); 279 #else 280 delete fAtomicFormatter; 281 fAtomicFormatter = src.fAtomicFormatter; 282 src.fAtomicFormatter = nullptr; 283 #endif 284 return *this; 285 } 286 287 288 LocalizedNumberRangeFormatter::~LocalizedNumberRangeFormatter() { 289 #ifndef __wasi__ 290 delete fAtomicFormatter.exchange(nullptr); 291 #else 292 delete fAtomicFormatter; 293 #endif 294 } 295 296 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const RangeMacroProps& macros, const Locale& locale) { 297 fMacros = macros; 298 fMacros.locale = locale; 299 touchRangeLocales(fMacros); 300 } 301 302 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(RangeMacroProps&& macros, const Locale& locale) { 303 fMacros = std::move(macros); 304 fMacros.locale = locale; 305 touchRangeLocales(fMacros); 306 } 307 308 LocalizedNumberRangeFormatter UnlocalizedNumberRangeFormatter::locale(const Locale& locale) const& { 309 return LocalizedNumberRangeFormatter(fMacros, locale); 310 } 311 312 LocalizedNumberRangeFormatter UnlocalizedNumberRangeFormatter::locale(const Locale& locale)&& { 313 return LocalizedNumberRangeFormatter(std::move(fMacros), locale); 314 } 315 316 317 UnlocalizedNumberRangeFormatter LocalizedNumberRangeFormatter::withoutLocale() const & { 318 RangeMacroProps macros(fMacros); 319 macros.locale = Locale(); 320 return UnlocalizedNumberRangeFormatter(macros); 321 } 322 323 UnlocalizedNumberRangeFormatter LocalizedNumberRangeFormatter::withoutLocale() && { 324 RangeMacroProps macros(std::move(fMacros)); 325 macros.locale = Locale(); 326 return UnlocalizedNumberRangeFormatter(std::move(macros)); 327 } 328 329 330 FormattedNumberRange LocalizedNumberRangeFormatter::formatFormattableRange( 331 const Formattable& first, const Formattable& second, UErrorCode& status) const { 332 if (U_FAILURE(status)) { 333 return FormattedNumberRange(U_ILLEGAL_ARGUMENT_ERROR); 334 } 335 336 LocalPointer<UFormattedNumberRangeData> results(new UFormattedNumberRangeData(), status); 337 if (U_FAILURE(status)) { 338 return FormattedNumberRange(status); 339 } 340 341 first.populateDecimalQuantity(results->quantity1, status); 342 if (U_FAILURE(status)) { 343 return FormattedNumberRange(status); 344 } 345 346 second.populateDecimalQuantity(results->quantity2, status); 347 if (U_FAILURE(status)) { 348 return FormattedNumberRange(status); 349 } 350 351 formatImpl(*results, first == second, status); 352 353 // Do not save the results object if we encountered a failure. 354 if (U_SUCCESS(status)) { 355 return FormattedNumberRange(results.orphan()); 356 } else { 357 return FormattedNumberRange(status); 358 } 359 } 360 361 void LocalizedNumberRangeFormatter::formatImpl( 362 UFormattedNumberRangeData& results, bool equalBeforeRounding, UErrorCode& status) const { 363 const auto* impl = getFormatter(status); 364 if (U_FAILURE(status)) { 365 return; 366 } 367 if (impl == nullptr) { 368 status = U_INTERNAL_PROGRAM_ERROR; 369 return; 370 } 371 impl->format(results, equalBeforeRounding, status); 372 if (U_FAILURE(status)) { 373 return; 374 } 375 results.getStringRef().writeTerminator(status); 376 } 377 378 const impl::NumberRangeFormatterImpl* 379 LocalizedNumberRangeFormatter::getFormatter(UErrorCode& status) const { 380 // TODO: Move this into umutex.h? (similar logic also in decimfmt.cpp) 381 // See ICU-20146 382 383 if (U_FAILURE(status)) { 384 return nullptr; 385 } 386 387 // First try to get the pre-computed formatter 388 #ifndef __wasi__ 389 auto* ptr = fAtomicFormatter.load(); 390 #else 391 auto* ptr = fAtomicFormatter; 392 #endif 393 if (ptr != nullptr) { 394 return ptr; 395 } 396 397 // Try computing the formatter on our own 398 LocalPointer<NumberRangeFormatterImpl> temp(new NumberRangeFormatterImpl(fMacros, status), status); 399 if (U_FAILURE(status)) { 400 return nullptr; 401 } 402 403 // Note: ptr starts as nullptr; during compare_exchange, 404 // it is set to what is actually stored in the atomic 405 // if another thread beat us to computing the formatter object. 406 auto* nonConstThis = const_cast<LocalizedNumberRangeFormatter*>(this); 407 #ifndef __wasi__ 408 if (!nonConstThis->fAtomicFormatter.compare_exchange_strong(ptr, temp.getAlias())) { 409 // Another thread beat us to computing the formatter 410 return ptr; 411 } else { 412 // Our copy of the formatter got stored in the atomic 413 return temp.orphan(); 414 } 415 #else 416 nonConstThis->fAtomicFormatter = temp.getAlias(); 417 return temp.orphan(); 418 #endif 419 420 } 421 422 423 #endif /* #if !UCONFIG_NO_FORMATTING */