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 CurrencySymbols * currencySymbols,const UNumberUnitWidth unitWidth,const PluralRules * rules)36 void MutablePatternModifier::setSymbols(const DecimalFormatSymbols* symbols,
37                                         const CurrencySymbols* currencySymbols,
38                                         const UNumberUnitWidth unitWidth, const PluralRules* rules) {
39     U_ASSERT((rules != nullptr) == needsPlurals());
40     fSymbols = symbols;
41     fCurrencySymbols = currencySymbols;
42     fUnitWidth = unitWidth;
43     fRules = rules;
44 }
45 
setNumberProperties(Signum signum,StandardPlural::Form plural)46 void MutablePatternModifier::setNumberProperties(Signum signum, StandardPlural::Form plural) {
47     fSignum = signum;
48     fPlural = plural;
49 }
50 
needsPlurals() const51 bool MutablePatternModifier::needsPlurals() const {
52     UErrorCode statusLocal = U_ZERO_ERROR;
53     return fPatternInfo->containsSymbolType(AffixPatternType::TYPE_CURRENCY_TRIPLE, statusLocal);
54     // Silently ignore any error codes.
55 }
56 
createImmutable(UErrorCode & status)57 ImmutablePatternModifier* MutablePatternModifier::createImmutable(UErrorCode& status) {
58     return createImmutableAndChain(nullptr, status);
59 }
60 
61 ImmutablePatternModifier*
createImmutableAndChain(const MicroPropsGenerator * parent,UErrorCode & status)62 MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator* parent, UErrorCode& status) {
63 
64     // TODO: Move StandardPlural VALUES to standardplural.h
65     static const StandardPlural::Form STANDARD_PLURAL_VALUES[] = {
66             StandardPlural::Form::ZERO,
67             StandardPlural::Form::ONE,
68             StandardPlural::Form::TWO,
69             StandardPlural::Form::FEW,
70             StandardPlural::Form::MANY,
71             StandardPlural::Form::OTHER};
72 
73     auto pm = new AdoptingModifierStore();
74     if (pm == nullptr) {
75         status = U_MEMORY_ALLOCATION_ERROR;
76         return nullptr;
77     }
78 
79     if (needsPlurals()) {
80         // Slower path when we require the plural keyword.
81         for (StandardPlural::Form plural : STANDARD_PLURAL_VALUES) {
82             setNumberProperties(SIGNUM_POS, plural);
83             pm->adoptModifier(SIGNUM_POS, plural, createConstantModifier(status));
84             setNumberProperties(SIGNUM_ZERO, plural);
85             pm->adoptModifier(SIGNUM_ZERO, plural, createConstantModifier(status));
86             setNumberProperties(SIGNUM_NEG, plural);
87             pm->adoptModifier(SIGNUM_NEG, plural, createConstantModifier(status));
88         }
89         if (U_FAILURE(status)) {
90             delete pm;
91             return nullptr;
92         }
93         return new ImmutablePatternModifier(pm, fRules, parent);  // adopts pm
94     } else {
95         // Faster path when plural keyword is not needed.
96         setNumberProperties(SIGNUM_POS, StandardPlural::Form::COUNT);
97         pm->adoptModifierWithoutPlural(SIGNUM_POS, createConstantModifier(status));
98         setNumberProperties(SIGNUM_ZERO, StandardPlural::Form::COUNT);
99         pm->adoptModifierWithoutPlural(SIGNUM_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, parent);  // 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,const MicroPropsGenerator * parent)123 ImmutablePatternModifier::ImmutablePatternModifier(AdoptingModifierStore* pm, const PluralRules* rules,
124                                                    const MicroPropsGenerator* parent)
125         : pm(pm), rules(rules), parent(parent) {}
126 
processQuantity(DecimalQuantity & quantity,MicroProps & micros,UErrorCode & status) const127 void ImmutablePatternModifier::processQuantity(DecimalQuantity& quantity, MicroProps& micros,
128                                                UErrorCode& status) const {
129     parent->processQuantity(quantity, micros, status);
130     applyToMicros(micros, quantity, status);
131 }
132 
applyToMicros(MicroProps & micros,const DecimalQuantity & quantity,UErrorCode & status) const133 void ImmutablePatternModifier::applyToMicros(
134         MicroProps& micros, const DecimalQuantity& quantity, UErrorCode& status) const {
135     if (rules == nullptr) {
136         micros.modMiddle = pm->getModifierWithoutPlural(quantity.signum());
137     } else {
138         StandardPlural::Form pluralForm = utils::getPluralSafe(micros.rounder, rules, quantity, status);
139         micros.modMiddle = pm->getModifier(quantity.signum(), pluralForm);
140     }
141 }
142 
getModifier(Signum signum,StandardPlural::Form plural) const143 const Modifier* ImmutablePatternModifier::getModifier(Signum signum, StandardPlural::Form plural) const {
144     if (rules == nullptr) {
145         return pm->getModifierWithoutPlural(signum);
146     } else {
147         return pm->getModifier(signum, plural);
148     }
149 }
150 
151 
152 /** Used by the unsafe code path. */
addToChain(const MicroPropsGenerator * parent)153 MicroPropsGenerator& MutablePatternModifier::addToChain(const MicroPropsGenerator* parent) {
154     fParent = parent;
155     return *this;
156 }
157 
processQuantity(DecimalQuantity & fq,MicroProps & micros,UErrorCode & status) const158 void MutablePatternModifier::processQuantity(DecimalQuantity& fq, MicroProps& micros,
159                                              UErrorCode& status) const {
160     fParent->processQuantity(fq, micros, status);
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     if (needsPlurals()) {
165         StandardPlural::Form pluralForm = utils::getPluralSafe(micros.rounder, fRules, fq, status);
166         nonConstThis->setNumberProperties(fq.signum(), pluralForm);
167     } else {
168         nonConstThis->setNumberProperties(fq.signum(), StandardPlural::Form::COUNT);
169     }
170     micros.modMiddle = this;
171 }
172 
apply(FormattedStringBuilder & output,int32_t leftIndex,int32_t rightIndex,UErrorCode & status) const173 int32_t MutablePatternModifier::apply(FormattedStringBuilder& output, int32_t leftIndex, int32_t rightIndex,
174                                       UErrorCode& status) const {
175     // The unsafe code path performs self-mutation, so we need a const_cast.
176     // This method needs to be const because it overrides a const method in the parent class.
177     auto nonConstThis = const_cast<MutablePatternModifier*>(this);
178     int32_t prefixLen = nonConstThis->insertPrefix(output, leftIndex, status);
179     int32_t suffixLen = nonConstThis->insertSuffix(output, rightIndex + prefixLen, status);
180     // If the pattern had no decimal stem body (like #,##0.00), overwrite the value.
181     int32_t overwriteLen = 0;
182     if (!fPatternInfo->hasBody()) {
183         overwriteLen = output.splice(
184                 leftIndex + prefixLen,
185                 rightIndex + prefixLen,
186                 UnicodeString(),
187                 0,
188                 0,
189                 UNUM_FIELD_COUNT,
190                 status);
191     }
192     CurrencySpacingEnabledModifier::applyCurrencySpacing(
193             output,
194             leftIndex,
195             prefixLen,
196             rightIndex + overwriteLen + prefixLen,
197             suffixLen,
198             *fSymbols,
199             status);
200     return prefixLen + overwriteLen + suffixLen;
201 }
202 
getPrefixLength() const203 int32_t MutablePatternModifier::getPrefixLength() const {
204     // The unsafe code path performs self-mutation, so we need a const_cast.
205     // This method needs to be const because it overrides a const method in the parent class.
206     auto nonConstThis = const_cast<MutablePatternModifier*>(this);
207 
208     // Enter and exit CharSequence Mode to get the length.
209     UErrorCode status = U_ZERO_ERROR; // status fails only with an iilegal argument exception
210     nonConstThis->prepareAffix(true);
211     int result = AffixUtils::unescapedCodePointCount(currentAffix, *this, status);  // prefix length
212     return result;
213 }
214 
getCodePointCount() const215 int32_t MutablePatternModifier::getCodePointCount() const {
216     // The unsafe code path performs self-mutation, so we need a const_cast.
217     // This method needs to be const because it overrides a const method in the parent class.
218     auto nonConstThis = const_cast<MutablePatternModifier*>(this);
219 
220     // Render the affixes to get the length
221     UErrorCode status = U_ZERO_ERROR; // status fails only with an iilegal argument exception
222     nonConstThis->prepareAffix(true);
223     int result = AffixUtils::unescapedCodePointCount(currentAffix, *this, status);  // prefix length
224     nonConstThis->prepareAffix(false);
225     result += AffixUtils::unescapedCodePointCount(currentAffix, *this, status);  // suffix length
226     return result;
227 }
228 
isStrong() const229 bool MutablePatternModifier::isStrong() const {
230     return fStrong;
231 }
232 
containsField(UNumberFormatFields field) const233 bool MutablePatternModifier::containsField(UNumberFormatFields field) const {
234     (void)field;
235     // This method is not currently used.
236     UPRV_UNREACHABLE;
237 }
238 
getParameters(Parameters & output) const239 void MutablePatternModifier::getParameters(Parameters& output) const {
240     (void)output;
241     // This method is not currently used.
242     UPRV_UNREACHABLE;
243 }
244 
semanticallyEquivalent(const Modifier & other) const245 bool MutablePatternModifier::semanticallyEquivalent(const Modifier& other) const {
246     (void)other;
247     // This method is not currently used.
248     UPRV_UNREACHABLE;
249 }
250 
insertPrefix(FormattedStringBuilder & sb,int position,UErrorCode & status)251 int32_t MutablePatternModifier::insertPrefix(FormattedStringBuilder& sb, int position, UErrorCode& status) {
252     prepareAffix(true);
253     int32_t length = AffixUtils::unescape(currentAffix, sb, position, *this, fField, status);
254     return length;
255 }
256 
insertSuffix(FormattedStringBuilder & sb,int position,UErrorCode & status)257 int32_t MutablePatternModifier::insertSuffix(FormattedStringBuilder& sb, int position, UErrorCode& status) {
258     prepareAffix(false);
259     int32_t length = AffixUtils::unescape(currentAffix, sb, position, *this, fField, status);
260     return length;
261 }
262 
263 /** This method contains the heart of the logic for rendering LDML affix strings. */
prepareAffix(bool isPrefix)264 void MutablePatternModifier::prepareAffix(bool isPrefix) {
265     PatternStringUtils::patternInfoToStringBuilder(
266             *fPatternInfo, isPrefix, fSignum, fSignDisplay, fPlural, fPerMilleReplacesPercent, currentAffix);
267 }
268 
getSymbol(AffixPatternType type) const269 UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const {
270     UErrorCode localStatus = U_ZERO_ERROR;
271     switch (type) {
272         case AffixPatternType::TYPE_MINUS_SIGN:
273             return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol);
274         case AffixPatternType::TYPE_PLUS_SIGN:
275             return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol);
276         case AffixPatternType::TYPE_PERCENT:
277             return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPercentSymbol);
278         case AffixPatternType::TYPE_PERMILLE:
279             return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPerMillSymbol);
280         case AffixPatternType::TYPE_CURRENCY_SINGLE: {
281             // UnitWidth ISO and HIDDEN overrides the singular currency symbol.
282             if (fUnitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE) {
283                 return fCurrencySymbols->getIntlCurrencySymbol(localStatus);
284             } else if (fUnitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_HIDDEN) {
285                 return UnicodeString();
286             } else if (fUnitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW) {
287                 return fCurrencySymbols->getNarrowCurrencySymbol(localStatus);
288             } else {
289                 return fCurrencySymbols->getCurrencySymbol(localStatus);
290             }
291         }
292         case AffixPatternType::TYPE_CURRENCY_DOUBLE:
293             return fCurrencySymbols->getIntlCurrencySymbol(localStatus);
294         case AffixPatternType::TYPE_CURRENCY_TRIPLE:
295             // NOTE: This is the code path only for patterns containing "¤¤¤".
296             // Plural currencies set via the API are formatted in LongNameHandler.
297             // This code path is used by DecimalFormat via CurrencyPluralInfo.
298             U_ASSERT(fPlural != StandardPlural::Form::COUNT);
299             return fCurrencySymbols->getPluralName(fPlural, localStatus);
300         case AffixPatternType::TYPE_CURRENCY_QUAD:
301             return UnicodeString(u"\uFFFD");
302         case AffixPatternType::TYPE_CURRENCY_QUINT:
303             return UnicodeString(u"\uFFFD");
304         default:
305             UPRV_UNREACHABLE;
306     }
307 }
308 
toUnicodeString() const309 UnicodeString MutablePatternModifier::toUnicodeString() const {
310     // Never called by AffixUtils
311     UPRV_UNREACHABLE;
312 }
313 
314 #endif /* #if !UCONFIG_NO_FORMATTING */
315