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 ¤cy, 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 µs, 119 UErrorCode &status) const { 120 parent->processQuantity(quantity, micros, status); 121 applyToMicros(micros, quantity); 122 } 123 124 void ImmutablePatternModifier::applyToMicros(MicroProps µs, 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 µs, 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