/src/icu/source/i18n/number_formatimpl.cpp
Line | Count | Source (jump to first uncovered line) |
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 "unicode/ures.h" |
10 | | #include "uresimp.h" |
11 | | #include "charstr.h" |
12 | | #include "number_formatimpl.h" |
13 | | #include "unicode/numfmt.h" |
14 | | #include "number_patternstring.h" |
15 | | #include "number_utils.h" |
16 | | #include "unicode/numberformatter.h" |
17 | | #include "unicode/dcfmtsym.h" |
18 | | #include "number_scientific.h" |
19 | | #include "number_compact.h" |
20 | | #include "uresimp.h" |
21 | | #include "ureslocs.h" |
22 | | |
23 | | using namespace icu; |
24 | | using namespace icu::number; |
25 | | using namespace icu::number::impl; |
26 | | |
27 | | |
28 | | NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, UErrorCode& status) |
29 | 0 | : NumberFormatterImpl(macros, true, status) { |
30 | 0 | } |
31 | | |
32 | | int32_t NumberFormatterImpl::formatStatic(const MacroProps ¯os, UFormattedNumberData *results, |
33 | 0 | UErrorCode &status) { |
34 | 0 | DecimalQuantity &inValue = results->quantity; |
35 | 0 | FormattedStringBuilder &outString = results->getStringRef(); |
36 | 0 | NumberFormatterImpl impl(macros, false, status); |
37 | 0 | MicroProps& micros = impl.preProcessUnsafe(inValue, status); |
38 | 0 | if (U_FAILURE(status)) { return 0; } |
39 | 0 | int32_t length = writeNumber(micros, inValue, outString, 0, status); |
40 | 0 | length += writeAffixes(micros, outString, 0, length, status); |
41 | 0 | results->outputUnit = std::move(micros.outputUnit); |
42 | 0 | results->gender = micros.gender; |
43 | 0 | return length; |
44 | 0 | } |
45 | | |
46 | | int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, Signum signum, |
47 | | StandardPlural::Form plural, |
48 | 0 | FormattedStringBuilder& outString, UErrorCode& status) { |
49 | 0 | NumberFormatterImpl impl(macros, false, status); |
50 | 0 | return impl.getPrefixSuffixUnsafe(signum, plural, outString, status); |
51 | 0 | } |
52 | | |
53 | | // NOTE: C++ SPECIFIC DIFFERENCE FROM JAVA: |
54 | | // The "safe" apply method uses a new MicroProps. In the MicroPropsGenerator, fMicros is copied into the new instance. |
55 | | // The "unsafe" method simply re-uses fMicros, eliminating the extra copy operation. |
56 | | // See MicroProps::processQuantity() for details. |
57 | | |
58 | 0 | int32_t NumberFormatterImpl::format(UFormattedNumberData *results, UErrorCode &status) const { |
59 | 0 | DecimalQuantity &inValue = results->quantity; |
60 | 0 | FormattedStringBuilder &outString = results->getStringRef(); |
61 | 0 | MicroProps micros; |
62 | 0 | preProcess(inValue, micros, status); |
63 | 0 | if (U_FAILURE(status)) { return 0; } |
64 | 0 | int32_t length = writeNumber(micros, inValue, outString, 0, status); |
65 | 0 | length += writeAffixes(micros, outString, 0, length, status); |
66 | 0 | results->outputUnit = std::move(micros.outputUnit); |
67 | 0 | results->gender = micros.gender; |
68 | 0 | return length; |
69 | 0 | } |
70 | | |
71 | | void NumberFormatterImpl::preProcess(DecimalQuantity& inValue, MicroProps& microsOut, |
72 | 0 | UErrorCode& status) const { |
73 | 0 | if (U_FAILURE(status)) { return; } |
74 | 0 | if (fMicroPropsGenerator == nullptr) { |
75 | 0 | status = U_INTERNAL_PROGRAM_ERROR; |
76 | 0 | return; |
77 | 0 | } |
78 | 0 | fMicroPropsGenerator->processQuantity(inValue, microsOut, status); |
79 | 0 | microsOut.integerWidth.apply(inValue, status); |
80 | 0 | } |
81 | | |
82 | 0 | MicroProps& NumberFormatterImpl::preProcessUnsafe(DecimalQuantity& inValue, UErrorCode& status) { |
83 | 0 | if (U_FAILURE(status)) { |
84 | 0 | return fMicros; // must always return a value |
85 | 0 | } |
86 | 0 | if (fMicroPropsGenerator == nullptr) { |
87 | 0 | status = U_INTERNAL_PROGRAM_ERROR; |
88 | 0 | return fMicros; // must always return a value |
89 | 0 | } |
90 | 0 | fMicroPropsGenerator->processQuantity(inValue, fMicros, status); |
91 | 0 | fMicros.integerWidth.apply(inValue, status); |
92 | 0 | return fMicros; |
93 | 0 | } |
94 | | |
95 | | int32_t NumberFormatterImpl::getPrefixSuffix(Signum signum, StandardPlural::Form plural, |
96 | 0 | FormattedStringBuilder& outString, UErrorCode& status) const { |
97 | 0 | if (U_FAILURE(status)) { return 0; } |
98 | | // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier). |
99 | | // Safe path: use fImmutablePatternModifier. |
100 | 0 | const Modifier* modifier = fImmutablePatternModifier->getModifier(signum, plural); |
101 | 0 | modifier->apply(outString, 0, 0, status); |
102 | 0 | if (U_FAILURE(status)) { return 0; } |
103 | 0 | return modifier->getPrefixLength(); |
104 | 0 | } |
105 | | |
106 | | int32_t NumberFormatterImpl::getPrefixSuffixUnsafe(Signum signum, StandardPlural::Form plural, |
107 | 0 | FormattedStringBuilder& outString, UErrorCode& status) { |
108 | 0 | if (U_FAILURE(status)) { return 0; } |
109 | | // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier). |
110 | | // Unsafe path: use fPatternModifier. |
111 | 0 | fPatternModifier->setNumberProperties(signum, plural); |
112 | 0 | fPatternModifier->apply(outString, 0, 0, status); |
113 | 0 | if (U_FAILURE(status)) { return 0; } |
114 | 0 | return fPatternModifier->getPrefixLength(); |
115 | 0 | } |
116 | | |
117 | 0 | NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, bool safe, UErrorCode& status) { |
118 | 0 | fMicroPropsGenerator = macrosToMicroGenerator(macros, safe, status); |
119 | 0 | } |
120 | | |
121 | | ////////// |
122 | | |
123 | | const MicroPropsGenerator* |
124 | 0 | NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe, UErrorCode& status) { |
125 | 0 | if (U_FAILURE(status)) { return nullptr; } |
126 | 0 | const MicroPropsGenerator* chain = &fMicros; |
127 | | |
128 | | // Check that macros is error-free before continuing. |
129 | 0 | if (macros.copyErrorTo(status)) { |
130 | 0 | return nullptr; |
131 | 0 | } |
132 | | |
133 | | // TODO: Accept currency symbols from DecimalFormatSymbols? |
134 | | |
135 | | // Pre-compute a few values for efficiency. |
136 | 0 | bool isCurrency = utils::unitIsCurrency(macros.unit); |
137 | 0 | bool isBaseUnit = utils::unitIsBaseUnit(macros.unit); |
138 | 0 | bool isPercent = utils::unitIsPercent(macros.unit); |
139 | 0 | bool isPermille = utils::unitIsPermille(macros.unit); |
140 | 0 | bool isCompactNotation = macros.notation.fType == Notation::NTN_COMPACT; |
141 | 0 | bool isAccounting = |
142 | 0 | macros.sign == UNUM_SIGN_ACCOUNTING || |
143 | 0 | macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS || |
144 | 0 | macros.sign == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO || |
145 | 0 | macros.sign == UNUM_SIGN_ACCOUNTING_NEGATIVE; |
146 | 0 | CurrencyUnit currency(u"", status); |
147 | 0 | if (isCurrency) { |
148 | 0 | currency = CurrencyUnit(macros.unit, status); // Restore CurrencyUnit from MeasureUnit |
149 | 0 | } |
150 | 0 | UNumberUnitWidth unitWidth = UNUM_UNIT_WIDTH_SHORT; |
151 | 0 | if (macros.unitWidth != UNUM_UNIT_WIDTH_COUNT) { |
152 | 0 | unitWidth = macros.unitWidth; |
153 | 0 | } |
154 | | // Use CLDR unit data for all MeasureUnits (not currency and not |
155 | | // no-unit), except use the dedicated percent pattern for percent and |
156 | | // permille. However, use the CLDR unit data for percent/permille if a |
157 | | // long name was requested OR if compact notation is being used, since |
158 | | // compact notation overrides the middle modifier (micros.modMiddle) |
159 | | // normally used for the percent pattern. |
160 | 0 | bool isCldrUnit = !isCurrency |
161 | 0 | && !isBaseUnit |
162 | 0 | && (unitWidth == UNUM_UNIT_WIDTH_FULL_NAME |
163 | 0 | || !(isPercent || isPermille) |
164 | 0 | || isCompactNotation |
165 | 0 | ); |
166 | 0 | bool isMixedUnit = isCldrUnit && (uprv_strcmp(macros.unit.getType(), "") == 0) && |
167 | 0 | macros.unit.getComplexity(status) == UMEASURE_UNIT_MIXED; |
168 | | |
169 | | // Select the numbering system. |
170 | 0 | LocalPointer<const NumberingSystem> nsLocal; |
171 | 0 | const NumberingSystem* ns; |
172 | 0 | if (macros.symbols.isNumberingSystem()) { |
173 | 0 | ns = macros.symbols.getNumberingSystem(); |
174 | 0 | } else { |
175 | | // TODO: Is there a way to avoid creating the NumberingSystem object? |
176 | 0 | ns = NumberingSystem::createInstance(macros.locale, status); |
177 | | // Give ownership to the function scope. |
178 | 0 | nsLocal.adoptInstead(ns); |
179 | 0 | } |
180 | 0 | const char* nsName = U_SUCCESS(status) ? ns->getName() : "latn"; |
181 | 0 | uprv_strncpy(fMicros.nsName, nsName, 8); |
182 | 0 | fMicros.nsName[8] = 0; // guarantee NUL-terminated |
183 | | |
184 | | // Default gender: none. |
185 | 0 | fMicros.gender = ""; |
186 | | |
187 | | // Resolve the symbols. Do this here because currency may need to customize them. |
188 | 0 | if (macros.symbols.isDecimalFormatSymbols()) { |
189 | 0 | fMicros.symbols = macros.symbols.getDecimalFormatSymbols(); |
190 | 0 | } else { |
191 | 0 | LocalPointer<DecimalFormatSymbols> newSymbols( |
192 | 0 | new DecimalFormatSymbols(macros.locale, *ns, status), status); |
193 | 0 | if (U_FAILURE(status)) { |
194 | 0 | return nullptr; |
195 | 0 | } |
196 | 0 | if (isCurrency) { |
197 | 0 | newSymbols->setCurrency(currency.getISOCurrency(), status); |
198 | 0 | if (U_FAILURE(status)) { |
199 | 0 | return nullptr; |
200 | 0 | } |
201 | 0 | } |
202 | 0 | fMicros.symbols = newSymbols.getAlias(); |
203 | 0 | fSymbols.adoptInstead(newSymbols.orphan()); |
204 | 0 | } |
205 | | |
206 | | // Load and parse the pattern string. It is used for grouping sizes and affixes only. |
207 | | // If we are formatting currency, check for a currency-specific pattern. |
208 | 0 | const char16_t* pattern = nullptr; |
209 | 0 | if (isCurrency && fMicros.symbols->getCurrencyPattern() != nullptr) { |
210 | 0 | pattern = fMicros.symbols->getCurrencyPattern(); |
211 | 0 | } |
212 | 0 | if (pattern == nullptr) { |
213 | 0 | CldrPatternStyle patternStyle; |
214 | 0 | if (isCldrUnit) { |
215 | 0 | patternStyle = CLDR_PATTERN_STYLE_DECIMAL; |
216 | 0 | } else if (isPercent || isPermille) { |
217 | 0 | patternStyle = CLDR_PATTERN_STYLE_PERCENT; |
218 | 0 | } else if (!isCurrency || unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) { |
219 | 0 | patternStyle = CLDR_PATTERN_STYLE_DECIMAL; |
220 | 0 | } else if (isAccounting) { |
221 | | // NOTE: Although ACCOUNTING and ACCOUNTING_ALWAYS are only supported in currencies right now, |
222 | | // the API contract allows us to add support to other units in the future. |
223 | 0 | patternStyle = CLDR_PATTERN_STYLE_ACCOUNTING; |
224 | 0 | } else { |
225 | 0 | patternStyle = CLDR_PATTERN_STYLE_CURRENCY; |
226 | 0 | } |
227 | 0 | pattern = utils::getPatternForStyle(macros.locale, nsName, patternStyle, status); |
228 | 0 | if (U_FAILURE(status)) { |
229 | 0 | return nullptr; |
230 | 0 | } |
231 | 0 | } |
232 | 0 | auto patternInfo = new ParsedPatternInfo(); |
233 | 0 | if (patternInfo == nullptr) { |
234 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
235 | 0 | return nullptr; |
236 | 0 | } |
237 | 0 | fPatternInfo.adoptInstead(patternInfo); |
238 | 0 | PatternParser::parseToPatternInfo(UnicodeString(pattern), *patternInfo, status); |
239 | 0 | if (U_FAILURE(status)) { |
240 | 0 | return nullptr; |
241 | 0 | } |
242 | | |
243 | | ///////////////////////////////////////////////////////////////////////////////////// |
244 | | /// START POPULATING THE DEFAULT MICROPROPS AND BUILDING THE MICROPROPS GENERATOR /// |
245 | | ///////////////////////////////////////////////////////////////////////////////////// |
246 | | |
247 | | // Unit Preferences and Conversions as our first step |
248 | 0 | if (macros.usage.isSet()) { |
249 | 0 | if (!isCldrUnit) { |
250 | | // We only support "usage" when the input unit is specified, and is |
251 | | // a CLDR Unit. |
252 | 0 | status = U_ILLEGAL_ARGUMENT_ERROR; |
253 | 0 | return nullptr; |
254 | 0 | } |
255 | 0 | auto usagePrefsHandler = |
256 | 0 | new UsagePrefsHandler(macros.locale, macros.unit, macros.usage.fValue, chain, status); |
257 | 0 | fUsagePrefsHandler.adoptInsteadAndCheckErrorCode(usagePrefsHandler, status); |
258 | 0 | chain = fUsagePrefsHandler.getAlias(); |
259 | 0 | } else if (isMixedUnit) { |
260 | 0 | auto unitConversionHandler = new UnitConversionHandler(macros.unit, chain, status); |
261 | 0 | fUnitConversionHandler.adoptInsteadAndCheckErrorCode(unitConversionHandler, status); |
262 | 0 | chain = fUnitConversionHandler.getAlias(); |
263 | 0 | } |
264 | | |
265 | | // Multiplier |
266 | 0 | if (macros.scale.isValid()) { |
267 | 0 | fMicros.helpers.multiplier.setAndChain(macros.scale, chain); |
268 | 0 | chain = &fMicros.helpers.multiplier; |
269 | 0 | } |
270 | | |
271 | | // Rounding strategy |
272 | 0 | Precision precision; |
273 | 0 | if (!macros.precision.isBogus()) { |
274 | 0 | precision = macros.precision; |
275 | 0 | } else if (isCompactNotation) { |
276 | 0 | precision = Precision::integer().withMinDigits(2); |
277 | 0 | } else if (isCurrency) { |
278 | 0 | precision = Precision::currency(UCURR_USAGE_STANDARD); |
279 | 0 | } else if (macros.usage.isSet()) { |
280 | | // Bogus Precision - it will get set in the UsagePrefsHandler instead |
281 | 0 | precision = Precision(); |
282 | 0 | } else { |
283 | 0 | precision = Precision::maxFraction(6); |
284 | 0 | } |
285 | 0 | UNumberFormatRoundingMode roundingMode; |
286 | 0 | roundingMode = macros.roundingMode; |
287 | 0 | fMicros.rounder = {precision, roundingMode, currency, status}; |
288 | 0 | if (U_FAILURE(status)) { |
289 | 0 | return nullptr; |
290 | 0 | } |
291 | | |
292 | | // Grouping strategy |
293 | 0 | if (!macros.grouper.isBogus()) { |
294 | 0 | fMicros.grouping = macros.grouper; |
295 | 0 | } else if (isCompactNotation) { |
296 | | // Compact notation uses minGrouping by default since ICU 59 |
297 | 0 | fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_MIN2); |
298 | 0 | } else { |
299 | 0 | fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_AUTO); |
300 | 0 | } |
301 | 0 | fMicros.grouping.setLocaleData(*fPatternInfo, macros.locale); |
302 | | |
303 | | // Padding strategy |
304 | 0 | if (!macros.padder.isBogus()) { |
305 | 0 | fMicros.padding = macros.padder; |
306 | 0 | } else { |
307 | 0 | fMicros.padding = Padder::none(); |
308 | 0 | } |
309 | | |
310 | | // Integer width |
311 | 0 | if (!macros.integerWidth.isBogus()) { |
312 | 0 | fMicros.integerWidth = macros.integerWidth; |
313 | 0 | } else { |
314 | 0 | fMicros.integerWidth = IntegerWidth::standard(); |
315 | 0 | } |
316 | | |
317 | | // Sign display |
318 | 0 | if (macros.sign != UNUM_SIGN_COUNT) { |
319 | 0 | fMicros.sign = macros.sign; |
320 | 0 | } else { |
321 | 0 | fMicros.sign = UNUM_SIGN_AUTO; |
322 | 0 | } |
323 | | |
324 | | // Decimal mark display |
325 | 0 | if (macros.decimal != UNUM_DECIMAL_SEPARATOR_COUNT) { |
326 | 0 | fMicros.decimal = macros.decimal; |
327 | 0 | } else { |
328 | 0 | fMicros.decimal = UNUM_DECIMAL_SEPARATOR_AUTO; |
329 | 0 | } |
330 | | |
331 | | // Use monetary separator symbols |
332 | 0 | fMicros.useCurrency = isCurrency; |
333 | | |
334 | | // Inner modifier (scientific notation) |
335 | 0 | if (macros.notation.fType == Notation::NTN_SCIENTIFIC) { |
336 | 0 | auto newScientificHandler = new ScientificHandler(¯os.notation, fMicros.symbols, chain); |
337 | 0 | if (newScientificHandler == nullptr) { |
338 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
339 | 0 | return nullptr; |
340 | 0 | } |
341 | 0 | fScientificHandler.adoptInstead(newScientificHandler); |
342 | 0 | chain = fScientificHandler.getAlias(); |
343 | 0 | } else { |
344 | | // No inner modifier required |
345 | 0 | fMicros.modInner = &fMicros.helpers.emptyStrongModifier; |
346 | 0 | } |
347 | | |
348 | | // Middle modifier (patterns, positive/negative, currency symbols, percent) |
349 | 0 | auto patternModifier = new MutablePatternModifier(false); |
350 | 0 | if (patternModifier == nullptr) { |
351 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
352 | 0 | return nullptr; |
353 | 0 | } |
354 | 0 | fPatternModifier.adoptInstead(patternModifier); |
355 | 0 | patternModifier->setPatternInfo( |
356 | 0 | macros.affixProvider != nullptr ? macros.affixProvider |
357 | 0 | : static_cast<const AffixPatternProvider*>(fPatternInfo.getAlias()), |
358 | 0 | kUndefinedField); |
359 | 0 | patternModifier->setPatternAttributes(fMicros.sign, isPermille, macros.approximately); |
360 | 0 | if (patternModifier->needsPlurals()) { |
361 | 0 | patternModifier->setSymbols( |
362 | 0 | fMicros.symbols, |
363 | 0 | currency, |
364 | 0 | unitWidth, |
365 | 0 | resolvePluralRules(macros.rules, macros.locale, status), |
366 | 0 | status); |
367 | 0 | } else { |
368 | 0 | patternModifier->setSymbols(fMicros.symbols, currency, unitWidth, nullptr, status); |
369 | 0 | } |
370 | 0 | if (safe) { |
371 | 0 | fImmutablePatternModifier.adoptInsteadAndCheckErrorCode(patternModifier->createImmutable(status), |
372 | 0 | status); |
373 | 0 | } |
374 | 0 | if (U_FAILURE(status)) { |
375 | 0 | return nullptr; |
376 | 0 | } |
377 | | |
378 | | // Outer modifier (CLDR units and currency long names) |
379 | 0 | if (isCldrUnit) { |
380 | 0 | const char *unitDisplayCase = ""; |
381 | 0 | if (macros.unitDisplayCase.isSet()) { |
382 | 0 | unitDisplayCase = macros.unitDisplayCase.fValue; |
383 | 0 | } |
384 | 0 | if (macros.usage.isSet()) { |
385 | 0 | fLongNameMultiplexer.adoptInsteadAndCheckErrorCode( |
386 | 0 | LongNameMultiplexer::forMeasureUnits( |
387 | 0 | macros.locale, *fUsagePrefsHandler->getOutputUnits(), unitWidth, unitDisplayCase, |
388 | 0 | resolvePluralRules(macros.rules, macros.locale, status), chain, status), |
389 | 0 | status); |
390 | 0 | chain = fLongNameMultiplexer.getAlias(); |
391 | 0 | } else if (isMixedUnit) { |
392 | 0 | fMixedUnitLongNameHandler.adoptInsteadAndCheckErrorCode(new MixedUnitLongNameHandler(), |
393 | 0 | status); |
394 | 0 | MixedUnitLongNameHandler::forMeasureUnit( |
395 | 0 | macros.locale, macros.unit, unitWidth, unitDisplayCase, |
396 | 0 | resolvePluralRules(macros.rules, macros.locale, status), chain, |
397 | 0 | fMixedUnitLongNameHandler.getAlias(), status); |
398 | 0 | chain = fMixedUnitLongNameHandler.getAlias(); |
399 | 0 | } else { |
400 | 0 | MeasureUnit unit = macros.unit; |
401 | 0 | if (!utils::unitIsBaseUnit(macros.perUnit)) { |
402 | 0 | unit = unit.product(macros.perUnit.reciprocal(status), status); |
403 | | // This isn't strictly necessary, but was what we specced out |
404 | | // when perUnit became a backward-compatibility thing: |
405 | | // unit/perUnit use case is only valid if both units are |
406 | | // built-ins, or the product is a built-in. |
407 | 0 | if (uprv_strcmp(unit.getType(), "") == 0 && |
408 | 0 | (uprv_strcmp(macros.unit.getType(), "") == 0 || |
409 | 0 | uprv_strcmp(macros.perUnit.getType(), "") == 0)) { |
410 | 0 | status = U_UNSUPPORTED_ERROR; |
411 | 0 | return nullptr; |
412 | 0 | } |
413 | 0 | } |
414 | 0 | fLongNameHandler.adoptInsteadAndCheckErrorCode(new LongNameHandler(), status); |
415 | 0 | LongNameHandler::forMeasureUnit(macros.locale, unit, unitWidth, unitDisplayCase, |
416 | 0 | resolvePluralRules(macros.rules, macros.locale, status), |
417 | 0 | chain, fLongNameHandler.getAlias(), status); |
418 | 0 | chain = fLongNameHandler.getAlias(); |
419 | 0 | } |
420 | 0 | } else if (isCurrency && unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) { |
421 | 0 | fLongNameHandler.adoptInsteadAndCheckErrorCode( |
422 | 0 | LongNameHandler::forCurrencyLongNames( |
423 | 0 | macros.locale, currency, resolvePluralRules(macros.rules, macros.locale, status), chain, |
424 | 0 | status), |
425 | 0 | status); |
426 | 0 | chain = fLongNameHandler.getAlias(); |
427 | 0 | } else { |
428 | | // No outer modifier required |
429 | 0 | fMicros.modOuter = &fMicros.helpers.emptyWeakModifier; |
430 | 0 | } |
431 | 0 | if (U_FAILURE(status)) { |
432 | 0 | return nullptr; |
433 | 0 | } |
434 | | |
435 | | // Compact notation |
436 | 0 | if (isCompactNotation) { |
437 | 0 | CompactType compactType = (isCurrency && unitWidth != UNUM_UNIT_WIDTH_FULL_NAME) |
438 | 0 | ? CompactType::TYPE_CURRENCY : CompactType::TYPE_DECIMAL; |
439 | 0 | auto newCompactHandler = new CompactHandler( |
440 | 0 | macros.notation.fUnion.compactStyle, |
441 | 0 | macros.locale, |
442 | 0 | nsName, |
443 | 0 | compactType, |
444 | 0 | resolvePluralRules(macros.rules, macros.locale, status), |
445 | 0 | patternModifier, |
446 | 0 | safe, |
447 | 0 | chain, |
448 | 0 | status); |
449 | 0 | if (U_FAILURE(status)) { |
450 | 0 | return nullptr; |
451 | 0 | } |
452 | 0 | if (newCompactHandler == nullptr) { |
453 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
454 | 0 | return nullptr; |
455 | 0 | } |
456 | 0 | fCompactHandler.adoptInstead(newCompactHandler); |
457 | 0 | chain = fCompactHandler.getAlias(); |
458 | 0 | } |
459 | 0 | if (U_FAILURE(status)) { |
460 | 0 | return nullptr; |
461 | 0 | } |
462 | | |
463 | | // Always add the pattern modifier as the last element of the chain. |
464 | 0 | if (safe) { |
465 | 0 | fImmutablePatternModifier->addToChain(chain); |
466 | 0 | chain = fImmutablePatternModifier.getAlias(); |
467 | 0 | } else { |
468 | 0 | patternModifier->addToChain(chain); |
469 | 0 | chain = patternModifier; |
470 | 0 | } |
471 | |
|
472 | 0 | return chain; |
473 | 0 | } |
474 | | |
475 | | const PluralRules* |
476 | | NumberFormatterImpl::resolvePluralRules(const PluralRules* rulesPtr, const Locale& locale, |
477 | 0 | UErrorCode& status) { |
478 | 0 | if (rulesPtr != nullptr) { |
479 | 0 | return rulesPtr; |
480 | 0 | } |
481 | | // Lazily create PluralRules |
482 | 0 | if (fRules.isNull()) { |
483 | 0 | fRules.adoptInstead(PluralRules::forLocale(locale, status)); |
484 | 0 | } |
485 | 0 | return fRules.getAlias(); |
486 | 0 | } |
487 | | |
488 | | int32_t NumberFormatterImpl::writeAffixes(const MicroProps& micros, FormattedStringBuilder& string, |
489 | 0 | int32_t start, int32_t end, UErrorCode& status) { |
490 | 0 | U_ASSERT(micros.modOuter != nullptr); |
491 | | // Always apply the inner modifier (which is "strong"). |
492 | 0 | int32_t length = micros.modInner->apply(string, start, end, status); |
493 | 0 | if (micros.padding.isValid()) { |
494 | 0 | length += micros.padding |
495 | 0 | .padAndApply(*micros.modMiddle, *micros.modOuter, string, start, length + end, status); |
496 | 0 | } else { |
497 | 0 | length += micros.modMiddle->apply(string, start, length + end, status); |
498 | 0 | length += micros.modOuter->apply(string, start, length + end, status); |
499 | 0 | } |
500 | 0 | return length; |
501 | 0 | } |
502 | | |
503 | | int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuantity& quantity, |
504 | | FormattedStringBuilder& string, int32_t index, |
505 | 0 | UErrorCode& status) { |
506 | 0 | int32_t length = 0; |
507 | 0 | if (quantity.isInfinite()) { |
508 | 0 | length += string.insert( |
509 | 0 | length + index, |
510 | 0 | micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kInfinitySymbol), |
511 | 0 | {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD}, |
512 | 0 | status); |
513 | |
|
514 | 0 | } else if (quantity.isNaN()) { |
515 | 0 | length += string.insert( |
516 | 0 | length + index, |
517 | 0 | micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kNaNSymbol), |
518 | 0 | {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD}, |
519 | 0 | status); |
520 | |
|
521 | 0 | } else { |
522 | | // Add the integer digits |
523 | 0 | length += writeIntegerDigits(micros, quantity, string, length + index, status); |
524 | | |
525 | | // Add the decimal point |
526 | 0 | if (quantity.getLowerDisplayMagnitude() < 0 || micros.decimal == UNUM_DECIMAL_SEPARATOR_ALWAYS) { |
527 | 0 | length += string.insert( |
528 | 0 | length + index, |
529 | 0 | micros.useCurrency ? micros.symbols->getSymbol( |
530 | 0 | DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol) : micros |
531 | 0 | .symbols |
532 | 0 | ->getSymbol( |
533 | 0 | DecimalFormatSymbols::ENumberFormatSymbol::kDecimalSeparatorSymbol), |
534 | 0 | {UFIELD_CATEGORY_NUMBER, UNUM_DECIMAL_SEPARATOR_FIELD}, |
535 | 0 | status); |
536 | 0 | } |
537 | | |
538 | | // Add the fraction digits |
539 | 0 | length += writeFractionDigits(micros, quantity, string, length + index, status); |
540 | |
|
541 | 0 | if (length == 0) { |
542 | | // Force output of the digit for value 0 |
543 | 0 | length += utils::insertDigitFromSymbols( |
544 | 0 | string, |
545 | 0 | index, |
546 | 0 | 0, |
547 | 0 | *micros.symbols, |
548 | 0 | {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD}, |
549 | 0 | status); |
550 | 0 | } |
551 | 0 | } |
552 | |
|
553 | 0 | return length; |
554 | 0 | } |
555 | | |
556 | | int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, DecimalQuantity& quantity, |
557 | | FormattedStringBuilder& string, int32_t index, |
558 | 0 | UErrorCode& status) { |
559 | 0 | int length = 0; |
560 | 0 | int integerCount = quantity.getUpperDisplayMagnitude() + 1; |
561 | 0 | for (int i = 0; i < integerCount; i++) { |
562 | | // Add grouping separator |
563 | 0 | if (micros.grouping.groupAtPosition(i, quantity)) { |
564 | 0 | length += string.insert( |
565 | 0 | index, |
566 | 0 | micros.useCurrency ? micros.symbols->getSymbol( |
567 | 0 | DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol) |
568 | 0 | : micros.symbols->getSymbol( |
569 | 0 | DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol), |
570 | 0 | {UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD}, |
571 | 0 | status); |
572 | 0 | } |
573 | | |
574 | | // Get and append the next digit value |
575 | 0 | int8_t nextDigit = quantity.getDigit(i); |
576 | 0 | length += utils::insertDigitFromSymbols( |
577 | 0 | string, |
578 | 0 | index, |
579 | 0 | nextDigit, |
580 | 0 | *micros.symbols, |
581 | 0 | {UFIELD_CATEGORY_NUMBER, |
582 | 0 | UNUM_INTEGER_FIELD}, |
583 | 0 | status); |
584 | 0 | } |
585 | 0 | return length; |
586 | 0 | } |
587 | | |
588 | | int32_t NumberFormatterImpl::writeFractionDigits(const MicroProps& micros, DecimalQuantity& quantity, |
589 | | FormattedStringBuilder& string, int32_t index, |
590 | 0 | UErrorCode& status) { |
591 | 0 | int length = 0; |
592 | 0 | int fractionCount = -quantity.getLowerDisplayMagnitude(); |
593 | 0 | for (int i = 0; i < fractionCount; i++) { |
594 | | // Get and append the next digit value |
595 | 0 | int8_t nextDigit = quantity.getDigit(-i - 1); |
596 | 0 | length += utils::insertDigitFromSymbols( |
597 | 0 | string, |
598 | 0 | length + index, |
599 | 0 | nextDigit, |
600 | 0 | *micros.symbols, |
601 | 0 | {UFIELD_CATEGORY_NUMBER, UNUM_FRACTION_FIELD}, |
602 | 0 | status); |
603 | 0 | } |
604 | 0 | return length; |
605 | 0 | } |
606 | | |
607 | | #endif /* #if !UCONFIG_NO_FORMATTING */ |