• 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 
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