// © 2018 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html #include "unicode/utypes.h" #if !UCONFIG_NO_FORMATTING // Allow implicit conversion from char16_t* to UnicodeString for this file: // Helpful in toString methods and elsewhere. #define UNISTR_FROM_STRING_EXPLICIT #include "numrange_impl.h" #include "util.h" #include "number_utypes.h" using namespace icu; using namespace icu::number; using namespace icu::number::impl; // This function needs to be declared in this namespace so it can be friended. // NOTE: In Java, this logic is handled in the resolve() function. void icu::number::impl::touchRangeLocales(RangeMacroProps& macros) { macros.formatter1.fMacros.locale = macros.locale; macros.formatter2.fMacros.locale = macros.locale; } template Derived NumberRangeFormatterSettings::numberFormatterBoth(const UnlocalizedNumberFormatter& formatter) const& { Derived copy(*this); copy.fMacros.formatter1 = formatter; copy.fMacros.singleFormatter = true; touchRangeLocales(copy.fMacros); return copy; } template Derived NumberRangeFormatterSettings::numberFormatterBoth(const UnlocalizedNumberFormatter& formatter) && { Derived move(std::move(*this)); move.fMacros.formatter1 = formatter; move.fMacros.singleFormatter = true; touchRangeLocales(move.fMacros); return move; } template Derived NumberRangeFormatterSettings::numberFormatterBoth(UnlocalizedNumberFormatter&& formatter) const& { Derived copy(*this); copy.fMacros.formatter1 = std::move(formatter); copy.fMacros.singleFormatter = true; touchRangeLocales(copy.fMacros); return copy; } template Derived NumberRangeFormatterSettings::numberFormatterBoth(UnlocalizedNumberFormatter&& formatter) && { Derived move(std::move(*this)); move.fMacros.formatter1 = std::move(formatter); move.fMacros.singleFormatter = true; touchRangeLocales(move.fMacros); return move; } template Derived NumberRangeFormatterSettings::numberFormatterFirst(const UnlocalizedNumberFormatter& formatter) const& { Derived copy(*this); copy.fMacros.formatter1 = formatter; copy.fMacros.singleFormatter = false; touchRangeLocales(copy.fMacros); return copy; } template Derived NumberRangeFormatterSettings::numberFormatterFirst(const UnlocalizedNumberFormatter& formatter) && { Derived move(std::move(*this)); move.fMacros.formatter1 = formatter; move.fMacros.singleFormatter = false; touchRangeLocales(move.fMacros); return move; } template Derived NumberRangeFormatterSettings::numberFormatterFirst(UnlocalizedNumberFormatter&& formatter) const& { Derived copy(*this); copy.fMacros.formatter1 = std::move(formatter); copy.fMacros.singleFormatter = false; touchRangeLocales(copy.fMacros); return copy; } template Derived NumberRangeFormatterSettings::numberFormatterFirst(UnlocalizedNumberFormatter&& formatter) && { Derived move(std::move(*this)); move.fMacros.formatter1 = std::move(formatter); move.fMacros.singleFormatter = false; touchRangeLocales(move.fMacros); return move; } template Derived NumberRangeFormatterSettings::numberFormatterSecond(const UnlocalizedNumberFormatter& formatter) const& { Derived copy(*this); copy.fMacros.formatter2 = formatter; copy.fMacros.singleFormatter = false; touchRangeLocales(copy.fMacros); return copy; } template Derived NumberRangeFormatterSettings::numberFormatterSecond(const UnlocalizedNumberFormatter& formatter) && { Derived move(std::move(*this)); move.fMacros.formatter2 = formatter; move.fMacros.singleFormatter = false; touchRangeLocales(move.fMacros); return move; } template Derived NumberRangeFormatterSettings::numberFormatterSecond(UnlocalizedNumberFormatter&& formatter) const& { Derived copy(*this); copy.fMacros.formatter2 = std::move(formatter); copy.fMacros.singleFormatter = false; touchRangeLocales(copy.fMacros); return copy; } template Derived NumberRangeFormatterSettings::numberFormatterSecond(UnlocalizedNumberFormatter&& formatter) && { Derived move(std::move(*this)); move.fMacros.formatter2 = std::move(formatter); move.fMacros.singleFormatter = false; touchRangeLocales(move.fMacros); return move; } template Derived NumberRangeFormatterSettings::collapse(UNumberRangeCollapse collapse) const& { Derived copy(*this); copy.fMacros.collapse = collapse; return copy; } template Derived NumberRangeFormatterSettings::collapse(UNumberRangeCollapse collapse) && { Derived move(std::move(*this)); move.fMacros.collapse = collapse; return move; } template Derived NumberRangeFormatterSettings::identityFallback(UNumberRangeIdentityFallback identityFallback) const& { Derived copy(*this); copy.fMacros.identityFallback = identityFallback; return copy; } template Derived NumberRangeFormatterSettings::identityFallback(UNumberRangeIdentityFallback identityFallback) && { Derived move(std::move(*this)); move.fMacros.identityFallback = identityFallback; return move; } // Declare all classes that implement NumberRangeFormatterSettings // See https://stackoverflow.com/a/495056/1407170 template class icu::number::NumberRangeFormatterSettings; template class icu::number::NumberRangeFormatterSettings; UnlocalizedNumberRangeFormatter NumberRangeFormatter::with() { UnlocalizedNumberRangeFormatter result; return result; } LocalizedNumberRangeFormatter NumberRangeFormatter::withLocale(const Locale& locale) { return with().locale(locale); } template using NFS = NumberRangeFormatterSettings; using LNF = LocalizedNumberRangeFormatter; using UNF = UnlocalizedNumberRangeFormatter; UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(const UNF& other) : UNF(static_cast&>(other)) {} UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(const NFS& other) : NFS(other) { // No additional fields to assign } // Make default copy constructor call the NumberRangeFormatterSettings copy constructor. UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(UNF&& src) U_NOEXCEPT : UNF(static_cast&&>(src)) {} UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(NFS&& src) U_NOEXCEPT : NFS(std::move(src)) { // No additional fields to assign } UnlocalizedNumberRangeFormatter& UnlocalizedNumberRangeFormatter::operator=(const UNF& other) { NFS::operator=(static_cast&>(other)); // No additional fields to assign return *this; } UnlocalizedNumberRangeFormatter& UnlocalizedNumberRangeFormatter::operator=(UNF&& src) U_NOEXCEPT { NFS::operator=(static_cast&&>(src)); // No additional fields to assign return *this; } // Make default copy constructor call the NumberRangeFormatterSettings copy constructor. LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const LNF& other) : LNF(static_cast&>(other)) {} LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const NFS& other) : NFS(other) { // No additional fields to assign } LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(LocalizedNumberRangeFormatter&& src) U_NOEXCEPT : LNF(static_cast&&>(src)) {} LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(NFS&& src) U_NOEXCEPT : NFS(std::move(src)) { // Steal the compiled formatter LNF&& _src = static_cast(src); auto* stolen = _src.fAtomicFormatter.exchange(nullptr); delete fAtomicFormatter.exchange(stolen); } LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(const LNF& other) { NFS::operator=(static_cast&>(other)); // Do not steal; just clear delete fAtomicFormatter.exchange(nullptr); return *this; } LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(LNF&& src) U_NOEXCEPT { NFS::operator=(static_cast&&>(src)); // Steal the compiled formatter auto* stolen = src.fAtomicFormatter.exchange(nullptr); delete fAtomicFormatter.exchange(stolen); return *this; } LocalizedNumberRangeFormatter::~LocalizedNumberRangeFormatter() { delete fAtomicFormatter.exchange(nullptr); } LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const RangeMacroProps& macros, const Locale& locale) { fMacros = macros; fMacros.locale = locale; touchRangeLocales(fMacros); } LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(RangeMacroProps&& macros, const Locale& locale) { fMacros = std::move(macros); fMacros.locale = locale; touchRangeLocales(fMacros); } LocalizedNumberRangeFormatter UnlocalizedNumberRangeFormatter::locale(const Locale& locale) const& { return LocalizedNumberRangeFormatter(fMacros, locale); } LocalizedNumberRangeFormatter UnlocalizedNumberRangeFormatter::locale(const Locale& locale)&& { return LocalizedNumberRangeFormatter(std::move(fMacros), locale); } FormattedNumberRange LocalizedNumberRangeFormatter::formatFormattableRange( const Formattable& first, const Formattable& second, UErrorCode& status) const { if (U_FAILURE(status)) { return FormattedNumberRange(U_ILLEGAL_ARGUMENT_ERROR); } auto results = new UFormattedNumberRangeData(); if (results == nullptr) { status = U_MEMORY_ALLOCATION_ERROR; return FormattedNumberRange(status); } first.populateDecimalQuantity(results->quantity1, status); if (U_FAILURE(status)) { return FormattedNumberRange(status); } second.populateDecimalQuantity(results->quantity2, status); if (U_FAILURE(status)) { return FormattedNumberRange(status); } formatImpl(*results, first == second, status); // Do not save the results object if we encountered a failure. if (U_SUCCESS(status)) { return FormattedNumberRange(results); } else { delete results; return FormattedNumberRange(status); } } void LocalizedNumberRangeFormatter::formatImpl( UFormattedNumberRangeData& results, bool equalBeforeRounding, UErrorCode& status) const { auto* impl = getFormatter(status); if (U_FAILURE(status)) { return; } if (impl == nullptr) { status = U_INTERNAL_PROGRAM_ERROR; return; } impl->format(results, equalBeforeRounding, status); if (U_FAILURE(status)) { return; } results.string.writeTerminator(status); } const impl::NumberRangeFormatterImpl* LocalizedNumberRangeFormatter::getFormatter(UErrorCode& status) const { // TODO: Move this into umutex.h? (similar logic also in decimfmt.cpp) // See ICU-20146 if (U_FAILURE(status)) { return nullptr; } // First try to get the pre-computed formatter auto* ptr = fAtomicFormatter.load(); if (ptr != nullptr) { return ptr; } // Try computing the formatter on our own auto* temp = new NumberRangeFormatterImpl(fMacros, status); if (U_FAILURE(status)) { return nullptr; } if (temp == nullptr) { status = U_MEMORY_ALLOCATION_ERROR; return nullptr; } // Note: ptr starts as nullptr; during compare_exchange, // it is set to what is actually stored in the atomic // if another thread beat us to computing the formatter object. auto* nonConstThis = const_cast(this); if (!nonConstThis->fAtomicFormatter.compare_exchange_strong(ptr, temp)) { // Another thread beat us to computing the formatter delete temp; return ptr; } else { // Our copy of the formatter got stored in the atomic return temp; } } FormattedNumberRange::FormattedNumberRange(FormattedNumberRange&& src) U_NOEXCEPT : fResults(src.fResults), fErrorCode(src.fErrorCode) { // Disown src.fResults to prevent double-deletion src.fResults = nullptr; src.fErrorCode = U_INVALID_STATE_ERROR; } FormattedNumberRange& FormattedNumberRange::operator=(FormattedNumberRange&& src) U_NOEXCEPT { delete fResults; fResults = src.fResults; fErrorCode = src.fErrorCode; // Disown src.fResults to prevent double-deletion src.fResults = nullptr; src.fErrorCode = U_INVALID_STATE_ERROR; return *this; } UnicodeString FormattedNumberRange::toString(UErrorCode& status) const { if (U_FAILURE(status)) { return ICU_Utility::makeBogusString(); } if (fResults == nullptr) { status = fErrorCode; return ICU_Utility::makeBogusString(); } return fResults->string.toUnicodeString(); } UnicodeString FormattedNumberRange::toTempString(UErrorCode& status) const { if (U_FAILURE(status)) { return ICU_Utility::makeBogusString(); } if (fResults == nullptr) { status = fErrorCode; return ICU_Utility::makeBogusString(); } return fResults->string.toTempUnicodeString(); } Appendable& FormattedNumberRange::appendTo(Appendable& appendable, UErrorCode& status) const { if (U_FAILURE(status)) { return appendable; } if (fResults == nullptr) { status = fErrorCode; return appendable; } appendable.appendString(fResults->string.chars(), fResults->string.length()); return appendable; } UBool FormattedNumberRange::nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const { if (U_FAILURE(status)) { return FALSE; } if (fResults == nullptr) { status = fErrorCode; return FALSE; } // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool return fResults->string.nextPosition(cfpos, status) ? TRUE : FALSE; } UBool FormattedNumberRange::nextFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) const { if (U_FAILURE(status)) { return FALSE; } if (fResults == nullptr) { status = fErrorCode; return FALSE; } // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool return fResults->string.nextFieldPosition(fieldPosition, status) ? TRUE : FALSE; } void FormattedNumberRange::getAllFieldPositions(FieldPositionIterator& iterator, UErrorCode& status) const { FieldPositionIteratorHandler fpih(&iterator, status); getAllFieldPositionsImpl(fpih, status); } void FormattedNumberRange::getAllFieldPositionsImpl( FieldPositionIteratorHandler& fpih, UErrorCode& status) const { if (U_FAILURE(status)) { return; } if (fResults == nullptr) { status = fErrorCode; return; } fResults->string.getAllFieldPositions(fpih, status); } UnicodeString FormattedNumberRange::getFirstDecimal(UErrorCode& status) const { if (U_FAILURE(status)) { return ICU_Utility::makeBogusString(); } if (fResults == nullptr) { status = fErrorCode; return ICU_Utility::makeBogusString(); } return fResults->quantity1.toScientificString(); } UnicodeString FormattedNumberRange::getSecondDecimal(UErrorCode& status) const { if (U_FAILURE(status)) { return ICU_Utility::makeBogusString(); } if (fResults == nullptr) { status = fErrorCode; return ICU_Utility::makeBogusString(); } return fResults->quantity2.toScientificString(); } UNumberRangeIdentityResult FormattedNumberRange::getIdentityResult(UErrorCode& status) const { if (U_FAILURE(status)) { return UNUM_IDENTITY_RESULT_NOT_EQUAL; } if (fResults == nullptr) { status = fErrorCode; return UNUM_IDENTITY_RESULT_NOT_EQUAL; } return fResults->identityResult; } FormattedNumberRange::~FormattedNumberRange() { delete fResults; } #endif /* #if !UCONFIG_NO_FORMATTING */