]>
Commit | Line | Data |
---|---|---|
0f5d89e8 A |
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 | ||
23 | MutablePatternModifier::MutablePatternModifier(bool isStrong) | |
24 | : fStrong(isStrong) {} | |
25 | ||
26 | void MutablePatternModifier::setPatternInfo(const AffixPatternProvider* patternInfo) { | |
27 | this->patternInfo = patternInfo; | |
28 | } | |
29 | ||
30 | void MutablePatternModifier::setPatternAttributes(UNumberSignDisplay signDisplay, bool perMille) { | |
31 | this->signDisplay = signDisplay; | |
32 | this->perMilleReplacesPercent = perMille; | |
33 | } | |
34 | ||
35 | void MutablePatternModifier::setSymbols(const DecimalFormatSymbols* symbols, | |
36 | const CurrencySymbols* currencySymbols, | |
37 | const UNumberUnitWidth unitWidth, const PluralRules* rules) { | |
38 | U_ASSERT((rules != nullptr) == needsPlurals()); | |
39 | this->symbols = symbols; | |
40 | this->currencySymbols = currencySymbols; | |
41 | this->unitWidth = unitWidth; | |
42 | this->rules = rules; | |
43 | } | |
44 | ||
45 | void MutablePatternModifier::setNumberProperties(int8_t signum, StandardPlural::Form plural) { | |
46 | this->signum = signum; | |
47 | this->plural = plural; | |
48 | } | |
49 | ||
50 | bool MutablePatternModifier::needsPlurals() const { | |
51 | UErrorCode statusLocal = U_ZERO_ERROR; | |
52 | return patternInfo->containsSymbolType(AffixPatternType::TYPE_CURRENCY_TRIPLE, statusLocal); | |
53 | // Silently ignore any error codes. | |
54 | } | |
55 | ||
56 | ImmutablePatternModifier* MutablePatternModifier::createImmutable(UErrorCode& status) { | |
57 | return createImmutableAndChain(nullptr, status); | |
58 | } | |
59 | ||
60 | ImmutablePatternModifier* | |
61 | MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator* parent, UErrorCode& status) { | |
62 | ||
63 | // TODO: Move StandardPlural VALUES to standardplural.h | |
64 | static const StandardPlural::Form STANDARD_PLURAL_VALUES[] = { | |
65 | StandardPlural::Form::ZERO, | |
66 | StandardPlural::Form::ONE, | |
67 | StandardPlural::Form::TWO, | |
68 | StandardPlural::Form::FEW, | |
69 | StandardPlural::Form::MANY, | |
70 | StandardPlural::Form::OTHER}; | |
71 | ||
72 | auto pm = new ParameterizedModifier(); | |
73 | if (pm == nullptr) { | |
74 | status = U_MEMORY_ALLOCATION_ERROR; | |
75 | return nullptr; | |
76 | } | |
77 | ||
78 | if (needsPlurals()) { | |
79 | // Slower path when we require the plural keyword. | |
80 | for (StandardPlural::Form plural : STANDARD_PLURAL_VALUES) { | |
81 | setNumberProperties(1, plural); | |
82 | pm->adoptSignPluralModifier(1, plural, createConstantModifier(status)); | |
83 | setNumberProperties(0, plural); | |
84 | pm->adoptSignPluralModifier(0, plural, createConstantModifier(status)); | |
85 | setNumberProperties(-1, plural); | |
86 | pm->adoptSignPluralModifier(-1, plural, createConstantModifier(status)); | |
87 | } | |
88 | if (U_FAILURE(status)) { | |
89 | delete pm; | |
90 | return nullptr; | |
91 | } | |
92 | return new ImmutablePatternModifier(pm, rules, parent); // adopts pm | |
93 | } else { | |
94 | // Faster path when plural keyword is not needed. | |
95 | setNumberProperties(1, StandardPlural::Form::COUNT); | |
96 | Modifier* positive = createConstantModifier(status); | |
97 | setNumberProperties(0, StandardPlural::Form::COUNT); | |
98 | Modifier* zero = createConstantModifier(status); | |
99 | setNumberProperties(-1, StandardPlural::Form::COUNT); | |
100 | Modifier* negative = createConstantModifier(status); | |
101 | pm->adoptPositiveNegativeModifiers(positive, zero, negative); | |
102 | if (U_FAILURE(status)) { | |
103 | delete pm; | |
104 | return nullptr; | |
105 | } | |
106 | return new ImmutablePatternModifier(pm, nullptr, parent); // adopts pm | |
107 | } | |
108 | } | |
109 | ||
110 | ConstantMultiFieldModifier* MutablePatternModifier::createConstantModifier(UErrorCode& status) { | |
111 | NumberStringBuilder a; | |
112 | NumberStringBuilder b; | |
113 | insertPrefix(a, 0, status); | |
114 | insertSuffix(b, 0, status); | |
115 | if (patternInfo->hasCurrencySign()) { | |
116 | return new CurrencySpacingEnabledModifier( | |
117 | a, b, !patternInfo->hasBody(), fStrong, *symbols, status); | |
118 | } else { | |
119 | return new ConstantMultiFieldModifier(a, b, !patternInfo->hasBody(), fStrong); | |
120 | } | |
121 | } | |
122 | ||
123 | ImmutablePatternModifier::ImmutablePatternModifier(ParameterizedModifier* pm, const PluralRules* rules, | |
124 | const MicroPropsGenerator* parent) | |
125 | : pm(pm), rules(rules), parent(parent) {} | |
126 | ||
127 | void ImmutablePatternModifier::processQuantity(DecimalQuantity& quantity, MicroProps& micros, | |
128 | UErrorCode& status) const { | |
129 | parent->processQuantity(quantity, micros, status); | |
130 | applyToMicros(micros, quantity); | |
131 | } | |
132 | ||
133 | void ImmutablePatternModifier::applyToMicros(MicroProps& micros, DecimalQuantity& quantity) const { | |
134 | if (rules == nullptr) { | |
135 | micros.modMiddle = pm->getModifier(quantity.signum()); | |
136 | } else { | |
137 | // TODO: Fix this. Avoid the copy. | |
138 | DecimalQuantity copy(quantity); | |
139 | copy.roundToInfinity(); | |
140 | StandardPlural::Form plural = utils::getStandardPlural(rules, copy); | |
141 | micros.modMiddle = pm->getModifier(quantity.signum(), plural); | |
142 | } | |
143 | } | |
144 | ||
145 | const Modifier* ImmutablePatternModifier::getModifier(int8_t signum, StandardPlural::Form plural) const { | |
146 | if (rules == nullptr) { | |
147 | return pm->getModifier(signum); | |
148 | } else { | |
149 | return pm->getModifier(signum, plural); | |
150 | } | |
151 | } | |
152 | ||
153 | ||
154 | /** Used by the unsafe code path. */ | |
155 | MicroPropsGenerator& MutablePatternModifier::addToChain(const MicroPropsGenerator* parent) { | |
156 | this->parent = parent; | |
157 | return *this; | |
158 | } | |
159 | ||
160 | void MutablePatternModifier::processQuantity(DecimalQuantity& fq, MicroProps& micros, | |
161 | UErrorCode& status) const { | |
162 | parent->processQuantity(fq, micros, status); | |
163 | // The unsafe code path performs self-mutation, so we need a const_cast. | |
164 | // This method needs to be const because it overrides a const method in the parent class. | |
165 | auto nonConstThis = const_cast<MutablePatternModifier*>(this); | |
166 | if (needsPlurals()) { | |
167 | // TODO: Fix this. Avoid the copy. | |
168 | DecimalQuantity copy(fq); | |
169 | micros.rounder.apply(copy, status); | |
170 | nonConstThis->setNumberProperties(fq.signum(), utils::getStandardPlural(rules, copy)); | |
171 | } else { | |
172 | nonConstThis->setNumberProperties(fq.signum(), StandardPlural::Form::COUNT); | |
173 | } | |
174 | micros.modMiddle = this; | |
175 | } | |
176 | ||
177 | int32_t MutablePatternModifier::apply(NumberStringBuilder& output, int32_t leftIndex, int32_t rightIndex, | |
178 | UErrorCode& status) const { | |
179 | // The unsafe code path performs self-mutation, so we need a const_cast. | |
180 | // This method needs to be const because it overrides a const method in the parent class. | |
181 | auto nonConstThis = const_cast<MutablePatternModifier*>(this); | |
182 | int32_t prefixLen = nonConstThis->insertPrefix(output, leftIndex, status); | |
183 | int32_t suffixLen = nonConstThis->insertSuffix(output, rightIndex + prefixLen, status); | |
184 | // If the pattern had no decimal stem body (like #,##0.00), overwrite the value. | |
185 | int32_t overwriteLen = 0; | |
186 | if (!patternInfo->hasBody()) { | |
187 | overwriteLen = output.splice( | |
188 | leftIndex + prefixLen, | |
189 | rightIndex + prefixLen, | |
190 | UnicodeString(), | |
191 | 0, | |
192 | 0, | |
193 | UNUM_FIELD_COUNT, | |
194 | status); | |
195 | } | |
196 | CurrencySpacingEnabledModifier::applyCurrencySpacing( | |
197 | output, | |
198 | leftIndex, | |
199 | prefixLen, | |
200 | rightIndex + overwriteLen + prefixLen, | |
201 | suffixLen, | |
202 | *symbols, | |
203 | status); | |
204 | return prefixLen + overwriteLen + suffixLen; | |
205 | } | |
206 | ||
207 | int32_t MutablePatternModifier::getPrefixLength(UErrorCode& status) const { | |
208 | // The unsafe code path performs self-mutation, so we need a const_cast. | |
209 | // This method needs to be const because it overrides a const method in the parent class. | |
210 | auto nonConstThis = const_cast<MutablePatternModifier*>(this); | |
211 | ||
212 | // Enter and exit CharSequence Mode to get the length. | |
213 | nonConstThis->prepareAffix(true); | |
214 | int result = AffixUtils::unescapedCodePointCount(currentAffix, *this, status); // prefix length | |
215 | return result; | |
216 | } | |
217 | ||
218 | int32_t MutablePatternModifier::getCodePointCount(UErrorCode& status) 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 | // Render the affixes to get the length | |
224 | nonConstThis->prepareAffix(true); | |
225 | int result = AffixUtils::unescapedCodePointCount(currentAffix, *this, status); // prefix length | |
226 | nonConstThis->prepareAffix(false); | |
227 | result += AffixUtils::unescapedCodePointCount(currentAffix, *this, status); // suffix length | |
228 | return result; | |
229 | } | |
230 | ||
231 | bool MutablePatternModifier::isStrong() const { | |
232 | return fStrong; | |
233 | } | |
234 | ||
235 | int32_t MutablePatternModifier::insertPrefix(NumberStringBuilder& sb, int position, UErrorCode& status) { | |
236 | prepareAffix(true); | |
237 | int length = AffixUtils::unescape(currentAffix, sb, position, *this, status); | |
238 | return length; | |
239 | } | |
240 | ||
241 | int32_t MutablePatternModifier::insertSuffix(NumberStringBuilder& sb, int position, UErrorCode& status) { | |
242 | prepareAffix(false); | |
243 | int length = AffixUtils::unescape(currentAffix, sb, position, *this, status); | |
244 | return length; | |
245 | } | |
246 | ||
247 | /** This method contains the heart of the logic for rendering LDML affix strings. */ | |
248 | void MutablePatternModifier::prepareAffix(bool isPrefix) { | |
249 | PatternStringUtils::patternInfoToStringBuilder( | |
250 | *patternInfo, isPrefix, signum, signDisplay, plural, perMilleReplacesPercent, currentAffix); | |
251 | } | |
252 | ||
253 | UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const { | |
254 | UErrorCode localStatus = U_ZERO_ERROR; | |
255 | switch (type) { | |
256 | case AffixPatternType::TYPE_MINUS_SIGN: | |
257 | return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol); | |
258 | case AffixPatternType::TYPE_PLUS_SIGN: | |
259 | return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol); | |
260 | case AffixPatternType::TYPE_PERCENT: | |
261 | return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPercentSymbol); | |
262 | case AffixPatternType::TYPE_PERMILLE: | |
263 | return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPerMillSymbol); | |
264 | case AffixPatternType::TYPE_CURRENCY_SINGLE: { | |
265 | // UnitWidth ISO and HIDDEN overrides the singular currency symbol. | |
266 | if (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE) { | |
267 | return currencySymbols->getIntlCurrencySymbol(localStatus); | |
268 | } else if (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_HIDDEN) { | |
269 | return UnicodeString(); | |
270 | } else if (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW) { | |
271 | return currencySymbols->getNarrowCurrencySymbol(localStatus); | |
272 | } else { | |
273 | return currencySymbols->getCurrencySymbol(localStatus); | |
274 | } | |
275 | } | |
276 | case AffixPatternType::TYPE_CURRENCY_DOUBLE: | |
277 | return currencySymbols->getIntlCurrencySymbol(localStatus); | |
278 | case AffixPatternType::TYPE_CURRENCY_TRIPLE: | |
279 | // NOTE: This is the code path only for patterns containing "¤¤¤". | |
280 | // Plural currencies set via the API are formatted in LongNameHandler. | |
281 | // This code path is used by DecimalFormat via CurrencyPluralInfo. | |
282 | U_ASSERT(plural != StandardPlural::Form::COUNT); | |
283 | return currencySymbols->getPluralName(plural, localStatus); | |
284 | case AffixPatternType::TYPE_CURRENCY_QUAD: | |
285 | return UnicodeString(u"\uFFFD"); | |
286 | case AffixPatternType::TYPE_CURRENCY_QUINT: | |
287 | return UnicodeString(u"\uFFFD"); | |
288 | default: | |
289 | U_ASSERT(false); | |
290 | return UnicodeString(); | |
291 | } | |
292 | } | |
293 | ||
294 | UnicodeString MutablePatternModifier::toUnicodeString() const { | |
295 | // Never called by AffixUtils | |
296 | U_ASSERT(false); | |
297 | return UnicodeString(); | |
298 | } | |
299 | ||
300 | #endif /* #if !UCONFIG_NO_FORMATTING */ |