tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

number_patternmodifier.cpp (13831B)


      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
      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 #include "number_microprops.h"
     14 
     15 using namespace icu;
     16 using namespace icu::number;
     17 using namespace icu::number::impl;
     18 
     19 
     20 AffixPatternProvider::~AffixPatternProvider() = default;
     21 
     22 
     23 MutablePatternModifier::MutablePatternModifier(bool isStrong)
     24        : fStrong(isStrong) {}
     25 
     26 void MutablePatternModifier::setPatternInfo(const AffixPatternProvider* patternInfo, Field field) {
     27    fPatternInfo = patternInfo;
     28    fField = field;
     29 }
     30 
     31 void MutablePatternModifier::setPatternAttributes(
     32        UNumberSignDisplay signDisplay,
     33        bool perMille,
     34        bool approximately) {
     35    fSignDisplay = signDisplay;
     36    fPerMilleReplacesPercent = perMille;
     37    fApproximately = approximately;
     38 }
     39 
     40 void MutablePatternModifier::setSymbols(const DecimalFormatSymbols* symbols,
     41                                        const CurrencyUnit& currency,
     42                                        const UNumberUnitWidth unitWidth,
     43                                        const PluralRules* rules,
     44                                        UErrorCode& status) {
     45    U_ASSERT((rules != nullptr) == needsPlurals());
     46    fSymbols = symbols;
     47    fCurrencySymbols = {currency, symbols->getLocale(), *symbols, status};
     48    fUnitWidth = unitWidth;
     49    fRules = rules;
     50 }
     51 
     52 void MutablePatternModifier::setNumberProperties(Signum signum, StandardPlural::Form plural) {
     53    fSignum = signum;
     54    fPlural = plural;
     55 }
     56 
     57 bool MutablePatternModifier::needsPlurals() const {
     58    UErrorCode statusLocal = U_ZERO_ERROR;
     59    return fPatternInfo->containsSymbolType(AffixPatternType::TYPE_CURRENCY_TRIPLE, statusLocal);
     60    // Silently ignore any error codes.
     61 }
     62 
     63 AdoptingSignumModifierStore MutablePatternModifier::createImmutableForPlural(StandardPlural::Form plural, UErrorCode& status) {
     64    AdoptingSignumModifierStore pm;
     65 
     66    setNumberProperties(SIGNUM_POS, plural);
     67    pm.adoptModifier(SIGNUM_POS, createConstantModifier(status));
     68    setNumberProperties(SIGNUM_NEG_ZERO, plural);
     69    pm.adoptModifier(SIGNUM_NEG_ZERO, createConstantModifier(status));
     70    setNumberProperties(SIGNUM_POS_ZERO, plural);
     71    pm.adoptModifier(SIGNUM_POS_ZERO, createConstantModifier(status));
     72    setNumberProperties(SIGNUM_NEG, plural);
     73    pm.adoptModifier(SIGNUM_NEG, createConstantModifier(status));
     74 
     75    return pm;
     76 }
     77 
     78 ImmutablePatternModifier* MutablePatternModifier::createImmutable(UErrorCode& status) {
     79    // TODO: Move StandardPlural VALUES to standardplural.h
     80    static const StandardPlural::Form STANDARD_PLURAL_VALUES[] = {
     81            StandardPlural::Form::ZERO,
     82            StandardPlural::Form::ONE,
     83            StandardPlural::Form::TWO,
     84            StandardPlural::Form::FEW,
     85            StandardPlural::Form::MANY,
     86            StandardPlural::Form::OTHER};
     87 
     88    auto* pm = new AdoptingModifierStore();
     89    if (pm == nullptr) {
     90        status = U_MEMORY_ALLOCATION_ERROR;
     91        return nullptr;
     92    }
     93 
     94    if (needsPlurals()) {
     95        // Slower path when we require the plural keyword.
     96        for (StandardPlural::Form plural : STANDARD_PLURAL_VALUES) {
     97            pm->adoptSignumModifierStore(plural, createImmutableForPlural(plural, status));
     98        }
     99        if (U_FAILURE(status)) {
    100            delete pm;
    101            return nullptr;
    102        }
    103        return new ImmutablePatternModifier(pm, fRules);  // adopts pm
    104    } else {
    105        // Faster path when plural keyword is not needed.
    106        pm->adoptSignumModifierStoreNoPlural(createImmutableForPlural(StandardPlural::Form::COUNT, status));
    107        if (U_FAILURE(status)) {
    108            delete pm;
    109            return nullptr;
    110        }
    111        return new ImmutablePatternModifier(pm, nullptr);  // adopts pm
    112    }
    113 }
    114 
    115 ConstantMultiFieldModifier* MutablePatternModifier::createConstantModifier(UErrorCode& status) {
    116    FormattedStringBuilder a;
    117    FormattedStringBuilder b;
    118    insertPrefix(a, 0, status);
    119    insertSuffix(b, 0, status);
    120    if (fPatternInfo->hasCurrencySign()) {
    121        return new CurrencySpacingEnabledModifier(
    122                a, b, !fPatternInfo->hasBody(), fStrong, *fSymbols, status);
    123    } else {
    124        return new ConstantMultiFieldModifier(a, b, !fPatternInfo->hasBody(), fStrong);
    125    }
    126 }
    127 
    128 ImmutablePatternModifier::ImmutablePatternModifier(AdoptingModifierStore* pm, const PluralRules* rules)
    129        : pm(pm), rules(rules), parent(nullptr) {}
    130 
    131 void ImmutablePatternModifier::processQuantity(DecimalQuantity& quantity, MicroProps& micros,
    132                                               UErrorCode& status) const {
    133    parent->processQuantity(quantity, micros, status);
    134    micros.rounder.apply(quantity, status);
    135    if (micros.modMiddle != nullptr) {
    136        return;
    137    }
    138    applyToMicros(micros, quantity, status);
    139 }
    140 
    141 void ImmutablePatternModifier::applyToMicros(
    142        MicroProps& micros, const DecimalQuantity& quantity, UErrorCode& status) const {
    143    if (rules == nullptr) {
    144        micros.modMiddle = pm->getModifierWithoutPlural(quantity.signum());
    145    } else {
    146        StandardPlural::Form pluralForm = utils::getPluralSafe(micros.rounder, rules, quantity, status);
    147        micros.modMiddle = pm->getModifier(quantity.signum(), pluralForm);
    148    }
    149 }
    150 
    151 const Modifier* ImmutablePatternModifier::getModifier(Signum signum, StandardPlural::Form plural) const {
    152    if (rules == nullptr) {
    153        return pm->getModifierWithoutPlural(signum);
    154    } else {
    155        return pm->getModifier(signum, plural);
    156    }
    157 }
    158 
    159 void ImmutablePatternModifier::addToChain(const MicroPropsGenerator* parent) {
    160    this->parent = parent;
    161 }
    162 
    163 
    164 /** Used by the unsafe code path. */
    165 MicroPropsGenerator& MutablePatternModifier::addToChain(const MicroPropsGenerator* parent) {
    166    fParent = parent;
    167    return *this;
    168 }
    169 
    170 void MutablePatternModifier::processQuantity(DecimalQuantity& fq, MicroProps& micros,
    171                                             UErrorCode& status) const {
    172    fParent->processQuantity(fq, micros, status);
    173    micros.rounder.apply(fq, status);
    174    if (micros.modMiddle != nullptr) {
    175        return;
    176    }
    177    // The unsafe code path performs self-mutation, so we need a const_cast.
    178    // This method needs to be const because it overrides a const method in the parent class.
    179    auto* nonConstThis = const_cast<MutablePatternModifier*>(this);
    180    if (needsPlurals()) {
    181        StandardPlural::Form pluralForm = utils::getPluralSafe(micros.rounder, fRules, fq, status);
    182        nonConstThis->setNumberProperties(fq.signum(), pluralForm);
    183    } else {
    184        nonConstThis->setNumberProperties(fq.signum(), StandardPlural::Form::COUNT);
    185    }
    186    micros.modMiddle = this;
    187 }
    188 
    189 int32_t MutablePatternModifier::apply(FormattedStringBuilder& output, int32_t leftIndex, int32_t rightIndex,
    190                                      UErrorCode& status) const {
    191    // The unsafe code path performs self-mutation, so we need a const_cast.
    192    // This method needs to be const because it overrides a const method in the parent class.
    193    auto* nonConstThis = const_cast<MutablePatternModifier*>(this);
    194    int32_t prefixLen = nonConstThis->insertPrefix(output, leftIndex, status);
    195    int32_t suffixLen = nonConstThis->insertSuffix(output, rightIndex + prefixLen, status);
    196    // If the pattern had no decimal stem body (like #,##0.00), overwrite the value.
    197    int32_t overwriteLen = 0;
    198    if (!fPatternInfo->hasBody()) {
    199        overwriteLen = output.splice(
    200                leftIndex + prefixLen,
    201                rightIndex + prefixLen,
    202                UnicodeString(),
    203                0,
    204                0,
    205                kUndefinedField,
    206                status);
    207    }
    208    CurrencySpacingEnabledModifier::applyCurrencySpacing(
    209            output,
    210            leftIndex,
    211            prefixLen,
    212            rightIndex + overwriteLen + prefixLen,
    213            suffixLen,
    214            *fSymbols,
    215            status);
    216    return prefixLen + overwriteLen + suffixLen;
    217 }
    218 
    219 int32_t MutablePatternModifier::getPrefixLength() const {
    220    // The unsafe code path performs self-mutation, so we need a const_cast.
    221    // This method needs to be const because it overrides a const method in the parent class.
    222    auto* nonConstThis = const_cast<MutablePatternModifier*>(this);
    223 
    224    // Enter and exit CharSequence Mode to get the length.
    225    UErrorCode status = U_ZERO_ERROR; // status fails only with an iilegal argument exception
    226    nonConstThis->prepareAffix(true);
    227    int result = AffixUtils::unescapedCodePointCount(currentAffix, *this, status);  // prefix length
    228    return result;
    229 }
    230 
    231 int32_t MutablePatternModifier::getCodePointCount() const {
    232    // The unsafe code path performs self-mutation, so we need a const_cast.
    233    // This method needs to be const because it overrides a const method in the parent class.
    234    auto* nonConstThis = const_cast<MutablePatternModifier*>(this);
    235 
    236    // Render the affixes to get the length
    237    UErrorCode status = U_ZERO_ERROR; // status fails only with an iilegal argument exception
    238    nonConstThis->prepareAffix(true);
    239    int result = AffixUtils::unescapedCodePointCount(currentAffix, *this, status);  // prefix length
    240    nonConstThis->prepareAffix(false);
    241    result += AffixUtils::unescapedCodePointCount(currentAffix, *this, status);  // suffix length
    242    return result;
    243 }
    244 
    245 bool MutablePatternModifier::isStrong() const {
    246    return fStrong;
    247 }
    248 
    249 bool MutablePatternModifier::containsField(Field field) const {
    250    (void)field;
    251    // This method is not currently used.
    252    UPRV_UNREACHABLE_EXIT;
    253 }
    254 
    255 void MutablePatternModifier::getParameters(Parameters& output) const {
    256    (void)output;
    257    // This method is not currently used.
    258    UPRV_UNREACHABLE_EXIT;
    259 }
    260 
    261 bool MutablePatternModifier::strictEquals(const Modifier& other) const {
    262    (void)other;
    263    // This method is not currently used.
    264    UPRV_UNREACHABLE_EXIT;
    265 }
    266 
    267 int32_t MutablePatternModifier::insertPrefix(FormattedStringBuilder& sb, int position, UErrorCode& status) {
    268    prepareAffix(true);
    269    int32_t length = AffixUtils::unescape(currentAffix, sb, position, *this, fField, status);
    270    return length;
    271 }
    272 
    273 int32_t MutablePatternModifier::insertSuffix(FormattedStringBuilder& sb, int position, UErrorCode& status) {
    274    prepareAffix(false);
    275    int32_t length = AffixUtils::unescape(currentAffix, sb, position, *this, fField, status);
    276    return length;
    277 }
    278 
    279 /** This method contains the heart of the logic for rendering LDML affix strings. */
    280 void MutablePatternModifier::prepareAffix(bool isPrefix) {
    281    PatternStringUtils::patternInfoToStringBuilder(
    282            *fPatternInfo,
    283            isPrefix,
    284            PatternStringUtils::resolveSignDisplay(fSignDisplay, fSignum),
    285            fApproximately,
    286            fPlural,
    287            fPerMilleReplacesPercent,
    288            false, // dropCurrencySymbols
    289            currentAffix);
    290 }
    291 
    292 UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const {
    293    UErrorCode localStatus = U_ZERO_ERROR;
    294    switch (type) {
    295        case AffixPatternType::TYPE_MINUS_SIGN:
    296            return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol);
    297        case AffixPatternType::TYPE_PLUS_SIGN:
    298            return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol);
    299        case AffixPatternType::TYPE_APPROXIMATELY_SIGN:
    300            return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kApproximatelySignSymbol);
    301        case AffixPatternType::TYPE_PERCENT:
    302            return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPercentSymbol);
    303        case AffixPatternType::TYPE_PERMILLE:
    304            return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPerMillSymbol);
    305        case AffixPatternType::TYPE_CURRENCY_SINGLE:
    306            return getCurrencySymbolForUnitWidth(localStatus);
    307        case AffixPatternType::TYPE_CURRENCY_DOUBLE:
    308            return fCurrencySymbols.getIntlCurrencySymbol(localStatus);
    309        case AffixPatternType::TYPE_CURRENCY_TRIPLE:
    310            // NOTE: This is the code path only for patterns containing "¤¤¤".
    311            // Plural currencies set via the API are formatted in LongNameHandler.
    312            // This code path is used by DecimalFormat via CurrencyPluralInfo.
    313            U_ASSERT(fPlural != StandardPlural::Form::COUNT);
    314            return fCurrencySymbols.getPluralName(fPlural, localStatus);
    315        case AffixPatternType::TYPE_CURRENCY_QUAD:
    316            return UnicodeString(u"\uFFFD");
    317        case AffixPatternType::TYPE_CURRENCY_QUINT:
    318            return UnicodeString(u"\uFFFD");
    319        default:
    320            UPRV_UNREACHABLE_EXIT;
    321    }
    322 }
    323 
    324 UnicodeString MutablePatternModifier::getCurrencySymbolForUnitWidth(UErrorCode& status) const {
    325    switch (fUnitWidth) {
    326    case UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW:
    327        return fCurrencySymbols.getNarrowCurrencySymbol(status);
    328    case UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT:
    329        return fCurrencySymbols.getCurrencySymbol(status);
    330    case UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE:
    331        return fCurrencySymbols.getIntlCurrencySymbol(status);
    332    case UNumberUnitWidth::UNUM_UNIT_WIDTH_FORMAL:
    333        return fCurrencySymbols.getFormalCurrencySymbol(status);
    334    case UNumberUnitWidth::UNUM_UNIT_WIDTH_VARIANT:
    335        return fCurrencySymbols.getVariantCurrencySymbol(status);
    336    case UNumberUnitWidth::UNUM_UNIT_WIDTH_HIDDEN:
    337        return {};
    338    default:
    339        return fCurrencySymbols.getCurrencySymbol(status);
    340    }
    341 }
    342 
    343 UnicodeString MutablePatternModifier::toUnicodeString() const {
    344    // Never called by AffixUtils
    345    UPRV_UNREACHABLE_EXIT;
    346 }
    347 
    348 #endif /* #if !UCONFIG_NO_FORMATTING */