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