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 && !UPRV_INCOMPLETE_CPP11_SUPPORT
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
14 using namespace icu;
15 using namespace icu::number;
16 using namespace icu::number::impl;
17
MutablePatternModifier(bool isStrong)18 MutablePatternModifier::MutablePatternModifier(bool isStrong) : fStrong(isStrong) {}
19
setPatternInfo(const AffixPatternProvider * patternInfo)20 void MutablePatternModifier::setPatternInfo(const AffixPatternProvider *patternInfo) {
21 this->patternInfo = patternInfo;
22 }
23
setPatternAttributes(UNumberSignDisplay signDisplay,bool perMille)24 void MutablePatternModifier::setPatternAttributes(UNumberSignDisplay signDisplay, bool perMille) {
25 this->signDisplay = signDisplay;
26 this->perMilleReplacesPercent = perMille;
27 }
28
29 void
setSymbols(const DecimalFormatSymbols * symbols,const CurrencyUnit & currency,const UNumberUnitWidth unitWidth,const PluralRules * rules)30 MutablePatternModifier::setSymbols(const DecimalFormatSymbols *symbols, const CurrencyUnit ¤cy,
31 const UNumberUnitWidth unitWidth, const PluralRules *rules) {
32 U_ASSERT((rules != nullptr) == needsPlurals());
33 this->symbols = symbols;
34 uprv_memcpy(static_cast<char16_t *>(this->currencyCode),
35 currency.getISOCurrency(),
36 sizeof(char16_t) * 4);
37 this->unitWidth = unitWidth;
38 this->rules = rules;
39 }
40
setNumberProperties(bool isNegative,StandardPlural::Form plural)41 void MutablePatternModifier::setNumberProperties(bool isNegative, StandardPlural::Form plural) {
42 this->isNegative = isNegative;
43 this->plural = plural;
44 }
45
needsPlurals() const46 bool MutablePatternModifier::needsPlurals() const {
47 UErrorCode statusLocal = U_ZERO_ERROR;
48 return patternInfo->containsSymbolType(AffixPatternType::TYPE_CURRENCY_TRIPLE, statusLocal);
49 // Silently ignore any error codes.
50 }
51
createImmutable(UErrorCode & status)52 ImmutablePatternModifier *MutablePatternModifier::createImmutable(UErrorCode &status) {
53 return createImmutableAndChain(nullptr, status);
54 }
55
56 ImmutablePatternModifier *
createImmutableAndChain(const MicroPropsGenerator * parent,UErrorCode & status)57 MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator *parent, UErrorCode &status) {
58
59 // TODO: Move StandardPlural VALUES to standardplural.h
60 static const StandardPlural::Form STANDARD_PLURAL_VALUES[] = {
61 StandardPlural::Form::ZERO,
62 StandardPlural::Form::ONE,
63 StandardPlural::Form::TWO,
64 StandardPlural::Form::FEW,
65 StandardPlural::Form::MANY,
66 StandardPlural::Form::OTHER};
67
68 auto pm = new ParameterizedModifier();
69 if (pm == nullptr) {
70 status = U_MEMORY_ALLOCATION_ERROR;
71 return nullptr;
72 }
73
74 if (needsPlurals()) {
75 // Slower path when we require the plural keyword.
76 for (StandardPlural::Form plural : STANDARD_PLURAL_VALUES) {
77 setNumberProperties(false, plural);
78 pm->adoptSignPluralModifier(false, plural, createConstantModifier(status));
79 setNumberProperties(true, plural);
80 pm->adoptSignPluralModifier(true, plural, createConstantModifier(status));
81 }
82 if (U_FAILURE(status)) {
83 delete pm;
84 return nullptr;
85 }
86 return new ImmutablePatternModifier(pm, rules, parent); // adopts pm
87 } else {
88 // Faster path when plural keyword is not needed.
89 setNumberProperties(false, StandardPlural::Form::COUNT);
90 Modifier *positive = createConstantModifier(status);
91 setNumberProperties(true, StandardPlural::Form::COUNT);
92 Modifier *negative = createConstantModifier(status);
93 pm->adoptPositiveNegativeModifiers(positive, negative);
94 if (U_FAILURE(status)) {
95 delete pm;
96 return nullptr;
97 }
98 return new ImmutablePatternModifier(pm, nullptr, parent); // adopts pm
99 }
100 }
101
createConstantModifier(UErrorCode & status)102 ConstantMultiFieldModifier *MutablePatternModifier::createConstantModifier(UErrorCode &status) {
103 NumberStringBuilder a;
104 NumberStringBuilder b;
105 insertPrefix(a, 0, status);
106 insertSuffix(b, 0, status);
107 if (patternInfo->hasCurrencySign()) {
108 return new CurrencySpacingEnabledModifier(a, b, fStrong, *symbols, status);
109 } else {
110 return new ConstantMultiFieldModifier(a, b, fStrong);
111 }
112 }
113
ImmutablePatternModifier(ParameterizedModifier * pm,const PluralRules * rules,const MicroPropsGenerator * parent)114 ImmutablePatternModifier::ImmutablePatternModifier(ParameterizedModifier *pm, const PluralRules *rules,
115 const MicroPropsGenerator *parent)
116 : pm(pm), rules(rules), parent(parent) {}
117
processQuantity(DecimalQuantity & quantity,MicroProps & micros,UErrorCode & status) const118 void ImmutablePatternModifier::processQuantity(DecimalQuantity &quantity, MicroProps µs,
119 UErrorCode &status) const {
120 parent->processQuantity(quantity, micros, status);
121 applyToMicros(micros, quantity);
122 }
123
applyToMicros(MicroProps & micros,DecimalQuantity & quantity) const124 void ImmutablePatternModifier::applyToMicros(MicroProps µs, DecimalQuantity &quantity) const {
125 if (rules == nullptr) {
126 micros.modMiddle = pm->getModifier(quantity.isNegative());
127 } else {
128 // TODO: Fix this. Avoid the copy.
129 DecimalQuantity copy(quantity);
130 copy.roundToInfinity();
131 StandardPlural::Form plural = copy.getStandardPlural(rules);
132 micros.modMiddle = pm->getModifier(quantity.isNegative(), plural);
133 }
134 }
135
136 /** Used by the unsafe code path. */
addToChain(const MicroPropsGenerator * parent)137 MicroPropsGenerator &MutablePatternModifier::addToChain(const MicroPropsGenerator *parent) {
138 this->parent = parent;
139 return *this;
140 }
141
processQuantity(DecimalQuantity & fq,MicroProps & micros,UErrorCode & status) const142 void MutablePatternModifier::processQuantity(DecimalQuantity &fq, MicroProps µs,
143 UErrorCode &status) const {
144 parent->processQuantity(fq, micros, status);
145 // The unsafe code path performs self-mutation, so we need a const_cast.
146 // This method needs to be const because it overrides a const method in the parent class.
147 auto nonConstThis = const_cast<MutablePatternModifier *>(this);
148 if (needsPlurals()) {
149 // TODO: Fix this. Avoid the copy.
150 DecimalQuantity copy(fq);
151 micros.rounding.apply(copy, status);
152 nonConstThis->setNumberProperties(fq.isNegative(), copy.getStandardPlural(rules));
153 } else {
154 nonConstThis->setNumberProperties(fq.isNegative(), StandardPlural::Form::COUNT);
155 }
156 micros.modMiddle = this;
157 }
158
apply(NumberStringBuilder & output,int32_t leftIndex,int32_t rightIndex,UErrorCode & status) const159 int32_t MutablePatternModifier::apply(NumberStringBuilder &output, int32_t leftIndex, int32_t rightIndex,
160 UErrorCode &status) const {
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 int32_t prefixLen = nonConstThis->insertPrefix(output, leftIndex, status);
165 int32_t suffixLen = nonConstThis->insertSuffix(output, rightIndex + prefixLen, status);
166 CurrencySpacingEnabledModifier::applyCurrencySpacing(
167 output, leftIndex, prefixLen, rightIndex + prefixLen, suffixLen, *symbols, status);
168 return prefixLen + suffixLen;
169 }
170
getPrefixLength(UErrorCode & status) const171 int32_t MutablePatternModifier::getPrefixLength(UErrorCode &status) const {
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
176 // Enter and exit CharSequence Mode to get the length.
177 nonConstThis->enterCharSequenceMode(true);
178 int result = AffixUtils::unescapedCodePointCount(*this, *this, status); // prefix length
179 nonConstThis->exitCharSequenceMode();
180 return result;
181 }
182
getCodePointCount(UErrorCode & status) const183 int32_t MutablePatternModifier::getCodePointCount(UErrorCode &status) const {
184 // The unsafe code path performs self-mutation, so we need a const_cast.
185 // This method needs to be const because it overrides a const method in the parent class.
186 auto nonConstThis = const_cast<MutablePatternModifier *>(this);
187
188 // Enter and exit CharSequence Mode to get the length.
189 nonConstThis->enterCharSequenceMode(true);
190 int result = AffixUtils::unescapedCodePointCount(*this, *this, status); // prefix length
191 nonConstThis->exitCharSequenceMode();
192 nonConstThis->enterCharSequenceMode(false);
193 result += AffixUtils::unescapedCodePointCount(*this, *this, status); // suffix length
194 nonConstThis->exitCharSequenceMode();
195 return result;
196 }
197
isStrong() const198 bool MutablePatternModifier::isStrong() const {
199 return fStrong;
200 }
201
insertPrefix(NumberStringBuilder & sb,int position,UErrorCode & status)202 int32_t MutablePatternModifier::insertPrefix(NumberStringBuilder &sb, int position, UErrorCode &status) {
203 enterCharSequenceMode(true);
204 int length = AffixUtils::unescape(*this, sb, position, *this, status);
205 exitCharSequenceMode();
206 return length;
207 }
208
insertSuffix(NumberStringBuilder & sb,int position,UErrorCode & status)209 int32_t MutablePatternModifier::insertSuffix(NumberStringBuilder &sb, int position, UErrorCode &status) {
210 enterCharSequenceMode(false);
211 int length = AffixUtils::unescape(*this, sb, position, *this, status);
212 exitCharSequenceMode();
213 return length;
214 }
215
getSymbol(AffixPatternType type) const216 UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const {
217 switch (type) {
218 case AffixPatternType::TYPE_MINUS_SIGN:
219 return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol);
220 case AffixPatternType::TYPE_PLUS_SIGN:
221 return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol);
222 case AffixPatternType::TYPE_PERCENT:
223 return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPercentSymbol);
224 case AffixPatternType::TYPE_PERMILLE:
225 return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPerMillSymbol);
226 case AffixPatternType::TYPE_CURRENCY_SINGLE: {
227 // UnitWidth ISO and HIDDEN overrides the singular currency symbol.
228 if (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE) {
229 return UnicodeString(currencyCode, 3);
230 } else if (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_HIDDEN) {
231 return UnicodeString();
232 } else {
233 UErrorCode status = U_ZERO_ERROR;
234 UBool isChoiceFormat = FALSE;
235 int32_t symbolLen = 0;
236 const char16_t *symbol = ucurr_getName(
237 currencyCode,
238 symbols->getLocale().getName(),
239 UCurrNameStyle::UCURR_SYMBOL_NAME,
240 &isChoiceFormat,
241 &symbolLen,
242 &status);
243 return UnicodeString(symbol, symbolLen);
244 }
245 }
246 case AffixPatternType::TYPE_CURRENCY_DOUBLE:
247 return UnicodeString(currencyCode, 3);
248 case AffixPatternType::TYPE_CURRENCY_TRIPLE: {
249 // NOTE: This is the code path only for patterns containing "¤¤¤".
250 // Plural currencies set via the API are formatted in LongNameHandler.
251 // This code path is used by DecimalFormat via CurrencyPluralInfo.
252 U_ASSERT(plural != StandardPlural::Form::COUNT);
253 UErrorCode status = U_ZERO_ERROR;
254 UBool isChoiceFormat = FALSE;
255 int32_t symbolLen = 0;
256 const char16_t *symbol = ucurr_getPluralName(
257 currencyCode,
258 symbols->getLocale().getName(),
259 &isChoiceFormat,
260 StandardPlural::getKeyword(plural),
261 &symbolLen,
262 &status);
263 return UnicodeString(symbol, symbolLen);
264 }
265 case AffixPatternType::TYPE_CURRENCY_QUAD:
266 return UnicodeString(u"\uFFFD");
267 case AffixPatternType::TYPE_CURRENCY_QUINT:
268 return UnicodeString(u"\uFFFD");
269 default:
270 U_ASSERT(false);
271 return UnicodeString();
272 }
273 }
274
275 /** This method contains the heart of the logic for rendering LDML affix strings. */
enterCharSequenceMode(bool isPrefix)276 void MutablePatternModifier::enterCharSequenceMode(bool isPrefix) {
277 U_ASSERT(!inCharSequenceMode);
278 inCharSequenceMode = true;
279
280 // Should the output render '+' where '-' would normally appear in the pattern?
281 plusReplacesMinusSign = !isNegative && (
282 signDisplay == UNUM_SIGN_ALWAYS ||
283 signDisplay == UNUM_SIGN_ACCOUNTING_ALWAYS) &&
284 patternInfo->positiveHasPlusSign() == false;
285
286 // Should we use the affix from the negative subpattern? (If not, we will use the positive subpattern.)
287 bool useNegativeAffixPattern = patternInfo->hasNegativeSubpattern() && (
288 isNegative || (patternInfo->negativeHasMinusSign() && plusReplacesMinusSign));
289
290 // Resolve the flags for the affix pattern.
291 fFlags = 0;
292 if (useNegativeAffixPattern) {
293 fFlags |= AffixPatternProvider::AFFIX_NEGATIVE_SUBPATTERN;
294 }
295 if (isPrefix) {
296 fFlags |= AffixPatternProvider::AFFIX_PREFIX;
297 }
298 if (plural != StandardPlural::Form::COUNT) {
299 U_ASSERT(plural == (AffixPatternProvider::AFFIX_PLURAL_MASK & plural));
300 fFlags |= plural;
301 }
302
303 // Should we prepend a sign to the pattern?
304 if (!isPrefix || useNegativeAffixPattern) {
305 prependSign = false;
306 } else if (isNegative) {
307 prependSign = signDisplay != UNUM_SIGN_NEVER;
308 } else {
309 prependSign = plusReplacesMinusSign;
310 }
311
312 // Finally, compute the length of the affix pattern.
313 fLength = patternInfo->length(fFlags) + (prependSign ? 1 : 0);
314 }
315
exitCharSequenceMode()316 void MutablePatternModifier::exitCharSequenceMode() {
317 U_ASSERT(inCharSequenceMode);
318 inCharSequenceMode = false;
319 }
320
length() const321 int32_t MutablePatternModifier::length() const {
322 U_ASSERT(inCharSequenceMode);
323 return fLength;
324 }
325
charAt(int32_t index) const326 char16_t MutablePatternModifier::charAt(int32_t index) const {
327 U_ASSERT(inCharSequenceMode);
328 char16_t candidate;
329 if (prependSign && index == 0) {
330 candidate = u'-';
331 } else if (prependSign) {
332 candidate = patternInfo->charAt(fFlags, index - 1);
333 } else {
334 candidate = patternInfo->charAt(fFlags, index);
335 }
336 if (plusReplacesMinusSign && candidate == u'-') {
337 return u'+';
338 }
339 if (perMilleReplacesPercent && candidate == u'%') {
340 return u'‰';
341 }
342 return candidate;
343 }
344
toUnicodeString() const345 UnicodeString MutablePatternModifier::toUnicodeString() const {
346 // Never called by AffixUtils
347 U_ASSERT(false);
348 return UnicodeString();
349 }
350
351 #endif /* #if !UCONFIG_NO_FORMATTING */
352