• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
createImmutable(UErrorCode & status)63 ImmutablePatternModifier* MutablePatternModifier::createImmutable(UErrorCode& status) {
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_NEG_ZERO, plural);
85             pm->adoptModifier(SIGNUM_NEG_ZERO, plural, createConstantModifier(status));
86             setNumberProperties(SIGNUM_POS_ZERO, plural);
87             pm->adoptModifier(SIGNUM_POS_ZERO, plural, createConstantModifier(status));
88             setNumberProperties(SIGNUM_NEG, plural);
89             pm->adoptModifier(SIGNUM_NEG, plural, createConstantModifier(status));
90         }
91         if (U_FAILURE(status)) {
92             delete pm;
93             return nullptr;
94         }
95         return new ImmutablePatternModifier(pm, fRules);  // adopts pm
96     } else {
97         // Faster path when plural keyword is not needed.
98         setNumberProperties(SIGNUM_POS, StandardPlural::Form::COUNT);
99         pm->adoptModifierWithoutPlural(SIGNUM_POS, createConstantModifier(status));
100         setNumberProperties(SIGNUM_NEG_ZERO, StandardPlural::Form::COUNT);
101         pm->adoptModifierWithoutPlural(SIGNUM_NEG_ZERO, createConstantModifier(status));
102         setNumberProperties(SIGNUM_POS_ZERO, StandardPlural::Form::COUNT);
103         pm->adoptModifierWithoutPlural(SIGNUM_POS_ZERO, createConstantModifier(status));
104         setNumberProperties(SIGNUM_NEG, StandardPlural::Form::COUNT);
105         pm->adoptModifierWithoutPlural(SIGNUM_NEG, createConstantModifier(status));
106         if (U_FAILURE(status)) {
107             delete pm;
108             return nullptr;
109         }
110         return new ImmutablePatternModifier(pm, nullptr);  // adopts pm
111     }
112 }
113 
createConstantModifier(UErrorCode & status)114 ConstantMultiFieldModifier* MutablePatternModifier::createConstantModifier(UErrorCode& status) {
115     FormattedStringBuilder a;
116     FormattedStringBuilder b;
117     insertPrefix(a, 0, status);
118     insertSuffix(b, 0, status);
119     if (fPatternInfo->hasCurrencySign()) {
120         return new CurrencySpacingEnabledModifier(
121                 a, b, !fPatternInfo->hasBody(), fStrong, *fSymbols, status);
122     } else {
123         return new ConstantMultiFieldModifier(a, b, !fPatternInfo->hasBody(), fStrong);
124     }
125 }
126 
ImmutablePatternModifier(AdoptingModifierStore * pm,const PluralRules * rules)127 ImmutablePatternModifier::ImmutablePatternModifier(AdoptingModifierStore* pm, const PluralRules* rules)
128         : pm(pm), rules(rules), parent(nullptr) {}
129 
processQuantity(DecimalQuantity & quantity,MicroProps & micros,UErrorCode & status) const130 void ImmutablePatternModifier::processQuantity(DecimalQuantity& quantity, MicroProps& micros,
131                                                UErrorCode& status) const {
132     parent->processQuantity(quantity, micros, status);
133     micros.rounder.apply(quantity, status);
134     if (micros.modMiddle != nullptr) {
135         return;
136     }
137     applyToMicros(micros, quantity, status);
138 }
139 
applyToMicros(MicroProps & micros,const DecimalQuantity & quantity,UErrorCode & status) const140 void ImmutablePatternModifier::applyToMicros(
141         MicroProps& micros, const DecimalQuantity& quantity, UErrorCode& status) const {
142     if (rules == nullptr) {
143         micros.modMiddle = pm->getModifierWithoutPlural(quantity.signum());
144     } else {
145         StandardPlural::Form pluralForm = utils::getPluralSafe(micros.rounder, rules, quantity, status);
146         micros.modMiddle = pm->getModifier(quantity.signum(), pluralForm);
147     }
148 }
149 
getModifier(Signum signum,StandardPlural::Form plural) const150 const Modifier* ImmutablePatternModifier::getModifier(Signum signum, StandardPlural::Form plural) const {
151     if (rules == nullptr) {
152         return pm->getModifierWithoutPlural(signum);
153     } else {
154         return pm->getModifier(signum, plural);
155     }
156 }
157 
addToChain(const MicroPropsGenerator * parent)158 void ImmutablePatternModifier::addToChain(const MicroPropsGenerator* parent) {
159     this->parent = parent;
160 }
161 
162 
163 /** Used by the unsafe code path. */
addToChain(const MicroPropsGenerator * parent)164 MicroPropsGenerator& MutablePatternModifier::addToChain(const MicroPropsGenerator* parent) {
165     fParent = parent;
166     return *this;
167 }
168 
processQuantity(DecimalQuantity & fq,MicroProps & micros,UErrorCode & status) const169 void MutablePatternModifier::processQuantity(DecimalQuantity& fq, MicroProps& micros,
170                                              UErrorCode& status) const {
171     fParent->processQuantity(fq, micros, status);
172     micros.rounder.apply(fq, status);
173     if (micros.modMiddle != nullptr) {
174         return;
175     }
176     // The unsafe code path performs self-mutation, so we need a const_cast.
177     // This method needs to be const because it overrides a const method in the parent class.
178     auto nonConstThis = const_cast<MutablePatternModifier*>(this);
179     if (needsPlurals()) {
180         StandardPlural::Form pluralForm = utils::getPluralSafe(micros.rounder, fRules, fq, status);
181         nonConstThis->setNumberProperties(fq.signum(), pluralForm);
182     } else {
183         nonConstThis->setNumberProperties(fq.signum(), StandardPlural::Form::COUNT);
184     }
185     micros.modMiddle = this;
186 }
187 
apply(FormattedStringBuilder & output,int32_t leftIndex,int32_t rightIndex,UErrorCode & status) const188 int32_t MutablePatternModifier::apply(FormattedStringBuilder& output, int32_t leftIndex, int32_t rightIndex,
189                                       UErrorCode& status) const {
190     // The unsafe code path performs self-mutation, so we need a const_cast.
191     // This method needs to be const because it overrides a const method in the parent class.
192     auto nonConstThis = const_cast<MutablePatternModifier*>(this);
193     int32_t prefixLen = nonConstThis->insertPrefix(output, leftIndex, status);
194     int32_t suffixLen = nonConstThis->insertSuffix(output, rightIndex + prefixLen, status);
195     // If the pattern had no decimal stem body (like #,##0.00), overwrite the value.
196     int32_t overwriteLen = 0;
197     if (!fPatternInfo->hasBody()) {
198         overwriteLen = output.splice(
199                 leftIndex + prefixLen,
200                 rightIndex + prefixLen,
201                 UnicodeString(),
202                 0,
203                 0,
204                 kUndefinedField,
205                 status);
206     }
207     CurrencySpacingEnabledModifier::applyCurrencySpacing(
208             output,
209             leftIndex,
210             prefixLen,
211             rightIndex + overwriteLen + prefixLen,
212             suffixLen,
213             *fSymbols,
214             status);
215     return prefixLen + overwriteLen + suffixLen;
216 }
217 
getPrefixLength() const218 int32_t MutablePatternModifier::getPrefixLength() const {
219     // The unsafe code path performs self-mutation, so we need a const_cast.
220     // This method needs to be const because it overrides a const method in the parent class.
221     auto nonConstThis = const_cast<MutablePatternModifier*>(this);
222 
223     // Enter and exit CharSequence Mode to get the length.
224     UErrorCode status = U_ZERO_ERROR; // status fails only with an iilegal argument exception
225     nonConstThis->prepareAffix(true);
226     int result = AffixUtils::unescapedCodePointCount(currentAffix, *this, status);  // prefix length
227     return result;
228 }
229 
getCodePointCount() const230 int32_t MutablePatternModifier::getCodePointCount() const {
231     // The unsafe code path performs self-mutation, so we need a const_cast.
232     // This method needs to be const because it overrides a const method in the parent class.
233     auto nonConstThis = const_cast<MutablePatternModifier*>(this);
234 
235     // Render the affixes to get the length
236     UErrorCode status = U_ZERO_ERROR; // status fails only with an iilegal argument exception
237     nonConstThis->prepareAffix(true);
238     int result = AffixUtils::unescapedCodePointCount(currentAffix, *this, status);  // prefix length
239     nonConstThis->prepareAffix(false);
240     result += AffixUtils::unescapedCodePointCount(currentAffix, *this, status);  // suffix length
241     return result;
242 }
243 
isStrong() const244 bool MutablePatternModifier::isStrong() const {
245     return fStrong;
246 }
247 
containsField(Field field) const248 bool MutablePatternModifier::containsField(Field field) const {
249     (void)field;
250     // This method is not currently used.
251     UPRV_UNREACHABLE_EXIT;
252 }
253 
getParameters(Parameters & output) const254 void MutablePatternModifier::getParameters(Parameters& output) const {
255     (void)output;
256     // This method is not currently used.
257     UPRV_UNREACHABLE_EXIT;
258 }
259 
semanticallyEquivalent(const Modifier & other) const260 bool MutablePatternModifier::semanticallyEquivalent(const Modifier& other) const {
261     (void)other;
262     // This method is not currently used.
263     UPRV_UNREACHABLE_EXIT;
264 }
265 
insertPrefix(FormattedStringBuilder & sb,int position,UErrorCode & status)266 int32_t MutablePatternModifier::insertPrefix(FormattedStringBuilder& sb, int position, UErrorCode& status) {
267     prepareAffix(true);
268     int32_t length = AffixUtils::unescape(currentAffix, sb, position, *this, fField, status);
269     return length;
270 }
271 
insertSuffix(FormattedStringBuilder & sb,int position,UErrorCode & status)272 int32_t MutablePatternModifier::insertSuffix(FormattedStringBuilder& sb, int position, UErrorCode& status) {
273     prepareAffix(false);
274     int32_t length = AffixUtils::unescape(currentAffix, sb, position, *this, fField, status);
275     return length;
276 }
277 
278 /** This method contains the heart of the logic for rendering LDML affix strings. */
prepareAffix(bool isPrefix)279 void MutablePatternModifier::prepareAffix(bool isPrefix) {
280     PatternStringUtils::patternInfoToStringBuilder(
281             *fPatternInfo,
282             isPrefix,
283             PatternStringUtils::resolveSignDisplay(fSignDisplay, fSignum),
284             fApproximately,
285             fPlural,
286             fPerMilleReplacesPercent,
287             false, // dropCurrencySymbols
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_APPROXIMATELY_SIGN:
299             return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kApproximatelySignSymbol);
300         case AffixPatternType::TYPE_PERCENT:
301             return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPercentSymbol);
302         case AffixPatternType::TYPE_PERMILLE:
303             return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPerMillSymbol);
304         case AffixPatternType::TYPE_CURRENCY_SINGLE:
305             return getCurrencySymbolForUnitWidth(localStatus);
306         case AffixPatternType::TYPE_CURRENCY_DOUBLE:
307             return fCurrencySymbols.getIntlCurrencySymbol(localStatus);
308         case AffixPatternType::TYPE_CURRENCY_TRIPLE:
309             // NOTE: This is the code path only for patterns containing "¤¤¤".
310             // Plural currencies set via the API are formatted in LongNameHandler.
311             // This code path is used by DecimalFormat via CurrencyPluralInfo.
312             U_ASSERT(fPlural != StandardPlural::Form::COUNT);
313             return fCurrencySymbols.getPluralName(fPlural, localStatus);
314         case AffixPatternType::TYPE_CURRENCY_QUAD:
315             return UnicodeString(u"\uFFFD");
316         case AffixPatternType::TYPE_CURRENCY_QUINT:
317             return UnicodeString(u"\uFFFD");
318         default:
319             UPRV_UNREACHABLE_EXIT;
320     }
321 }
322 
getCurrencySymbolForUnitWidth(UErrorCode & status) const323 UnicodeString MutablePatternModifier::getCurrencySymbolForUnitWidth(UErrorCode& status) const {
324     switch (fUnitWidth) {
325     case UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW:
326         return fCurrencySymbols.getNarrowCurrencySymbol(status);
327     case UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT:
328         return fCurrencySymbols.getCurrencySymbol(status);
329     case UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE:
330         return fCurrencySymbols.getIntlCurrencySymbol(status);
331     case UNumberUnitWidth::UNUM_UNIT_WIDTH_FORMAL:
332         return fCurrencySymbols.getFormalCurrencySymbol(status);
333     case UNumberUnitWidth::UNUM_UNIT_WIDTH_VARIANT:
334         return fCurrencySymbols.getVariantCurrencySymbol(status);
335     case UNumberUnitWidth::UNUM_UNIT_WIDTH_HIDDEN:
336         return UnicodeString();
337     default:
338         return fCurrencySymbols.getCurrencySymbol(status);
339     }
340 }
341 
toUnicodeString() const342 UnicodeString MutablePatternModifier::toUnicodeString() const {
343     // Never called by AffixUtils
344     UPRV_UNREACHABLE_EXIT;
345 }
346 
347 #endif /* #if !UCONFIG_NO_FORMATTING */
348