diff options
author | romankoshelev <romankoshelev@yandex-team.com> | 2024-05-13 11:00:27 +0300 |
---|---|---|
committer | romankoshelev <romankoshelev@yandex-team.com> | 2024-05-13 11:13:05 +0300 |
commit | 5b22fadb0f035a3b82c328e0ae710ad2b92f6eac (patch) | |
tree | e15dc649c79c4fb78f35cd6694dfe9af9bfcc0ad /contrib/libs/icu/i18n/messageformat2_formattable.cpp | |
parent | 5946aa7d3cbca62f6bcf074e8a2b9346e7a96af4 (diff) | |
download | ydb-5b22fadb0f035a3b82c328e0ae710ad2b92f6eac.tar.gz |
Update ICU to 75.1
904da4ae1c86fc5542eac7f1cd18d97b72eb8517
Diffstat (limited to 'contrib/libs/icu/i18n/messageformat2_formattable.cpp')
-rw-r--r-- | contrib/libs/icu/i18n/messageformat2_formattable.cpp | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/contrib/libs/icu/i18n/messageformat2_formattable.cpp b/contrib/libs/icu/i18n/messageformat2_formattable.cpp new file mode 100644 index 0000000000..3152ccb44f --- /dev/null +++ b/contrib/libs/icu/i18n/messageformat2_formattable.cpp @@ -0,0 +1,338 @@ +// © 2024 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#if !UCONFIG_NO_MF2 + +#include "unicode/messageformat2_formattable.h" +#include "unicode/smpdtfmt.h" +#include "messageformat2_macros.h" + +#include "limits.h" + +U_NAMESPACE_BEGIN + +namespace message2 { + + // Fallback values are enclosed in curly braces; + // see https://github.com/unicode-org/message-format-wg/blob/main/spec/formatting.md#formatting-fallback-values + + static UnicodeString fallbackToString(const UnicodeString& s) { + UnicodeString result; + result += LEFT_CURLY_BRACE; + result += s; + result += RIGHT_CURLY_BRACE; + return result; + } + + Formattable& Formattable::operator=(Formattable other) noexcept { + swap(*this, other); + return *this; + } + + Formattable::Formattable(const Formattable& other) { + contents = other.contents; + holdsDate = other.holdsDate; + } + + Formattable Formattable::forDecimal(std::string_view number, UErrorCode &status) { + Formattable f; + // The relevant overload of the StringPiece constructor + // casts the string length to int32_t, so we have to check + // that the length makes sense + if (number.size() > INT_MAX) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } else { + f.contents = icu::Formattable(StringPiece(number), status); + } + return f; + } + + UFormattableType Formattable::getType() const { + if (std::holds_alternative<double>(contents)) { + return holdsDate ? UFMT_DATE : UFMT_DOUBLE; + } + if (std::holds_alternative<int64_t>(contents)) { + return UFMT_INT64; + } + if (std::holds_alternative<UnicodeString>(contents)) { + return UFMT_STRING; + } + if (isDecimal()) { + switch (std::get_if<icu::Formattable>(&contents)->getType()) { + case icu::Formattable::Type::kLong: { + return UFMT_LONG; + } + case icu::Formattable::Type::kDouble: { + return UFMT_DOUBLE; + } + default: { + return UFMT_INT64; + } + } + } + if (std::holds_alternative<const FormattableObject*>(contents)) { + return UFMT_OBJECT; + } + return UFMT_ARRAY; + } + + const Formattable* Formattable::getArray(int32_t& len, UErrorCode& status) const { + NULL_ON_ERROR(status); + + if (getType() != UFMT_ARRAY) { + len = 0; + status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + const std::pair<const Formattable*, int32_t>& p = *std::get_if<std::pair<const Formattable*, int32_t>>(&contents); + U_ASSERT(p.first != nullptr); + len = p.second; + return p.first; + } + + int64_t Formattable::getInt64(UErrorCode& status) const { + if (isDecimal() && isNumeric()) { + return std::get_if<icu::Formattable>(&contents)->getInt64(status); + } + + switch (getType()) { + case UFMT_LONG: + case UFMT_INT64: { + return *std::get_if<int64_t>(&contents); + } + case UFMT_DOUBLE: { + return icu::Formattable(*std::get_if<double>(&contents)).getInt64(status); + } + default: { + status = U_INVALID_FORMAT_ERROR; + return 0; + } + } + } + + icu::Formattable Formattable::asICUFormattable(UErrorCode& status) const { + if (U_FAILURE(status)) { + return {}; + } + // Type must not be UFMT_ARRAY or UFMT_OBJECT + if (getType() == UFMT_ARRAY || getType() == UFMT_OBJECT) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return {}; + } + + if (isDecimal()) { + return *std::get_if<icu::Formattable>(&contents); + } + + switch (getType()) { + case UFMT_DATE: { + return icu::Formattable(*std::get_if<double>(&contents), icu::Formattable::kIsDate); + } + case UFMT_DOUBLE: { + return icu::Formattable(*std::get_if<double>(&contents)); + } + case UFMT_LONG: { + return icu::Formattable(static_cast<int32_t>(*std::get_if<double>(&contents))); + } + case UFMT_INT64: { + return icu::Formattable(*std::get_if<int64_t>(&contents)); + } + case UFMT_STRING: { + return icu::Formattable(*std::get_if<UnicodeString>(&contents)); + } + default: { + // Already checked for UFMT_ARRAY and UFMT_OBJECT + return icu::Formattable(); + } + } + } + + Formattable::~Formattable() {} + + FormattableObject::~FormattableObject() {} + + FormattedMessage::~FormattedMessage() {} + + FormattedValue::FormattedValue(const UnicodeString& s) { + type = kString; + stringOutput = std::move(s); + } + + FormattedValue::FormattedValue(number::FormattedNumber&& n) { + type = kNumber; + numberOutput = std::move(n); + } + + FormattedValue& FormattedValue::operator=(FormattedValue&& other) noexcept { + type = other.type; + if (type == kString) { + stringOutput = std::move(other.stringOutput); + } else { + numberOutput = std::move(other.numberOutput); + } + return *this; + } + + FormattedValue::~FormattedValue() {} + + FormattedPlaceholder& FormattedPlaceholder::operator=(FormattedPlaceholder&& other) noexcept { + type = other.type; + source = other.source; + if (type == kEvaluated) { + formatted = std::move(other.formatted); + previousOptions = std::move(other.previousOptions); + } + fallback = other.fallback; + return *this; + } + + const Formattable& FormattedPlaceholder::asFormattable() const { + return source; + } + + // Default formatters + // ------------------ + + number::FormattedNumber formatNumberWithDefaults(const Locale& locale, double toFormat, UErrorCode& errorCode) { + return number::NumberFormatter::withLocale(locale).formatDouble(toFormat, errorCode); + } + + number::FormattedNumber formatNumberWithDefaults(const Locale& locale, int32_t toFormat, UErrorCode& errorCode) { + return number::NumberFormatter::withLocale(locale).formatInt(toFormat, errorCode); + } + + number::FormattedNumber formatNumberWithDefaults(const Locale& locale, int64_t toFormat, UErrorCode& errorCode) { + return number::NumberFormatter::withLocale(locale).formatInt(toFormat, errorCode); + } + + number::FormattedNumber formatNumberWithDefaults(const Locale& locale, StringPiece toFormat, UErrorCode& errorCode) { + return number::NumberFormatter::withLocale(locale).formatDecimal(toFormat, errorCode); + } + + DateFormat* defaultDateTimeInstance(const Locale& locale, UErrorCode& errorCode) { + NULL_ON_ERROR(errorCode); + LocalPointer<DateFormat> df(DateFormat::createDateTimeInstance(DateFormat::SHORT, DateFormat::SHORT, locale)); + if (!df.isValid()) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + return df.orphan(); + } + + void formatDateWithDefaults(const Locale& locale, UDate date, UnicodeString& result, UErrorCode& errorCode) { + CHECK_ERROR(errorCode); + + LocalPointer<DateFormat> df(defaultDateTimeInstance(locale, errorCode)); + CHECK_ERROR(errorCode); + df->format(date, result, 0, errorCode); + } + + // Called when output is required and the contents are an unevaluated `Formattable`; + // formats the source `Formattable` to a string with defaults, if it can be + // formatted with a default formatter + static FormattedPlaceholder formatWithDefaults(const Locale& locale, const FormattedPlaceholder& input, UErrorCode& status) { + if (U_FAILURE(status)) { + return {}; + } + + const Formattable& toFormat = input.asFormattable(); + // Try as decimal number first + if (toFormat.isNumeric()) { + // Note: the ICU Formattable has to be created here since the StringPiece + // refers to state inside the Formattable; so otherwise we'll have a reference + // to a temporary object + icu::Formattable icuFormattable = toFormat.asICUFormattable(status); + StringPiece asDecimal = icuFormattable.getDecimalNumber(status); + if (U_FAILURE(status)) { + return {}; + } + if (asDecimal != nullptr) { + return FormattedPlaceholder(input, FormattedValue(formatNumberWithDefaults(locale, asDecimal, status))); + } + } + + UFormattableType type = toFormat.getType(); + switch (type) { + case UFMT_DATE: { + UnicodeString result; + UDate d = toFormat.getDate(status); + U_ASSERT(U_SUCCESS(status)); + formatDateWithDefaults(locale, d, result, status); + return FormattedPlaceholder(input, FormattedValue(std::move(result))); + } + case UFMT_DOUBLE: { + double d = toFormat.getDouble(status); + U_ASSERT(U_SUCCESS(status)); + return FormattedPlaceholder(input, FormattedValue(formatNumberWithDefaults(locale, d, status))); + } + case UFMT_LONG: { + int32_t l = toFormat.getLong(status); + U_ASSERT(U_SUCCESS(status)); + return FormattedPlaceholder(input, FormattedValue(formatNumberWithDefaults(locale, l, status))); + } + case UFMT_INT64: { + int64_t i = toFormat.getInt64Value(status); + U_ASSERT(U_SUCCESS(status)); + return FormattedPlaceholder(input, FormattedValue(formatNumberWithDefaults(locale, i, status))); + } + case UFMT_STRING: { + const UnicodeString& s = toFormat.getString(status); + U_ASSERT(U_SUCCESS(status)); + return FormattedPlaceholder(input, FormattedValue(UnicodeString(s))); + } + default: { + // No default formatters for other types; use fallback + status = U_MF_FORMATTING_ERROR; + // Note: it would be better to set an internal formatting error so that a string + // (e.g. the type tag) can be provided. However, this method is called by the + // public method formatToString() and thus can't take a MessageContext + return FormattedPlaceholder(input.getFallback()); + } + } + } + + // Called when string output is required; forces output to be produced + // if none is present (including formatting number output as a string) + UnicodeString FormattedPlaceholder::formatToString(const Locale& locale, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return {}; + } + if (isFallback() || isNullOperand()) { + return fallbackToString(fallback); + } + + // Evaluated value: either just return the string, or format the number + // as a string and return it + if (isEvaluated()) { + if (formatted.isString()) { + return formatted.getString(); + } else { + return formatted.getNumber().toString(status); + } + } + // Unevaluated value: first evaluate it fully, then format + UErrorCode savedStatus = status; + FormattedPlaceholder evaluated = formatWithDefaults(locale, *this, status); + if (status == U_MF_FORMATTING_ERROR) { + U_ASSERT(evaluated.isFallback()); + return evaluated.getFallback(); + } + // Ignore U_USING_DEFAULT_WARNING + if (status == U_USING_DEFAULT_WARNING) { + status = savedStatus; + } + return evaluated.formatToString(locale, status); + } + +} // namespace message2 + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_MF2 */ + +#endif /* #if !UCONFIG_NO_FORMATTING */ |