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