/src/icu/source/i18n/number_formatimpl.cpp
Line  | Count  | Source (jump to first uncovered line)  | 
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  | 0  |     : NumberFormatterImpl(macros, true, status) { | 
30  | 0  | }  | 
31  |  |  | 
32  |  | int32_t NumberFormatterImpl::formatStatic(const MacroProps ¯os, UFormattedNumberData *results,  | 
33  | 0  |                                           UErrorCode &status) { | 
34  | 0  |     DecimalQuantity &inValue = results->quantity;  | 
35  | 0  |     FormattedStringBuilder &outString = results->getStringRef();  | 
36  | 0  |     NumberFormatterImpl impl(macros, false, status);  | 
37  | 0  |     MicroProps& micros = impl.preProcessUnsafe(inValue, status);  | 
38  | 0  |     if (U_FAILURE(status)) { return 0; } | 
39  | 0  |     int32_t length = writeNumber(micros, inValue, outString, 0, status);  | 
40  | 0  |     length += writeAffixes(micros, outString, 0, length, status);  | 
41  | 0  |     results->outputUnit = std::move(micros.outputUnit);  | 
42  | 0  |     results->gender = micros.gender;  | 
43  | 0  |     return length;  | 
44  | 0  | }  | 
45  |  |  | 
46  |  | int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, Signum signum,  | 
47  |  |                                                    StandardPlural::Form plural,  | 
48  | 0  |                                                    FormattedStringBuilder& outString, UErrorCode& status) { | 
49  | 0  |     NumberFormatterImpl impl(macros, false, status);  | 
50  | 0  |     return impl.getPrefixSuffixUnsafe(signum, plural, outString, status);  | 
51  | 0  | }  | 
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  | 0  | int32_t NumberFormatterImpl::format(UFormattedNumberData *results, UErrorCode &status) const { | 
59  | 0  |     DecimalQuantity &inValue = results->quantity;  | 
60  | 0  |     FormattedStringBuilder &outString = results->getStringRef();  | 
61  | 0  |     MicroProps micros;  | 
62  | 0  |     preProcess(inValue, micros, status);  | 
63  | 0  |     if (U_FAILURE(status)) { return 0; } | 
64  | 0  |     int32_t length = writeNumber(micros, inValue, outString, 0, status);  | 
65  | 0  |     length += writeAffixes(micros, outString, 0, length, status);  | 
66  | 0  |     results->outputUnit = std::move(micros.outputUnit);  | 
67  | 0  |     results->gender = micros.gender;  | 
68  | 0  |     return length;  | 
69  | 0  | }  | 
70  |  |  | 
71  |  | void NumberFormatterImpl::preProcess(DecimalQuantity& inValue, MicroProps& microsOut,  | 
72  | 0  |                                      UErrorCode& status) const { | 
73  | 0  |     if (U_FAILURE(status)) { return; } | 
74  | 0  |     if (fMicroPropsGenerator == nullptr) { | 
75  | 0  |         status = U_INTERNAL_PROGRAM_ERROR;  | 
76  | 0  |         return;  | 
77  | 0  |     }  | 
78  | 0  |     fMicroPropsGenerator->processQuantity(inValue, microsOut, status);  | 
79  | 0  |     microsOut.integerWidth.apply(inValue, status);  | 
80  | 0  | }  | 
81  |  |  | 
82  | 0  | MicroProps& NumberFormatterImpl::preProcessUnsafe(DecimalQuantity& inValue, UErrorCode& status) { | 
83  | 0  |     if (U_FAILURE(status)) { | 
84  | 0  |         return fMicros; // must always return a value  | 
85  | 0  |     }  | 
86  | 0  |     if (fMicroPropsGenerator == nullptr) { | 
87  | 0  |         status = U_INTERNAL_PROGRAM_ERROR;  | 
88  | 0  |         return fMicros; // must always return a value  | 
89  | 0  |     }  | 
90  | 0  |     fMicroPropsGenerator->processQuantity(inValue, fMicros, status);  | 
91  | 0  |     fMicros.integerWidth.apply(inValue, status);  | 
92  | 0  |     return fMicros;  | 
93  | 0  | }  | 
94  |  |  | 
95  |  | int32_t NumberFormatterImpl::getPrefixSuffix(Signum signum, StandardPlural::Form plural,  | 
96  | 0  |                                              FormattedStringBuilder& outString, UErrorCode& status) const { | 
97  | 0  |     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  | 0  |     const Modifier* modifier = fImmutablePatternModifier->getModifier(signum, plural);  | 
101  | 0  |     modifier->apply(outString, 0, 0, status);  | 
102  | 0  |     if (U_FAILURE(status)) { return 0; } | 
103  | 0  |     return modifier->getPrefixLength();  | 
104  | 0  | }  | 
105  |  |  | 
106  |  | int32_t NumberFormatterImpl::getPrefixSuffixUnsafe(Signum signum, StandardPlural::Form plural,  | 
107  | 0  |                                                    FormattedStringBuilder& outString, UErrorCode& status) { | 
108  | 0  |     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  | 0  |     fPatternModifier->setNumberProperties(signum, plural);  | 
112  | 0  |     fPatternModifier->apply(outString, 0, 0, status);  | 
113  | 0  |     if (U_FAILURE(status)) { return 0; } | 
114  | 0  |     return fPatternModifier->getPrefixLength();  | 
115  | 0  | }  | 
116  |  |  | 
117  | 0  | NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, bool safe, UErrorCode& status) { | 
118  | 0  |     fMicroPropsGenerator = macrosToMicroGenerator(macros, safe, status);  | 
119  | 0  | }  | 
120  |  |  | 
121  |  | //////////  | 
122  |  |  | 
123  |  | const MicroPropsGenerator*  | 
124  | 0  | NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe, UErrorCode& status) { | 
125  | 0  |     if (U_FAILURE(status)) { return nullptr; } | 
126  | 0  |     const MicroPropsGenerator* chain = &fMicros;  | 
127  |  |  | 
128  |  |     // Check that macros is error-free before continuing.  | 
129  | 0  |     if (macros.copyErrorTo(status)) { | 
130  | 0  |         return nullptr;  | 
131  | 0  |     }  | 
132  |  |  | 
133  |  |     // TODO: Accept currency symbols from DecimalFormatSymbols?  | 
134  |  |  | 
135  |  |     // Pre-compute a few values for efficiency.  | 
136  | 0  |     bool isCurrency = utils::unitIsCurrency(macros.unit);  | 
137  | 0  |     bool isBaseUnit = utils::unitIsBaseUnit(macros.unit);  | 
138  | 0  |     bool isPercent = utils::unitIsPercent(macros.unit);  | 
139  | 0  |     bool isPermille = utils::unitIsPermille(macros.unit);  | 
140  | 0  |     bool isCompactNotation = macros.notation.fType == Notation::NTN_COMPACT;  | 
141  | 0  |     bool isAccounting =  | 
142  | 0  |             macros.sign == UNUM_SIGN_ACCOUNTING ||  | 
143  | 0  |             macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS ||  | 
144  | 0  |             macros.sign == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO ||  | 
145  | 0  |             macros.sign == UNUM_SIGN_ACCOUNTING_NEGATIVE;  | 
146  | 0  |     CurrencyUnit currency(u"", status);  | 
147  | 0  |     if (isCurrency) { | 
148  | 0  |         currency = CurrencyUnit(macros.unit, status); // Restore CurrencyUnit from MeasureUnit  | 
149  | 0  |     }  | 
150  | 0  |     UNumberUnitWidth unitWidth = UNUM_UNIT_WIDTH_SHORT;  | 
151  | 0  |     if (macros.unitWidth != UNUM_UNIT_WIDTH_COUNT) { | 
152  | 0  |         unitWidth = macros.unitWidth;  | 
153  | 0  |     }  | 
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  | 0  |     bool isCldrUnit = !isCurrency  | 
161  | 0  |         && !isBaseUnit  | 
162  | 0  |         && (unitWidth == UNUM_UNIT_WIDTH_FULL_NAME  | 
163  | 0  |             || !(isPercent || isPermille)  | 
164  | 0  |             || isCompactNotation  | 
165  | 0  |         );  | 
166  | 0  |     bool isMixedUnit = isCldrUnit && (uprv_strcmp(macros.unit.getType(), "") == 0) &&  | 
167  | 0  |                        macros.unit.getComplexity(status) == UMEASURE_UNIT_MIXED;  | 
168  |  |  | 
169  |  |     // Select the numbering system.  | 
170  | 0  |     LocalPointer<const NumberingSystem> nsLocal;  | 
171  | 0  |     const NumberingSystem* ns;  | 
172  | 0  |     if (macros.symbols.isNumberingSystem()) { | 
173  | 0  |         ns = macros.symbols.getNumberingSystem();  | 
174  | 0  |     } else { | 
175  |  |         // TODO: Is there a way to avoid creating the NumberingSystem object?  | 
176  | 0  |         ns = NumberingSystem::createInstance(macros.locale, status);  | 
177  |  |         // Give ownership to the function scope.  | 
178  | 0  |         nsLocal.adoptInstead(ns);  | 
179  | 0  |     }  | 
180  | 0  |     const char* nsName = U_SUCCESS(status) ? ns->getName() : "latn";  | 
181  | 0  |     uprv_strncpy(fMicros.nsName, nsName, 8);  | 
182  | 0  |     fMicros.nsName[8] = 0; // guarantee NUL-terminated  | 
183  |  |  | 
184  |  |     // Default gender: none.  | 
185  | 0  |     fMicros.gender = "";  | 
186  |  |  | 
187  |  |     // Resolve the symbols. Do this here because currency may need to customize them.  | 
188  | 0  |     if (macros.symbols.isDecimalFormatSymbols()) { | 
189  | 0  |         fMicros.symbols = macros.symbols.getDecimalFormatSymbols();  | 
190  | 0  |     } else { | 
191  | 0  |         LocalPointer<DecimalFormatSymbols> newSymbols(  | 
192  | 0  |             new DecimalFormatSymbols(macros.locale, *ns, status), status);  | 
193  | 0  |         if (U_FAILURE(status)) { | 
194  | 0  |             return nullptr;  | 
195  | 0  |         }  | 
196  | 0  |         if (isCurrency) { | 
197  | 0  |             newSymbols->setCurrency(currency.getISOCurrency(), status);  | 
198  | 0  |             if (U_FAILURE(status)) { | 
199  | 0  |                 return nullptr;  | 
200  | 0  |             }  | 
201  | 0  |         }  | 
202  | 0  |         fMicros.symbols = newSymbols.getAlias();  | 
203  | 0  |         fSymbols.adoptInstead(newSymbols.orphan());  | 
204  | 0  |     }  | 
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  | 0  |     const char16_t* pattern = nullptr;  | 
209  | 0  |     if (isCurrency && fMicros.symbols->getCurrencyPattern() != nullptr) { | 
210  | 0  |         pattern = fMicros.symbols->getCurrencyPattern();  | 
211  | 0  |     }  | 
212  | 0  |     if (pattern == nullptr) { | 
213  | 0  |         CldrPatternStyle patternStyle;  | 
214  | 0  |         if (isCldrUnit) { | 
215  | 0  |             patternStyle = CLDR_PATTERN_STYLE_DECIMAL;  | 
216  | 0  |         } else if (isPercent || isPermille) { | 
217  | 0  |             patternStyle = CLDR_PATTERN_STYLE_PERCENT;  | 
218  | 0  |         } else if (!isCurrency || unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) { | 
219  | 0  |             patternStyle = CLDR_PATTERN_STYLE_DECIMAL;  | 
220  | 0  |         } 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  | 0  |             patternStyle = CLDR_PATTERN_STYLE_ACCOUNTING;  | 
224  | 0  |         } else { | 
225  | 0  |             patternStyle = CLDR_PATTERN_STYLE_CURRENCY;  | 
226  | 0  |         }  | 
227  | 0  |         pattern = utils::getPatternForStyle(macros.locale, nsName, patternStyle, status);  | 
228  | 0  |         if (U_FAILURE(status)) { | 
229  | 0  |             return nullptr;  | 
230  | 0  |         }  | 
231  | 0  |     }  | 
232  | 0  |     auto patternInfo = new ParsedPatternInfo();  | 
233  | 0  |     if (patternInfo == nullptr) { | 
234  | 0  |         status = U_MEMORY_ALLOCATION_ERROR;  | 
235  | 0  |         return nullptr;  | 
236  | 0  |     }  | 
237  | 0  |     fPatternInfo.adoptInstead(patternInfo);  | 
238  | 0  |     PatternParser::parseToPatternInfo(UnicodeString(pattern), *patternInfo, status);  | 
239  | 0  |     if (U_FAILURE(status)) { | 
240  | 0  |         return nullptr;  | 
241  | 0  |     }  | 
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  | 0  |     if (macros.usage.isSet()) { | 
249  | 0  |         if (!isCldrUnit) { | 
250  |  |             // We only support "usage" when the input unit is specified, and is  | 
251  |  |             // a CLDR Unit.  | 
252  | 0  |             status = U_ILLEGAL_ARGUMENT_ERROR;  | 
253  | 0  |             return nullptr;  | 
254  | 0  |         }  | 
255  | 0  |         auto usagePrefsHandler =  | 
256  | 0  |             new UsagePrefsHandler(macros.locale, macros.unit, macros.usage.fValue, chain, status);  | 
257  | 0  |         fUsagePrefsHandler.adoptInsteadAndCheckErrorCode(usagePrefsHandler, status);  | 
258  | 0  |         chain = fUsagePrefsHandler.getAlias();  | 
259  | 0  |     } else if (isMixedUnit) { | 
260  | 0  |         auto unitConversionHandler = new UnitConversionHandler(macros.unit, chain, status);  | 
261  | 0  |         fUnitConversionHandler.adoptInsteadAndCheckErrorCode(unitConversionHandler, status);  | 
262  | 0  |         chain = fUnitConversionHandler.getAlias();  | 
263  | 0  |     }  | 
264  |  |  | 
265  |  |     // Multiplier  | 
266  | 0  |     if (macros.scale.isValid()) { | 
267  | 0  |         fMicros.helpers.multiplier.setAndChain(macros.scale, chain);  | 
268  | 0  |         chain = &fMicros.helpers.multiplier;  | 
269  | 0  |     }  | 
270  |  |  | 
271  |  |     // Rounding strategy  | 
272  | 0  |     Precision precision;  | 
273  | 0  |     if (!macros.precision.isBogus()) { | 
274  | 0  |         precision = macros.precision;  | 
275  | 0  |     } else if (isCompactNotation) { | 
276  | 0  |         precision = Precision::integer().withMinDigits(2);  | 
277  | 0  |     } else if (isCurrency) { | 
278  | 0  |         precision = Precision::currency(UCURR_USAGE_STANDARD);  | 
279  | 0  |     } else if (macros.usage.isSet()) { | 
280  |  |         // Bogus Precision - it will get set in the UsagePrefsHandler instead  | 
281  | 0  |         precision = Precision();  | 
282  | 0  |     } else { | 
283  | 0  |         precision = Precision::maxFraction(6);  | 
284  | 0  |     }  | 
285  | 0  |     UNumberFormatRoundingMode roundingMode;  | 
286  | 0  |     roundingMode = macros.roundingMode;  | 
287  | 0  |     fMicros.rounder = {precision, roundingMode, currency, status}; | 
288  | 0  |     if (U_FAILURE(status)) { | 
289  | 0  |         return nullptr;  | 
290  | 0  |     }  | 
291  |  |  | 
292  |  |     // Grouping strategy  | 
293  | 0  |     if (!macros.grouper.isBogus()) { | 
294  | 0  |         fMicros.grouping = macros.grouper;  | 
295  | 0  |     } else if (isCompactNotation) { | 
296  |  |         // Compact notation uses minGrouping by default since ICU 59  | 
297  | 0  |         fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_MIN2);  | 
298  | 0  |     } else { | 
299  | 0  |         fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_AUTO);  | 
300  | 0  |     }  | 
301  | 0  |     fMicros.grouping.setLocaleData(*fPatternInfo, macros.locale);  | 
302  |  |  | 
303  |  |     // Padding strategy  | 
304  | 0  |     if (!macros.padder.isBogus()) { | 
305  | 0  |         fMicros.padding = macros.padder;  | 
306  | 0  |     } else { | 
307  | 0  |         fMicros.padding = Padder::none();  | 
308  | 0  |     }  | 
309  |  |  | 
310  |  |     // Integer width  | 
311  | 0  |     if (!macros.integerWidth.isBogus()) { | 
312  | 0  |         fMicros.integerWidth = macros.integerWidth;  | 
313  | 0  |     } else { | 
314  | 0  |         fMicros.integerWidth = IntegerWidth::standard();  | 
315  | 0  |     }  | 
316  |  |  | 
317  |  |     // Sign display  | 
318  | 0  |     if (macros.sign != UNUM_SIGN_COUNT) { | 
319  | 0  |         fMicros.sign = macros.sign;  | 
320  | 0  |     } else { | 
321  | 0  |         fMicros.sign = UNUM_SIGN_AUTO;  | 
322  | 0  |     }  | 
323  |  |  | 
324  |  |     // Decimal mark display  | 
325  | 0  |     if (macros.decimal != UNUM_DECIMAL_SEPARATOR_COUNT) { | 
326  | 0  |         fMicros.decimal = macros.decimal;  | 
327  | 0  |     } else { | 
328  | 0  |         fMicros.decimal = UNUM_DECIMAL_SEPARATOR_AUTO;  | 
329  | 0  |     }  | 
330  |  |  | 
331  |  |     // Use monetary separator symbols  | 
332  | 0  |     fMicros.useCurrency = isCurrency;  | 
333  |  |  | 
334  |  |     // Inner modifier (scientific notation)  | 
335  | 0  |     if (macros.notation.fType == Notation::NTN_SCIENTIFIC) { | 
336  | 0  |         auto newScientificHandler = new ScientificHandler(¯os.notation, fMicros.symbols, chain);  | 
337  | 0  |         if (newScientificHandler == nullptr) { | 
338  | 0  |             status = U_MEMORY_ALLOCATION_ERROR;  | 
339  | 0  |             return nullptr;  | 
340  | 0  |         }  | 
341  | 0  |         fScientificHandler.adoptInstead(newScientificHandler);  | 
342  | 0  |         chain = fScientificHandler.getAlias();  | 
343  | 0  |     } else { | 
344  |  |         // No inner modifier required  | 
345  | 0  |         fMicros.modInner = &fMicros.helpers.emptyStrongModifier;  | 
346  | 0  |     }  | 
347  |  |  | 
348  |  |     // Middle modifier (patterns, positive/negative, currency symbols, percent)  | 
349  | 0  |     auto patternModifier = new MutablePatternModifier(false);  | 
350  | 0  |     if (patternModifier == nullptr) { | 
351  | 0  |         status = U_MEMORY_ALLOCATION_ERROR;  | 
352  | 0  |         return nullptr;  | 
353  | 0  |     }  | 
354  | 0  |     fPatternModifier.adoptInstead(patternModifier);  | 
355  | 0  |     patternModifier->setPatternInfo(  | 
356  | 0  |             macros.affixProvider != nullptr ? macros.affixProvider  | 
357  | 0  |                                             : static_cast<const AffixPatternProvider*>(fPatternInfo.getAlias()),  | 
358  | 0  |             kUndefinedField);  | 
359  | 0  |     patternModifier->setPatternAttributes(fMicros.sign, isPermille, macros.approximately);  | 
360  | 0  |     if (patternModifier->needsPlurals()) { | 
361  | 0  |         patternModifier->setSymbols(  | 
362  | 0  |                 fMicros.symbols,  | 
363  | 0  |                 currency,  | 
364  | 0  |                 unitWidth,  | 
365  | 0  |                 resolvePluralRules(macros.rules, macros.locale, status),  | 
366  | 0  |                 status);  | 
367  | 0  |     } else { | 
368  | 0  |         patternModifier->setSymbols(fMicros.symbols, currency, unitWidth, nullptr, status);  | 
369  | 0  |     }  | 
370  | 0  |     if (safe) { | 
371  | 0  |         fImmutablePatternModifier.adoptInsteadAndCheckErrorCode(patternModifier->createImmutable(status),  | 
372  | 0  |                                                                 status);  | 
373  | 0  |     }  | 
374  | 0  |     if (U_FAILURE(status)) { | 
375  | 0  |         return nullptr;  | 
376  | 0  |     }  | 
377  |  |  | 
378  |  |     // Outer modifier (CLDR units and currency long names)  | 
379  | 0  |     if (isCldrUnit) { | 
380  | 0  |         const char *unitDisplayCase = "";  | 
381  | 0  |         if (macros.unitDisplayCase.isSet()) { | 
382  | 0  |             unitDisplayCase = macros.unitDisplayCase.fValue;  | 
383  | 0  |         }  | 
384  | 0  |         if (macros.usage.isSet()) { | 
385  | 0  |             fLongNameMultiplexer.adoptInsteadAndCheckErrorCode(  | 
386  | 0  |                 LongNameMultiplexer::forMeasureUnits(  | 
387  | 0  |                     macros.locale, *fUsagePrefsHandler->getOutputUnits(), unitWidth, unitDisplayCase,  | 
388  | 0  |                     resolvePluralRules(macros.rules, macros.locale, status), chain, status),  | 
389  | 0  |                 status);  | 
390  | 0  |             chain = fLongNameMultiplexer.getAlias();  | 
391  | 0  |         } else if (isMixedUnit) { | 
392  | 0  |             fMixedUnitLongNameHandler.adoptInsteadAndCheckErrorCode(new MixedUnitLongNameHandler(),  | 
393  | 0  |                                                                     status);  | 
394  | 0  |             MixedUnitLongNameHandler::forMeasureUnit(  | 
395  | 0  |                 macros.locale, macros.unit, unitWidth, unitDisplayCase,  | 
396  | 0  |                 resolvePluralRules(macros.rules, macros.locale, status), chain,  | 
397  | 0  |                 fMixedUnitLongNameHandler.getAlias(), status);  | 
398  | 0  |             chain = fMixedUnitLongNameHandler.getAlias();  | 
399  | 0  |         } else { | 
400  | 0  |             MeasureUnit unit = macros.unit;  | 
401  | 0  |             if (!utils::unitIsBaseUnit(macros.perUnit)) { | 
402  | 0  |                 unit = unit.product(macros.perUnit.reciprocal(status), status);  | 
403  |  |                 // This isn't strictly necessary, but was what we specced out  | 
404  |  |                 // when perUnit became a backward-compatibility thing:  | 
405  |  |                 // unit/perUnit use case is only valid if both units are  | 
406  |  |                 // built-ins, or the product is a built-in.  | 
407  | 0  |                 if (uprv_strcmp(unit.getType(), "") == 0 &&  | 
408  | 0  |                     (uprv_strcmp(macros.unit.getType(), "") == 0 ||  | 
409  | 0  |                      uprv_strcmp(macros.perUnit.getType(), "") == 0)) { | 
410  | 0  |                     status = U_UNSUPPORTED_ERROR;  | 
411  | 0  |                     return nullptr;  | 
412  | 0  |                 }  | 
413  | 0  |             }  | 
414  | 0  |             fLongNameHandler.adoptInsteadAndCheckErrorCode(new LongNameHandler(), status);  | 
415  | 0  |             LongNameHandler::forMeasureUnit(macros.locale, unit, unitWidth, unitDisplayCase,  | 
416  | 0  |                                             resolvePluralRules(macros.rules, macros.locale, status),  | 
417  | 0  |                                             chain, fLongNameHandler.getAlias(), status);  | 
418  | 0  |             chain = fLongNameHandler.getAlias();  | 
419  | 0  |         }  | 
420  | 0  |     } else if (isCurrency && unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) { | 
421  | 0  |         fLongNameHandler.adoptInsteadAndCheckErrorCode(  | 
422  | 0  |             LongNameHandler::forCurrencyLongNames(  | 
423  | 0  |                 macros.locale, currency, resolvePluralRules(macros.rules, macros.locale, status), chain,  | 
424  | 0  |                 status),  | 
425  | 0  |             status);  | 
426  | 0  |         chain = fLongNameHandler.getAlias();  | 
427  | 0  |     } else { | 
428  |  |         // No outer modifier required  | 
429  | 0  |         fMicros.modOuter = &fMicros.helpers.emptyWeakModifier;  | 
430  | 0  |     }  | 
431  | 0  |     if (U_FAILURE(status)) { | 
432  | 0  |         return nullptr;  | 
433  | 0  |     }  | 
434  |  |  | 
435  |  |     // Compact notation  | 
436  | 0  |     if (isCompactNotation) { | 
437  | 0  |         CompactType compactType = (isCurrency && unitWidth != UNUM_UNIT_WIDTH_FULL_NAME)  | 
438  | 0  |                                   ? CompactType::TYPE_CURRENCY : CompactType::TYPE_DECIMAL;  | 
439  | 0  |         auto newCompactHandler = new CompactHandler(  | 
440  | 0  |             macros.notation.fUnion.compactStyle,  | 
441  | 0  |             macros.locale,  | 
442  | 0  |             nsName,  | 
443  | 0  |             compactType,  | 
444  | 0  |             resolvePluralRules(macros.rules, macros.locale, status),  | 
445  | 0  |             patternModifier,  | 
446  | 0  |             safe,  | 
447  | 0  |             chain,  | 
448  | 0  |             status);  | 
449  | 0  |         if (U_FAILURE(status)) { | 
450  | 0  |             return nullptr;  | 
451  | 0  |         }  | 
452  | 0  |         if (newCompactHandler == nullptr) { | 
453  | 0  |             status = U_MEMORY_ALLOCATION_ERROR;  | 
454  | 0  |             return nullptr;  | 
455  | 0  |         }  | 
456  | 0  |         fCompactHandler.adoptInstead(newCompactHandler);  | 
457  | 0  |         chain = fCompactHandler.getAlias();  | 
458  | 0  |     }  | 
459  | 0  |     if (U_FAILURE(status)) { | 
460  | 0  |         return nullptr;  | 
461  | 0  |     }  | 
462  |  |  | 
463  |  |     // Always add the pattern modifier as the last element of the chain.  | 
464  | 0  |     if (safe) { | 
465  | 0  |         fImmutablePatternModifier->addToChain(chain);  | 
466  | 0  |         chain = fImmutablePatternModifier.getAlias();  | 
467  | 0  |     } else { | 
468  | 0  |         patternModifier->addToChain(chain);  | 
469  | 0  |         chain = patternModifier;  | 
470  | 0  |     }  | 
471  |  | 
  | 
472  | 0  |     return chain;  | 
473  | 0  | }  | 
474  |  |  | 
475  |  | const PluralRules*  | 
476  |  | NumberFormatterImpl::resolvePluralRules(const PluralRules* rulesPtr, const Locale& locale,  | 
477  | 0  |                                         UErrorCode& status) { | 
478  | 0  |     if (rulesPtr != nullptr) { | 
479  | 0  |         return rulesPtr;  | 
480  | 0  |     }  | 
481  |  |     // Lazily create PluralRules  | 
482  | 0  |     if (fRules.isNull()) { | 
483  | 0  |         fRules.adoptInstead(PluralRules::forLocale(locale, status));  | 
484  | 0  |     }  | 
485  | 0  |     return fRules.getAlias();  | 
486  | 0  | }  | 
487  |  |  | 
488  |  | int32_t NumberFormatterImpl::writeAffixes(const MicroProps& micros, FormattedStringBuilder& string,  | 
489  | 0  |                                           int32_t start, int32_t end, UErrorCode& status) { | 
490  | 0  |     U_ASSERT(micros.modOuter != nullptr);  | 
491  |  |     // Always apply the inner modifier (which is "strong").  | 
492  | 0  |     int32_t length = micros.modInner->apply(string, start, end, status);  | 
493  | 0  |     if (micros.padding.isValid()) { | 
494  | 0  |         length += micros.padding  | 
495  | 0  |                 .padAndApply(*micros.modMiddle, *micros.modOuter, string, start, length + end, status);  | 
496  | 0  |     } else { | 
497  | 0  |         length += micros.modMiddle->apply(string, start, length + end, status);  | 
498  | 0  |         length += micros.modOuter->apply(string, start, length + end, status);  | 
499  | 0  |     }  | 
500  | 0  |     return length;  | 
501  | 0  | }  | 
502  |  |  | 
503  |  | int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuantity& quantity,  | 
504  |  |                                          FormattedStringBuilder& string, int32_t index,  | 
505  | 0  |                                          UErrorCode& status) { | 
506  | 0  |     int32_t length = 0;  | 
507  | 0  |     if (quantity.isInfinite()) { | 
508  | 0  |         length += string.insert(  | 
509  | 0  |                 length + index,  | 
510  | 0  |                 micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kInfinitySymbol),  | 
511  | 0  |                 {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD}, | 
512  | 0  |                 status);  | 
513  |  | 
  | 
514  | 0  |     } else if (quantity.isNaN()) { | 
515  | 0  |         length += string.insert(  | 
516  | 0  |                 length + index,  | 
517  | 0  |                 micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kNaNSymbol),  | 
518  | 0  |                 {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD}, | 
519  | 0  |                 status);  | 
520  |  | 
  | 
