| // © 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 */ |