Home | History | Annotate | Download | only in i18n
      1 //  2017 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 
      4 #include "unicode/utypes.h"
      5 
      6 #if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
      7 
      8 #include "cstring.h"
      9 #include "number_patternmodifier.h"
     10 #include "unicode/dcfmtsym.h"
     11 #include "unicode/ucurr.h"
     12 #include "unicode/unistr.h"
     13 
     14 using namespace icu;
     15 using namespace icu::number;
     16 using namespace icu::number::impl;
     17 
     18 MutablePatternModifier::MutablePatternModifier(bool isStrong) : fStrong(isStrong) {}
     19 
     20 void MutablePatternModifier::setPatternInfo(const AffixPatternProvider *patternInfo) {
     21     this->patternInfo = patternInfo;
     22 }
     23 
     24 void MutablePatternModifier::setPatternAttributes(UNumberSignDisplay signDisplay, bool perMille) {
     25     this->signDisplay = signDisplay;
     26     this->perMilleReplacesPercent = perMille;
     27 }
     28 
     29 void
     30 MutablePatternModifier::setSymbols(const DecimalFormatSymbols *symbols, const CurrencyUnit &currency,
     31                                    const UNumberUnitWidth unitWidth, const PluralRules *rules) {
     32     U_ASSERT((rules != nullptr) == needsPlurals());
     33     this->symbols = symbols;
     34     uprv_memcpy(static_cast<char16_t *>(this->currencyCode),
     35             currency.getISOCurrency(),
     36             sizeof(char16_t) * 4);
     37     this->unitWidth = unitWidth;
     38     this->rules = rules;
     39 }
     40 
     41 void MutablePatternModifier::setNumberProperties(bool isNegative, StandardPlural::Form plural) {
     42     this->isNegative = isNegative;
     43     this->plural = plural;
     44 }
     45 
     46 bool MutablePatternModifier::needsPlurals() const {
     47     UErrorCode statusLocal = U_ZERO_ERROR;
     48     return patternInfo->containsSymbolType(AffixPatternType::TYPE_CURRENCY_TRIPLE, statusLocal);
     49     // Silently ignore any error codes.
     50 }
     51 
     52 ImmutablePatternModifier *MutablePatternModifier::createImmutable(UErrorCode &status) {
     53     return createImmutableAndChain(nullptr, status);
     54 }
     55 
     56 ImmutablePatternModifier *
     57 MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator *parent, UErrorCode &status) {
     58 
     59     // TODO: Move StandardPlural VALUES to standardplural.h
     60     static const StandardPlural::Form STANDARD_PLURAL_VALUES[] = {
     61             StandardPlural::Form::ZERO,
     62             StandardPlural::Form::ONE,
     63             StandardPlural::Form::TWO,
     64             StandardPlural::Form::FEW,
     65             StandardPlural::Form::MANY,
     66             StandardPlural::Form::OTHER};
     67 
     68     auto pm = new ParameterizedModifier();
     69     if (pm == nullptr) {
     70         status = U_MEMORY_ALLOCATION_ERROR;
     71         return nullptr;
     72     }
     73 
     74     if (needsPlurals()) {
     75         // Slower path when we require the plural keyword.
     76         for (StandardPlural::Form plural : STANDARD_PLURAL_VALUES) {
     77             setNumberProperties(false, plural);
     78             pm->adoptSignPluralModifier(false, plural, createConstantModifier(status));
     79             setNumberProperties(true, plural);
     80             pm->adoptSignPluralModifier(true, plural, createConstantModifier(status));
     81         }
     82         if (U_FAILURE(status)) {
     83             delete pm;
     84             return nullptr;
     85         }
     86         return new ImmutablePatternModifier(pm, rules, parent);  // adopts pm
     87     } else {
     88         // Faster path when plural keyword is not needed.
     89         setNumberProperties(false, StandardPlural::Form::COUNT);
     90         Modifier *positive = createConstantModifier(status);
     91         setNumberProperties(true, StandardPlural::Form::COUNT);
     92         Modifier *negative = createConstantModifier(status);
     93         pm->adoptPositiveNegativeModifiers(positive, negative);
     94         if (U_FAILURE(status)) {
     95             delete pm;
     96             return nullptr;
     97         }
     98         return new ImmutablePatternModifier(pm, nullptr, parent);  // adopts pm
     99     }
    100 }
    101 
    102 ConstantMultiFieldModifier *MutablePatternModifier::createConstantModifier(UErrorCode &status) {
    103     NumberStringBuilder a;
    104     NumberStringBuilder b;
    105     insertPrefix(a, 0, status);
    106     insertSuffix(b, 0, status);
    107     if (patternInfo->hasCurrencySign()) {
    108         return new CurrencySpacingEnabledModifier(a, b, fStrong, *symbols, status);
    109     } else {
    110         return new ConstantMultiFieldModifier(a, b, fStrong);
    111     }
    112 }
    113 
    114 ImmutablePatternModifier::ImmutablePatternModifier(ParameterizedModifier *pm, const PluralRules *rules,
    115                                                    const MicroPropsGenerator *parent)
    116         : pm(pm), rules(rules), parent(parent) {}
    117 
    118 void ImmutablePatternModifier::processQuantity(DecimalQuantity &quantity, MicroProps &micros,
    119                                                UErrorCode &status) const {
    120     parent->processQuantity(quantity, micros, status);
    121     applyToMicros(micros, quantity);
    122 }
    123 
    124 void ImmutablePatternModifier::applyToMicros(MicroProps &micros, DecimalQuantity &quantity) const {
    125     if (rules == nullptr) {
    126         micros.modMiddle = pm->getModifier(quantity.isNegative());
    127     } else {
    128         // TODO: Fix this. Avoid the copy.
    129         DecimalQuantity copy(quantity);
    130         copy.roundToInfinity();
    131         StandardPlural::Form plural = copy.getStandardPlural(rules);
    132         micros.modMiddle = pm->getModifier(quantity.isNegative(), plural);
    133     }
    134 }
    135 
    136 /** Used by the unsafe code path. */
    137 MicroPropsGenerator &MutablePatternModifier::addToChain(const MicroPropsGenerator *parent) {
    138     this->parent = parent;
    139     return *this;
    140 }
    141 
    142 void MutablePatternModifier::processQuantity(DecimalQuantity &fq, MicroProps &micros,
    143                                              UErrorCode &status) const {
    144     parent->processQuantity(fq, micros, status);
    145     // The unsafe code path performs self-mutation, so we need a const_cast.
    146     // This method needs to be const because it overrides a const method in the parent class.
    147     auto nonConstThis = const_cast<MutablePatternModifier *>(this);
    148     if (needsPlurals()) {
    149         // TODO: Fix this. Avoid the copy.
    150         DecimalQuantity copy(fq);
    151         micros.rounding.apply(copy, status);
    152         nonConstThis->setNumberProperties(fq.isNegative(), copy.getStandardPlural(rules));
    153     } else {
    154         nonConstThis->setNumberProperties(fq.isNegative(), StandardPlural::Form::COUNT);
    155     }
    156     micros.modMiddle = this;
    157 }
    158 
    159 int32_t MutablePatternModifier::apply(NumberStringBuilder &output, int32_t leftIndex, int32_t rightIndex,
    160                                       UErrorCode &status) const {
    161     // The unsafe code path performs self-mutation, so we need a const_cast.
    162     // This method needs to be const because it overrides a const method in the parent class.
    163     auto nonConstThis = const_cast<MutablePatternModifier *>(this);
    164     int32_t prefixLen = nonConstThis->insertPrefix(output, leftIndex, status);
    165     int32_t suffixLen = nonConstThis->insertSuffix(output, rightIndex + prefixLen, status);
    166     CurrencySpacingEnabledModifier::applyCurrencySpacing(
    167             output, leftIndex, prefixLen, rightIndex + prefixLen, suffixLen, *symbols, status);
    168     return prefixLen + suffixLen;
    169 }
    170 
    171 int32_t MutablePatternModifier::getPrefixLength(UErrorCode &status) const {
    172     // The unsafe code path performs self-mutation, so we need a const_cast.
    173     // This method needs to be const because it overrides a const method in the parent class.
    174     auto nonConstThis = const_cast<MutablePatternModifier *>(this);
    175 
    176     // Enter and exit CharSequence Mode to get the length.
    177     nonConstThis->enterCharSequenceMode(true);
    178     int result = AffixUtils::unescapedCodePointCount(*this, *this, status);  // prefix length
    179     nonConstThis->exitCharSequenceMode();
    180     return result;
    181 }
    182 
    183 int32_t MutablePatternModifier::getCodePointCount(UErrorCode &status) const {
    184     // The unsafe code path performs self-mutation, so we need a const_cast.
    185     // This method needs to be const because it overrides a const method in the parent class.
    186     auto nonConstThis = const_cast<MutablePatternModifier *>(this);
    187 
    188     // Enter and exit CharSequence Mode to get the length.
    189     nonConstThis->enterCharSequenceMode(true);
    190     int result = AffixUtils::unescapedCodePointCount(*this, *this, status);  // prefix length
    191     nonConstThis->exitCharSequenceMode();
    192     nonConstThis->enterCharSequenceMode(false);
    193     result += AffixUtils::unescapedCodePointCount(*this, *this, status);  // suffix length
    194     nonConstThis->exitCharSequenceMode();
    195     return result;
    196 }
    197 
    198 bool MutablePatternModifier::isStrong() const {
    199     return fStrong;
    200 }
    201 
    202 int32_t MutablePatternModifier::insertPrefix(NumberStringBuilder &sb, int position, UErrorCode &status) {
    203     enterCharSequenceMode(true);
    204     int length = AffixUtils::unescape(*this, sb, position, *this, status);
    205     exitCharSequenceMode();
    206     return length;
    207 }
    208 
    209 int32_t MutablePatternModifier::insertSuffix(NumberStringBuilder &sb, int position, UErrorCode &status) {
    210     enterCharSequenceMode(false);
    211     int length = AffixUtils::unescape(*this, sb, position, *this, status);
    212     exitCharSequenceMode();
    213     return length;
    214 }
    215 
    216 UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const {
    217     switch (type) {
    218         case AffixPatternType::TYPE_MINUS_SIGN:
    219             return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol);
    220         case AffixPatternType::TYPE_PLUS_SIGN:
    221             return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol);
    222         case AffixPatternType::TYPE_PERCENT:
    223             return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPercentSymbol);
    224         case AffixPatternType::TYPE_PERMILLE:
    225             return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPerMillSymbol);
    226         case AffixPatternType::TYPE_CURRENCY_SINGLE: {
    227             // UnitWidth ISO and HIDDEN overrides the singular currency symbol.
    228             if (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE) {
    229                 return UnicodeString(currencyCode, 3);
    230             } else if (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_HIDDEN) {
    231                 return UnicodeString();
    232             } else {
    233                 UErrorCode status = U_ZERO_ERROR;
    234                 UBool isChoiceFormat = FALSE;
    235                 int32_t symbolLen = 0;
    236                 const char16_t *symbol = ucurr_getName(
    237                         currencyCode,
    238                         symbols->getLocale().getName(),
    239                         UCurrNameStyle::UCURR_SYMBOL_NAME,
    240                         &isChoiceFormat,
    241                         &symbolLen,
    242                         &status);
    243                 return UnicodeString(symbol, symbolLen);
    244             }
    245         }
    246         case AffixPatternType::TYPE_CURRENCY_DOUBLE:
    247             return UnicodeString(currencyCode, 3);
    248         case AffixPatternType::TYPE_CURRENCY_TRIPLE: {
    249             // NOTE: This is the code path only for patterns containing "".
    250             // Plural currencies set via the API are formatted in LongNameHandler.
    251             // This code path is used by DecimalFormat via CurrencyPluralInfo.
    252             U_ASSERT(plural != StandardPlural::Form::COUNT);
    253             UErrorCode status = U_ZERO_ERROR;
    254             UBool isChoiceFormat = FALSE;
    255             int32_t symbolLen = 0;
    256             const char16_t *symbol = ucurr_getPluralName(
    257                     currencyCode,
    258                     symbols->getLocale().getName(),
    259                     &isChoiceFormat,
    260                     StandardPlural::getKeyword(plural),
    261                     &symbolLen,
    262                     &status);
    263             return UnicodeString(symbol, symbolLen);
    264         }
    265         case AffixPatternType::TYPE_CURRENCY_QUAD:
    266             return UnicodeString(u"\uFFFD");
    267         case AffixPatternType::TYPE_CURRENCY_QUINT:
    268             return UnicodeString(u"\uFFFD");
    269         default:
    270             U_ASSERT(false);
    271             return UnicodeString();
    272     }
    273 }
    274 
    275 /** This method contains the heart of the logic for rendering LDML affix strings. */
    276 void MutablePatternModifier::enterCharSequenceMode(bool isPrefix) {
    277     U_ASSERT(!inCharSequenceMode);
    278     inCharSequenceMode = true;
    279 
    280     // Should the output render '+' where '-' would normally appear in the pattern?
    281     plusReplacesMinusSign = !isNegative && (
    282             signDisplay == UNUM_SIGN_ALWAYS ||
    283             signDisplay == UNUM_SIGN_ACCOUNTING_ALWAYS) &&
    284                             patternInfo->positiveHasPlusSign() == false;
    285 
    286     // Should we use the affix from the negative subpattern? (If not, we will use the positive subpattern.)
    287     bool useNegativeAffixPattern = patternInfo->hasNegativeSubpattern() && (
    288             isNegative || (patternInfo->negativeHasMinusSign() && plusReplacesMinusSign));
    289 
    290     // Resolve the flags for the affix pattern.
    291     fFlags = 0;
    292     if (useNegativeAffixPattern) {
    293         fFlags |= AffixPatternProvider::AFFIX_NEGATIVE_SUBPATTERN;
    294     }
    295     if (isPrefix) {
    296         fFlags |= AffixPatternProvider::AFFIX_PREFIX;
    297     }
    298     if (plural != StandardPlural::Form::COUNT) {
    299         U_ASSERT(plural == (AffixPatternProvider::AFFIX_PLURAL_MASK & plural));
    300         fFlags |= plural;
    301     }
    302 
    303     // Should we prepend a sign to the pattern?
    304     if (!isPrefix || useNegativeAffixPattern) {
    305         prependSign = false;
    306     } else if (isNegative) {
    307         prependSign = signDisplay != UNUM_SIGN_NEVER;
    308     } else {
    309         prependSign = plusReplacesMinusSign;
    310     }
    311 
    312     // Finally, compute the length of the affix pattern.
    313     fLength = patternInfo->length(fFlags) + (prependSign ? 1 : 0);
    314 }
    315 
    316 void MutablePatternModifier::exitCharSequenceMode() {
    317     U_ASSERT(inCharSequenceMode);
    318     inCharSequenceMode = false;
    319 }
    320 
    321 int32_t MutablePatternModifier::length() const {
    322     U_ASSERT(inCharSequenceMode);
    323     return fLength;
    324 }
    325 
    326 char16_t MutablePatternModifier::charAt(int32_t index) const {
    327     U_ASSERT(inCharSequenceMode);
    328     char16_t candidate;
    329     if (prependSign && index == 0) {
    330         candidate = u'-';
    331     } else if (prependSign) {
    332         candidate = patternInfo->charAt(fFlags, index - 1);
    333     } else {
    334         candidate = patternInfo->charAt(fFlags, index);
    335     }
    336     if (plusReplacesMinusSign && candidate == u'-') {
    337         return u'+';
    338     }
    339     if (perMilleReplacesPercent && candidate == u'%') {
    340         return u'';
    341     }
    342     return candidate;
    343 }
    344 
    345 UnicodeString MutablePatternModifier::toUnicodeString() const {
    346     // Never called by AffixUtils
    347     U_ASSERT(false);
    348     return UnicodeString();
    349 }
    350 
    351 #endif /* #if !UCONFIG_NO_FORMATTING */
    352