number_formatimpl.cpp (25913B)
1 // © 2017 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 4 #include "unicode/utypes.h" 5 6 #if !UCONFIG_NO_FORMATTING 7 8 #include "cstring.h" 9 #include "unicode/ures.h" 10 #include "uresimp.h" 11 #include "charstr.h" 12 #include "number_formatimpl.h" 13 #include "unicode/numfmt.h" 14 #include "number_patternstring.h" 15 #include "number_utils.h" 16 #include "unicode/numberformatter.h" 17 #include "unicode/dcfmtsym.h" 18 #include "number_scientific.h" 19 #include "number_compact.h" 20 #include "uresimp.h" 21 #include "ureslocs.h" 22 23 using namespace icu; 24 using namespace icu::number; 25 using namespace icu::number::impl; 26 27 28 NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, UErrorCode& status) 29 : NumberFormatterImpl(macros, true, status) { 30 } 31 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 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 58 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 71 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 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 95 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 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 117 NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, bool safe, UErrorCode& status) { 118 fMicroPropsGenerator = macrosToMicroGenerator(macros, safe, status); 119 } 120 121 ////////// 122 123 const MicroPropsGenerator* 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* 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 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 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 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 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 */