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 | namespace { |
28 | |
29 | struct CurrencyFormatInfoResult { |
30 | bool exists; |
31 | const char16_t* pattern; |
32 | const char16_t* decimalSeparator; |
33 | const char16_t* groupingSeparator; |
34 | }; |
35 | |
36 | CurrencyFormatInfoResult |
37 | getCurrencyFormatInfo(const Locale& locale, const char* isoCode, UErrorCode& status) { |
38 | // TODO: Load this data in a centralized location like ICU4J? |
39 | // TODO: Move this into the CurrencySymbols class? |
40 | // TODO: Parts of this same data are loaded in dcfmtsym.cpp; should clean up. |
41 | CurrencyFormatInfoResult result = {false, nullptr, nullptr, nullptr}; |
42 | if (U_FAILURE(status)) { return result; } |
43 | CharString key; |
44 | key.append("Currencies/" , status); |
45 | key.append(isoCode, status); |
46 | UErrorCode localStatus = status; |
47 | LocalUResourceBundlePointer bundle(ures_open(U_ICUDATA_CURR, locale.getName(), &localStatus)); |
48 | ures_getByKeyWithFallback(bundle.getAlias(), key.data(), bundle.getAlias(), &localStatus); |
49 | if (U_SUCCESS(localStatus) && |
50 | ures_getSize(bundle.getAlias()) > 2) { // the length is 3 if more data is present |
51 | ures_getByIndex(bundle.getAlias(), 2, bundle.getAlias(), &localStatus); |
52 | int32_t dummy; |
53 | result.exists = true; |
54 | result.pattern = ures_getStringByIndex(bundle.getAlias(), 0, &dummy, &localStatus); |
55 | result.decimalSeparator = ures_getStringByIndex(bundle.getAlias(), 1, &dummy, &localStatus); |
56 | result.groupingSeparator = ures_getStringByIndex(bundle.getAlias(), 2, &dummy, &localStatus); |
57 | status = localStatus; |
58 | } else if (localStatus != U_MISSING_RESOURCE_ERROR) { |
59 | status = localStatus; |
60 | } |
61 | return result; |
62 | } |
63 | |
64 | } // namespace |
65 | |
66 | |
67 | MicroPropsGenerator::~MicroPropsGenerator() = default; |
68 | |
69 | |
70 | NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, UErrorCode& status) |
71 | : NumberFormatterImpl(macros, true, status) { |
72 | } |
73 | |
74 | int32_t NumberFormatterImpl::formatStatic(const MacroProps& macros, DecimalQuantity& inValue, |
75 | FormattedStringBuilder& outString, UErrorCode& status) { |
76 | NumberFormatterImpl impl(macros, false, status); |
77 | MicroProps& micros = impl.preProcessUnsafe(inValue, status); |
78 | if (U_FAILURE(status)) { return 0; } |
79 | int32_t length = writeNumber(micros, inValue, outString, 0, status); |
80 | length += writeAffixes(micros, outString, 0, length, status); |
81 | return length; |
82 | } |
83 | |
84 | int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, Signum signum, |
85 | StandardPlural::Form plural, |
86 | FormattedStringBuilder& outString, UErrorCode& status) { |
87 | NumberFormatterImpl impl(macros, false, status); |
88 | return impl.getPrefixSuffixUnsafe(signum, plural, outString, status); |
89 | } |
90 | |
91 | // NOTE: C++ SPECIFIC DIFFERENCE FROM JAVA: |
92 | // The "safe" apply method uses a new MicroProps. In the MicroPropsGenerator, fMicros is copied into the new instance. |
93 | // The "unsafe" method simply re-uses fMicros, eliminating the extra copy operation. |
94 | // See MicroProps::processQuantity() for details. |
95 | |
96 | int32_t NumberFormatterImpl::format(DecimalQuantity& inValue, FormattedStringBuilder& outString, |
97 | UErrorCode& status) const { |
98 | MicroProps micros; |
99 | preProcess(inValue, micros, status); |
100 | if (U_FAILURE(status)) { return 0; } |
101 | int32_t length = writeNumber(micros, inValue, outString, 0, status); |
102 | length += writeAffixes(micros, outString, 0, length, status); |
103 | return length; |
104 | } |
105 | |
106 | void NumberFormatterImpl::preProcess(DecimalQuantity& inValue, MicroProps& microsOut, |
107 | UErrorCode& status) const { |
108 | if (U_FAILURE(status)) { return; } |
109 | if (fMicroPropsGenerator == nullptr) { |
110 | status = U_INTERNAL_PROGRAM_ERROR; |
111 | return; |
112 | } |
113 | fMicroPropsGenerator->processQuantity(inValue, microsOut, status); |
114 | microsOut.rounder.apply(inValue, status); |
115 | microsOut.integerWidth.apply(inValue, status); |
116 | } |
117 | |
118 | MicroProps& NumberFormatterImpl::preProcessUnsafe(DecimalQuantity& inValue, UErrorCode& status) { |
119 | if (U_FAILURE(status)) { |
120 | return fMicros; // must always return a value |
121 | } |
122 | if (fMicroPropsGenerator == nullptr) { |
123 | status = U_INTERNAL_PROGRAM_ERROR; |
124 | return fMicros; // must always return a value |
125 | } |
126 | fMicroPropsGenerator->processQuantity(inValue, fMicros, status); |
127 | fMicros.rounder.apply(inValue, status); |
128 | fMicros.integerWidth.apply(inValue, status); |
129 | return fMicros; |
130 | } |
131 | |
132 | int32_t NumberFormatterImpl::getPrefixSuffix(Signum signum, StandardPlural::Form plural, |
133 | FormattedStringBuilder& outString, UErrorCode& status) const { |
134 | if (U_FAILURE(status)) { return 0; } |
135 | // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier). |
136 | // Safe path: use fImmutablePatternModifier. |
137 | const Modifier* modifier = fImmutablePatternModifier->getModifier(signum, plural); |
138 | modifier->apply(outString, 0, 0, status); |
139 | if (U_FAILURE(status)) { return 0; } |
140 | return modifier->getPrefixLength(); |
141 | } |
142 | |
143 | int32_t NumberFormatterImpl::getPrefixSuffixUnsafe(Signum signum, StandardPlural::Form plural, |
144 | FormattedStringBuilder& outString, UErrorCode& status) { |
145 | if (U_FAILURE(status)) { return 0; } |
146 | // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier). |
147 | // Unsafe path: use fPatternModifier. |
148 | fPatternModifier->setNumberProperties(signum, plural); |
149 | fPatternModifier->apply(outString, 0, 0, status); |
150 | if (U_FAILURE(status)) { return 0; } |
151 | return fPatternModifier->getPrefixLength(); |
152 | } |
153 | |
154 | NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, bool safe, UErrorCode& status) { |
155 | fMicroPropsGenerator = macrosToMicroGenerator(macros, safe, status); |
156 | } |
157 | |
158 | ////////// |
159 | |
160 | const MicroPropsGenerator* |
161 | NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe, UErrorCode& status) { |
162 | if (U_FAILURE(status)) { return nullptr; } |
163 | const MicroPropsGenerator* chain = &fMicros; |
164 | |
165 | // Check that macros is error-free before continuing. |
166 | if (macros.copyErrorTo(status)) { |
167 | return nullptr; |
168 | } |
169 | |
170 | // TODO: Accept currency symbols from DecimalFormatSymbols? |
171 | |
172 | // Pre-compute a few values for efficiency. |
173 | bool isCurrency = utils::unitIsCurrency(macros.unit); |
174 | bool isNoUnit = utils::unitIsNoUnit(macros.unit); |
175 | bool isPercent = utils::unitIsPercent(macros.unit); |
176 | bool isPermille = utils::unitIsPermille(macros.unit); |
177 | bool isAccounting = |
178 | macros.sign == UNUM_SIGN_ACCOUNTING || macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS || |
179 | macros.sign == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO; |
180 | CurrencyUnit currency(u"" , status); |
181 | if (isCurrency) { |
182 | currency = CurrencyUnit(macros.unit, status); // Restore CurrencyUnit from MeasureUnit |
183 | } |
184 | const CurrencySymbols* currencySymbols; |
185 | if (macros.currencySymbols != nullptr) { |
186 | // Used by the DecimalFormat code path |
187 | currencySymbols = macros.currencySymbols; |
188 | } else { |
189 | fWarehouse.fCurrencySymbols = {currency, macros.locale, status}; |
190 | currencySymbols = &fWarehouse.fCurrencySymbols; |
191 | } |
192 | UNumberUnitWidth unitWidth = UNUM_UNIT_WIDTH_SHORT; |
193 | if (macros.unitWidth != UNUM_UNIT_WIDTH_COUNT) { |
194 | unitWidth = macros.unitWidth; |
195 | } |
196 | bool isCldrUnit = !isCurrency && !isNoUnit && |
197 | (unitWidth == UNUM_UNIT_WIDTH_FULL_NAME || !(isPercent || isPermille)); |
198 | |
199 | // Select the numbering system. |
200 | LocalPointer<const NumberingSystem> nsLocal; |
201 | const NumberingSystem* ns; |
202 | if (macros.symbols.isNumberingSystem()) { |
203 | ns = macros.symbols.getNumberingSystem(); |
204 | } else { |
205 | // TODO: Is there a way to avoid creating the NumberingSystem object? |
206 | ns = NumberingSystem::createInstance(macros.locale, status); |
207 | // Give ownership to the function scope. |
208 | nsLocal.adoptInstead(ns); |
209 | } |
210 | const char* nsName = U_SUCCESS(status) ? ns->getName() : "latn" ; |
211 | uprv_strncpy(fMicros.nsName, nsName, 8); |
212 | fMicros.nsName[8] = 0; // guarantee NUL-terminated |
213 | |
214 | // Resolve the symbols. Do this here because currency may need to customize them. |
215 | if (macros.symbols.isDecimalFormatSymbols()) { |
216 | fMicros.symbols = macros.symbols.getDecimalFormatSymbols(); |
217 | } else { |
218 | auto newSymbols = new DecimalFormatSymbols(macros.locale, *ns, status); |
219 | if (newSymbols == nullptr) { |
220 | status = U_MEMORY_ALLOCATION_ERROR; |
221 | return nullptr; |
222 | } |
223 | fMicros.symbols = newSymbols; |
224 | // Give ownership to the NumberFormatterImpl. |
225 | fSymbols.adoptInstead(fMicros.symbols); |
226 | } |
227 | |
228 | // Load and parse the pattern string. It is used for grouping sizes and affixes only. |
229 | // If we are formatting currency, check for a currency-specific pattern. |
230 | const char16_t* pattern = nullptr; |
231 | if (isCurrency) { |
232 | CurrencyFormatInfoResult info = getCurrencyFormatInfo( |
233 | macros.locale, currency.getSubtype(), status); |
234 | if (info.exists) { |
235 | pattern = info.pattern; |
236 | // It's clunky to clone an object here, but this code is not frequently executed. |
237 | auto symbols = new DecimalFormatSymbols(*fMicros.symbols); |
238 | if (symbols == nullptr) { |
239 | status = U_MEMORY_ALLOCATION_ERROR; |
240 | return nullptr; |
241 | } |
242 | fMicros.symbols = symbols; |
243 | fSymbols.adoptInstead(symbols); |
244 | symbols->setSymbol( |
245 | DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol, |
246 | UnicodeString(info.decimalSeparator), |
247 | FALSE); |
248 | symbols->setSymbol( |
249 | DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol, |
250 | UnicodeString(info.groupingSeparator), |
251 | FALSE); |
252 | } |
253 | } |
254 | if (pattern == nullptr) { |
255 | CldrPatternStyle patternStyle; |
256 | if (isCldrUnit) { |
257 | patternStyle = CLDR_PATTERN_STYLE_DECIMAL; |
258 | } else if (isPercent || isPermille) { |
259 | patternStyle = CLDR_PATTERN_STYLE_PERCENT; |
260 | } else if (!isCurrency || unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) { |
261 | patternStyle = CLDR_PATTERN_STYLE_DECIMAL; |
262 | } else if (isAccounting) { |
263 | // NOTE: Although ACCOUNTING and ACCOUNTING_ALWAYS are only supported in currencies right now, |
264 | // the API contract allows us to add support to other units in the future. |
265 | patternStyle = CLDR_PATTERN_STYLE_ACCOUNTING; |
266 | } else { |
267 | patternStyle = CLDR_PATTERN_STYLE_CURRENCY; |
268 | } |
269 | pattern = utils::getPatternForStyle(macros.locale, nsName, patternStyle, status); |
270 | } |
271 | auto patternInfo = new ParsedPatternInfo(); |
272 | if (patternInfo == nullptr) { |
273 | status = U_MEMORY_ALLOCATION_ERROR; |
274 | return nullptr; |
275 | } |
276 | fPatternInfo.adoptInstead(patternInfo); |
277 | PatternParser::parseToPatternInfo(UnicodeString(pattern), *patternInfo, status); |
278 | |
279 | ///////////////////////////////////////////////////////////////////////////////////// |
280 | /// START POPULATING THE DEFAULT MICROPROPS AND BUILDING THE MICROPROPS GENERATOR /// |
281 | ///////////////////////////////////////////////////////////////////////////////////// |
282 | |
283 | // Multiplier |
284 | if (macros.scale.isValid()) { |
285 | fMicros.helpers.multiplier.setAndChain(macros.scale, chain); |
286 | chain = &fMicros.helpers.multiplier; |
287 | } |
288 | |
289 | // Rounding strategy |
290 | Precision precision; |
291 | if (!macros.precision.isBogus()) { |
292 | precision = macros.precision; |
293 | } else if (macros.notation.fType == Notation::NTN_COMPACT) { |
294 | precision = Precision::integer().withMinDigits(2); |
295 | } else if (isCurrency) { |
296 | precision = Precision::currency(UCURR_USAGE_STANDARD); |
297 | } else { |
298 | precision = Precision::maxFraction(6); |
299 | } |
300 | UNumberFormatRoundingMode roundingMode; |
301 | if (macros.roundingMode != kDefaultMode) { |
302 | roundingMode = macros.roundingMode; |
303 | } else { |
304 | // Temporary until ICU 64 |
305 | roundingMode = precision.fRoundingMode; |
306 | } |
307 | fMicros.rounder = {precision, roundingMode, currency, status}; |
308 | |
309 | // Grouping strategy |
310 | if (!macros.grouper.isBogus()) { |
311 | fMicros.grouping = macros.grouper; |
312 | } else if (macros.notation.fType == Notation::NTN_COMPACT) { |
313 | // Compact notation uses minGrouping by default since ICU 59 |
314 | fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_MIN2); |
315 | } else { |
316 | fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_AUTO); |
317 | } |
318 | fMicros.grouping.setLocaleData(*fPatternInfo, macros.locale); |
319 | |
320 | // Padding strategy |
321 | if (!macros.padder.isBogus()) { |
322 | fMicros.padding = macros.padder; |
323 | } else { |
324 | fMicros.padding = Padder::none(); |
325 | } |
326 | |
327 | // Integer width |
328 | if (!macros.integerWidth.isBogus()) { |
329 | fMicros.integerWidth = macros.integerWidth; |
330 | } else { |
331 | fMicros.integerWidth = IntegerWidth::standard(); |
332 | } |
333 | |
334 | // Sign display |
335 | if (macros.sign != UNUM_SIGN_COUNT) { |
336 | fMicros.sign = macros.sign; |
337 | } else { |
338 | fMicros.sign = UNUM_SIGN_AUTO; |
339 | } |
340 | |
341 | // Decimal mark display |
342 | if (macros.decimal != UNUM_DECIMAL_SEPARATOR_COUNT) { |
343 | fMicros.decimal = macros.decimal; |
344 | } else { |
345 | fMicros.decimal = UNUM_DECIMAL_SEPARATOR_AUTO; |
346 | } |
347 | |
348 | // Use monetary separator symbols |
349 | fMicros.useCurrency = isCurrency; |
350 | |
351 | // Inner modifier (scientific notation) |
352 | if (macros.notation.fType == Notation::NTN_SCIENTIFIC) { |
353 | auto newScientificHandler = new ScientificHandler(¯os.notation, fMicros.symbols, chain); |
354 | if (newScientificHandler == nullptr) { |
355 | status = U_MEMORY_ALLOCATION_ERROR; |
356 | return nullptr; |
357 | } |
358 | fScientificHandler.adoptInstead(newScientificHandler); |
359 | chain = fScientificHandler.getAlias(); |
360 | } else { |
361 | // No inner modifier required |
362 | fMicros.modInner = &fMicros.helpers.emptyStrongModifier; |
363 | } |
364 | |
365 | // Middle modifier (patterns, positive/negative, currency symbols, percent) |
366 | auto patternModifier = new MutablePatternModifier(false); |
367 | if (patternModifier == nullptr) { |
368 | status = U_MEMORY_ALLOCATION_ERROR; |
369 | return nullptr; |
370 | } |
371 | fPatternModifier.adoptInstead(patternModifier); |
372 | patternModifier->setPatternInfo( |
373 | macros.affixProvider != nullptr ? macros.affixProvider |
374 | : static_cast<const AffixPatternProvider*>(fPatternInfo.getAlias()), |
375 | UNUM_FIELD_COUNT); |
376 | patternModifier->setPatternAttributes(fMicros.sign, isPermille); |
377 | if (patternModifier->needsPlurals()) { |
378 | patternModifier->setSymbols( |
379 | fMicros.symbols, |
380 | currencySymbols, |
381 | unitWidth, |
382 | resolvePluralRules(macros.rules, macros.locale, status)); |
383 | } else { |
384 | patternModifier->setSymbols(fMicros.symbols, currencySymbols, unitWidth, nullptr); |
385 | } |
386 | if (safe) { |
387 | fImmutablePatternModifier.adoptInstead(patternModifier->createImmutableAndChain(chain, status)); |
388 | chain = fImmutablePatternModifier.getAlias(); |
389 | } else { |
390 | patternModifier->addToChain(chain); |
391 | chain = patternModifier; |
392 | } |
393 | |
394 | // Outer modifier (CLDR units and currency long names) |
395 | if (isCldrUnit) { |
396 | fLongNameHandler.adoptInstead( |
397 | LongNameHandler::forMeasureUnit( |
398 | macros.locale, |
399 | macros.unit, |
400 | macros.perUnit, |
401 | unitWidth, |
402 | resolvePluralRules(macros.rules, macros.locale, status), |
403 | chain, |
404 | status)); |
405 | chain = fLongNameHandler.getAlias(); |
406 | } else if (isCurrency && unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) { |
407 | fLongNameHandler.adoptInstead( |
408 | LongNameHandler::forCurrencyLongNames( |
409 | macros.locale, |
410 | currency, |
411 | resolvePluralRules(macros.rules, macros.locale, status), |
412 | chain, |
413 | status)); |
414 | chain = fLongNameHandler.getAlias(); |
415 | } else { |
416 | // No outer modifier required |
417 | fMicros.modOuter = &fMicros.helpers.emptyWeakModifier; |
418 | } |
419 | |
420 | // Compact notation |
421 | // NOTE: Compact notation can (but might not) override the middle modifier and rounding. |
422 | // It therefore needs to go at the end of the chain. |
423 | if (macros.notation.fType == Notation::NTN_COMPACT) { |
424 | CompactType compactType = (isCurrency && unitWidth != UNUM_UNIT_WIDTH_FULL_NAME) |
425 | ? CompactType::TYPE_CURRENCY : CompactType::TYPE_DECIMAL; |
426 | auto newCompactHandler = new CompactHandler( |
427 | macros.notation.fUnion.compactStyle, |
428 | macros.locale, |
429 | nsName, |
430 | compactType, |
431 | resolvePluralRules(macros.rules, macros.locale, status), |
432 | safe ? patternModifier : nullptr, |
433 | chain, |
434 | status); |
435 | if (newCompactHandler == nullptr) { |
436 | status = U_MEMORY_ALLOCATION_ERROR; |
437 | return nullptr; |
438 | } |
439 | fCompactHandler.adoptInstead(newCompactHandler); |
440 | chain = fCompactHandler.getAlias(); |
441 | } |
442 | |
443 | return chain; |
444 | } |
445 | |
446 | const PluralRules* |
447 | NumberFormatterImpl::resolvePluralRules(const PluralRules* rulesPtr, const Locale& locale, |
448 | UErrorCode& status) { |
449 | if (rulesPtr != nullptr) { |
450 | return rulesPtr; |
451 | } |
452 | // Lazily create PluralRules |
453 | if (fRules.isNull()) { |
454 | fRules.adoptInstead(PluralRules::forLocale(locale, status)); |
455 | } |
456 | return fRules.getAlias(); |
457 | } |
458 | |
459 | int32_t NumberFormatterImpl::writeAffixes(const MicroProps& micros, FormattedStringBuilder& string, |
460 | int32_t start, int32_t end, UErrorCode& status) { |
461 | // Always apply the inner modifier (which is "strong"). |
462 | int32_t length = micros.modInner->apply(string, start, end, status); |
463 | if (micros.padding.isValid()) { |
464 | length += micros.padding |
465 | .padAndApply(*micros.modMiddle, *micros.modOuter, string, start, length + end, status); |
466 | } else { |
467 | length += micros.modMiddle->apply(string, start, length + end, status); |
468 | length += micros.modOuter->apply(string, start, length + end, status); |
469 | } |
470 | return length; |
471 | } |
472 | |
473 | int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuantity& quantity, |
474 | FormattedStringBuilder& string, int32_t index, |
475 | UErrorCode& status) { |
476 | int32_t length = 0; |
477 | if (quantity.isInfinite()) { |
478 | length += string.insert( |
479 | length + index, |
480 | micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kInfinitySymbol), |
481 | UNUM_INTEGER_FIELD, |
482 | status); |
483 | |
484 | } else if (quantity.isNaN()) { |
485 | length += string.insert( |
486 | length + index, |
487 | micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kNaNSymbol), |
488 | UNUM_INTEGER_FIELD, |
489 | status); |
490 | |
491 | } else { |
492 | // Add the integer digits |
493 | length += writeIntegerDigits(micros, quantity, string, length + index, status); |
494 | |
495 | // Add the decimal point |
496 | if (quantity.getLowerDisplayMagnitude() < 0 || micros.decimal == UNUM_DECIMAL_SEPARATOR_ALWAYS) { |
497 | length += string.insert( |
498 | length + index, |
499 | micros.useCurrency ? micros.symbols->getSymbol( |
500 | DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol) : micros |
501 | .symbols |
502 | ->getSymbol( |
503 | DecimalFormatSymbols::ENumberFormatSymbol::kDecimalSeparatorSymbol), |
504 | UNUM_DECIMAL_SEPARATOR_FIELD, |
505 | status); |
506 | } |
507 | |
508 | // Add the fraction digits |
509 | length += writeFractionDigits(micros, quantity, string, length + index, status); |
510 | } |
511 | |
512 | return length; |
513 | } |
514 | |
515 | int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, DecimalQuantity& quantity, |
516 | FormattedStringBuilder& string, int32_t index, |
517 | UErrorCode& status) { |
518 | int length = 0; |
519 | int integerCount = quantity.getUpperDisplayMagnitude() + 1; |
520 | for (int i = 0; i < integerCount; i++) { |
521 | // Add grouping separator |
522 | if (micros.grouping.groupAtPosition(i, quantity)) { |
523 | length += string.insert( |
524 | index, |
525 | micros.useCurrency ? micros.symbols->getSymbol( |
526 | DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol) |
527 | : micros.symbols->getSymbol( |
528 | DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol), |
529 | UNUM_GROUPING_SEPARATOR_FIELD, |
530 | status); |
531 | } |
532 | |
533 | // Get and append the next digit value |
534 | int8_t nextDigit = quantity.getDigit(i); |
535 | length += utils::insertDigitFromSymbols( |
536 | string, index, nextDigit, *micros.symbols, UNUM_INTEGER_FIELD, status); |
537 | } |
538 | return length; |
539 | } |
540 | |
541 | int32_t NumberFormatterImpl::writeFractionDigits(const MicroProps& micros, DecimalQuantity& quantity, |
542 | FormattedStringBuilder& string, int32_t index, |
543 | UErrorCode& status) { |
544 | int length = 0; |
545 | int fractionCount = -quantity.getLowerDisplayMagnitude(); |
546 | for (int i = 0; i < fractionCount; i++) { |
547 | // Get and append the next digit value |
548 | int8_t nextDigit = quantity.getDigit(-i - 1); |
549 | length += utils::insertDigitFromSymbols( |
550 | string, length + index, nextDigit, *micros.symbols, UNUM_FRACTION_FIELD, status); |
551 | } |
552 | return length; |
553 | } |
554 | |
555 | #endif /* #if !UCONFIG_NO_FORMATTING */ |
556 | |