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