diff options
Diffstat (limited to 'intl/icu/source/i18n/number_patternmodifier.cpp')
-rw-r--r-- | intl/icu/source/i18n/number_patternmodifier.cpp | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/intl/icu/source/i18n/number_patternmodifier.cpp b/intl/icu/source/i18n/number_patternmodifier.cpp new file mode 100644 index 0000000000..6f398c6acf --- /dev/null +++ b/intl/icu/source/i18n/number_patternmodifier.cpp @@ -0,0 +1,348 @@ +// © 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 + +#include "cstring.h" +#include "number_patternmodifier.h" +#include "unicode/dcfmtsym.h" +#include "unicode/ucurr.h" +#include "unicode/unistr.h" +#include "number_microprops.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + + +AffixPatternProvider::~AffixPatternProvider() = default; + + +MutablePatternModifier::MutablePatternModifier(bool isStrong) + : fStrong(isStrong) {} + +void MutablePatternModifier::setPatternInfo(const AffixPatternProvider* patternInfo, Field field) { + fPatternInfo = patternInfo; + fField = field; +} + +void MutablePatternModifier::setPatternAttributes( + UNumberSignDisplay signDisplay, + bool perMille, + bool approximately) { + fSignDisplay = signDisplay; + fPerMilleReplacesPercent = perMille; + fApproximately = approximately; +} + +void MutablePatternModifier::setSymbols(const DecimalFormatSymbols* symbols, + const CurrencyUnit& currency, + const UNumberUnitWidth unitWidth, + const PluralRules* rules, + UErrorCode& status) { + U_ASSERT((rules != nullptr) == needsPlurals()); + fSymbols = symbols; + fCurrencySymbols = {currency, symbols->getLocale(), *symbols, status}; + fUnitWidth = unitWidth; + fRules = rules; +} + +void MutablePatternModifier::setNumberProperties(Signum signum, StandardPlural::Form plural) { + fSignum = signum; + fPlural = plural; +} + +bool MutablePatternModifier::needsPlurals() const { + UErrorCode statusLocal = U_ZERO_ERROR; + return fPatternInfo->containsSymbolType(AffixPatternType::TYPE_CURRENCY_TRIPLE, statusLocal); + // Silently ignore any error codes. +} + +AdoptingSignumModifierStore MutablePatternModifier::createImmutableForPlural(StandardPlural::Form plural, UErrorCode& status) { + AdoptingSignumModifierStore pm; + + setNumberProperties(SIGNUM_POS, plural); + pm.adoptModifier(SIGNUM_POS, createConstantModifier(status)); + setNumberProperties(SIGNUM_NEG_ZERO, plural); + pm.adoptModifier(SIGNUM_NEG_ZERO, createConstantModifier(status)); + setNumberProperties(SIGNUM_POS_ZERO, plural); + pm.adoptModifier(SIGNUM_POS_ZERO, createConstantModifier(status)); + setNumberProperties(SIGNUM_NEG, plural); + pm.adoptModifier(SIGNUM_NEG, createConstantModifier(status)); + + return pm; +} + +ImmutablePatternModifier* MutablePatternModifier::createImmutable(UErrorCode& status) { + // TODO: Move StandardPlural VALUES to standardplural.h + static const StandardPlural::Form STANDARD_PLURAL_VALUES[] = { + StandardPlural::Form::ZERO, + StandardPlural::Form::ONE, + StandardPlural::Form::TWO, + StandardPlural::Form::FEW, + StandardPlural::Form::MANY, + StandardPlural::Form::OTHER}; + + auto pm = new AdoptingModifierStore(); + if (pm == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + + if (needsPlurals()) { + // Slower path when we require the plural keyword. + for (StandardPlural::Form plural : STANDARD_PLURAL_VALUES) { + pm->adoptSignumModifierStore(plural, createImmutableForPlural(plural, status)); + } + if (U_FAILURE(status)) { + delete pm; + return nullptr; + } + return new ImmutablePatternModifier(pm, fRules); // adopts pm + } else { + // Faster path when plural keyword is not needed. + pm->adoptSignumModifierStoreNoPlural(createImmutableForPlural(StandardPlural::Form::COUNT, status)); + if (U_FAILURE(status)) { + delete pm; + return nullptr; + } + return new ImmutablePatternModifier(pm, nullptr); // adopts pm + } +} + +ConstantMultiFieldModifier* MutablePatternModifier::createConstantModifier(UErrorCode& status) { + FormattedStringBuilder a; + FormattedStringBuilder b; + insertPrefix(a, 0, status); + insertSuffix(b, 0, status); + if (fPatternInfo->hasCurrencySign()) { + return new CurrencySpacingEnabledModifier( + a, b, !fPatternInfo->hasBody(), fStrong, *fSymbols, status); + } else { + return new ConstantMultiFieldModifier(a, b, !fPatternInfo->hasBody(), fStrong); + } +} + +ImmutablePatternModifier::ImmutablePatternModifier(AdoptingModifierStore* pm, const PluralRules* rules) + : pm(pm), rules(rules), parent(nullptr) {} + +void ImmutablePatternModifier::processQuantity(DecimalQuantity& quantity, MicroProps& micros, + UErrorCode& status) const { + parent->processQuantity(quantity, micros, status); + micros.rounder.apply(quantity, status); + if (micros.modMiddle != nullptr) { + return; + } + applyToMicros(micros, quantity, status); +} + +void ImmutablePatternModifier::applyToMicros( + MicroProps& micros, const DecimalQuantity& quantity, UErrorCode& status) const { + if (rules == nullptr) { + micros.modMiddle = pm->getModifierWithoutPlural(quantity.signum()); + } else { + StandardPlural::Form pluralForm = utils::getPluralSafe(micros.rounder, rules, quantity, status); + micros.modMiddle = pm->getModifier(quantity.signum(), pluralForm); + } +} + +const Modifier* ImmutablePatternModifier::getModifier(Signum signum, StandardPlural::Form plural) const { + if (rules == nullptr) { + return pm->getModifierWithoutPlural(signum); + } else { + return pm->getModifier(signum, plural); + } +} + +void ImmutablePatternModifier::addToChain(const MicroPropsGenerator* parent) { + this->parent = parent; +} + + +/** Used by the unsafe code path. */ +MicroPropsGenerator& MutablePatternModifier::addToChain(const MicroPropsGenerator* parent) { + fParent = parent; + return *this; +} + +void MutablePatternModifier::processQuantity(DecimalQuantity& fq, MicroProps& micros, + UErrorCode& status) const { + fParent->processQuantity(fq, micros, status); + micros.rounder.apply(fq, status); + if (micros.modMiddle != nullptr) { + return; + } + // The unsafe code path performs self-mutation, so we need a const_cast. + // This method needs to be const because it overrides a const method in the parent class. + auto nonConstThis = const_cast<MutablePatternModifier*>(this); + if (needsPlurals()) { + StandardPlural::Form pluralForm = utils::getPluralSafe(micros.rounder, fRules, fq, status); + nonConstThis->setNumberProperties(fq.signum(), pluralForm); + } else { + nonConstThis->setNumberProperties(fq.signum(), StandardPlural::Form::COUNT); + } + micros.modMiddle = this; +} + +int32_t MutablePatternModifier::apply(FormattedStringBuilder& output, int32_t leftIndex, int32_t rightIndex, + UErrorCode& status) const { + // The unsafe code path performs self-mutation, so we need a const_cast. + // This method needs to be const because it overrides a const method in the parent class. + auto nonConstThis = const_cast<MutablePatternModifier*>(this); + int32_t prefixLen = nonConstThis->insertPrefix(output, leftIndex, status); + int32_t suffixLen = nonConstThis->insertSuffix(output, rightIndex + prefixLen, status); + // If the pattern had no decimal stem body (like #,##0.00), overwrite the value. + int32_t overwriteLen = 0; + if (!fPatternInfo->hasBody()) { + overwriteLen = output.splice( + leftIndex + prefixLen, + rightIndex + prefixLen, + UnicodeString(), + 0, + 0, + kUndefinedField, + status); + } + CurrencySpacingEnabledModifier::applyCurrencySpacing( + output, + leftIndex, + prefixLen, + rightIndex + overwriteLen + prefixLen, + suffixLen, + *fSymbols, + status); + return prefixLen + overwriteLen + suffixLen; +} + +int32_t MutablePatternModifier::getPrefixLength() const { + // The unsafe code path performs self-mutation, so we need a const_cast. + // This method needs to be const because it overrides a const method in the parent class. + auto nonConstThis = const_cast<MutablePatternModifier*>(this); + + // Enter and exit CharSequence Mode to get the length. + UErrorCode status = U_ZERO_ERROR; // status fails only with an iilegal argument exception + nonConstThis->prepareAffix(true); + int result = AffixUtils::unescapedCodePointCount(currentAffix, *this, status); // prefix length + return result; +} + +int32_t MutablePatternModifier::getCodePointCount() const { + // The unsafe code path performs self-mutation, so we need a const_cast. + // This method needs to be const because it overrides a const method in the parent class. + auto nonConstThis = const_cast<MutablePatternModifier*>(this); + + // Render the affixes to get the length + UErrorCode status = U_ZERO_ERROR; // status fails only with an iilegal argument exception + nonConstThis->prepareAffix(true); + int result = AffixUtils::unescapedCodePointCount(currentAffix, *this, status); // prefix length + nonConstThis->prepareAffix(false); + result += AffixUtils::unescapedCodePointCount(currentAffix, *this, status); // suffix length + return result; +} + +bool MutablePatternModifier::isStrong() const { + return fStrong; +} + +bool MutablePatternModifier::containsField(Field field) const { + (void)field; + // This method is not currently used. + UPRV_UNREACHABLE_EXIT; +} + +void MutablePatternModifier::getParameters(Parameters& output) const { + (void)output; + // This method is not currently used. + UPRV_UNREACHABLE_EXIT; +} + +bool MutablePatternModifier::semanticallyEquivalent(const Modifier& other) const { + (void)other; + // This method is not currently used. + UPRV_UNREACHABLE_EXIT; +} + +int32_t MutablePatternModifier::insertPrefix(FormattedStringBuilder& sb, int position, UErrorCode& status) { + prepareAffix(true); + int32_t length = AffixUtils::unescape(currentAffix, sb, position, *this, fField, status); + return length; +} + +int32_t MutablePatternModifier::insertSuffix(FormattedStringBuilder& sb, int position, UErrorCode& status) { + prepareAffix(false); + int32_t length = AffixUtils::unescape(currentAffix, sb, position, *this, fField, status); + return length; +} + +/** This method contains the heart of the logic for rendering LDML affix strings. */ +void MutablePatternModifier::prepareAffix(bool isPrefix) { + PatternStringUtils::patternInfoToStringBuilder( + *fPatternInfo, + isPrefix, + PatternStringUtils::resolveSignDisplay(fSignDisplay, fSignum), + fApproximately, + fPlural, + fPerMilleReplacesPercent, + false, // dropCurrencySymbols + currentAffix); +} + +UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const { + UErrorCode localStatus = U_ZERO_ERROR; + switch (type) { + case AffixPatternType::TYPE_MINUS_SIGN: + return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol); + case AffixPatternType::TYPE_PLUS_SIGN: + return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol); + case AffixPatternType::TYPE_APPROXIMATELY_SIGN: + return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kApproximatelySignSymbol); + case AffixPatternType::TYPE_PERCENT: + return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPercentSymbol); + case AffixPatternType::TYPE_PERMILLE: + return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPerMillSymbol); + case AffixPatternType::TYPE_CURRENCY_SINGLE: + return getCurrencySymbolForUnitWidth(localStatus); + case AffixPatternType::TYPE_CURRENCY_DOUBLE: + return fCurrencySymbols.getIntlCurrencySymbol(localStatus); + case AffixPatternType::TYPE_CURRENCY_TRIPLE: + // NOTE: This is the code path only for patterns containing "¤¤¤". + // Plural currencies set via the API are formatted in LongNameHandler. + // This code path is used by DecimalFormat via CurrencyPluralInfo. + U_ASSERT(fPlural != StandardPlural::Form::COUNT); + return fCurrencySymbols.getPluralName(fPlural, localStatus); + case AffixPatternType::TYPE_CURRENCY_QUAD: + return UnicodeString(u"\uFFFD"); + case AffixPatternType::TYPE_CURRENCY_QUINT: + return UnicodeString(u"\uFFFD"); + default: + UPRV_UNREACHABLE_EXIT; + } +} + +UnicodeString MutablePatternModifier::getCurrencySymbolForUnitWidth(UErrorCode& status) const { + switch (fUnitWidth) { + case UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW: + return fCurrencySymbols.getNarrowCurrencySymbol(status); + case UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT: + return fCurrencySymbols.getCurrencySymbol(status); + case UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE: + return fCurrencySymbols.getIntlCurrencySymbol(status); + case UNumberUnitWidth::UNUM_UNIT_WIDTH_FORMAL: + return fCurrencySymbols.getFormalCurrencySymbol(status); + case UNumberUnitWidth::UNUM_UNIT_WIDTH_VARIANT: + return fCurrencySymbols.getVariantCurrencySymbol(status); + case UNumberUnitWidth::UNUM_UNIT_WIDTH_HIDDEN: + return UnicodeString(); + default: + return fCurrencySymbols.getCurrencySymbol(status); + } +} + +UnicodeString MutablePatternModifier::toUnicodeString() const { + // Never called by AffixUtils + UPRV_UNREACHABLE_EXIT; +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ |