]>
Commit | Line | Data |
---|---|---|
0f5d89e8 A |
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 | ||
3d1f044b A |
70 | NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, UErrorCode& status) |
71 | : NumberFormatterImpl(macros, true, status) { | |
0f5d89e8 A |
72 | } |
73 | ||
3d1f044b | 74 | int32_t NumberFormatterImpl::formatStatic(const MacroProps& macros, DecimalQuantity& inValue, |
340931cb | 75 | FormattedStringBuilder& outString, UErrorCode& status) { |
0f5d89e8 | 76 | NumberFormatterImpl impl(macros, false, status); |
3d1f044b A |
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; | |
0f5d89e8 A |
82 | } |
83 | ||
340931cb | 84 | int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, Signum signum, |
0f5d89e8 | 85 | StandardPlural::Form plural, |
340931cb | 86 | FormattedStringBuilder& outString, UErrorCode& status) { |
0f5d89e8 A |
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 | ||
340931cb | 96 | int32_t NumberFormatterImpl::format(DecimalQuantity& inValue, FormattedStringBuilder& outString, |
0f5d89e8 | 97 | UErrorCode& status) const { |
0f5d89e8 | 98 | MicroProps micros; |
3d1f044b A |
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; | |
0f5d89e8 A |
104 | } |
105 | ||
3d1f044b A |
106 | void NumberFormatterImpl::preProcess(DecimalQuantity& inValue, MicroProps& microsOut, |
107 | UErrorCode& status) const { | |
0f5d89e8 | 108 | if (U_FAILURE(status)) { return; } |
3d1f044b A |
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 | } | |
0f5d89e8 | 126 | fMicroPropsGenerator->processQuantity(inValue, fMicros, status); |
3d1f044b A |
127 | fMicros.rounder.apply(inValue, status); |
128 | fMicros.integerWidth.apply(inValue, status); | |
129 | return fMicros; | |
0f5d89e8 A |
130 | } |
131 | ||
340931cb A |
132 | int32_t NumberFormatterImpl::getPrefixSuffix(Signum signum, StandardPlural::Form plural, |
133 | FormattedStringBuilder& outString, UErrorCode& status) const { | |
0f5d89e8 A |
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; } | |
3d1f044b | 140 | return modifier->getPrefixLength(); |
0f5d89e8 A |
141 | } |
142 | ||
340931cb A |
143 | int32_t NumberFormatterImpl::getPrefixSuffixUnsafe(Signum signum, StandardPlural::Form plural, |
144 | FormattedStringBuilder& outString, UErrorCode& status) { | |
0f5d89e8 A |
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; } | |
3d1f044b | 151 | return fPatternModifier->getPrefixLength(); |
0f5d89e8 A |
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); | |
3d1f044b A |
175 | bool isPercent = utils::unitIsPercent(macros.unit); |
176 | bool isPermille = utils::unitIsPermille(macros.unit); | |
0f5d89e8 A |
177 | bool isAccounting = |
178 | macros.sign == UNUM_SIGN_ACCOUNTING || macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS || | |
179 | macros.sign == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO; | |
3d1f044b | 180 | CurrencyUnit currency(u"", status); |
0f5d89e8 A |
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 | } | |
3d1f044b A |
196 | bool isCldrUnit = !isCurrency && !isNoUnit && |
197 | (unitWidth == UNUM_UNIT_WIDTH_FULL_NAME || !(isPercent || isPermille)); | |
0f5d89e8 A |
198 | |
199 | // Select the numbering system. | |
3d1f044b | 200 | const NumberingSystem* ns = nullptr; |
0f5d89e8 A |
201 | if (macros.symbols.isNumberingSystem()) { |
202 | ns = macros.symbols.getNumberingSystem(); | |
0f5d89e8 | 203 | } |
3d1f044b A |
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; | |
0f5d89e8 A |
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 { | |
340931cb | 218 | auto newSymbols = (ns != nullptr)? |
3d1f044b A |
219 | new DecimalFormatSymbols(macros.locale, *ns, status): |
220 | new DecimalFormatSymbols(macros.locale, status); | |
340931cb A |
221 | if (newSymbols == nullptr) { |
222 | status = U_MEMORY_ALLOCATION_ERROR; | |
223 | return nullptr; | |
224 | } | |
225 | fMicros.symbols = newSymbols; | |
0f5d89e8 A |
226 | // Give ownership to the NumberFormatterImpl. |
227 | fSymbols.adoptInstead(fMicros.symbols); | |
228 | } | |
3d1f044b A |
229 | // Resolve nsName and save (Apple rdar://51672521) |
230 | if (nsName == nullptr && U_SUCCESS(status)) { | |
231 | nsName = fMicros.symbols->getNSName(); | |
232 | } | |
233 | if (nsName == nullptr || nsName[0] == 0) { | |
234 | nsName = "latn"; | |
235 | } | |
236 | uprv_strncpy(fMicros.nsName, nsName, 8); | |
237 | fMicros.nsName[8] = 0; // guarantee NUL-terminated | |
0f5d89e8 A |
238 | |
239 | // Load and parse the pattern string. It is used for grouping sizes and affixes only. | |
240 | // If we are formatting currency, check for a currency-specific pattern. | |
241 | const char16_t* pattern = nullptr; | |
242 | if (isCurrency) { | |
243 | CurrencyFormatInfoResult info = getCurrencyFormatInfo( | |
244 | macros.locale, currency.getSubtype(), status); | |
245 | if (info.exists) { | |
246 | pattern = info.pattern; | |
247 | // It's clunky to clone an object here, but this code is not frequently executed. | |
340931cb A |
248 | auto symbols = new DecimalFormatSymbols(*fMicros.symbols); |
249 | if (symbols == nullptr) { | |
250 | status = U_MEMORY_ALLOCATION_ERROR; | |
251 | return nullptr; | |
252 | } | |
0f5d89e8 A |
253 | fMicros.symbols = symbols; |
254 | fSymbols.adoptInstead(symbols); | |
255 | symbols->setSymbol( | |
256 | DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol, | |
257 | UnicodeString(info.decimalSeparator), | |
258 | FALSE); | |
259 | symbols->setSymbol( | |
260 | DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol, | |
261 | UnicodeString(info.groupingSeparator), | |
262 | FALSE); | |
263 | } | |
264 | } | |
265 | if (pattern == nullptr) { | |
266 | CldrPatternStyle patternStyle; | |
3d1f044b A |
267 | if (isCldrUnit) { |
268 | patternStyle = CLDR_PATTERN_STYLE_DECIMAL; | |
269 | } else if (isPercent || isPermille) { | |
0f5d89e8 A |
270 | patternStyle = CLDR_PATTERN_STYLE_PERCENT; |
271 | } else if (!isCurrency || unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) { | |
272 | patternStyle = CLDR_PATTERN_STYLE_DECIMAL; | |
273 | } else if (isAccounting) { | |
274 | // NOTE: Although ACCOUNTING and ACCOUNTING_ALWAYS are only supported in currencies right now, | |
275 | // the API contract allows us to add support to other units in the future. | |
276 | patternStyle = CLDR_PATTERN_STYLE_ACCOUNTING; | |
277 | } else { | |
278 | patternStyle = CLDR_PATTERN_STYLE_CURRENCY; | |
279 | } | |
280 | pattern = utils::getPatternForStyle(macros.locale, nsName, patternStyle, status); | |
281 | } | |
282 | auto patternInfo = new ParsedPatternInfo(); | |
340931cb A |
283 | if (patternInfo == nullptr) { |
284 | status = U_MEMORY_ALLOCATION_ERROR; | |
285 | return nullptr; | |
286 | } | |
0f5d89e8 A |
287 | fPatternInfo.adoptInstead(patternInfo); |
288 | PatternParser::parseToPatternInfo(UnicodeString(pattern), *patternInfo, status); | |
289 | ||
290 | ///////////////////////////////////////////////////////////////////////////////////// | |
291 | /// START POPULATING THE DEFAULT MICROPROPS AND BUILDING THE MICROPROPS GENERATOR /// | |
292 | ///////////////////////////////////////////////////////////////////////////////////// | |
293 | ||
294 | // Multiplier | |
295 | if (macros.scale.isValid()) { | |
296 | fMicros.helpers.multiplier.setAndChain(macros.scale, chain); | |
297 | chain = &fMicros.helpers.multiplier; | |
298 | } | |
299 | ||
300 | // Rounding strategy | |
301 | Precision precision; | |
302 | if (!macros.precision.isBogus()) { | |
303 | precision = macros.precision; | |
304 | } else if (macros.notation.fType == Notation::NTN_COMPACT) { | |
305 | precision = Precision::integer().withMinDigits(2); | |
306 | } else if (isCurrency) { | |
307 | precision = Precision::currency(UCURR_USAGE_STANDARD); | |
308 | } else { | |
309 | precision = Precision::maxFraction(6); | |
310 | } | |
311 | UNumberFormatRoundingMode roundingMode; | |
312 | if (macros.roundingMode != kDefaultMode) { | |
313 | roundingMode = macros.roundingMode; | |
314 | } else { | |
315 | // Temporary until ICU 64 | |
316 | roundingMode = precision.fRoundingMode; | |
317 | } | |
318 | fMicros.rounder = {precision, roundingMode, currency, status}; | |
319 | ||
320 | // Grouping strategy | |
321 | if (!macros.grouper.isBogus()) { | |
322 | fMicros.grouping = macros.grouper; | |
323 | } else if (macros.notation.fType == Notation::NTN_COMPACT) { | |
324 | // Compact notation uses minGrouping by default since ICU 59 | |
325 | fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_MIN2); | |
326 | } else { | |
327 | fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_AUTO); | |
328 | } | |
329 | fMicros.grouping.setLocaleData(*fPatternInfo, macros.locale); | |
330 | ||
331 | // Padding strategy | |
332 | if (!macros.padder.isBogus()) { | |
333 | fMicros.padding = macros.padder; | |
334 | } else { | |
335 | fMicros.padding = Padder::none(); | |
336 | } | |
337 | ||
338 | // Integer width | |
339 | if (!macros.integerWidth.isBogus()) { | |
340 | fMicros.integerWidth = macros.integerWidth; | |
341 | } else { | |
342 | fMicros.integerWidth = IntegerWidth::standard(); | |
343 | } | |
344 | ||
345 | // Sign display | |
346 | if (macros.sign != UNUM_SIGN_COUNT) { | |
347 | fMicros.sign = macros.sign; | |
348 | } else { | |
349 | fMicros.sign = UNUM_SIGN_AUTO; | |
350 | } | |
351 | ||
352 | // Decimal mark display | |
353 | if (macros.decimal != UNUM_DECIMAL_SEPARATOR_COUNT) { | |
354 | fMicros.decimal = macros.decimal; | |
355 | } else { | |
356 | fMicros.decimal = UNUM_DECIMAL_SEPARATOR_AUTO; | |
357 | } | |
358 | ||
359 | // Use monetary separator symbols | |
360 | fMicros.useCurrency = isCurrency; | |
361 | ||
362 | // Inner modifier (scientific notation) | |
363 | if (macros.notation.fType == Notation::NTN_SCIENTIFIC) { | |
340931cb A |
364 | auto newScientificHandler = new ScientificHandler(¯os.notation, fMicros.symbols, chain); |
365 | if (newScientificHandler == nullptr) { | |
366 | status = U_MEMORY_ALLOCATION_ERROR; | |
367 | return nullptr; | |
368 | } | |
369 | fScientificHandler.adoptInstead(newScientificHandler); | |
0f5d89e8 A |
370 | chain = fScientificHandler.getAlias(); |
371 | } else { | |
372 | // No inner modifier required | |
373 | fMicros.modInner = &fMicros.helpers.emptyStrongModifier; | |
374 | } | |
375 | ||
376 | // Middle modifier (patterns, positive/negative, currency symbols, percent) | |
377 | auto patternModifier = new MutablePatternModifier(false); | |
340931cb A |
378 | if (patternModifier == nullptr) { |
379 | status = U_MEMORY_ALLOCATION_ERROR; | |
380 | return nullptr; | |
381 | } | |
0f5d89e8 A |
382 | fPatternModifier.adoptInstead(patternModifier); |
383 | patternModifier->setPatternInfo( | |
384 | macros.affixProvider != nullptr ? macros.affixProvider | |
3d1f044b A |
385 | : static_cast<const AffixPatternProvider*>(fPatternInfo.getAlias()), |
386 | UNUM_FIELD_COUNT); | |
0f5d89e8 A |
387 | patternModifier->setPatternAttributes(fMicros.sign, isPermille); |
388 | if (patternModifier->needsPlurals()) { | |
389 | patternModifier->setSymbols( | |
390 | fMicros.symbols, | |
391 | currencySymbols, | |
392 | unitWidth, | |
393 | resolvePluralRules(macros.rules, macros.locale, status)); | |
394 | } else { | |
395 | patternModifier->setSymbols(fMicros.symbols, currencySymbols, unitWidth, nullptr); | |
396 | } | |
397 | if (safe) { | |
398 | fImmutablePatternModifier.adoptInstead(patternModifier->createImmutableAndChain(chain, status)); | |
399 | chain = fImmutablePatternModifier.getAlias(); | |
400 | } else { | |
401 | patternModifier->addToChain(chain); | |
402 | chain = patternModifier; | |
403 | } | |
404 | ||
405 | // Outer modifier (CLDR units and currency long names) | |
406 | if (isCldrUnit) { | |
407 | fLongNameHandler.adoptInstead( | |
3d1f044b A |
408 | LongNameHandler::forMeasureUnit( |
409 | macros.locale, | |
410 | macros.unit, | |
411 | macros.perUnit, | |
412 | unitWidth, | |
413 | resolvePluralRules(macros.rules, macros.locale, status), | |
414 | chain, | |
415 | status)); | |
0f5d89e8 A |
416 | chain = fLongNameHandler.getAlias(); |
417 | } else if (isCurrency && unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) { | |
418 | fLongNameHandler.adoptInstead( | |
3d1f044b A |
419 | LongNameHandler::forCurrencyLongNames( |
420 | macros.locale, | |
421 | currency, | |
422 | resolvePluralRules(macros.rules, macros.locale, status), | |
423 | chain, | |
424 | status)); | |
0f5d89e8 A |
425 | chain = fLongNameHandler.getAlias(); |
426 | } else { | |
427 | // No outer modifier required | |
428 | fMicros.modOuter = &fMicros.helpers.emptyWeakModifier; | |
429 | } | |
430 | ||
431 | // Compact notation | |
432 | // NOTE: Compact notation can (but might not) override the middle modifier and rounding. | |
433 | // It therefore needs to go at the end of the chain. | |
434 | if (macros.notation.fType == Notation::NTN_COMPACT) { | |
435 | CompactType compactType = (isCurrency && unitWidth != UNUM_UNIT_WIDTH_FULL_NAME) | |
436 | ? CompactType::TYPE_CURRENCY : CompactType::TYPE_DECIMAL; | |
340931cb A |
437 | auto newCompactHandler = new CompactHandler( |
438 | macros.notation.fUnion.compactStyle, | |
439 | macros.locale, | |
440 | nsName, | |
441 | compactType, | |
442 | resolvePluralRules(macros.rules, macros.locale, status), | |
443 | safe ? patternModifier : nullptr, | |
444 | chain, | |
445 | status); | |
446 | if (newCompactHandler == nullptr) { | |
447 | status = U_MEMORY_ALLOCATION_ERROR; | |
448 | return nullptr; | |
449 | } | |
450 | fCompactHandler.adoptInstead(newCompactHandler); | |
0f5d89e8 A |
451 | chain = fCompactHandler.getAlias(); |
452 | } | |
453 | ||
454 | return chain; | |
455 | } | |
456 | ||
457 | const PluralRules* | |
458 | NumberFormatterImpl::resolvePluralRules(const PluralRules* rulesPtr, const Locale& locale, | |
459 | UErrorCode& status) { | |
460 | if (rulesPtr != nullptr) { | |
461 | return rulesPtr; | |
462 | } | |
463 | // Lazily create PluralRules | |
464 | if (fRules.isNull()) { | |
465 | fRules.adoptInstead(PluralRules::forLocale(locale, status)); | |
466 | } | |
467 | return fRules.getAlias(); | |
468 | } | |
469 | ||
340931cb | 470 | int32_t NumberFormatterImpl::writeAffixes(const MicroProps& micros, FormattedStringBuilder& string, |
3d1f044b | 471 | int32_t start, int32_t end, UErrorCode& status) { |
0f5d89e8 | 472 | // Always apply the inner modifier (which is "strong"). |
3d1f044b | 473 | int32_t length = micros.modInner->apply(string, start, end, status); |
0f5d89e8 A |
474 | if (micros.padding.isValid()) { |
475 | length += micros.padding | |
3d1f044b | 476 | .padAndApply(*micros.modMiddle, *micros.modOuter, string, start, length + end, status); |
0f5d89e8 | 477 | } else { |
3d1f044b A |
478 | length += micros.modMiddle->apply(string, start, length + end, status); |
479 | length += micros.modOuter->apply(string, start, length + end, status); | |
0f5d89e8 A |
480 | } |
481 | return length; | |
482 | } | |
483 | ||
484 | int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuantity& quantity, | |
340931cb | 485 | FormattedStringBuilder& string, int32_t index, |
3d1f044b | 486 | UErrorCode& status) { |
0f5d89e8 A |
487 | int32_t length = 0; |
488 | if (quantity.isInfinite()) { | |
489 | length += string.insert( | |
3d1f044b | 490 | length + index, |
0f5d89e8 A |
491 | micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kInfinitySymbol), |
492 | UNUM_INTEGER_FIELD, | |
493 | status); | |
494 | ||
495 | } else if (quantity.isNaN()) { | |
496 | length += string.insert( | |
3d1f044b | 497 | length + index, |
0f5d89e8 A |
498 | micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kNaNSymbol), |
499 | UNUM_INTEGER_FIELD, | |
500 | status); | |
501 | ||
502 | } else { | |
503 | // Add the integer digits | |
3d1f044b | 504 | length += writeIntegerDigits(micros, quantity, string, length + index, status); |
0f5d89e8 A |
505 | |
506 | // Add the decimal point | |
507 | if (quantity.getLowerDisplayMagnitude() < 0 || micros.decimal == UNUM_DECIMAL_SEPARATOR_ALWAYS) { | |
508 | length += string.insert( | |
3d1f044b | 509 | length + index, |
0f5d89e8 A |
510 | micros.useCurrency ? micros.symbols->getSymbol( |
511 | DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol) : micros | |
512 | .symbols | |
513 | ->getSymbol( | |
514 | DecimalFormatSymbols::ENumberFormatSymbol::kDecimalSeparatorSymbol), | |
515 | UNUM_DECIMAL_SEPARATOR_FIELD, | |
516 | status); | |
517 | } | |
518 | ||
519 | // Add the fraction digits | |
3d1f044b | 520 | length += writeFractionDigits(micros, quantity, string, length + index, status); |
a01113dc A |
521 | |
522 | if (length == 0) { | |
523 | // Force output of the digit for value 0 | |
524 | length += utils::insertDigitFromSymbols( | |
525 | string, index, 0, *micros.symbols, UNUM_INTEGER_FIELD, status); | |
526 | } | |
0f5d89e8 A |
527 | } |
528 | ||
529 | return length; | |
530 | } | |
531 | ||
532 | int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, DecimalQuantity& quantity, | |
340931cb | 533 | FormattedStringBuilder& string, int32_t index, |
3d1f044b | 534 | UErrorCode& status) { |
0f5d89e8 A |
535 | int length = 0; |
536 | int integerCount = quantity.getUpperDisplayMagnitude() + 1; | |
537 | for (int i = 0; i < integerCount; i++) { | |
538 | // Add grouping separator | |
539 | if (micros.grouping.groupAtPosition(i, quantity)) { | |
540 | length += string.insert( | |
3d1f044b | 541 | index, |
0f5d89e8 A |
542 | micros.useCurrency ? micros.symbols->getSymbol( |
543 | DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol) | |
544 | : micros.symbols->getSymbol( | |
545 | DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol), | |
546 | UNUM_GROUPING_SEPARATOR_FIELD, | |
547 | status); | |
548 | } | |
549 | ||
550 | // Get and append the next digit value | |
551 | int8_t nextDigit = quantity.getDigit(i); | |
552 | length += utils::insertDigitFromSymbols( | |
3d1f044b | 553 | string, index, nextDigit, *micros.symbols, UNUM_INTEGER_FIELD, status); |
0f5d89e8 A |
554 | } |
555 | return length; | |
556 | } | |
557 | ||
558 | int32_t NumberFormatterImpl::writeFractionDigits(const MicroProps& micros, DecimalQuantity& quantity, | |
340931cb | 559 | FormattedStringBuilder& string, int32_t index, |
3d1f044b | 560 | UErrorCode& status) { |
0f5d89e8 A |
561 | int length = 0; |
562 | int fractionCount = -quantity.getLowerDisplayMagnitude(); | |
563 | for (int i = 0; i < fractionCount; i++) { | |
564 | // Get and append the next digit value | |
565 | int8_t nextDigit = quantity.getDigit(-i - 1); | |
566 | length += utils::insertDigitFromSymbols( | |
3d1f044b | 567 | string, length + index, nextDigit, *micros.symbols, UNUM_FRACTION_FIELD, status); |
0f5d89e8 A |
568 | } |
569 | return length; | |
570 | } | |
571 | ||
572 | #endif /* #if !UCONFIG_NO_FORMATTING */ |