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