521  | 0  |     } else { | 
522  |  |         // Add the integer digits  | 
523  | 0  |         length += writeIntegerDigits(micros, quantity, string, length + index, status);  | 
524  |  |  | 
525  |  |         // Add the decimal point  | 
526  | 0  |         if (quantity.getLowerDisplayMagnitude() < 0 || micros.decimal == UNUM_DECIMAL_SEPARATOR_ALWAYS) { | 
527  | 0  |             length += string.insert(  | 
528  | 0  |                     length + index,  | 
529  | 0  |                     micros.useCurrency ? micros.symbols->getSymbol(  | 
530  | 0  |                             DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol) : micros  | 
531  | 0  |                             .symbols  | 
532  | 0  |                             ->getSymbol(  | 
533  | 0  |                                     DecimalFormatSymbols::ENumberFormatSymbol::kDecimalSeparatorSymbol),  | 
534  | 0  |                     {UFIELD_CATEGORY_NUMBER, UNUM_DECIMAL_SEPARATOR_FIELD}, | 
535  | 0  |                     status);  | 
536  | 0  |         }  | 
537  |  |  | 
538  |  |         // Add the fraction digits  | 
539  | 0  |         length += writeFractionDigits(micros, quantity, string, length + index, status);  | 
540  |  | 
  | 
541  | 0  |         if (length == 0) { | 
542  |  |             // Force output of the digit for value 0  | 
543  | 0  |             length += utils::insertDigitFromSymbols(  | 
544  | 0  |                     string,  | 
545  | 0  |                     index,  | 
546  | 0  |                     0,  | 
547  | 0  |                     *micros.symbols,  | 
548  | 0  |                     {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD}, | 
549  | 0  |                     status);  | 
550  | 0  |         }  | 
551  | 0  |     }  | 
552  |  | 
  | 
