]>
Commit | Line | Data |
---|---|---|
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, Field field) { | |
27 | fPatternInfo = patternInfo; | |
28 | fField = field; | |
29 | } | |
30 | ||
31 | void MutablePatternModifier::setPatternAttributes(UNumberSignDisplay signDisplay, bool perMille) { | |
32 | fSignDisplay = signDisplay; | |
33 | fPerMilleReplacesPercent = perMille; | |
34 | } | |
35 | ||
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 | ||
46 | void MutablePatternModifier::setNumberProperties(int8_t signum, StandardPlural::Form plural) { | |
47 | fSignum = signum; | |
48 | fPlural = plural; | |
49 | } | |
50 | ||
51 | 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 | ||
57 | ImmutablePatternModifier* MutablePatternModifier::createImmutable(UErrorCode& status) { | |
58 | return createImmutableAndChain(nullptr, status); | |
59 | } | |
60 | ||
61 | ImmutablePatternModifier* | |
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(1, plural); | |
83 | pm->adoptModifier(1, plural, createConstantModifier(status)); | |
84 | setNumberProperties(0, plural); | |
85 | pm->adoptModifier(0, plural, createConstantModifier(status)); | |
86 | setNumberProperties(-1, plural); | |
87 | pm->adoptModifier(-1, 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(1, StandardPlural::Form::COUNT); | |
97 | pm->adoptModifierWithoutPlural(1, createConstantModifier(status)); | |
98 | setNumberProperties(0, StandardPlural::Form::COUNT); | |
99 | pm->adoptModifierWithoutPlural(0, createConstantModifier(status)); | |
100 | setNumberProperties(-1, StandardPlural::Form::COUNT); | |
101 | pm->adoptModifierWithoutPlural(-1, 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 | ||
110 | ConstantMultiFieldModifier* MutablePatternModifier::createConstantModifier(UErrorCode& status) { | |
111 | NumberStringBuilder a; | |
112 | NumberStringBuilder 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 | ||
123 | ImmutablePatternModifier::ImmutablePatternModifier(AdoptingModifierStore* 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, status); | |
131 | } | |
132 | ||
133 | 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 | ||
143 | const Modifier* ImmutablePatternModifier::getModifier(int8_t 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. */ | |
153 | MicroPropsGenerator& MutablePatternModifier::addToChain(const MicroPropsGenerator* parent) { | |
154 | fParent = parent; | |
155 | return *this; | |
156 | } | |
157 | ||
158 | 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 | ||
173 | int32_t MutablePatternModifier::apply(NumberStringBuilder& 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 | ||
203 | 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 | ||
215 | 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 | ||
229 | bool MutablePatternModifier::isStrong() const { | |
230 | return fStrong; | |
231 | } | |
232 | ||
233 | bool MutablePatternModifier::containsField(UNumberFormatFields field) const { | |
234 | (void)field; | |
235 | // This method is not currently used. | |
236 | UPRV_UNREACHABLE; | |
237 | } | |
238 | ||
239 | void MutablePatternModifier::getParameters(Parameters& output) const { | |
240 | (void)output; | |
241 | // This method is not currently used. | |
242 | UPRV_UNREACHABLE; | |
243 | } | |
244 | ||
245 | bool MutablePatternModifier::semanticallyEquivalent(const Modifier& other) const { | |
246 | (void)other; | |
247 | // This method is not currently used. | |
248 | UPRV_UNREACHABLE; | |
249 | } | |
250 | ||
251 | int32_t MutablePatternModifier::insertPrefix(NumberStringBuilder& 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 | ||
257 | int32_t MutablePatternModifier::insertSuffix(NumberStringBuilder& 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. */ | |
264 | void MutablePatternModifier::prepareAffix(bool isPrefix) { | |
265 | PatternStringUtils::patternInfoToStringBuilder( | |
266 | *fPatternInfo, isPrefix, fSignum, fSignDisplay, fPlural, fPerMilleReplacesPercent, currentAffix); | |
267 | } | |
268 | ||
269 | 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 | ||
309 | UnicodeString MutablePatternModifier::toUnicodeString() const { | |
310 | // Never called by AffixUtils | |
311 | UPRV_UNREACHABLE; | |
312 | } | |
313 | ||
314 | #endif /* #if !UCONFIG_NO_FORMATTING */ |