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.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(¯os.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