diff options
Diffstat (limited to 'intl/components/src/NumberFormat.cpp')
-rw-r--r-- | intl/components/src/NumberFormat.cpp | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/intl/components/src/NumberFormat.cpp b/intl/components/src/NumberFormat.cpp new file mode 100644 index 0000000000..6575c7a032 --- /dev/null +++ b/intl/components/src/NumberFormat.cpp @@ -0,0 +1,154 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "mozilla/intl/NumberFormat.h" +#include "NumberFormatFields.h" +#include "NumberFormatterSkeleton.h" +#include "ScopedICUObject.h" + +#include "unicode/unumberformatter.h" +#include "unicode/upluralrules.h" + +namespace mozilla::intl { + +/*static*/ Result<UniquePtr<NumberFormat>, ICUError> NumberFormat::TryCreate( + std::string_view aLocale, const NumberFormatOptions& aOptions) { + UniquePtr<NumberFormat> nf = MakeUnique<NumberFormat>(); + Result<Ok, ICUError> result = nf->initialize(aLocale, aOptions); + if (result.isOk()) { + return nf; + } + + return Err(result.unwrapErr()); +} + +NumberFormat::~NumberFormat() { + if (mFormattedNumber) { + unumf_closeResult(mFormattedNumber); + } + if (mNumberFormatter) { + unumf_close(mNumberFormatter); + } +} + +Result<Ok, ICUError> NumberFormat::initialize( + std::string_view aLocale, const NumberFormatOptions& aOptions) { + mFormatForUnit = aOptions.mUnit.isSome(); + NumberFormatterSkeleton skeleton(aOptions); + mNumberFormatter = skeleton.toFormatter(aLocale); + if (mNumberFormatter) { + UErrorCode status = U_ZERO_ERROR; + mFormattedNumber = unumf_openResult(&status); + if (U_FAILURE(status)) { + return Err(ToICUError(status)); + } + return Ok(); + } + return Err(ICUError::InternalError); +} + +Result<std::u16string_view, ICUError> NumberFormat::formatToParts( + double number, NumberPartVector& parts) const { + if (!formatInternal(number)) { + return Err(ICUError::InternalError); + } + + bool isNegative = !std::isnan(number) && IsNegative(number); + + return FormatResultToParts(mFormattedNumber, Some(number), isNegative, + mFormatForUnit, parts); +} + +Result<std::u16string_view, ICUError> NumberFormat::formatToParts( + int64_t number, NumberPartVector& parts) const { + if (!formatInternal(number)) { + return Err(ICUError::InternalError); + } + + return FormatResultToParts(mFormattedNumber, Nothing(), number < 0, + mFormatForUnit, parts); +} + +Result<std::u16string_view, ICUError> NumberFormat::formatToParts( + std::string_view number, NumberPartVector& parts) const { + if (!formatInternal(number)) { + return Err(ICUError::InternalError); + } + + // Non-finite numbers aren't currently supported here. If we ever need to + // support those, the |Maybe<double>| argument must be computed here. + MOZ_ASSERT(number != "Infinity"); + MOZ_ASSERT(number != "+Infinity"); + MOZ_ASSERT(number != "-Infinity"); + MOZ_ASSERT(number != "NaN"); + + bool isNegative = !number.empty() && number[0] == '-'; + + return FormatResultToParts(mFormattedNumber, Nothing(), isNegative, + mFormatForUnit, parts); +} + +Result<int32_t, ICUError> NumberFormat::selectFormatted( + double number, char16_t* keyword, int32_t keywordSize, + UPluralRules* pluralRules) const { + MOZ_ASSERT(keyword && pluralRules); + UErrorCode status = U_ZERO_ERROR; + + MOZ_TRY(format(number)); + + int32_t utf16KeywordLength = uplrules_selectFormatted( + pluralRules, mFormattedNumber, keyword, keywordSize, &status); + + if (U_FAILURE(status)) { + return Err(ToICUError(status)); + } + + return utf16KeywordLength; +} + +bool NumberFormat::formatInternal(double number) const { + // ICU incorrectly formats NaN values with the sign bit set, as if they + // were negative. Replace all NaNs with a single pattern with sign bit + // unset ("positive", that is) until ICU is fixed. + if (MOZ_UNLIKELY(std::isnan(number))) { + number = SpecificNaN<double>(0, 1); + } + + UErrorCode status = U_ZERO_ERROR; + unumf_formatDouble(mNumberFormatter, number, mFormattedNumber, &status); + return U_SUCCESS(status); +} + +bool NumberFormat::formatInternal(int64_t number) const { + UErrorCode status = U_ZERO_ERROR; + unumf_formatInt(mNumberFormatter, number, mFormattedNumber, &status); + return U_SUCCESS(status); +} + +bool NumberFormat::formatInternal(std::string_view number) const { + UErrorCode status = U_ZERO_ERROR; + unumf_formatDecimal(mNumberFormatter, number.data(), number.size(), + mFormattedNumber, &status); + return U_SUCCESS(status); +} + +Result<std::u16string_view, ICUError> NumberFormat::formatResult() const { + UErrorCode status = U_ZERO_ERROR; + + const UFormattedValue* formattedValue = + unumf_resultAsValue(mFormattedNumber, &status); + if (U_FAILURE(status)) { + return Err(ToICUError(status)); + } + + int32_t utf16Length; + const char16_t* utf16Str = + ufmtval_getString(formattedValue, &utf16Length, &status); + if (U_FAILURE(status)) { + return Err(ToICUError(status)); + } + + return std::u16string_view(utf16Str, static_cast<size_t>(utf16Length)); +} + +} // namespace mozilla::intl |