| // © 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) { |
| fSignDisplay = signDisplay; |
| fPerMilleReplacesPercent = perMille; |
| } |
| |
| 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. |
| } |
| |
| 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) { |
| setNumberProperties(SIGNUM_POS, plural); |
| pm->adoptModifier(SIGNUM_POS, plural, createConstantModifier(status)); |
| setNumberProperties(SIGNUM_NEG_ZERO, plural); |
| pm->adoptModifier(SIGNUM_NEG_ZERO, plural, createConstantModifier(status)); |
| setNumberProperties(SIGNUM_POS_ZERO, plural); |
| pm->adoptModifier(SIGNUM_POS_ZERO, plural, createConstantModifier(status)); |
| setNumberProperties(SIGNUM_NEG, plural); |
| pm->adoptModifier(SIGNUM_NEG, plural, createConstantModifier(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. |
| setNumberProperties(SIGNUM_POS, StandardPlural::Form::COUNT); |
| pm->adoptModifierWithoutPlural(SIGNUM_POS, createConstantModifier(status)); |
| setNumberProperties(SIGNUM_NEG_ZERO, StandardPlural::Form::COUNT); |
| pm->adoptModifierWithoutPlural(SIGNUM_NEG_ZERO, createConstantModifier(status)); |
| setNumberProperties(SIGNUM_POS_ZERO, StandardPlural::Form::COUNT); |
| pm->adoptModifierWithoutPlural(SIGNUM_POS_ZERO, createConstantModifier(status)); |
| setNumberProperties(SIGNUM_NEG, StandardPlural::Form::COUNT); |
| pm->adoptModifierWithoutPlural(SIGNUM_NEG, createConstantModifier(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; |
| } |
| |
| void MutablePatternModifier::getParameters(Parameters& output) const { |
| (void)output; |
| // This method is not currently used. |
| UPRV_UNREACHABLE; |
| } |
| |
| bool MutablePatternModifier::semanticallyEquivalent(const Modifier& other) const { |
| (void)other; |
| // This method is not currently used. |
| UPRV_UNREACHABLE; |
| } |
| |
| 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), |
| fPlural, |
| fPerMilleReplacesPercent, |
| 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_PERCENT: |
| return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPercentSymbol); |
| case AffixPatternType::TYPE_PERMILLE: |
| return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPerMillSymbol); |
| case AffixPatternType::TYPE_CURRENCY_SINGLE: { |
| // UnitWidth ISO and HIDDEN overrides the singular currency symbol. |
| if (fUnitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE) { |
| return fCurrencySymbols.getIntlCurrencySymbol(localStatus); |
| } else if (fUnitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_HIDDEN) { |
| return UnicodeString(); |
| } else if (fUnitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW) { |
| return fCurrencySymbols.getNarrowCurrencySymbol(localStatus); |
| } else { |
| return fCurrencySymbols.getCurrencySymbol(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; |
| } |
| } |
| |
| UnicodeString MutablePatternModifier::toUnicodeString() const { |
| // Never called by AffixUtils |
| UPRV_UNREACHABLE; |
| } |
| |
| #endif /* #if !UCONFIG_NO_FORMATTING */ |