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
MutablePatternModifier(bool isStrong)23 MutablePatternModifier::MutablePatternModifier(bool isStrong)
24 : fStrong(isStrong) {}
25
setPatternInfo(const AffixPatternProvider * patternInfo,Field field)26 void MutablePatternModifier::setPatternInfo(const AffixPatternProvider* patternInfo, Field field) {
27 fPatternInfo = patternInfo;
28 fField = field;
29 }
30
setPatternAttributes(UNumberSignDisplay signDisplay,bool perMille,bool approximately)31 void MutablePatternModifier::setPatternAttributes(
32 UNumberSignDisplay signDisplay,
33 bool perMille,
34 bool approximately) {
35 fSignDisplay = signDisplay;
36 fPerMilleReplacesPercent = perMille;
37 fApproximately = approximately;
38 }
39
setSymbols(const DecimalFormatSymbols * symbols,const CurrencyUnit & currency,const UNumberUnitWidth unitWidth,const PluralRules * rules,UErrorCode & status)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
setNumberProperties(Signum signum,StandardPlural::Form plural)52 void MutablePatternModifier::setNumberProperties(Signum signum, StandardPlural::Form plural) {
53 fSignum = signum;
54 fPlural = plural;
55 }
56
needsPlurals() const57 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
createImmutableForPlural(StandardPlural::Form plural,UErrorCode & status)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
createImmutable(UErrorCode & status)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
createConstantModifier(UErrorCode & status)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
ImmutablePatternModifier(AdoptingModifierStore * pm,const PluralRules * rules)128 ImmutablePatternModifier::ImmutablePatternModifier(AdoptingModifierStore* pm, const PluralRules* rules)
129 : pm(pm), rules(rules), parent(nullptr) {}
130
processQuantity(DecimalQuantity & quantity,MicroProps & micros,UErrorCode & status) const131 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
applyToMicros(MicroProps & micros,const DecimalQuantity & quantity,UErrorCode & status) const141 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
getModifier(Signum signum,StandardPlural::Form plural) const151 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
addToChain(const MicroPropsGenerator * parent)159 void ImmutablePatternModifier::addToChain(const MicroPropsGenerator* parent) {
160 this->parent = parent;
161 }
162
163
164 /** Used by the unsafe code path. */
addToChain(const MicroPropsGenerator * parent)165 MicroPropsGenerator& MutablePatternModifier::addToChain(const MicroPropsGenerator* parent) {
166 fParent = parent;
167 return *this;
168 }
169
processQuantity(DecimalQuantity & fq,MicroProps & micros,UErrorCode & status) const170 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
apply(FormattedStringBuilder & output,int32_t leftIndex,int32_t rightIndex,UErrorCode & status) const189 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
getPrefixLength() const219 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
getCodePointCount() const231 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
isStrong() const245 bool MutablePatternModifier::isStrong() const {
246 return fStrong;
247 }
248
containsField(Field field) const249 bool MutablePatternModifier::containsField(Field field) const {
250 (void)field;
251 // This method is not currently used.
252 UPRV_UNREACHABLE_EXIT;
253 }
254
getParameters(Parameters & output) const255 void MutablePatternModifier::getParameters(Parameters& output) const {
256 (void)output;
257 // This method is not currently used.
258 UPRV_UNREACHABLE_EXIT;
259 }
260
semanticallyEquivalent(const Modifier & other) const261 bool MutablePatternModifier::semanticallyEquivalent(const Modifier& other) const {
262 (void)other;
263 // This method is not currently used.
264 UPRV_UNREACHABLE_EXIT;
265 }
266
insertPrefix(FormattedStringBuilder & sb,int position,UErrorCode & status)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
insertSuffix(FormattedStringBuilder & sb,int position,UErrorCode & status)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. */
prepareAffix(bool isPrefix)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
getSymbol(AffixPatternType type) const292 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
getCurrencySymbolForUnitWidth(UErrorCode & status) const324 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 UnicodeString();
338 default:
339 return fCurrencySymbols.getCurrencySymbol(status);
340 }
341 }
342
toUnicodeString() const343 UnicodeString MutablePatternModifier::toUnicodeString() const {
344 // Never called by AffixUtils
345 UPRV_UNREACHABLE_EXIT;
346 }
347
348 #endif /* #if !UCONFIG_NO_FORMATTING */
349