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