]>
Commit | Line | Data |
---|---|---|
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 | NumberStringBuilder& 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, int8_t signum, | |
85 | StandardPlural::Form plural, | |
86 | NumberStringBuilder& 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, NumberStringBuilder& 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.rounder.apply(inValue, status); | |
115 | microsOut.integerWidth.apply(inValue, status); | |
116 | } | |
117 | ||
118 | MicroProps& NumberFormatterImpl::preProcessUnsafe(DecimalQuantity& inValue, UErrorCode& status) { | |
119 | if (U_FAILURE(status)) { | |
120 | return fMicros; // must always return a value | |
121 | } | |
122 | if (fMicroPropsGenerator == nullptr) { | |
123 | status = U_INTERNAL_PROGRAM_ERROR; | |
124 | return fMicros; // must always return a value | |
125 | } | |
126 | fMicroPropsGenerator->processQuantity(inValue, fMicros, status); | |
127 | fMicros.rounder.apply(inValue, status); | |
128 | fMicros.integerWidth.apply(inValue, status); | |
129 | return fMicros; | |
130 | } | |
131 | ||
132 | int32_t NumberFormatterImpl::getPrefixSuffix(int8_t signum, StandardPlural::Form plural, | |
133 | NumberStringBuilder& outString, UErrorCode& status) const { | |
134 | if (U_FAILURE(status)) { return 0; } | |
135 | // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier). | |
136 | // Safe path: use fImmutablePatternModifier. | |
137 | const Modifier* modifier = fImmutablePatternModifier->getModifier(signum, plural); | |
138 | modifier->apply(outString, 0, 0, status); | |
139 | if (U_FAILURE(status)) { return 0; } | |
140 | return modifier->getPrefixLength(); | |
141 | } | |
142 | ||
143 | int32_t NumberFormatterImpl::getPrefixSuffixUnsafe(int8_t signum, StandardPlural::Form plural, | |
144 | NumberStringBuilder& outString, UErrorCode& status) { | |
145 | if (U_FAILURE(status)) { return 0; } | |
146 | // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier). | |
147 | // Unsafe path: use fPatternModifier. | |
148 | fPatternModifier->setNumberProperties(signum, plural); | |
149 | fPatternModifier->apply(outString, 0, 0, status); | |
150 | if (U_FAILURE(status)) { return 0; } | |
151 | return fPatternModifier->getPrefixLength(); | |
152 | } | |
153 | ||
154 | NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, bool safe, UErrorCode& status) { | |
155 | fMicroPropsGenerator = macrosToMicroGenerator(macros, safe, status); | |
156 | } | |
157 | ||
158 | ////////// | |
159 | ||
160 | const MicroPropsGenerator* | |
161 | NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe, UErrorCode& status) { | |
162 | if (U_FAILURE(status)) { return nullptr; } | |
163 | const MicroPropsGenerator* chain = &fMicros; | |
164 | ||
165 | // Check that macros is error-free before continuing. | |
166 | if (macros.copyErrorTo(status)) { | |
167 | return nullptr; | |
168 | } | |
169 | ||
170 | // TODO: Accept currency symbols from DecimalFormatSymbols? | |
171 | ||
172 | // Pre-compute a few values for efficiency. | |
173 | bool isCurrency = utils::unitIsCurrency(macros.unit); | |
174 | bool isNoUnit = utils::unitIsNoUnit(macros.unit); | |
175 | bool isPercent = utils::unitIsPercent(macros.unit); | |
176 | bool isPermille = utils::unitIsPermille(macros.unit); | |
177 | bool isAccounting = | |
178 | macros.sign == UNUM_SIGN_ACCOUNTING || macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS || | |
179 | macros.sign == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO; | |
180 | CurrencyUnit currency(u"", status); | |
181 | if (isCurrency) { | |
182 | currency = CurrencyUnit(macros.unit, status); // Restore CurrencyUnit from MeasureUnit | |
183 | } | |
184 | const CurrencySymbols* currencySymbols; | |
185 | if (macros.currencySymbols != nullptr) { | |
186 | // Used by the DecimalFormat code path | |
187 | currencySymbols = macros.currencySymbols; | |
188 | } else { | |
189 | fWarehouse.fCurrencySymbols = {currency, macros.locale, status}; | |
190 | currencySymbols = &fWarehouse.fCurrencySymbols; | |
191 | } | |
192 | UNumberUnitWidth unitWidth = UNUM_UNIT_WIDTH_SHORT; | |
193 | if (macros.unitWidth != UNUM_UNIT_WIDTH_COUNT) { | |
194 | unitWidth = macros.unitWidth; | |
195 | } | |
196 | bool isCldrUnit = !isCurrency && !isNoUnit && | |
197 | (unitWidth == UNUM_UNIT_WIDTH_FULL_NAME || !(isPercent || isPermille)); | |
198 | ||
199 | // Select the numbering system. | |
200 | const NumberingSystem* ns = nullptr; | |
201 | if (macros.symbols.isNumberingSystem()) { | |
202 | ns = macros.symbols.getNumberingSystem(); | |
203 | } | |
204 | // else there is no need to create a NumberingSystem object, it is only used | |
205 | // for two things (Apple rdar://51672521): | |
206 | // 1. Passing to new DecimalFormatSymbols; but if we do not have one, new | |
207 | // DecimalFormat symbols can create one anyway so there is no need to do it | |
208 | // ahead of time, and | |
209 | // 2. Getting the nsName. But with a small DecimalFormatSymbols change (per | |
210 | // rdar://51672521) we can get the name from DecimalFormatSymbols which we need | |
211 | // here anyway. | |
212 | const char* nsName = (ns != nullptr)? ns->getName() : nullptr; | |
213 | ||
214 | // Resolve the symbols. Do this here because currency may need to customize them. | |
215 | if (macros.symbols.isDecimalFormatSymbols()) { | |
216 | fMicros.symbols = macros.symbols.getDecimalFormatSymbols(); | |
217 | } else { | |
218 | fMicros.symbols = (ns != nullptr)? | |
219 | new DecimalFormatSymbols(macros.locale, *ns, status): | |
220 | new DecimalFormatSymbols(macros.locale, status); | |
221 | // Give ownership to the NumberFormatterImpl. | |
222 | fSymbols.adoptInstead(fMicros.symbols); | |
223 | } | |
224 | // Resolve nsName and save (Apple rdar://51672521) | |
225 | if (nsName == nullptr && U_SUCCESS(status)) { | |
226 | nsName = fMicros.symbols->getNSName(); | |
227 | } | |
228 | if (nsName == nullptr || nsName[0] == 0) { | |
229 | nsName = "latn"; | |
230 | } | |
231 | uprv_strncpy(fMicros.nsName, nsName, 8); | |
232 | fMicros.nsName[8] = 0; // guarantee NUL-terminated | |
233 | ||
234 | // Load and parse the pattern string. It is used for grouping sizes and affixes only. | |
235 | // If we are formatting currency, check for a currency-specific pattern. | |
236 | const char16_t* pattern = nullptr; | |
237 | if (isCurrency) { | |
238 | CurrencyFormatInfoResult info = getCurrencyFormatInfo( | |
239 | macros.locale, currency.getSubtype(), status); | |
240 | if (info.exists) { | |
241 | pattern = info.pattern; | |
242 | // It's clunky to clone an object here, but this code is not frequently executed. | |
243 | auto* symbols = new DecimalFormatSymbols(*fMicros.symbols); | |
244 | fMicros.symbols = symbols; | |
245 | fSymbols.adoptInstead(symbols); | |
246 | symbols->setSymbol( | |
247 | DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol, | |
248 | UnicodeString(info.decimalSeparator), | |
249 | FALSE); | |
250 | symbols->setSymbol( | |
251 | DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol, | |
252 | UnicodeString(info.groupingSeparator), | |
253 | FALSE); | |
254 | } | |
255 | } | |
256 | if (pattern == nullptr) { | |
257 | CldrPatternStyle patternStyle; | |
258 | if (isCldrUnit) { | |
259 | patternStyle = CLDR_PATTERN_STYLE_DECIMAL; | |
260 | } else if (isPercent || isPermille) { | |
261 | patternStyle = CLDR_PATTERN_STYLE_PERCENT; | |
262 | } else if (!isCurrency || unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) { | |
263 | patternStyle = CLDR_PATTERN_STYLE_DECIMAL; | |
264 | } else if (isAccounting) { | |
265 | // NOTE: Although ACCOUNTING and ACCOUNTING_ALWAYS are only supported in currencies right now, | |
266 | // the API contract allows us to add support to other units in the future. | |
267 | patternStyle = CLDR_PATTERN_STYLE_ACCOUNTING; | |
268 | } else { | |
269 | patternStyle = CLDR_PATTERN_STYLE_CURRENCY; | |
270 | } | |
271 | pattern = utils::getPatternForStyle(macros.locale, nsName, patternStyle, status); | |
272 | } | |
273 | auto patternInfo = new ParsedPatternInfo(); | |
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 | fScientificHandler.adoptInstead(new ScientificHandler(¯os.notation, fMicros.symbols, chain)); | |
352 | chain = fScientificHandler.getAlias(); | |
353 | } else { | |
354 | // No inner modifier required | |
355 | fMicros.modInner = &fMicros.helpers.emptyStrongModifier; | |
356 | } | |
357 | ||
358 | // Middle modifier (patterns, positive/negative, currency symbols, percent) | |
359 | auto patternModifier = new MutablePatternModifier(false); | |
360 | fPatternModifier.adoptInstead(patternModifier); | |
361 | patternModifier->setPatternInfo( | |
362 | macros.affixProvider != nullptr ? macros.affixProvider | |
363 | : static_cast<const AffixPatternProvider*>(fPatternInfo.getAlias()), | |
364 | UNUM_FIELD_COUNT); | |
365 | patternModifier->setPatternAttributes(fMicros.sign, isPermille); | |
366 | if (patternModifier->needsPlurals()) { | |
367 | patternModifier->setSymbols( | |
368 | fMicros.symbols, | |
369 | currencySymbols, | |
370 | unitWidth, | |
371 | resolvePluralRules(macros.rules, macros.locale, status)); | |
372 | } else { | |
373 | patternModifier->setSymbols(fMicros.symbols, currencySymbols, unitWidth, nullptr); | |
374 | } | |
375 | if (safe) { | |
376 | fImmutablePatternModifier.adoptInstead(patternModifier->createImmutableAndChain(chain, status)); | |
377 | chain = fImmutablePatternModifier.getAlias(); | |
378 | } else { | |
379 | patternModifier->addToChain(chain); | |
380 | chain = patternModifier; | |
381 | } | |
382 | ||
383 | // Outer modifier (CLDR units and currency long names) | |
384 | if (isCldrUnit) { | |
385 | fLongNameHandler.adoptInstead( | |
386 | LongNameHandler::forMeasureUnit( | |
387 | macros.locale, | |
388 | macros.unit, | |
389 | macros.perUnit, | |
390 | unitWidth, | |
391 | resolvePluralRules(macros.rules, macros.locale, status), | |
392 | chain, | |
393 | status)); | |
394 | chain = fLongNameHandler.getAlias(); | |
395 | } else if (isCurrency && unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) { | |
396 | fLongNameHandler.adoptInstead( | |
397 | LongNameHandler::forCurrencyLongNames( | |
398 | macros.locale, | |
399 | currency, | |
400 | resolvePluralRules(macros.rules, macros.locale, status), | |
401 | chain, | |
402 | status)); | |
403 | chain = fLongNameHandler.getAlias(); | |
404 | } else { | |
405 | // No outer modifier required | |
406 | fMicros.modOuter = &fMicros.helpers.emptyWeakModifier; | |
407 | } | |
408 | ||
409 | // Compact notation | |
410 | // NOTE: Compact notation can (but might not) override the middle modifier and rounding. | |
411 | // It therefore needs to go at the end of the chain. | |
412 | if (macros.notation.fType == Notation::NTN_COMPACT) { | |
413 | CompactType compactType = (isCurrency && unitWidth != UNUM_UNIT_WIDTH_FULL_NAME) | |
414 | ? CompactType::TYPE_CURRENCY : CompactType::TYPE_DECIMAL; | |
415 | fCompactHandler.adoptInstead( | |
416 | new CompactHandler( | |
417 | macros.notation.fUnion.compactStyle, | |
418 | macros.locale, | |
419 | nsName, | |
420 | compactType, | |
421 | resolvePluralRules(macros.rules, macros.locale, status), | |
422 | safe ? patternModifier : nullptr, | |
423 | chain, | |
424 | status)); | |
425 | chain = fCompactHandler.getAlias(); | |
426 | } | |
427 | ||
428 | return chain; | |
429 | } | |
430 | ||
431 | const PluralRules* | |
432 | NumberFormatterImpl::resolvePluralRules(const PluralRules* rulesPtr, const Locale& locale, | |
433 | UErrorCode& status) { | |
434 | if (rulesPtr != nullptr) { | |
435 | return rulesPtr; | |
436 | } | |
437 | // Lazily create PluralRules | |
438 | if (fRules.isNull()) { | |
439 | fRules.adoptInstead(PluralRules::forLocale(locale, status)); | |
440 | } | |
441 | return fRules.getAlias(); | |
442 | } | |
443 | ||
444 | int32_t NumberFormatterImpl::writeAffixes(const MicroProps& micros, NumberStringBuilder& string, | |
445 | int32_t start, int32_t end, UErrorCode& status) { | |
446 | // Always apply the inner modifier (which is "strong"). | |
447 | int32_t length = micros.modInner->apply(string, start, end, status); | |
448 | if (micros.padding.isValid()) { | |
449 | length += micros.padding | |
450 | .padAndApply(*micros.modMiddle, *micros.modOuter, string, start, length + end, status); | |
451 | } else { | |
452 | length += micros.modMiddle->apply(string, start, length + end, status); | |
453 | length += micros.modOuter->apply(string, start, length + end, status); | |
454 | } | |
455 | return length; | |
456 | } | |
457 | ||
458 | int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuantity& quantity, | |
459 | NumberStringBuilder& string, int32_t index, | |
460 | UErrorCode& status) { | |
461 | int32_t length = 0; | |
462 | if (quantity.isInfinite()) { | |
463 | length += string.insert( | |
464 | length + index, | |
465 | micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kInfinitySymbol), | |
466 | UNUM_INTEGER_FIELD, | |
467 | status); | |
468 | ||
469 | } else if (quantity.isNaN()) { | |
470 | length += string.insert( | |
471 | length + index, | |
472 | micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kNaNSymbol), | |
473 | UNUM_INTEGER_FIELD, | |
474 | status); | |
475 | ||
476 | } else { | |
477 | // Add the integer digits | |
478 | length += writeIntegerDigits(micros, quantity, string, length + index, status); | |
479 | ||
480 | // Add the decimal point | |
481 | if (quantity.getLowerDisplayMagnitude() < 0 || micros.decimal == UNUM_DECIMAL_SEPARATOR_ALWAYS) { | |
482 | length += string.insert( | |
483 | length + index, | |
484 | micros.useCurrency ? micros.symbols->getSymbol( | |
485 | DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol) : micros | |
486 | .symbols | |
487 | ->getSymbol( | |
488 | DecimalFormatSymbols::ENumberFormatSymbol::kDecimalSeparatorSymbol), | |
489 | UNUM_DECIMAL_SEPARATOR_FIELD, | |
490 | status); | |
491 | } | |
492 | ||
493 | // Add the fraction digits | |
494 | length += writeFractionDigits(micros, quantity, string, length + index, status); | |
495 | } | |
496 | ||
497 | return length; | |
498 | } | |
499 | ||
500 | int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, DecimalQuantity& quantity, | |
501 | NumberStringBuilder& string, int32_t index, | |
502 | UErrorCode& status) { | |
503 | int length = 0; | |
504 | int integerCount = quantity.getUpperDisplayMagnitude() + 1; | |
505 | for (int i = 0; i < integerCount; i++) { | |
506 | // Add grouping separator | |
507 | if (micros.grouping.groupAtPosition(i, quantity)) { | |
508 | length += string.insert( | |
509 | index, | |
510 | micros.useCurrency ? micros.symbols->getSymbol( | |
511 | DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol) | |
512 | : micros.symbols->getSymbol( | |
513 | DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol), | |
514 | UNUM_GROUPING_SEPARATOR_FIELD, | |
515 | status); | |
516 | } | |
517 | ||
518 | // Get and append the next digit value | |
519 | int8_t nextDigit = quantity.getDigit(i); | |
520 | length += utils::insertDigitFromSymbols( | |
521 | string, index, nextDigit, *micros.symbols, UNUM_INTEGER_FIELD, status); | |
522 | } | |
523 | return length; | |
524 | } | |
525 | ||
526 | int32_t NumberFormatterImpl::writeFractionDigits(const MicroProps& micros, DecimalQuantity& quantity, | |
527 | NumberStringBuilder& string, int32_t index, | |
528 | UErrorCode& status) { | |
529 | int length = 0; | |
530 | int fractionCount = -quantity.getLowerDisplayMagnitude(); | |
531 | for (int i = 0; i < fractionCount; i++) { | |
532 | // Get and append the next digit value | |
533 | int8_t nextDigit = quantity.getDigit(-i - 1); | |
534 | length += utils::insertDigitFromSymbols( | |
535 | string, length + index, nextDigit, *micros.symbols, UNUM_FRACTION_FIELD, status); | |
536 | } | |
537 | return length; | |
538 | } | |
539 | ||
540 | #endif /* #if !UCONFIG_NO_FORMATTING */ |