diff --git a/icu4c/source/i18n/Makefile.in b/icu4c/source/i18n/Makefile.in index a66b65a8744..7d4e21b93bb 100644 --- a/icu4c/source/i18n/Makefile.in +++ b/icu4c/source/i18n/Makefile.in @@ -102,7 +102,7 @@ number_affixutils.o number_compact.o number_decimalquantity.o \ number_decimfmtprops.o number_fluent.o number_formatimpl.o number_grouping.o \ number_integerwidth.o number_longnames.o number_modifiers.o number_notation.o \ number_padding.o number_patternmodifier.o number_patternstring.o \ -number_rounding.o number_scientific.o number_stringbuilder.o number_utils.o \ +number_rounding.o number_scientific.o number_stringbuilder.o number_utils.o number_asformat.o \ number_mapper.o number_multiplier.o number_currencysymbols.o number_skeletons.o number_capi.o \ double-conversion.o double-conversion-bignum-dtoa.o double-conversion-bignum.o \ double-conversion-cached-powers.o double-conversion-diy-fp.o \ diff --git a/icu4c/source/i18n/i18n.vcxproj b/icu4c/source/i18n/i18n.vcxproj index 9a1f34893fb..219967a2f8d 100644 --- a/icu4c/source/i18n/i18n.vcxproj +++ b/icu4c/source/i18n/i18n.vcxproj @@ -253,6 +253,7 @@ + @@ -515,11 +516,14 @@ + + + @@ -553,4 +557,4 @@ - \ No newline at end of file + diff --git a/icu4c/source/i18n/i18n.vcxproj.filters b/icu4c/source/i18n/i18n.vcxproj.filters index 0ffe5f8bd24..99e8c75656b 100644 --- a/icu4c/source/i18n/i18n.vcxproj.filters +++ b/icu4c/source/i18n/i18n.vcxproj.filters @@ -519,6 +519,9 @@ formatting + + formatting + formatting @@ -803,6 +806,9 @@ formatting + + formatting + formatting @@ -812,12 +818,18 @@ formatting + + formatting + formatting formatting + + formatting + formatting @@ -1414,4 +1426,4 @@ formatting - \ No newline at end of file + diff --git a/icu4c/source/i18n/i18n_uwp.vcxproj b/icu4c/source/i18n/i18n_uwp.vcxproj index d8d7fd80892..009e66daac6 100644 --- a/icu4c/source/i18n/i18n_uwp.vcxproj +++ b/icu4c/source/i18n/i18n_uwp.vcxproj @@ -360,6 +360,7 @@ + @@ -620,11 +621,14 @@ + + + @@ -656,4 +660,4 @@ - \ No newline at end of file + diff --git a/icu4c/source/i18n/number_asformat.cpp b/icu4c/source/i18n/number_asformat.cpp new file mode 100644 index 00000000000..96b0a1c37a0 --- /dev/null +++ b/icu4c/source/i18n/number_asformat.cpp @@ -0,0 +1,103 @@ +// © 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 +#include +#include "number_asformat.h" +#include "number_types.h" +#include "number_utils.h" +#include "fphdlimp.h" +#include "number_utypes.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + +LocalizedNumberFormatterAsFormat::LocalizedNumberFormatterAsFormat( + const LocalizedNumberFormatter& formatter, const Locale& locale) + : fFormatter(formatter), fLocale(locale) { + const char* localeName = locale.getName(); + setLocaleIDs(localeName, localeName); +} + +LocalizedNumberFormatterAsFormat::~LocalizedNumberFormatterAsFormat() = default; + +UBool LocalizedNumberFormatterAsFormat::operator==(const Format& other) const { + auto* _other = dynamic_cast(&other); + if (_other == nullptr) { + return false; + } + // TODO: Change this to use LocalizedNumberFormatter::operator== if it is ever proposed. + // This implementation is fine, but not particularly efficient. + UErrorCode localStatus = U_ZERO_ERROR; + return fFormatter.toSkeleton(localStatus) == _other->fFormatter.toSkeleton(localStatus); +} + +Format* LocalizedNumberFormatterAsFormat::clone() const { + return new LocalizedNumberFormatterAsFormat(*this); +} + +UnicodeString& LocalizedNumberFormatterAsFormat::format(const Formattable& obj, UnicodeString& appendTo, + FieldPosition& pos, UErrorCode& status) const { + if (U_FAILURE(status)) { return appendTo; } + UFormattedNumberData data; + obj.populateDecimalQuantity(data.quantity, status); + if (U_FAILURE(status)) { + return appendTo; + } + fFormatter.formatImpl(&data, status); + if (U_FAILURE(status)) { + return appendTo; + } + // always return first occurrence: + pos.setBeginIndex(0); + pos.setEndIndex(0); + bool found = data.string.nextFieldPosition(pos, status); + if (found && appendTo.length() != 0) { + pos.setBeginIndex(pos.getBeginIndex() + appendTo.length()); + pos.setEndIndex(pos.getEndIndex() + appendTo.length()); + } + appendTo.append(data.string.toTempUnicodeString()); + return appendTo; +} + +UnicodeString& LocalizedNumberFormatterAsFormat::format(const Formattable& obj, UnicodeString& appendTo, + FieldPositionIterator* posIter, + UErrorCode& status) const { + if (U_FAILURE(status)) { return appendTo; } + UFormattedNumberData data; + obj.populateDecimalQuantity(data.quantity, status); + if (U_FAILURE(status)) { + return appendTo; + } + fFormatter.formatImpl(&data, status); + if (U_FAILURE(status)) { + return appendTo; + } + appendTo.append(data.string.toTempUnicodeString()); + if (posIter != nullptr) { + FieldPositionIteratorHandler fpih(posIter, status); + data.string.getAllFieldPositions(fpih, status); + } + return appendTo; +} + +void LocalizedNumberFormatterAsFormat::parseObject(const UnicodeString&, Formattable&, + ParsePosition& parse_pos) const { + // Not supported. + parse_pos.setErrorIndex(0); +} + +const LocalizedNumberFormatter& LocalizedNumberFormatterAsFormat::getNumberFormatter() const { + return fFormatter; +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/i18n/number_asformat.h b/icu4c/source/i18n/number_asformat.h new file mode 100644 index 00000000000..0ceb0c1479e --- /dev/null +++ b/icu4c/source/i18n/number_asformat.h @@ -0,0 +1,104 @@ +// © 2017 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __NUMBER_ASFORMAT_H__ +#define __NUMBER_ASFORMAT_H__ + +#include "unicode/numberformatter.h" +#include "number_types.h" +#include "number_decimalquantity.h" +#include "number_scientific.h" +#include "number_patternstring.h" +#include "number_modifiers.h" +#include "number_multiplier.h" +#include "number_roundingutils.h" +#include "decNumber.h" +#include "charstr.h" + +U_NAMESPACE_BEGIN namespace number { +namespace impl { + +/** + * A wrapper around LocalizedNumberFormatter implementing the Format interface, enabling improved + * compatibility with other APIs. + * + * @draft ICU 62 + * @see NumberFormatter + */ +class U_I18N_API LocalizedNumberFormatterAsFormat : public Format { + public: + LocalizedNumberFormatterAsFormat(const LocalizedNumberFormatter& formatter, const Locale& locale); + + /** + * Destructor. + */ + ~LocalizedNumberFormatterAsFormat() U_OVERRIDE; + + /** + * Equals operator. + */ + UBool operator==(const Format& other) const U_OVERRIDE; + + /** + * Creates a copy of this object. + */ + Format* clone() const U_OVERRIDE; + + /** + * Formats a Number using the wrapped LocalizedNumberFormatter. The provided formattable must be a + * number type. + */ + UnicodeString& format(const Formattable& obj, UnicodeString& appendTo, FieldPosition& pos, + UErrorCode& status) const U_OVERRIDE; + + /** + * Formats a Number using the wrapped LocalizedNumberFormatter. The provided formattable must be a + * number type. + */ + UnicodeString& format(const Formattable& obj, UnicodeString& appendTo, FieldPositionIterator* posIter, + UErrorCode& status) const U_OVERRIDE; + + /** + * Not supported: sets an error index and returns. + */ + void parseObject(const UnicodeString& source, Formattable& result, + ParsePosition& parse_pos) const U_OVERRIDE; + + /** + * Gets the LocalizedNumberFormatter that this wrapper class uses to format numbers. + * + * For maximum efficiency, this function returns by const reference. You must copy the return value + * into a local variable if you want to use it beyond the lifetime of the current object: + * + *
+     * LocalizedNumberFormatter localFormatter = fmt->getNumberFormatter();
+     * 
+ * + * You can however use the return value directly when chaining: + * + *
+     * FormattedNumber result = fmt->getNumberFormatter().formatDouble(514.23, status);
+     * 
+ * + * @return The unwrapped LocalizedNumberFormatter. + */ + const LocalizedNumberFormatter& getNumberFormatter() const; + + private: + LocalizedNumberFormatter fFormatter; + + // Even though the locale is inside the LocalizedNumberFormatter, we have to keep it here, too, because + // LocalizedNumberFormatter doesn't have a getLocale() method, and ICU-TC didn't want to add one. + Locale fLocale; +}; + +} // namespace impl +} // namespace number +U_NAMESPACE_END + +#endif // __NUMBER_ASFORMAT_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/i18n/number_compact.cpp b/icu4c/source/i18n/number_compact.cpp index 7cadba688a1..40278e1a012 100644 --- a/icu4c/source/i18n/number_compact.cpp +++ b/icu4c/source/i18n/number_compact.cpp @@ -5,12 +5,13 @@ #if !UCONFIG_NO_FORMATTING -#include "resource.h" -#include "number_compact.h" #include "unicode/ustring.h" #include "unicode/ures.h" #include "cstring.h" #include "charstr.h" +#include "resource.h" +#include "number_compact.h" +#include "number_microprops.h" #include "uresimp.h" using namespace icu; @@ -283,7 +284,7 @@ void CompactHandler::processQuantity(DecimalQuantity &quantity, MicroProps &micr magnitude -= multiplier; } - StandardPlural::Form plural = quantity.getStandardPlural(rules); + StandardPlural::Form plural = utils::getStandardPlural(rules, quantity); const UChar *patternString = data.getPattern(magnitude, plural); if (patternString == nullptr) { // Use the default (non-compact) modifier. diff --git a/icu4c/source/i18n/number_decimalquantity.cpp b/icu4c/source/i18n/number_decimalquantity.cpp index 24b5263fdab..88304190dbb 100644 --- a/icu4c/source/i18n/number_decimalquantity.cpp +++ b/icu4c/source/i18n/number_decimalquantity.cpp @@ -12,6 +12,7 @@ #include "unicode/plurrule.h" #include "cmemory.h" +#include "number_decnum.h" #include "putilimp.h" #include "number_decimalquantity.h" #include "number_roundingutils.h" @@ -68,6 +69,7 @@ static double DOUBLE_MULTIPLIERS[] = { } // namespace +icu::IFixedDecimal::~IFixedDecimal() = default; DecimalQuantity::DecimalQuantity() { setBcdToZero(); @@ -236,16 +238,6 @@ bool DecimalQuantity::adjustMagnitude(int32_t delta) { return false; } -StandardPlural::Form DecimalQuantity::getStandardPlural(const PluralRules *rules) const { - if (rules == nullptr) { - // Fail gracefully if the user didn't provide a PluralRules - return StandardPlural::Form::OTHER; - } else { - UnicodeString ruleString = rules->select(*this); - return StandardPlural::orOtherFromString(ruleString); - } -} - double DecimalQuantity::getPluralOperand(PluralOperand operand) const { // If this assertion fails, you need to call roundToInfinity() or some other rounding method. // See the comment at the top of this file explaining the "isApproximate" field. diff --git a/icu4c/source/i18n/number_decimalquantity.h b/icu4c/source/i18n/number_decimalquantity.h index 7619ce2e4d1..8e04dea7eb5 100644 --- a/icu4c/source/i18n/number_decimalquantity.h +++ b/icu4c/source/i18n/number_decimalquantity.h @@ -194,15 +194,6 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory { */ void appendDigit(int8_t value, int32_t leadingZeros, bool appendAsInteger); - /** - * Computes the plural form for this number based on the specified set of rules. - * - * @param rules A {@link PluralRules} object representing the set of rules. - * @return The {@link StandardPlural} according to the PluralRules. If the plural form is not in - * the set of standard plurals, {@link StandardPlural#OTHER} is returned instead. - */ - StandardPlural::Form getStandardPlural(const PluralRules *rules) const; - double getPluralOperand(PluralOperand operand) const U_OVERRIDE; bool hasIntegerValue() const U_OVERRIDE; diff --git a/icu4c/source/i18n/number_decnum.h b/icu4c/source/i18n/number_decnum.h new file mode 100644 index 00000000000..a7793470b55 --- /dev/null +++ b/icu4c/source/i18n/number_decnum.h @@ -0,0 +1,77 @@ +// © 2017 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __NUMBER_DECNUM_H__ +#define __NUMBER_DECNUM_H__ + +#include "decNumber.h" +#include "charstr.h" + +U_NAMESPACE_BEGIN + +#define DECNUM_INITIAL_CAPACITY 34 + +// Export an explicit template instantiation of the MaybeStackHeaderAndArray that is used as a data member of DecNum. +// When building DLLs for Windows this is required even though no direct access to the MaybeStackHeaderAndArray leaks out of the i18n library. +// (See digitlst.h, pluralaffix.h, datefmt.h, and others for similar examples.) +#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN +template class U_I18N_API MaybeStackHeaderAndArray; +#endif + +namespace number { +namespace impl { + +/** A very thin C++ wrapper around decNumber.h */ +// Exported as U_I18N_API for tests +class U_I18N_API DecNum : public UMemory { + public: + DecNum(); // leaves object in valid but undefined state + + // Copy-like constructor; use the default move operators. + DecNum(const DecNum& other, UErrorCode& status); + + /** Sets the decNumber to the StringPiece. */ + void setTo(StringPiece str, UErrorCode& status); + + /** Sets the decNumber to the NUL-terminated char string. */ + void setTo(const char* str, UErrorCode& status); + + /** Uses double_conversion to set this decNumber to the given double. */ + void setTo(double d, UErrorCode& status); + + /** Sets the decNumber to the BCD representation. */ + void setTo(const uint8_t* bcd, int32_t length, int32_t scale, bool isNegative, UErrorCode& status); + + void normalize(); + + void multiplyBy(const DecNum& rhs, UErrorCode& status); + + void divideBy(const DecNum& rhs, UErrorCode& status); + + bool isNegative() const; + + bool isZero() const; + + inline const decNumber* getRawDecNumber() const { + return fData.getAlias(); + } + + private: + static constexpr int32_t kDefaultDigits = DECNUM_INITIAL_CAPACITY; + MaybeStackHeaderAndArray fData; + decContext fContext; + + void _setTo(const char* str, int32_t maxDigits, UErrorCode& status); +}; + +} // namespace impl +} // namespace number + +U_NAMESPACE_END + +#endif // __NUMBER_DECNUM_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/i18n/number_fluent.cpp b/icu4c/source/i18n/number_fluent.cpp index 67ada4460fd..687adb6b5ba 100644 --- a/icu4c/source/i18n/number_fluent.cpp +++ b/icu4c/source/i18n/number_fluent.cpp @@ -10,6 +10,7 @@ #include "number_decimalquantity.h" #include "number_formatimpl.h" #include "umutex.h" +#include "number_asformat.h" #include "number_skeletons.h" #include "number_utils.h" #include "number_utypes.h" diff --git a/icu4c/source/i18n/number_formatimpl.h b/icu4c/source/i18n/number_formatimpl.h index f425bbca48a..744fecec13f 100644 --- a/icu4c/source/i18n/number_formatimpl.h +++ b/icu4c/source/i18n/number_formatimpl.h @@ -14,6 +14,7 @@ #include "number_patternmodifier.h" #include "number_longnames.h" #include "number_compact.h" +#include "number_microprops.h" U_NAMESPACE_BEGIN namespace number { namespace impl { diff --git a/icu4c/source/i18n/number_longnames.cpp b/icu4c/source/i18n/number_longnames.cpp index cc0dd78d6d2..26f9af4c9bd 100644 --- a/icu4c/source/i18n/number_longnames.cpp +++ b/icu4c/source/i18n/number_longnames.cpp @@ -11,6 +11,7 @@ #include "charstr.h" #include "uresimp.h" #include "number_longnames.h" +#include "number_microprops.h" #include #include "cstring.h" @@ -261,7 +262,7 @@ void LongNameHandler::processQuantity(DecimalQuantity &quantity, MicroProps &mic // TODO: Avoid the copy here? DecimalQuantity copy(quantity); micros.rounder.apply(copy, status); - micros.modOuter = &fModifiers[copy.getStandardPlural(rules)]; + micros.modOuter = &fModifiers[utils::getStandardPlural(rules, copy)]; } #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/i18n/number_microprops.h b/icu4c/source/i18n/number_microprops.h new file mode 100644 index 00000000000..daa887bb0dd --- /dev/null +++ b/icu4c/source/i18n/number_microprops.h @@ -0,0 +1,82 @@ +// © 2017 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __NUMBER_MICROPROPS_H__ +#define __NUMBER_MICROPROPS_H__ + +// TODO: minimize includes +#include "unicode/numberformatter.h" +#include "number_types.h" +#include "number_decimalquantity.h" +#include "number_scientific.h" +#include "number_patternstring.h" +#include "number_modifiers.h" +#include "number_multiplier.h" +#include "number_roundingutils.h" +#include "decNumber.h" +#include "charstr.h" + +U_NAMESPACE_BEGIN namespace number { +namespace impl { + +struct MicroProps : public MicroPropsGenerator { + + // NOTE: All of these fields are properly initialized in NumberFormatterImpl. + RoundingImpl rounder; + Grouper grouping; + Padder padding; + IntegerWidth integerWidth; + UNumberSignDisplay sign; + UNumberDecimalSeparatorDisplay decimal; + bool useCurrency; + + // Note: This struct has no direct ownership of the following pointers. + const DecimalFormatSymbols* symbols; + const Modifier* modOuter; + const Modifier* modMiddle; + const Modifier* modInner; + + // The following "helper" fields may optionally be used during the MicroPropsGenerator. + // They live here to retain memory. + struct { + ScientificModifier scientificModifier; + EmptyModifier emptyWeakModifier{false}; + EmptyModifier emptyStrongModifier{true}; + MultiplierFormatHandler multiplier; + } helpers; + + + MicroProps() = default; + + MicroProps(const MicroProps& other) = default; + + MicroProps& operator=(const MicroProps& other) = default; + + void processQuantity(DecimalQuantity&, MicroProps& micros, UErrorCode& status) const U_OVERRIDE { + (void) status; + if (this == µs) { + // Unsafe path: no need to perform a copy. + U_ASSERT(!exhausted); + micros.exhausted = true; + U_ASSERT(exhausted); + } else { + // Safe path: copy self into the output micros. + micros = *this; + } + } + + private: + // Internal fields: + bool exhausted = false; +}; + +} // namespace impl +} // namespace number +U_NAMESPACE_END + +#endif // __NUMBER_MICROPROPS_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/i18n/number_multiplier.cpp b/icu4c/source/i18n/number_multiplier.cpp index 1faeac41d7b..a27142c9bd6 100644 --- a/icu4c/source/i18n/number_multiplier.cpp +++ b/icu4c/source/i18n/number_multiplier.cpp @@ -9,6 +9,7 @@ // Helpful in toString methods and elsewhere. #define UNISTR_FROM_STRING_EXPLICIT +#include "number_decnum.h" #include "number_types.h" #include "number_multiplier.h" #include "numparse_validators.h" @@ -152,21 +153,4 @@ void MultiplierFormatHandler::processQuantity(DecimalQuantity& quantity, MicroPr multiplier.applyTo(quantity); } - -// NOTE: MultiplierParseHandler is declared in the header numparse_validators.h -MultiplierParseHandler::MultiplierParseHandler(::icu::number::Scale multiplier) - : fMultiplier(std::move(multiplier)) {} - -void MultiplierParseHandler::postProcess(ParsedNumber& result) const { - if (!result.quantity.bogus) { - fMultiplier.applyReciprocalTo(result.quantity); - // NOTE: It is okay if the multiplier was negative. - } -} - -UnicodeString MultiplierParseHandler::toString() const { - return u""; -} - - #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/i18n/number_patternmodifier.cpp b/icu4c/source/i18n/number_patternmodifier.cpp index b45647d143d..6417e14378b 100644 --- a/icu4c/source/i18n/number_patternmodifier.cpp +++ b/icu4c/source/i18n/number_patternmodifier.cpp @@ -10,6 +10,7 @@ #include "unicode/dcfmtsym.h" #include "unicode/ucurr.h" #include "unicode/unistr.h" +#include "number_microprops.h" using namespace icu; using namespace icu::number; @@ -136,7 +137,7 @@ void ImmutablePatternModifier::applyToMicros(MicroProps& micros, DecimalQuantity // TODO: Fix this. Avoid the copy. DecimalQuantity copy(quantity); copy.roundToInfinity(); - StandardPlural::Form plural = copy.getStandardPlural(rules); + StandardPlural::Form plural = utils::getStandardPlural(rules, copy); micros.modMiddle = pm->getModifier(quantity.signum(), plural); } } @@ -166,7 +167,7 @@ void MutablePatternModifier::processQuantity(DecimalQuantity& fq, MicroProps& mi // TODO: Fix this. Avoid the copy. DecimalQuantity copy(fq); micros.rounder.apply(copy, status); - nonConstThis->setNumberProperties(fq.signum(), copy.getStandardPlural(rules)); + nonConstThis->setNumberProperties(fq.signum(), utils::getStandardPlural(rules, copy)); } else { nonConstThis->setNumberProperties(fq.signum(), StandardPlural::Form::COUNT); } diff --git a/icu4c/source/i18n/number_scientific.cpp b/icu4c/source/i18n/number_scientific.cpp index 72288f208a3..e9adfb402bd 100644 --- a/icu4c/source/i18n/number_scientific.cpp +++ b/icu4c/source/i18n/number_scientific.cpp @@ -10,6 +10,7 @@ #include "number_utils.h" #include "number_stringbuilder.h" #include "unicode/unum.h" +#include "number_microprops.h" using namespace icu; using namespace icu::number; diff --git a/icu4c/source/i18n/number_skeletons.cpp b/icu4c/source/i18n/number_skeletons.cpp index bae38ac445e..db2aabcffb6 100644 --- a/icu4c/source/i18n/number_skeletons.cpp +++ b/icu4c/source/i18n/number_skeletons.cpp @@ -9,6 +9,7 @@ // Helpful in toString methods and elsewhere. #define UNISTR_FROM_STRING_EXPLICIT +#include "number_decnum.h" #include "number_skeletons.h" #include "umutex.h" #include "ucln_in.h" diff --git a/icu4c/source/i18n/number_utils.cpp b/icu4c/source/i18n/number_utils.cpp index ab6d5fab72c..c79d2de9fa3 100644 --- a/icu4c/source/i18n/number_utils.cpp +++ b/icu4c/source/i18n/number_utils.cpp @@ -11,6 +11,7 @@ #include #include +#include "number_decnum.h" #include "number_types.h" #include "number_utils.h" #include "charstr.h" @@ -20,7 +21,6 @@ #include "fphdlimp.h" #include "uresimp.h" #include "ureslocs.h" -#include "number_utypes.h" using namespace icu; using namespace icu::number; @@ -49,86 +49,6 @@ doGetPattern(UResourceBundle* res, const char* nsName, const char* patternKey, U } -LocalizedNumberFormatterAsFormat::LocalizedNumberFormatterAsFormat( - const LocalizedNumberFormatter& formatter, const Locale& locale) - : fFormatter(formatter), fLocale(locale) { - const char* localeName = locale.getName(); - setLocaleIDs(localeName, localeName); -} - -LocalizedNumberFormatterAsFormat::~LocalizedNumberFormatterAsFormat() = default; - -UBool LocalizedNumberFormatterAsFormat::operator==(const Format& other) const { - auto* _other = dynamic_cast(&other); - if (_other == nullptr) { - return false; - } - // TODO: Change this to use LocalizedNumberFormatter::operator== if it is ever proposed. - // This implementation is fine, but not particularly efficient. - UErrorCode localStatus = U_ZERO_ERROR; - return fFormatter.toSkeleton(localStatus) == _other->fFormatter.toSkeleton(localStatus); -} - -Format* LocalizedNumberFormatterAsFormat::clone() const { - return new LocalizedNumberFormatterAsFormat(*this); -} - -UnicodeString& LocalizedNumberFormatterAsFormat::format(const Formattable& obj, UnicodeString& appendTo, - FieldPosition& pos, UErrorCode& status) const { - if (U_FAILURE(status)) { return appendTo; } - UFormattedNumberData data; - obj.populateDecimalQuantity(data.quantity, status); - if (U_FAILURE(status)) { - return appendTo; - } - fFormatter.formatImpl(&data, status); - if (U_FAILURE(status)) { - return appendTo; - } - // always return first occurrence: - pos.setBeginIndex(0); - pos.setEndIndex(0); - bool found = data.string.nextFieldPosition(pos, status); - if (found && appendTo.length() != 0) { - pos.setBeginIndex(pos.getBeginIndex() + appendTo.length()); - pos.setEndIndex(pos.getEndIndex() + appendTo.length()); - } - appendTo.append(data.string.toTempUnicodeString()); - return appendTo; -} - -UnicodeString& LocalizedNumberFormatterAsFormat::format(const Formattable& obj, UnicodeString& appendTo, - FieldPositionIterator* posIter, - UErrorCode& status) const { - if (U_FAILURE(status)) { return appendTo; } - UFormattedNumberData data; - obj.populateDecimalQuantity(data.quantity, status); - if (U_FAILURE(status)) { - return appendTo; - } - fFormatter.formatImpl(&data, status); - if (U_FAILURE(status)) { - return appendTo; - } - appendTo.append(data.string.toTempUnicodeString()); - if (posIter != nullptr) { - FieldPositionIteratorHandler fpih(posIter, status); - data.string.getAllFieldPositions(fpih, status); - } - return appendTo; -} - -void LocalizedNumberFormatterAsFormat::parseObject(const UnicodeString&, Formattable&, - ParsePosition& parse_pos) const { - // Not supported. - parse_pos.setErrorIndex(0); -} - -const LocalizedNumberFormatter& LocalizedNumberFormatterAsFormat::getNumberFormatter() const { - return fFormatter; -} - - const char16_t* utils::getPatternForStyle(const Locale& locale, const char* nsName, CldrPatternStyle style, UErrorCode& status) { const char* patternKey; @@ -330,6 +250,4 @@ bool DecNum::isZero() const { return decNumberIsZero(fData.getAlias()); } - - #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/i18n/number_utils.h b/icu4c/source/i18n/number_utils.h index e11da139422..c367166009c 100644 --- a/icu4c/source/i18n/number_utils.h +++ b/icu4c/source/i18n/number_utils.h @@ -20,145 +20,9 @@ U_NAMESPACE_BEGIN -#define DECNUM_INITIAL_CAPACITY 34 - -// Export an explicit template instantiation of the MaybeStackHeaderAndArray that is used as a data member of DecNum. -// When building DLLs for Windows this is required even though no direct access to the MaybeStackHeaderAndArray leaks out of the i18n library. -// (See digitlst.h, pluralaffix.h, datefmt.h, and others for similar examples.) -#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN -template class U_I18N_API MaybeStackHeaderAndArray; -#endif - namespace number { namespace impl { -struct MicroProps : public MicroPropsGenerator { - - // NOTE: All of these fields are properly initialized in NumberFormatterImpl. - RoundingImpl rounder; - Grouper grouping; - Padder padding; - IntegerWidth integerWidth; - UNumberSignDisplay sign; - UNumberDecimalSeparatorDisplay decimal; - bool useCurrency; - - // Note: This struct has no direct ownership of the following pointers. - const DecimalFormatSymbols* symbols; - const Modifier* modOuter; - const Modifier* modMiddle; - const Modifier* modInner; - - // The following "helper" fields may optionally be used during the MicroPropsGenerator. - // They live here to retain memory. - struct { - ScientificModifier scientificModifier; - EmptyModifier emptyWeakModifier{false}; - EmptyModifier emptyStrongModifier{true}; - MultiplierFormatHandler multiplier; - } helpers; - - - MicroProps() = default; - - MicroProps(const MicroProps& other) = default; - - MicroProps& operator=(const MicroProps& other) = default; - - void processQuantity(DecimalQuantity&, MicroProps& micros, UErrorCode& status) const U_OVERRIDE { - (void) status; - if (this == µs) { - // Unsafe path: no need to perform a copy. - U_ASSERT(!exhausted); - micros.exhausted = true; - U_ASSERT(exhausted); - } else { - // Safe path: copy self into the output micros. - micros = *this; - } - } - - private: - // Internal fields: - bool exhausted = false; -}; - - -/** - * A wrapper around LocalizedNumberFormatter implementing the Format interface, enabling improved - * compatibility with other APIs. - * - * @draft ICU 62 - * @see NumberFormatter - */ -class U_I18N_API LocalizedNumberFormatterAsFormat : public Format { - public: - LocalizedNumberFormatterAsFormat(const LocalizedNumberFormatter& formatter, const Locale& locale); - - /** - * Destructor. - */ - ~LocalizedNumberFormatterAsFormat() U_OVERRIDE; - - /** - * Equals operator. - */ - UBool operator==(const Format& other) const U_OVERRIDE; - - /** - * Creates a copy of this object. - */ - Format* clone() const U_OVERRIDE; - - /** - * Formats a Number using the wrapped LocalizedNumberFormatter. The provided formattable must be a - * number type. - */ - UnicodeString& format(const Formattable& obj, UnicodeString& appendTo, FieldPosition& pos, - UErrorCode& status) const U_OVERRIDE; - - /** - * Formats a Number using the wrapped LocalizedNumberFormatter. The provided formattable must be a - * number type. - */ - UnicodeString& format(const Formattable& obj, UnicodeString& appendTo, FieldPositionIterator* posIter, - UErrorCode& status) const U_OVERRIDE; - - /** - * Not supported: sets an error index and returns. - */ - void parseObject(const UnicodeString& source, Formattable& result, - ParsePosition& parse_pos) const U_OVERRIDE; - - /** - * Gets the LocalizedNumberFormatter that this wrapper class uses to format numbers. - * - * For maximum efficiency, this function returns by const reference. You must copy the return value - * into a local variable if you want to use it beyond the lifetime of the current object: - * - *
-     * LocalizedNumberFormatter localFormatter = fmt->getNumberFormatter();
-     * 
- * - * You can however use the return value directly when chaining: - * - *
-     * FormattedNumber result = fmt->getNumberFormatter().formatDouble(514.23, status);
-     * 
- * - * @return The unwrapped LocalizedNumberFormatter. - */ - const LocalizedNumberFormatter& getNumberFormatter() const; - - private: - LocalizedNumberFormatter fFormatter; - - // Even though the locale is inside the LocalizedNumberFormatter, we have to keep it here, too, because - // LocalizedNumberFormatter doesn't have a getLocale() method, and ICU-TC didn't want to add one. - Locale fLocale; -}; - - enum CldrPatternStyle { CLDR_PATTERN_STYLE_DECIMAL, CLDR_PATTERN_STYLE_CURRENCY, @@ -200,55 +64,29 @@ inline bool unitIsPermille(const MeasureUnit& unit) { const char16_t* getPatternForStyle(const Locale& locale, const char* nsName, CldrPatternStyle style, UErrorCode& status); -} // namespace utils - - -/** A very thin C++ wrapper around decNumber.h */ -// Exported as U_I18N_API for tests -class U_I18N_API DecNum : public UMemory { - public: - DecNum(); // leaves object in valid but undefined state - - // Copy-like constructor; use the default move operators. - DecNum(const DecNum& other, UErrorCode& status); - - /** Sets the decNumber to the StringPiece. */ - void setTo(StringPiece str, UErrorCode& status); - - /** Sets the decNumber to the NUL-terminated char string. */ - void setTo(const char* str, UErrorCode& status); - - /** Uses double_conversion to set this decNumber to the given double. */ - void setTo(double d, UErrorCode& status); - - /** Sets the decNumber to the BCD representation. */ - void setTo(const uint8_t* bcd, int32_t length, int32_t scale, bool isNegative, UErrorCode& status); - - void normalize(); - - void multiplyBy(const DecNum& rhs, UErrorCode& status); - - void divideBy(const DecNum& rhs, UErrorCode& status); - - bool isNegative() const; - - bool isZero() const; - - inline const decNumber* getRawDecNumber() const { - return fData.getAlias(); +/** + * Computes the plural form for this number based on the specified set of rules. + * + * @param rules A {@link PluralRules} object representing the set of rules. + * @return The {@link StandardPlural} according to the PluralRules. If the plural form is not in + * the set of standard plurals, {@link StandardPlural#OTHER} is returned instead. + */ +inline StandardPlural::Form getStandardPlural(const PluralRules *rules, + const IFixedDecimal &fdec) { + if (rules == nullptr) { + // Fail gracefully if the user didn't provide a PluralRules + return StandardPlural::Form::OTHER; + } else { + UnicodeString ruleString = rules->select(fdec); + return StandardPlural::orOtherFromString(ruleString); } +} - private: - static constexpr int32_t kDefaultDigits = DECNUM_INITIAL_CAPACITY; - MaybeStackHeaderAndArray fData; - decContext fContext; - - void _setTo(const char* str, int32_t maxDigits, UErrorCode& status); -}; - +} // namespace utils } // namespace impl } // namespace number + U_NAMESPACE_END #endif //__NUMBER_UTILS_H__ diff --git a/icu4c/source/i18n/numparse_validators.cpp b/icu4c/source/i18n/numparse_validators.cpp index 62622ae976b..12d3465c4ef 100644 --- a/icu4c/source/i18n/numparse_validators.cpp +++ b/icu4c/source/i18n/numparse_validators.cpp @@ -68,5 +68,18 @@ UnicodeString RequireNumberValidator::toString() const { return u""; } +MultiplierParseHandler::MultiplierParseHandler(::icu::number::Scale multiplier) + : fMultiplier(std::move(multiplier)) {} + +void MultiplierParseHandler::postProcess(ParsedNumber& result) const { + if (!result.quantity.bogus) { + fMultiplier.applyReciprocalTo(result.quantity); + // NOTE: It is okay if the multiplier was negative. + } +} + +UnicodeString MultiplierParseHandler::toString() const { + return u""; +} #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/i18n/numparse_validators.h b/icu4c/source/i18n/numparse_validators.h index f6768938c1e..5d43b779d0b 100644 --- a/icu4c/source/i18n/numparse_validators.h +++ b/icu4c/source/i18n/numparse_validators.h @@ -71,8 +71,6 @@ class RequireNumberValidator : public ValidationMatcher, public UMemory { /** * Wraps a {@link Multiplier} for use in the number parsing pipeline. - * - * NOTE: Implemented in number_multiplier.cpp */ class MultiplierParseHandler : public ValidationMatcher, public UMemory { public: diff --git a/icu4c/source/i18n/plurrule.cpp b/icu4c/source/i18n/plurrule.cpp index 04e4d825f6a..aedb924c618 100644 --- a/icu4c/source/i18n/plurrule.cpp +++ b/icu4c/source/i18n/plurrule.cpp @@ -248,26 +248,6 @@ PluralRules::select(double number) const { return select(FixedDecimal(number)); } -UnicodeString -PluralRules::select(const Formattable& obj, const NumberFormat& fmt, UErrorCode& status) const { - if (U_SUCCESS(status)) { - const DecimalFormat *decFmt = dynamic_cast(&fmt); - if (decFmt != NULL) { - number::impl::DecimalQuantity dq; - decFmt->formatToDecimalQuantity(obj, dq, status); - if (U_SUCCESS(status)) { - return select(dq); - } - } else { - double number = obj.getDouble(status); - if (U_SUCCESS(status)) { - return select(number); - } - } - } - return UnicodeString(); -} - UnicodeString PluralRules::select(const IFixedDecimal &number) const { if (mRules == NULL) { @@ -1418,8 +1398,6 @@ PluralOperand tokenTypeToPluralOperand(tokenType tt) { } } -IFixedDecimal::~IFixedDecimal() = default; - FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f) { init(n, v, f); // check values. TODO make into unit test. diff --git a/icu4c/source/i18n/unicode/plurrule.h b/icu4c/source/i18n/unicode/plurrule.h index 7a1ad1d1a12..03dea3f1b92 100644 --- a/icu4c/source/i18n/unicode/plurrule.h +++ b/icu4c/source/i18n/unicode/plurrule.h @@ -347,22 +347,6 @@ public: UnicodeString select(double number) const; #ifndef U_HIDE_INTERNAL_API - /** - * Given a number and a format, returns the keyword of the first applicable - * rule for this PluralRules object. - * Note: This internal preview interface may be removed in the future if - * an architecturally cleaner solution reaches stable status. - * @param obj The numeric object for which the rule should be determined. - * @param fmt The NumberFormat specifying how the number will be formatted - * (this can affect the plural form, e.g. "1 dollar" vs "1.0 dollars"). - * @param status Input/output parameter. If at entry this indicates a - * failure status, the method returns immediately; otherwise - * this is set to indicate the outcome of the call. - * @return The keyword of the selected rule. Undefined in the case of an error. - * @internal ICU 59 technology preview, may be removed in the future - */ - UnicodeString select(const Formattable& obj, const NumberFormat& fmt, UErrorCode& status) const; - /** * @internal */ diff --git a/icu4c/source/i18n/upluralrules.cpp b/icu4c/source/i18n/upluralrules.cpp index 24e74e3ee22..bba6dfe3101 100644 --- a/icu4c/source/i18n/upluralrules.cpp +++ b/icu4c/source/i18n/upluralrules.cpp @@ -17,9 +17,44 @@ #include "unicode/unistr.h" #include "unicode/unum.h" #include "unicode/numfmt.h" +#include "number_decimalquantity.h" U_NAMESPACE_USE +namespace { + +/** + * Given a number and a format, returns the keyword of the first applicable + * rule for the PluralRules object. + * @param rules The plural rules. + * @param obj The numeric object for which the rule should be determined. + * @param fmt The NumberFormat specifying how the number will be formatted + * (this can affect the plural form, e.g. "1 dollar" vs "1.0 dollars"). + * @param status Input/output parameter. If at entry this indicates a + * failure status, the method returns immediately; otherwise + * this is set to indicate the outcome of the call. + * @return The keyword of the selected rule. Undefined in the case of an error. + */ +UnicodeString select(const PluralRules &rules, const Formattable& obj, const NumberFormat& fmt, UErrorCode& status) { + if (U_SUCCESS(status)) { + const DecimalFormat *decFmt = dynamic_cast(&fmt); + if (decFmt != NULL) { + number::impl::DecimalQuantity dq; + decFmt->formatToDecimalQuantity(obj, dq, status); + if (U_SUCCESS(status)) { + return rules.select(dq); + } + } else { + double number = obj.getDouble(status); + if (U_SUCCESS(status)) { + return rules.select(number); + } + } + } + return UnicodeString(); +} + +} // namespace U_CAPI UPluralRules* U_EXPORT2 uplrules_open(const char *locale, UErrorCode *status) @@ -73,7 +108,7 @@ uplrules_selectWithFormat(const UPluralRules *uplrules, return 0; } Formattable obj(number); - UnicodeString result = plrules->select(obj, *nf, *status); + UnicodeString result = select(*plrules, obj, *nf, *status); return result.extract(keyword, capacity, *status); } diff --git a/icu4c/source/test/depstest/dependencies.txt b/icu4c/source/test/depstest/dependencies.txt index f4605547dc7..55783ee04ed 100644 --- a/icu4c/source/test/depstest/dependencies.txt +++ b/icu4c/source/test/depstest/dependencies.txt @@ -18,7 +18,7 @@ system_symbols: # C PIC system_misc system_debug malloc_functions ubsan c_strings c_string_formatting - floating_point trigonometry + int_functions floating_point trigonometry stdlib_qsort pthread system_locale stdio_input stdio_output file_io readlink_function dir_io mmap_functions dlfcn @@ -58,8 +58,11 @@ group: c_string_formatting # Additional symbols in an optimized build. __sprintf_chk +group: int_functions + div + group: floating_point - abs fabs floor ceil modf fmod log pow round sqrt + abs fabs floor ceil modf fmod log pow round sqrt trunc group: trigonometry acos asin atan atan2 cos sin tan @@ -154,6 +157,7 @@ library: common unistr_props unistr_case unistr_case_locale unistr_titlecase_brkiter unistr_cnv cstr uniset_core uniset_props uniset_closure usetiter uset uset_props + static_unicode_sets uiter edits ucasemap ucasemap_titlecase_brkiter script_runs uprops ubidi_props ucase uscript uscript_props @@ -281,6 +285,11 @@ group: punycode deps platform +group: static_unicode_sets + static_unicode_sets.o + deps + resourcebundle uniset_props + group: uset_props uset_props.o deps @@ -475,6 +484,8 @@ group: ustr_titlecase_brkiter group: edits edits.o deps + # Edits::Iterator::toString() calls ICU_Utility::appendNumber() + icu_utility platform group: ucasemap_titlecase_brkiter @@ -574,6 +585,7 @@ group: currency deps loclikely resourcebundle ulist ustring_case_locale stdlib_qsort # for ucurr.o (which does not use ICU's uarrsort.o) + static_unicode_sets usetiter group: icudataver # u_getDataVersion() icudataver.o @@ -804,7 +816,7 @@ library: i18n alphabetic_index collation collation_builder string_search dayperiodrules formatting formattable_cnv regex regex_cnv translit - double_conversion numberformatter + double_conversion number_representation numberformatter numberparser universal_time_scale uclean_i18n @@ -881,39 +893,57 @@ group: dayperiodrules group: double_conversion double-conversion.o double-conversion-bignum.o double-conversion-bignum-dtoa.o double-conversion-cached-powers.o double-conversion-diy-fp.o - double-conversion-fast-dtoa.o + double-conversion-fast-dtoa.o double-conversion-strtod.o deps platform +group: number_representation + number_decimalquantity.o number_stringbuilder.o numparse_stringsegment.o number_utils.o + deps + decnumber double_conversion + # for data loading; that could be split off + resourcebundle + int_functions + ucase uniset_core + group: numberformatter # ICU 60+ NumberFormatter API - number_affixutils.o number_compact.o number_decimalquantity.o - number_decimfmtprops.o number_fluent.o number_formatimpl.o + number_affixutils.o number_asformat.o + number_capi.o number_compact.o number_currencysymbols.o + number_decimfmtprops.o + number_fluent.o number_formatimpl.o number_grouping.o number_integerwidth.o number_longnames.o - number_modifiers.o number_notation.o number_padding.o + number_mapper.o number_modifiers.o number_multiplier.o + number_notation.o number_padding.o number_patternmodifier.o number_patternstring.o number_rounding.o - number_scientific.o number_stringbuilder.o + number_scientific.o number_skeletons.o + currpinf.o dcfmtsym.o numsys.o + # pluralrules + standardplural.o plurrule.o deps - digitlist formattable format units - # TODO: fix: dependency on formatting needed for circular dependency pluralrules <-> decimfmt.o - # We should be able to have a small pluralrules group and depend on that here. - formatting + decnumber double_conversion formattable format units + number_representation uclean_i18n common +group: numberparser + numparse_affixes.o numparse_compositions.o numparse_currency.o + numparse_decimal.o numparse_impl.o numparse_parsednumber.o + numparse_scientific.o numparse_symbols.o + numparse_validators.o + deps + numberformatter + group: formatting # TODO: Try to subdivide this ball of wax. # currencyformat - curramt.o currfmt.o currpinf.o + curramt.o currfmt.o + # pluralrules C API + upluralrules.o # decimalformat - dcfmtsym.o numsys.o unumsys.o - affixpatternparser.o decimfmtimpl.o digitaffix.o digitaffixesandpadding.o - digitformatter.o digitgrouping.o digitinterval.o - pluralaffix.o precision.o smallintformatter.o valueformatter.o - decfmtst.o decimfmt.o decimalformatpattern.o compactdecimalformat.o + unumsys.o + decimfmt.o compactdecimalformat.o numfmt.o unum.o winnmfmt.o - # pluralrules - standardplural.o plurrule.o upluralrules.o # scientificnumberformatter - would depend on dcfmtsym, so would be circular. scientificnumberformatter.o # rbnf @@ -934,7 +964,7 @@ group: formatting # messageformat choicfmt.o msgfmt.o plurfmt.o selfmt.o umsg.o deps - digitlist formattable format units + decnumber formattable format units numberformatter numberparser dayperiodrules collation collation_builder # for rbnf common @@ -953,8 +983,8 @@ group: units deps stringenumeration -group: digitlist - digitlst.o decContext.o decNumber.o visibledigits.o +group: decnumber + decContext.o decNumber.o deps double_conversion uclean_i18n @@ -962,7 +992,7 @@ group: formattable fmtable.o measure.o deps - digitlist + decnumber number_representation group: formattable_cnv fmtable_cnv.o diff --git a/icu4c/source/test/intltest/numbertest_api.cpp b/icu4c/source/test/intltest/numbertest_api.cpp index e6fe8242bd3..2885f25203b 100644 --- a/icu4c/source/test/intltest/numbertest_api.cpp +++ b/icu4c/source/test/intltest/numbertest_api.cpp @@ -10,6 +10,7 @@ #include #include "unicode/unum.h" #include "unicode/numberformatter.h" +#include "number_asformat.h" #include "number_types.h" #include "number_utils.h" #include "numbertest.h" diff --git a/icu4c/source/test/intltest/numbertest_decimalquantity.cpp b/icu4c/source/test/intltest/numbertest_decimalquantity.cpp index cd62ac7ee9b..48b9f91e27a 100644 --- a/icu4c/source/test/intltest/numbertest_decimalquantity.cpp +++ b/icu4c/source/test/intltest/numbertest_decimalquantity.cpp @@ -6,6 +6,7 @@ #if !UCONFIG_NO_FORMATTING #include "number_decimalquantity.h" +#include "number_decnum.h" #include "math.h" #include #include "number_utils.h" diff --git a/icu4c/source/test/intltest/numbertest_patternmodifier.cpp b/icu4c/source/test/intltest/numbertest_patternmodifier.cpp index f9e08ae96c8..13a25545d3c 100644 --- a/icu4c/source/test/intltest/numbertest_patternmodifier.cpp +++ b/icu4c/source/test/intltest/numbertest_patternmodifier.cpp @@ -6,6 +6,7 @@ #if !UCONFIG_NO_FORMATTING #include "numbertest.h" +#include "number_microprops.h" #include "number_patternmodifier.h" void PatternModifierTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char *) {