| // © 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 && !UPRV_INCOMPLETE_CPP11_SUPPORT |
| |
| #include "cstring.h" |
| #include "number_patternmodifier.h" |
| #include "unicode/dcfmtsym.h" |
| #include "unicode/ucurr.h" |
| #include "unicode/unistr.h" |
| |
| using namespace icu; |
| using namespace icu::number; |
| using namespace icu::number::impl; |
| |
| MutablePatternModifier::MutablePatternModifier(bool isStrong) : fStrong(isStrong) {} |
| |
| void MutablePatternModifier::setPatternInfo(const AffixPatternProvider *patternInfo) { |
| this->patternInfo = patternInfo; |
| } |
| |
| void MutablePatternModifier::setPatternAttributes(UNumberSignDisplay signDisplay, bool perMille) { |
| this->signDisplay = signDisplay; |
| this->perMilleReplacesPercent = perMille; |
| } |
| |
| void |
| MutablePatternModifier::setSymbols(const DecimalFormatSymbols *symbols, const CurrencyUnit ¤cy, |
| const UNumberUnitWidth unitWidth, const PluralRules *rules) { |
| U_ASSERT((rules != nullptr) == needsPlurals()); |
| this->symbols = symbols; |
| uprv_memcpy(static_cast<char16_t *>(this->currencyCode), |
| currency.getISOCurrency(), |
| sizeof(char16_t) * 4); |
| this->unitWidth = unitWidth; |
| this->rules = rules; |
| } |
| |
| void MutablePatternModifier::setNumberProperties(int8_t signum, StandardPlural::Form plural) { |
| this->signum = signum; |
| this->plural = plural; |
| } |
| |
| bool MutablePatternModifier::needsPlurals() const { |
| UErrorCode statusLocal = U_ZERO_ERROR; |
| return patternInfo->containsSymbolType(AffixPatternType::TYPE_CURRENCY_TRIPLE, statusLocal); |
| // Silently ignore any error codes. |
| } |
| |
| ImmutablePatternModifier *MutablePatternModifier::createImmutable(UErrorCode &status) { |
| return createImmutableAndChain(nullptr, status); |
| } |
| |
| ImmutablePatternModifier * |
| MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator *parent, 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 ParameterizedModifier(); |
| 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(1, plural); |
| pm->adoptSignPluralModifier(1, plural, createConstantModifier(status)); |
| setNumberProperties(0, plural); |
| pm->adoptSignPluralModifier(0, plural, createConstantModifier(status)); |
| setNumberProperties(-1, plural); |
| pm->adoptSignPluralModifier(-1, plural, createConstantModifier(status)); |
| } |
| if (U_FAILURE(status)) { |
| delete pm; |
| return nullptr; |
| } |
| return new ImmutablePatternModifier(pm, rules, parent); // adopts pm |
| } else { |
| // Faster path when plural keyword is not needed. |
| setNumberProperties(1, StandardPlural::Form::COUNT); |
| Modifier *positive = createConstantModifier(status); |
| setNumberProperties(0, StandardPlural::Form::COUNT); |
| Modifier *zero = createConstantModifier(status); |
| setNumberProperties(-1, StandardPlural::Form::COUNT); |
| Modifier *negative = createConstantModifier(status); |
| pm->adoptPositiveNegativeModifiers(positive, zero, negative); |
| if (U_FAILURE(status)) { |
| delete pm; |
| return nullptr; |
| } |
| return new ImmutablePatternModifier(pm, nullptr, parent); // adopts pm |
| } |
| } |
| |
| ConstantMultiFieldModifier *MutablePatternModifier::createConstantModifier(UErrorCode &status) { |
| NumberStringBuilder a; |
| NumberStringBuilder b; |
| insertPrefix(a, 0, status); |
| insertSuffix(b, 0, status); |
| if (patternInfo->hasCurrencySign()) { |
| return new CurrencySpacingEnabledModifier(a, b, !patternInfo->hasBody(), fStrong, *symbols, status); |
| } else { |
| return new ConstantMultiFieldModifier(a, b, !patternInfo->hasBody(), fStrong); |
| } |
| } |
| |
| ImmutablePatternModifier::ImmutablePatternModifier(ParameterizedModifier *pm, const PluralRules *rules, |
| const MicroPropsGenerator *parent) |
| : pm(pm), rules(rules), parent(parent) {} |
| |
| void ImmutablePatternModifier::processQuantity(DecimalQuantity &quantity, MicroProps µs, |
| UErrorCode &status) const { |
| parent->processQuantity(quantity, micros, status); |
| applyToMicros(micros, quantity); |
| } |
| |
| void ImmutablePatternModifier::applyToMicros(MicroProps µs, DecimalQuantity &quantity) const { |
| if (rules == nullptr) { |
| micros.modMiddle = pm->getModifier(quantity.signum()); |
| } else { |
| // TODO: Fix this. Avoid the copy. |
| DecimalQuantity copy(quantity); |
| copy.roundToInfinity(); |
| StandardPlural::Form plural = copy.getStandardPlural(rules); |
| micros.modMiddle = pm->getModifier(quantity.signum(), plural); |
| } |
| } |
| |
| /** Used by the unsafe code path. */ |
| MicroPropsGenerator &MutablePatternModifier::addToChain(const MicroPropsGenerator *parent) { |
| this->parent = parent; |
| return *this; |
| } |
| |
| void MutablePatternModifier::processQuantity(DecimalQuantity &fq, MicroProps µs, |
| UErrorCode &status) const { |
| parent->processQuantity(fq, micros, status); |
| // 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()) { |
| // TODO: Fix this. Avoid the copy. |
| DecimalQuantity copy(fq); |
| micros.rounding.apply(copy, status); |
| nonConstThis->setNumberProperties(fq.signum(), copy.getStandardPlural(rules)); |
| } else { |
| nonConstThis->setNumberProperties(fq.signum(), StandardPlural::Form::COUNT); |
| } |
| micros.modMiddle = this; |
| } |
| |
| int32_t MutablePatternModifier::apply(NumberStringBuilder &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 (!patternInfo->hasBody()) { |
| overwriteLen = output.splice( |
| leftIndex + prefixLen, rightIndex + prefixLen, |
| UnicodeString(), 0, 0, UNUM_FIELD_COUNT, |
| status); |
| } |
| CurrencySpacingEnabledModifier::applyCurrencySpacing( |
| output, |
| leftIndex, |
| prefixLen, |
| rightIndex + overwriteLen + prefixLen, |
| suffixLen, |
| *symbols, |
| status); |
| return prefixLen + overwriteLen + suffixLen; |
| } |
| |
| int32_t MutablePatternModifier::getPrefixLength(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); |
| |
| // Enter and exit CharSequence Mode to get the length. |
| nonConstThis->enterCharSequenceMode(true); |
| int result = AffixUtils::unescapedCodePointCount(*this, *this, status); // prefix length |
| nonConstThis->exitCharSequenceMode(); |
| return result; |
| } |
| |
| int32_t MutablePatternModifier::getCodePointCount(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); |
| |
| // Enter and exit CharSequence Mode to get the length. |
| nonConstThis->enterCharSequenceMode(true); |
| int result = AffixUtils::unescapedCodePointCount(*this, *this, status); // prefix length |
| nonConstThis->exitCharSequenceMode(); |
| nonConstThis->enterCharSequenceMode(false); |
| result += AffixUtils::unescapedCodePointCount(*this, *this, status); // suffix length |
| nonConstThis->exitCharSequenceMode(); |
| return result; |
| } |
| |
| bool MutablePatternModifier::isStrong() const { |
| return fStrong; |
| } |
| |
| int32_t MutablePatternModifier::insertPrefix(NumberStringBuilder &sb, int position, UErrorCode &status) { |
| enterCharSequenceMode(true); |
| int length = AffixUtils::unescape(*this, sb, position, *this, status); |
| exitCharSequenceMode(); |
| return length; |
| } |
| |
| int32_t MutablePatternModifier::insertSuffix(NumberStringBuilder &sb, int position, UErrorCode &status) { |
| enterCharSequenceMode(false); |
| int length = AffixUtils::unescape(*this, sb, position, *this, status); |
| exitCharSequenceMode(); |
| return length; |
| } |
| |
| UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const { |
| switch (type) { |
| case AffixPatternType::TYPE_MINUS_SIGN: |
| return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol); |
| case AffixPatternType::TYPE_PLUS_SIGN: |
| return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol); |
| case AffixPatternType::TYPE_PERCENT: |
| return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPercentSymbol); |
| case AffixPatternType::TYPE_PERMILLE: |
| return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPerMillSymbol); |
| case AffixPatternType::TYPE_CURRENCY_SINGLE: { |
| // UnitWidth ISO and HIDDEN overrides the singular currency symbol. |
| if (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE) { |
| return UnicodeString(currencyCode, 3); |
| } else if (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_HIDDEN) { |
| return UnicodeString(); |
| } else { |
| UCurrNameStyle selector = (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW) |
| ? UCurrNameStyle::UCURR_NARROW_SYMBOL_NAME |
| : UCurrNameStyle::UCURR_SYMBOL_NAME; |
| UErrorCode status = U_ZERO_ERROR; |
| UBool isChoiceFormat = FALSE; |
| int32_t symbolLen = 0; |
| const char16_t *symbol = ucurr_getName( |
| currencyCode, |
| symbols->getLocale().getName(), |
| selector, |
| &isChoiceFormat, |
| &symbolLen, |
| &status); |
| return UnicodeString(symbol, symbolLen); |
| } |
| } |
| case AffixPatternType::TYPE_CURRENCY_DOUBLE: |
| return UnicodeString(currencyCode, 3); |
| 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(plural != StandardPlural::Form::COUNT); |
| UErrorCode status = U_ZERO_ERROR; |
| UBool isChoiceFormat = FALSE; |
| int32_t symbolLen = 0; |
| const char16_t *symbol = ucurr_getPluralName( |
| currencyCode, |
| symbols->getLocale().getName(), |
| &isChoiceFormat, |
| StandardPlural::getKeyword(plural), |
| &symbolLen, |
| &status); |
| return UnicodeString(symbol, symbolLen); |
| } |
| case AffixPatternType::TYPE_CURRENCY_QUAD: |
| return UnicodeString(u"\uFFFD"); |
| case AffixPatternType::TYPE_CURRENCY_QUINT: |
| return UnicodeString(u"\uFFFD"); |
| default: |
| U_ASSERT(false); |
| return UnicodeString(); |
| } |
| } |
| |
| /** This method contains the heart of the logic for rendering LDML affix strings. */ |
| void MutablePatternModifier::enterCharSequenceMode(bool isPrefix) { |
| U_ASSERT(!inCharSequenceMode); |
| inCharSequenceMode = true; |
| |
| // Should the output render '+' where '-' would normally appear in the pattern? |
| plusReplacesMinusSign = signum != -1 |
| && (signDisplay == UNUM_SIGN_ALWAYS |
| || signDisplay == UNUM_SIGN_ACCOUNTING_ALWAYS |
| || (signum == 1 |
| && (signDisplay == UNUM_SIGN_EXCEPT_ZERO |
| || signDisplay == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO))) |
| && patternInfo->positiveHasPlusSign() == false; |
| |
| // Should we use the affix from the negative subpattern? (If not, we will use the positive subpattern.) |
| bool useNegativeAffixPattern = patternInfo->hasNegativeSubpattern() && ( |
| signum == -1 || (patternInfo->negativeHasMinusSign() && plusReplacesMinusSign)); |
| |
| // Resolve the flags for the affix pattern. |
| fFlags = 0; |
| if (useNegativeAffixPattern) { |
| fFlags |= AffixPatternProvider::AFFIX_NEGATIVE_SUBPATTERN; |
| } |
| if (isPrefix) { |
| fFlags |= AffixPatternProvider::AFFIX_PREFIX; |
| } |
| if (plural != StandardPlural::Form::COUNT) { |
| U_ASSERT(plural == (AffixPatternProvider::AFFIX_PLURAL_MASK & plural)); |
| fFlags |= plural; |
| } |
| |
| // Should we prepend a sign to the pattern? |
| if (!isPrefix || useNegativeAffixPattern) { |
| prependSign = false; |
| } else if (signum == -1) { |
| prependSign = signDisplay != UNUM_SIGN_NEVER; |
| } else { |
| prependSign = plusReplacesMinusSign; |
| } |
| |
| // Finally, compute the length of the affix pattern. |
| fLength = patternInfo->length(fFlags) + (prependSign ? 1 : 0); |
| } |
| |
| void MutablePatternModifier::exitCharSequenceMode() { |
| U_ASSERT(inCharSequenceMode); |
| inCharSequenceMode = false; |
| } |
| |
| int32_t MutablePatternModifier::length() const { |
| U_ASSERT(inCharSequenceMode); |
| return fLength; |
| } |
| |
| char16_t MutablePatternModifier::charAt(int32_t index) const { |
| U_ASSERT(inCharSequenceMode); |
| char16_t candidate; |
| if (prependSign && index == 0) { |
| candidate = u'-'; |
| } else if (prependSign) { |
| candidate = patternInfo->charAt(fFlags, index - 1); |
| } else { |
| candidate = patternInfo->charAt(fFlags, index); |
| } |
| if (plusReplacesMinusSign && candidate == u'-') { |
| return u'+'; |
| } |
| if (perMilleReplacesPercent && candidate == u'%') { |
| return u'‰'; |
| } |
| return candidate; |
| } |
| |
| UnicodeString MutablePatternModifier::toUnicodeString() const { |
| // Never called by AffixUtils |
| U_ASSERT(false); |
| return UnicodeString(); |
| } |
| |
| #endif /* #if !UCONFIG_NO_FORMATTING */ |