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