553  | 0  |     return length;  | 
554  | 0  | }  | 
555  |  |  | 
556  |  | int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, DecimalQuantity& quantity,  | 
557  |  |                                                 FormattedStringBuilder& string, int32_t index,  | 
558  | 0  |                                                 UErrorCode& status) { | 
559  | 0  |     int length = 0;  | 
560  | 0  |     int integerCount = quantity.getUpperDisplayMagnitude() + 1;  | 
561  | 0  |     for (int i = 0; i < integerCount; i++) { | 
562  |  |         // Add grouping separator  | 
563  | 0  |         if (micros.grouping.groupAtPosition(i, quantity)) { | 
564  | 0  |             length += string.insert(  | 
565  | 0  |                     index,  | 
566  | 0  |                     micros.useCurrency ? micros.symbols->getSymbol(  | 
567  | 0  |                             DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol)  | 
568  | 0  |                                        : micros.symbols->getSymbol(  | 
569  | 0  |                             DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol),  | 
570  | 0  |                     {UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD}, | 
571  | 0  |                     status);  | 
572  | 0  |         }  | 
573  |  |  | 
574  |  |         // Get and append the next digit value  | 
575  | 0  |         int8_t nextDigit = quantity.getDigit(i);  | 
576  | 0  |         length += utils::insertDigitFromSymbols(  | 
577  | 0  |                 string,  | 
578  | 0  |                 index,  | 
579  | 0  |                 nextDigit,  | 
580  | 0  |                 *micros.symbols,  | 
581  | 0  |                 {UFIELD_CATEGORY_NUMBER, | 
582  | 0  |                 UNUM_INTEGER_FIELD},  | 
583  | 0  |                 status);  | 
584  | 0  |     }  | 
585  | 0  |     return length;  | 
586  | 0  | }  | 
587  |  |  | 
588  |  | int32_t NumberFormatterImpl::writeFractionDigits(const MicroProps& micros, DecimalQuantity& quantity,  | 
589  |  |                                                  FormattedStringBuilder& string, int32_t index,  | 
590  | 0  |                                                  UErrorCode& status) { | 
591  | 0  |     int length = 0;  | 
592  | 0  |     int fractionCount = -quantity.getLowerDisplayMagnitude();  | 
593  | 0  |     for (int i = 0; i < fractionCount; i++) { | 
594  |  |         // Get and append the next digit value  | 
595  | 0  |         int8_t nextDigit = quantity.getDigit(-i - 1);  | 
596  | 0  |         length += utils::insertDigitFromSymbols(  | 
597  | 0  |                 string,  | 
598  | 0  |                 length + index,  | 
599  | 0  |                 nextDigit,  | 
600  | 0  |                 *micros.symbols,  | 
601  | 0  |                 {UFIELD_CATEGORY_NUMBER, UNUM_FRACTION_FIELD}, | 
602  | 0  |                 status);  | 
603  | 0  |     }  | 
604  | 0  |     return length;  | 
605  | 0  | }  | 
606  |  |  | 
607  |  | #endif /* #if !UCONFIG_NO_FORMATTING */  |