From: Shane Carr Date: Thu, 15 Mar 2018 10:08:26 +0000 (+0000) Subject: ICU-13634 The property mapper appears to be basically functional; data passes from... X-Git-Tag: release-62-rc~200^2~93 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=00a23a07f783bcf5184a3589a4a8628582d14ced;p=icu ICU-13634 The property mapper appears to be basically functional; data passes from the old API through the mapper into the new API and then back out through the old API again. X-SVN-Rev: 41108 --- diff --git a/icu4c/source/i18n/Makefile.in b/icu4c/source/i18n/Makefile.in index a1d186b4350..326ce2170f5 100644 --- a/icu4c/source/i18n/Makefile.in +++ b/icu4c/source/i18n/Makefile.in @@ -108,7 +108,7 @@ double-conversion-cached-powers.o double-conversion-diy-fp.o double-conversion-f numparse_stringsegment.o numparse_unisets.o numparse_parsednumber.o \ numparse_impl.o numparse_symbols.o numparse_decimal.o numparse_scientific.o \ numparse_currency.o numparse_affixes.o numparse_compositions.o \ -number_mapper.o +number_mapper.o number_multiplier.o ## Header files to install diff --git a/icu4c/source/i18n/decimfmt.cpp b/icu4c/source/i18n/decimfmt.cpp index 7772f330d67..a6012690490 100644 --- a/icu4c/source/i18n/decimfmt.cpp +++ b/icu4c/source/i18n/decimfmt.cpp @@ -64,6 +64,7 @@ DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* DecimalFormat::DecimalFormat(const DecimalFormatSymbols* symbolsToAdopt, UErrorCode& status) { fProperties.adoptInsteadAndCheckErrorCode(new DecimalFormatProperties(), status); fExportedProperties.adoptInsteadAndCheckErrorCode(new DecimalFormatProperties(), status); + fWarehouse.adoptInsteadAndCheckErrorCode(new DecimalFormatWarehouse(), status); if (symbolsToAdopt == nullptr) { fSymbols.adoptInsteadAndCheckErrorCode(new DecimalFormatSymbols(status), status); } else { @@ -313,8 +314,10 @@ DecimalFormat::DecimalFormat(const UnicodeString& pattern, const DecimalFormatSy DecimalFormat::DecimalFormat(const DecimalFormat& source) { fProperties.adoptInstead(new DecimalFormatProperties()); fExportedProperties.adoptInstead(new DecimalFormatProperties()); + fWarehouse.adoptInstead(new DecimalFormatWarehouse()); fSymbols.adoptInstead(new DecimalFormatSymbols(*source.fSymbols)); - if (fProperties == nullptr || fExportedProperties == nullptr || fSymbols == nullptr) { + if (fProperties == nullptr || fExportedProperties == nullptr || fWarehouse == nullptr || + fSymbols == nullptr) { return; } refreshFormatterNoError(); @@ -862,7 +865,7 @@ void DecimalFormat::formatToDecimalQuantity(const Formattable& number, DecimalQu status = U_UNSUPPORTED_ERROR; } -number::LocalizedNumberFormatter DecimalFormat::toNumberFormatter() const { +const number::LocalizedNumberFormatter& DecimalFormat::toNumberFormatter() const { return *fFormatter; } @@ -897,14 +900,16 @@ void DecimalFormat::refreshFormatter(UErrorCode& status) { fFormatter.adoptInsteadAndCheckErrorCode( new LocalizedNumberFormatter( NumberPropertyMapper::create( - *fProperties, *fSymbols, *fExportedProperties, status).locale( + *fProperties, *fSymbols, *fWarehouse, *fExportedProperties, status).locale( locale)), status); - fParser.adoptInsteadAndCheckErrorCode( - NumberParserImpl::createParserFromProperties( - *fProperties, *fSymbols, false, false, status), status); - fParserWithCurrency.adoptInsteadAndCheckErrorCode( - NumberParserImpl::createParserFromProperties( - *fProperties, *fSymbols, true, false, status), status); + + // fParser.adoptInsteadAndCheckErrorCode( + // NumberParserImpl::createParserFromProperties( + // *fProperties, *fSymbols, false, false, status), status); + + // fParserWithCurrency.adoptInsteadAndCheckErrorCode( + // NumberParserImpl::createParserFromProperties( + // *fProperties, *fSymbols, true, false, status), status); } void DecimalFormat::refreshFormatterNoError() { diff --git a/icu4c/source/i18n/number_formatimpl.cpp b/icu4c/source/i18n/number_formatimpl.cpp index 795c3d13482..04344b368fd 100644 --- a/icu4c/source/i18n/number_formatimpl.cpp +++ b/icu4c/source/i18n/number_formatimpl.cpp @@ -276,6 +276,12 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe, /// START POPULATING THE DEFAULT MICROPROPS AND BUILDING THE MICROPROPS GENERATOR /// ///////////////////////////////////////////////////////////////////////////////////// + // Multiplier (compatibility mode value). + if (macros.multiplier.isValid()) { + fMicros.helpers.multiplier.setAndChain(macros.multiplier, chain); + chain = &fMicros.helpers.multiplier; + } + // Rounding strategy if (!macros.rounder.isBogus()) { fMicros.rounding = macros.rounder; @@ -342,7 +348,9 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe, // Middle modifier (patterns, positive/negative, currency symbols, percent) auto patternModifier = new MutablePatternModifier(false); fPatternModifier.adoptInstead(patternModifier); - patternModifier->setPatternInfo(fPatternInfo.getAlias()); + patternModifier->setPatternInfo( + macros.affixProvider != nullptr ? macros.affixProvider + : static_cast(fPatternInfo.getAlias())); patternModifier->setPatternAttributes(fMicros.sign, isPermille); if (patternModifier->needsPlurals()) { patternModifier->setSymbols( diff --git a/icu4c/source/i18n/number_mapper.cpp b/icu4c/source/i18n/number_mapper.cpp index 949f88aadd2..0f5567c442c 100644 --- a/icu4c/source/i18n/number_mapper.cpp +++ b/icu4c/source/i18n/number_mapper.cpp @@ -11,6 +11,8 @@ #include "number_mapper.h" #include "number_patternstring.h" +#include "unicode/errorcode.h" +#include "number_utils.h" using namespace icu; using namespace icu::number; @@ -61,10 +63,10 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert AffixPatternProvider* affixProvider; if (properties.currencyPluralInfo.fPtr.isNull()) { warehouse.currencyPluralInfoAPP.setToBogus(); - warehouse.propertiesAPP.setTo(properties); + warehouse.propertiesAPP.setTo(properties, status); affixProvider = &warehouse.propertiesAPP; } else { - warehouse.currencyPluralInfoAPP.setTo(*properties.currencyPluralInfo.fPtr); + warehouse.currencyPluralInfoAPP.setTo(*properties.currencyPluralInfo.fPtr, status); warehouse.propertiesAPP.setToBogus(); affixProvider = &warehouse.currencyPluralInfoAPP; } @@ -229,8 +231,8 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert macros.rounder = Rounder::constructSignificant(1, maxFrac_ + 1).withMode(roundingMode); } else { // All other scientific patterns, which mean round to minInt+maxFrac - macros.rounder = Rounder::constructSignificant(minInt_ + minFrac_, minInt_ + maxFrac_) - .withMode(roundingMode); + macros.rounder = Rounder::constructSignificant( + minInt_ + minFrac_, minInt_ + maxFrac_).withMode(roundingMode); } } } @@ -254,9 +256,9 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert ///////////////// if (properties.magnitudeMultiplier != 0) { - macros.multiplier = MultiplierImpl::magnitude(properties.magnitudeMultiplier); + macros.multiplier = Multiplier::magnitude(properties.magnitudeMultiplier); } else if (properties.multiplier != 1) { - macros.multiplier = MultiplierImpl::integer(properties.multiplier); + macros.multiplier = Multiplier::integer(properties.multiplier); } ////////////////////// @@ -303,8 +305,127 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert } -void PropertiesAffixPatternProvider::setTo(const DecimalFormatProperties& properties) { - // TODO +void PropertiesAffixPatternProvider::setTo(const DecimalFormatProperties& properties, UErrorCode&) { + // There are two ways to set affixes in DecimalFormat: via the pattern string (applyPattern), and via the + // explicit setters (setPositivePrefix and friends). The way to resolve the settings is as follows: + // + // 1) If the explicit setting is present for the field, use it. + // 2) Otherwise, follows UTS 35 rules based on the pattern string. + // + // Importantly, the explicit setters affect only the one field they override. If you set the positive + // prefix, that should not affect the negative prefix. Since it is impossible for the user of this class + // to know whether the origin for a string was the override or the pattern, we have to say that we always + // have a negative subpattern and perform all resolution logic here. + + // Convenience: Extract the properties into local variables. + // Variables are named with three chars: [p/n][p/s][o/p] + // [p/n] => p for positive, n for negative + // [p/s] => p for prefix, s for suffix + // [o/p] => o for escaped custom override string, p for pattern string + UnicodeString ppo = AffixUtils::escape(UnicodeStringCharSequence(properties.positivePrefix)); + UnicodeString pso = AffixUtils::escape(UnicodeStringCharSequence(properties.positiveSuffix)); + UnicodeString npo = AffixUtils::escape(UnicodeStringCharSequence(properties.negativePrefix)); + UnicodeString nso = AffixUtils::escape(UnicodeStringCharSequence(properties.negativeSuffix)); + const UnicodeString& ppp = properties.positivePrefixPattern; + const UnicodeString& psp = properties.positiveSuffixPattern; + const UnicodeString& npp = properties.negativePrefixPattern; + const UnicodeString& nsp = properties.negativeSuffixPattern; + + if (!properties.positivePrefix.isBogus()) { + posPrefix = ppo; + } else if (!ppp.isBogus()) { + posPrefix = ppp; + } else { + // UTS 35: Default positive prefix is empty string. + posPrefix = u""; + } + + if (!properties.positiveSuffix.isBogus()) { + posSuffix = pso; + } else if (!psp.isBogus()) { + posSuffix = psp; + } else { + // UTS 35: Default positive suffix is empty string. + posSuffix = u""; + } + + if (!properties.negativePrefix.isBogus()) { + negPrefix = npo; + } else if (!npp.isBogus()) { + negPrefix = npp; + } else { + // UTS 35: Default negative prefix is "-" with positive prefix. + // Important: We prepend the "-" to the pattern, not the override! + negPrefix = ppp.isBogus() ? u"-" : u"-" + ppp; + } + + if (!properties.negativeSuffix.isBogus()) { + negSuffix = nso; + } else if (!nsp.isBogus()) { + negSuffix = nsp; + } else { + // UTS 35: Default negative prefix is the positive prefix. + negSuffix = psp.isBogus() ? u"" : psp; + } +} + +char16_t PropertiesAffixPatternProvider::charAt(int flags, int i) const { + return getStringInternal(flags).charAt(i); +} + +int PropertiesAffixPatternProvider::length(int flags) const { + return getStringInternal(flags).length(); +} + +UnicodeString PropertiesAffixPatternProvider::getString(int32_t flags) const { + return getStringInternal(flags); +} + +const UnicodeString& PropertiesAffixPatternProvider::getStringInternal(int32_t flags) const { + bool prefix = (flags & AFFIX_PREFIX) != 0; + bool negative = (flags & AFFIX_NEGATIVE_SUBPATTERN) != 0; + if (prefix && negative) { + return negPrefix; + } else if (prefix) { + return posPrefix; + } else if (negative) { + return negSuffix; + } else { + return posSuffix; + } +} + +bool PropertiesAffixPatternProvider::positiveHasPlusSign() const { + // TODO: Change the internal APIs to propagate out the error? + ErrorCode localStatus; + return AffixUtils::containsType(UnicodeStringCharSequence(posPrefix), TYPE_PLUS_SIGN, localStatus) || + AffixUtils::containsType(UnicodeStringCharSequence(posSuffix), TYPE_PLUS_SIGN, localStatus); +} + +bool PropertiesAffixPatternProvider::hasNegativeSubpattern() const { + // See comments in the constructor for more information on why this is always true. + return true; +} + +bool PropertiesAffixPatternProvider::negativeHasMinusSign() const { + ErrorCode localStatus; + return AffixUtils::containsType(UnicodeStringCharSequence(negPrefix), TYPE_MINUS_SIGN, localStatus) || + AffixUtils::containsType(UnicodeStringCharSequence(negSuffix), TYPE_MINUS_SIGN, localStatus); +} + +bool PropertiesAffixPatternProvider::hasCurrencySign() const { + ErrorCode localStatus; + return AffixUtils::hasCurrencySymbols(UnicodeStringCharSequence(posPrefix), localStatus) || + AffixUtils::hasCurrencySymbols(UnicodeStringCharSequence(posSuffix), localStatus) || + AffixUtils::hasCurrencySymbols(UnicodeStringCharSequence(negPrefix), localStatus) || + AffixUtils::hasCurrencySymbols(UnicodeStringCharSequence(negSuffix), localStatus); +} + +bool PropertiesAffixPatternProvider::containsSymbolType(AffixPatternType type, UErrorCode& status) const { + return AffixUtils::containsType(UnicodeStringCharSequence(posPrefix), type, status) || + AffixUtils::containsType(UnicodeStringCharSequence(posSuffix), type, status) || + AffixUtils::containsType(UnicodeStringCharSequence(negPrefix), type, status) || + AffixUtils::containsType(UnicodeStringCharSequence(negSuffix), type, status); } bool PropertiesAffixPatternProvider::hasBody() const { @@ -312,8 +433,48 @@ bool PropertiesAffixPatternProvider::hasBody() const { } -void CurrencyPluralInfoAffixProvider::setTo(const CurrencyPluralInfo& cpi) { - // TODO +void CurrencyPluralInfoAffixProvider::setTo(const CurrencyPluralInfo& cpi, UErrorCode& status) { + for (int32_t plural = 0; plural < StandardPlural::COUNT; plural++) { + const char* keyword = StandardPlural::getKeyword(static_cast(plural)); + UnicodeString patternString; + patternString = cpi.getCurrencyPluralPattern(keyword, patternString); + PatternParser::parseToPatternInfo(patternString, affixesByPlural[plural], status); + } +} + +char16_t CurrencyPluralInfoAffixProvider::charAt(int32_t flags, int32_t i) const { + int32_t pluralOrdinal = (flags & AFFIX_PLURAL_MASK); + return affixesByPlural[pluralOrdinal].charAt(flags, i); +} + +int32_t CurrencyPluralInfoAffixProvider::length(int32_t flags) const { + int32_t pluralOrdinal = (flags & AFFIX_PLURAL_MASK); + return affixesByPlural[pluralOrdinal].length(flags); +} + +UnicodeString CurrencyPluralInfoAffixProvider::getString(int32_t flags) const { + int32_t pluralOrdinal = (flags & AFFIX_PLURAL_MASK); + return affixesByPlural[pluralOrdinal].getString(flags); +} + +bool CurrencyPluralInfoAffixProvider::positiveHasPlusSign() const { + return affixesByPlural[StandardPlural::OTHER].positiveHasPlusSign(); +} + +bool CurrencyPluralInfoAffixProvider::hasNegativeSubpattern() const { + return affixesByPlural[StandardPlural::OTHER].hasNegativeSubpattern(); +} + +bool CurrencyPluralInfoAffixProvider::negativeHasMinusSign() const { + return affixesByPlural[StandardPlural::OTHER].negativeHasMinusSign(); +} + +bool CurrencyPluralInfoAffixProvider::hasCurrencySign() const { + return affixesByPlural[StandardPlural::OTHER].hasCurrencySign(); +} + +bool CurrencyPluralInfoAffixProvider::containsSymbolType(AffixPatternType type, UErrorCode& status) const { + return affixesByPlural[StandardPlural::OTHER].containsSymbolType(type, status); } bool CurrencyPluralInfoAffixProvider::hasBody() const { diff --git a/icu4c/source/i18n/number_mapper.h b/icu4c/source/i18n/number_mapper.h index 842b183fd53..d9af7954995 100644 --- a/icu4c/source/i18n/number_mapper.h +++ b/icu4c/source/i18n/number_mapper.h @@ -18,19 +18,23 @@ namespace impl { class PropertiesAffixPatternProvider : public AffixPatternProvider, public UMemory { public: - bool isBogus() const; + bool isBogus() const { + return fBogus; + } - void setTo(const DecimalFormatProperties& properties); + void setToBogus() { + fBogus = true; + } - void setToBogus(); + void setTo(const DecimalFormatProperties& properties, UErrorCode& status); // AffixPatternProvider Methods: - char16_t charAt(int flags, int i) const U_OVERRIDE; + char16_t charAt(int32_t flags, int32_t i) const U_OVERRIDE; - int length(int flags) const U_OVERRIDE; + int32_t length(int32_t flags) const U_OVERRIDE; - UnicodeString getString(int flags) const U_OVERRIDE; + UnicodeString getString(int32_t flags) const U_OVERRIDE; bool hasCurrencySign() const U_OVERRIDE; @@ -42,7 +46,7 @@ class PropertiesAffixPatternProvider : public AffixPatternProvider, public UMemo bool containsSymbolType(AffixPatternType, UErrorCode&) const U_OVERRIDE; - virtual bool hasBody() const U_OVERRIDE; + bool hasBody() const U_OVERRIDE; private: UnicodeString posPrefix; @@ -50,25 +54,31 @@ class PropertiesAffixPatternProvider : public AffixPatternProvider, public UMemo UnicodeString negPrefix; UnicodeString negSuffix; + const UnicodeString& getStringInternal(int32_t flags) const; + bool fBogus{true}; }; class CurrencyPluralInfoAffixProvider : public AffixPatternProvider, public UMemory { public: - bool isBogus() const; + bool isBogus() const { + return fBogus; + } - void setTo(const CurrencyPluralInfo& cpi); + void setToBogus() { + fBogus = true; + } - void setToBogus(); + void setTo(const CurrencyPluralInfo& cpi, UErrorCode& status); // AffixPatternProvider Methods: - char16_t charAt(int flags, int i) const U_OVERRIDE; + char16_t charAt(int32_t flags, int32_t i) const U_OVERRIDE; - int length(int flags) const U_OVERRIDE; + int32_t length(int32_t flags) const U_OVERRIDE; - UnicodeString getString(int flags) const U_OVERRIDE; + UnicodeString getString(int32_t flags) const U_OVERRIDE; bool hasCurrencySign() const U_OVERRIDE; @@ -80,7 +90,7 @@ class CurrencyPluralInfoAffixProvider : public AffixPatternProvider, public UMem bool containsSymbolType(AffixPatternType, UErrorCode&) const U_OVERRIDE; - virtual bool hasBody() const U_OVERRIDE; + bool hasBody() const U_OVERRIDE; private: ParsedPatternInfo affixesByPlural[StandardPlural::COUNT]; diff --git a/icu4c/source/i18n/number_multiplier.cpp b/icu4c/source/i18n/number_multiplier.cpp new file mode 100644 index 00000000000..ca445ba4dda --- /dev/null +++ b/icu4c/source/i18n/number_multiplier.cpp @@ -0,0 +1,47 @@ +// © 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 && !UPRV_INCOMPLETE_CPP11_SUPPORT + +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT + +#include "number_types.h" +#include "number_multiplier.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + + +Multiplier::Multiplier(int32_t magnitudeMultiplier, int32_t multiplier) + : magnitudeMultiplier(magnitudeMultiplier), multiplier(multiplier) {} + +Multiplier Multiplier::magnitude(int32_t magnitudeMultiplier) { + return {magnitudeMultiplier, 1}; +} + +Multiplier Multiplier::integer(int32_t multiplier) { + return {0, multiplier}; +} + + +void MultiplierChain::setAndChain(const Multiplier& multiplier, const MicroPropsGenerator* parent) { + this->multiplier = multiplier; + this->parent = parent; +} + +void +MultiplierChain::processQuantity(DecimalQuantity& quantity, MicroProps& micros, UErrorCode& status) const { + parent->processQuantity(quantity, micros, status); + quantity.adjustMagnitude(multiplier.magnitudeMultiplier); + if (multiplier.multiplier != 1) { + quantity.multiplyBy(multiplier.multiplier); + } +} + + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/i18n/number_multiplier.h b/icu4c/source/i18n/number_multiplier.h new file mode 100644 index 00000000000..93d103dd84a --- /dev/null +++ b/icu4c/source/i18n/number_multiplier.h @@ -0,0 +1,34 @@ +// © 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 && !UPRV_INCOMPLETE_CPP11_SUPPORT +#ifndef __SOURCE_NUMBER_MULTIPLIER_H__ +#define __SOURCE_NUMBER_MULTIPLIER_H__ + +#include "numparse_types.h" + +U_NAMESPACE_BEGIN namespace number { +namespace impl { + + +class MultiplierChain : public MicroPropsGenerator, public UMemory { + public: + void setAndChain(const Multiplier& other, const MicroPropsGenerator* parent); + + void processQuantity(DecimalQuantity& quantity, MicroProps& micros, + UErrorCode& status) const U_OVERRIDE; + + private: + Multiplier multiplier; + const MicroPropsGenerator *parent; +}; + + +} // namespace impl +} // namespace number +U_NAMESPACE_END + +#endif //__SOURCE_NUMBER_MULTIPLIER_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/i18n/number_utils.h b/icu4c/source/i18n/number_utils.h index a889c69eb74..1e5494dc813 100644 --- a/icu4c/source/i18n/number_utils.h +++ b/icu4c/source/i18n/number_utils.h @@ -13,6 +13,7 @@ #include "number_scientific.h" #include "number_patternstring.h" #include "number_modifiers.h" +#include "number_multiplier.h" U_NAMESPACE_BEGIN namespace number { namespace impl { @@ -73,6 +74,7 @@ struct MicroProps : public MicroPropsGenerator { ScientificModifier scientificModifier; EmptyModifier emptyWeakModifier{false}; EmptyModifier emptyStrongModifier{true}; + MultiplierChain multiplier; } helpers;