blob: 4fb190b744b7c15fa60114b75b6159c3d9e78d6b [file] [log] [blame]
Jungshik Shinb3189662017-11-07 11:18:34 -08001// © 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
Jungshik Shina9a2bd32018-07-07 03:36:01 -07006#if !UCONFIG_NO_FORMATTING
Jungshik Shinb3189662017-11-07 11:18:34 -08007
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"
Jungshik Shinf61e46d2018-05-04 13:00:45 -070020#include "uresimp.h"
21#include "ureslocs.h"
Jungshik Shinb3189662017-11-07 11:18:34 -080022
23using namespace icu;
24using namespace icu::number;
25using namespace icu::number::impl;
26
Jungshik Shina9a2bd32018-07-07 03:36:01 -070027
Jungshik Shin42d50272018-10-24 01:22:09 -070028NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, UErrorCode& status)
29 : NumberFormatterImpl(macros, true, status) {
Jungshik Shinb3189662017-11-07 11:18:34 -080030}
31
Frank Tangf90543d2020-10-30 19:02:04 -070032int32_t NumberFormatterImpl::formatStatic(const MacroProps &macros, UFormattedNumberData *results,
33 UErrorCode &status) {
34 DecimalQuantity &inValue = results->quantity;
35 FormattedStringBuilder &outString = results->getStringRef();
Jungshik Shinb3189662017-11-07 11:18:34 -080036 NumberFormatterImpl impl(macros, false, status);
Jungshik Shin42d50272018-10-24 01:22:09 -070037 MicroProps& micros = impl.preProcessUnsafe(inValue, status);
38 if (U_FAILURE(status)) { return 0; }
39 int32_t length = writeNumber(micros, inValue, outString, 0, status);
40 length += writeAffixes(micros, outString, 0, length, status);
Frank Tangf90543d2020-10-30 19:02:04 -070041 results->outputUnit = std::move(micros.outputUnit);
Frank Tang7e7574b2021-04-13 21:19:13 -070042 results->gender = micros.gender;
Jungshik Shin42d50272018-10-24 01:22:09 -070043 return length;
Jungshik Shinb3189662017-11-07 11:18:34 -080044}
45
Frank Tangb8696612019-10-25 14:58:21 -070046int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, Signum signum,
Jungshik Shina9a2bd32018-07-07 03:36:01 -070047 StandardPlural::Form plural,
Frank Tangb8696612019-10-25 14:58:21 -070048 FormattedStringBuilder& outString, UErrorCode& status) {
Jungshik Shina9a2bd32018-07-07 03:36:01 -070049 NumberFormatterImpl impl(macros, false, status);
50 return impl.getPrefixSuffixUnsafe(signum, plural, outString, status);
51}
52
Jungshik Shinb3189662017-11-07 11:18:34 -080053// 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
Frank Tangf90543d2020-10-30 19:02:04 -070058int32_t NumberFormatterImpl::format(UFormattedNumberData *results, UErrorCode &status) const {
59 DecimalQuantity &inValue = results->quantity;
60 FormattedStringBuilder &outString = results->getStringRef();
Jungshik Shinb3189662017-11-07 11:18:34 -080061 MicroProps micros;
Jungshik Shin42d50272018-10-24 01:22:09 -070062 preProcess(inValue, micros, status);
63 if (U_FAILURE(status)) { return 0; }
64 int32_t length = writeNumber(micros, inValue, outString, 0, status);
65 length += writeAffixes(micros, outString, 0, length, status);
Frank Tangf90543d2020-10-30 19:02:04 -070066 results->outputUnit = std::move(micros.outputUnit);
Frank Tang7e7574b2021-04-13 21:19:13 -070067 results->gender = micros.gender;
Jungshik Shin42d50272018-10-24 01:22:09 -070068 return length;
Jungshik Shinb3189662017-11-07 11:18:34 -080069}
70
Jungshik Shin42d50272018-10-24 01:22:09 -070071void NumberFormatterImpl::preProcess(DecimalQuantity& inValue, MicroProps& microsOut,
72 UErrorCode& status) const {
Jungshik Shinb3189662017-11-07 11:18:34 -080073 if (U_FAILURE(status)) { return; }
Jungshik Shin42d50272018-10-24 01:22:09 -070074 if (fMicroPropsGenerator == nullptr) {
75 status = U_INTERNAL_PROGRAM_ERROR;
76 return;
77 }
78 fMicroPropsGenerator->processQuantity(inValue, microsOut, status);
Jungshik Shin42d50272018-10-24 01:22:09 -070079 microsOut.integerWidth.apply(inValue, status);
80}
81
82MicroProps& NumberFormatterImpl::preProcessUnsafe(DecimalQuantity& inValue, UErrorCode& status) {
83 if (U_FAILURE(status)) {
84 return fMicros; // must always return a value
85 }
86 if (fMicroPropsGenerator == nullptr) {
87 status = U_INTERNAL_PROGRAM_ERROR;
88 return fMicros; // must always return a value
89 }
Jungshik Shinb3189662017-11-07 11:18:34 -080090 fMicroPropsGenerator->processQuantity(inValue, fMicros, status);
Jungshik Shin42d50272018-10-24 01:22:09 -070091 fMicros.integerWidth.apply(inValue, status);
92 return fMicros;
Jungshik Shinb3189662017-11-07 11:18:34 -080093}
94
Frank Tangb8696612019-10-25 14:58:21 -070095int32_t NumberFormatterImpl::getPrefixSuffix(Signum signum, StandardPlural::Form plural,
96 FormattedStringBuilder& outString, UErrorCode& status) const {
Jungshik Shina9a2bd32018-07-07 03:36:01 -070097 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 const Modifier* modifier = fImmutablePatternModifier->getModifier(signum, plural);
101 modifier->apply(outString, 0, 0, status);
102 if (U_FAILURE(status)) { return 0; }
Jungshik Shin42d50272018-10-24 01:22:09 -0700103 return modifier->getPrefixLength();
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700104}
105
Frank Tangb8696612019-10-25 14:58:21 -0700106int32_t NumberFormatterImpl::getPrefixSuffixUnsafe(Signum signum, StandardPlural::Form plural,
107 FormattedStringBuilder& outString, UErrorCode& status) {
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700108 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 fPatternModifier->setNumberProperties(signum, plural);
112 fPatternModifier->apply(outString, 0, 0, status);
113 if (U_FAILURE(status)) { return 0; }
Jungshik Shin42d50272018-10-24 01:22:09 -0700114 return fPatternModifier->getPrefixLength();
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700115}
116
117NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, bool safe, UErrorCode& status) {
Jungshik Shinb3189662017-11-07 11:18:34 -0800118 fMicroPropsGenerator = macrosToMicroGenerator(macros, safe, status);
119}
120
121//////////
122
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700123const MicroPropsGenerator*
124NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe, UErrorCode& status) {
125 if (U_FAILURE(status)) { return nullptr; }
126 const MicroPropsGenerator* chain = &fMicros;
Jungshik Shinb3189662017-11-07 11:18:34 -0800127
128 // Check that macros is error-free before continuing.
129 if (macros.copyErrorTo(status)) {
130 return nullptr;
131 }
132
133 // TODO: Accept currency symbols from DecimalFormatSymbols?
134
135 // Pre-compute a few values for efficiency.
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700136 bool isCurrency = utils::unitIsCurrency(macros.unit);
Frank Tangf90543d2020-10-30 19:02:04 -0700137 bool isBaseUnit = utils::unitIsBaseUnit(macros.unit);
Frank Tang69c72a62019-04-03 21:41:21 -0700138 bool isPercent = utils::unitIsPercent(macros.unit);
139 bool isPermille = utils::unitIsPermille(macros.unit);
Frank Tangf90543d2020-10-30 19:02:04 -0700140 bool isCompactNotation = macros.notation.fType == Notation::NTN_COMPACT;
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700141 bool isAccounting =
Frank Tang7e7574b2021-04-13 21:19:13 -0700142 macros.sign == UNUM_SIGN_ACCOUNTING ||
143 macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS ||
144 macros.sign == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO ||
145 macros.sign == UNUM_SIGN_ACCOUNTING_NEGATIVE;
Frank Tang69c72a62019-04-03 21:41:21 -0700146 CurrencyUnit currency(u"", status);
Jungshik Shinb3189662017-11-07 11:18:34 -0800147 if (isCurrency) {
148 currency = CurrencyUnit(macros.unit, status); // Restore CurrencyUnit from MeasureUnit
149 }
150 UNumberUnitWidth unitWidth = UNUM_UNIT_WIDTH_SHORT;
151 if (macros.unitWidth != UNUM_UNIT_WIDTH_COUNT) {
152 unitWidth = macros.unitWidth;
153 }
Frank Tangf90543d2020-10-30 19:02:04 -0700154 // 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 bool isCldrUnit = !isCurrency
161 && !isBaseUnit
162 && (unitWidth == UNUM_UNIT_WIDTH_FULL_NAME
163 || !(isPercent || isPermille)
164 || isCompactNotation
165 );
166 bool isMixedUnit = isCldrUnit && (uprv_strcmp(macros.unit.getType(), "") == 0) &&
167 macros.unit.getComplexity(status) == UMEASURE_UNIT_MIXED;
Jungshik Shinb3189662017-11-07 11:18:34 -0800168
169 // Select the numbering system.
170 LocalPointer<const NumberingSystem> nsLocal;
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700171 const NumberingSystem* ns;
Jungshik Shinb3189662017-11-07 11:18:34 -0800172 if (macros.symbols.isNumberingSystem()) {
173 ns = macros.symbols.getNumberingSystem();
174 } else {
175 // TODO: Is there a way to avoid creating the NumberingSystem object?
176 ns = NumberingSystem::createInstance(macros.locale, status);
177 // Give ownership to the function scope.
178 nsLocal.adoptInstead(ns);
179 }
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700180 const char* nsName = U_SUCCESS(status) ? ns->getName() : "latn";
Frank Tang69c72a62019-04-03 21:41:21 -0700181 uprv_strncpy(fMicros.nsName, nsName, 8);
182 fMicros.nsName[8] = 0; // guarantee NUL-terminated
Jungshik Shinb3189662017-11-07 11:18:34 -0800183
Frank Tang7e7574b2021-04-13 21:19:13 -0700184 // Default gender: none.
185 fMicros.gender = "";
186
Jungshik Shinf61e46d2018-05-04 13:00:45 -0700187 // Resolve the symbols. Do this here because currency may need to customize them.
Jungshik Shinb3189662017-11-07 11:18:34 -0800188 if (macros.symbols.isDecimalFormatSymbols()) {
189 fMicros.symbols = macros.symbols.getDecimalFormatSymbols();
190 } else {
Frank Tangf2223962020-04-27 18:25:29 -0700191 LocalPointer<DecimalFormatSymbols> newSymbols(
192 new DecimalFormatSymbols(macros.locale, *ns, status), status);
193 if (U_FAILURE(status)) {
Frank Tangb8696612019-10-25 14:58:21 -0700194 return nullptr;
195 }
Frank Tangf2223962020-04-27 18:25:29 -0700196 if (isCurrency) {
197 newSymbols->setCurrency(currency.getISOCurrency(), status);
198 if (U_FAILURE(status)) {
199 return nullptr;
200 }
201 }
202 fMicros.symbols = newSymbols.getAlias();
203 fSymbols.adoptInstead(newSymbols.orphan());
Jungshik Shinb3189662017-11-07 11:18:34 -0800204 }
205
Jungshik Shinf61e46d2018-05-04 13:00:45 -0700206 // 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 const char16_t* pattern = nullptr;
Frank Tangf2223962020-04-27 18:25:29 -0700209 if (isCurrency && fMicros.symbols->getCurrencyPattern() != nullptr) {
210 pattern = fMicros.symbols->getCurrencyPattern();
Jungshik Shinf61e46d2018-05-04 13:00:45 -0700211 }
212 if (pattern == nullptr) {
213 CldrPatternStyle patternStyle;
Frank Tang69c72a62019-04-03 21:41:21 -0700214 if (isCldrUnit) {
215 patternStyle = CLDR_PATTERN_STYLE_DECIMAL;
216 } else if (isPercent || isPermille) {
Jungshik Shinf61e46d2018-05-04 13:00:45 -0700217 patternStyle = CLDR_PATTERN_STYLE_PERCENT;
218 } else if (!isCurrency || unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) {
219 patternStyle = CLDR_PATTERN_STYLE_DECIMAL;
220 } 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 patternStyle = CLDR_PATTERN_STYLE_ACCOUNTING;
224 } else {
225 patternStyle = CLDR_PATTERN_STYLE_CURRENCY;
226 }
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700227 pattern = utils::getPatternForStyle(macros.locale, nsName, patternStyle, status);
Frank Tangf2223962020-04-27 18:25:29 -0700228 if (U_FAILURE(status)) {
229 return nullptr;
230 }
Jungshik Shinf61e46d2018-05-04 13:00:45 -0700231 }
232 auto patternInfo = new ParsedPatternInfo();
Frank Tangb8696612019-10-25 14:58:21 -0700233 if (patternInfo == nullptr) {
234 status = U_MEMORY_ALLOCATION_ERROR;
235 return nullptr;
236 }
Jungshik Shinf61e46d2018-05-04 13:00:45 -0700237 fPatternInfo.adoptInstead(patternInfo);
238 PatternParser::parseToPatternInfo(UnicodeString(pattern), *patternInfo, status);
Frank Tangf2223962020-04-27 18:25:29 -0700239 if (U_FAILURE(status)) {
240 return nullptr;
241 }
Jungshik Shinf61e46d2018-05-04 13:00:45 -0700242
243 /////////////////////////////////////////////////////////////////////////////////////
244 /// START POPULATING THE DEFAULT MICROPROPS AND BUILDING THE MICROPROPS GENERATOR ///
245 /////////////////////////////////////////////////////////////////////////////////////
246
Frank Tangf90543d2020-10-30 19:02:04 -0700247 // Unit Preferences and Conversions as our first step
248 if (macros.usage.isSet()) {
249 if (!isCldrUnit) {
250 // We only support "usage" when the input unit is specified, and is
251 // a CLDR Unit.
252 status = U_ILLEGAL_ARGUMENT_ERROR;
253 return nullptr;
254 }
255 auto usagePrefsHandler =
Frank Tang7e7574b2021-04-13 21:19:13 -0700256 new UsagePrefsHandler(macros.locale, macros.unit, macros.usage.fValue, chain, status);
Frank Tangf90543d2020-10-30 19:02:04 -0700257 fUsagePrefsHandler.adoptInsteadAndCheckErrorCode(usagePrefsHandler, status);
258 chain = fUsagePrefsHandler.getAlias();
259 } else if (isMixedUnit) {
Frank Tang7e7574b2021-04-13 21:19:13 -0700260 auto unitConversionHandler = new UnitConversionHandler(macros.unit, chain, status);
Frank Tangf90543d2020-10-30 19:02:04 -0700261 fUnitConversionHandler.adoptInsteadAndCheckErrorCode(unitConversionHandler, status);
262 chain = fUnitConversionHandler.getAlias();
263 }
264
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700265 // Multiplier
266 if (macros.scale.isValid()) {
267 fMicros.helpers.multiplier.setAndChain(macros.scale, chain);
268 chain = &fMicros.helpers.multiplier;
Jungshik Shinb3189662017-11-07 11:18:34 -0800269 }
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700270
271 // Rounding strategy
272 Precision precision;
273 if (!macros.precision.isBogus()) {
274 precision = macros.precision;
Frank Tangf90543d2020-10-30 19:02:04 -0700275 } else if (isCompactNotation) {
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700276 precision = Precision::integer().withMinDigits(2);
277 } else if (isCurrency) {
278 precision = Precision::currency(UCURR_USAGE_STANDARD);
Frank Tangf90543d2020-10-30 19:02:04 -0700279 } else if (macros.usage.isSet()) {
280 // Bogus Precision - it will get set in the UsagePrefsHandler instead
281 precision = Precision();
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700282 } else {
283 precision = Precision::maxFraction(6);
284 }
285 UNumberFormatRoundingMode roundingMode;
Frank Tangf90543d2020-10-30 19:02:04 -0700286 roundingMode = macros.roundingMode;
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700287 fMicros.rounder = {precision, roundingMode, currency, status};
Frank Tangf2223962020-04-27 18:25:29 -0700288 if (U_FAILURE(status)) {
289 return nullptr;
290 }
Jungshik Shinb3189662017-11-07 11:18:34 -0800291
292 // Grouping strategy
293 if (!macros.grouper.isBogus()) {
294 fMicros.grouping = macros.grouper;
Frank Tangf90543d2020-10-30 19:02:04 -0700295 } else if (isCompactNotation) {
Jungshik Shinb3189662017-11-07 11:18:34 -0800296 // Compact notation uses minGrouping by default since ICU 59
Jungshik Shinf61e46d2018-05-04 13:00:45 -0700297 fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_MIN2);
Jungshik Shinb3189662017-11-07 11:18:34 -0800298 } else {
Jungshik Shinf61e46d2018-05-04 13:00:45 -0700299 fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_AUTO);
Jungshik Shinb3189662017-11-07 11:18:34 -0800300 }
Jungshik Shinf61e46d2018-05-04 13:00:45 -0700301 fMicros.grouping.setLocaleData(*fPatternInfo, macros.locale);
Jungshik Shinb3189662017-11-07 11:18:34 -0800302
303 // Padding strategy
304 if (!macros.padder.isBogus()) {
305 fMicros.padding = macros.padder;
306 } else {
307 fMicros.padding = Padder::none();
308 }
309
310 // Integer width
311 if (!macros.integerWidth.isBogus()) {
312 fMicros.integerWidth = macros.integerWidth;
313 } else {
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700314 fMicros.integerWidth = IntegerWidth::standard();
Jungshik Shinb3189662017-11-07 11:18:34 -0800315 }
316
317 // Sign display
318 if (macros.sign != UNUM_SIGN_COUNT) {
319 fMicros.sign = macros.sign;
320 } else {
321 fMicros.sign = UNUM_SIGN_AUTO;
322 }
323
324 // Decimal mark display
325 if (macros.decimal != UNUM_DECIMAL_SEPARATOR_COUNT) {
326 fMicros.decimal = macros.decimal;
327 } else {
328 fMicros.decimal = UNUM_DECIMAL_SEPARATOR_AUTO;
329 }
330
331 // Use monetary separator symbols
332 fMicros.useCurrency = isCurrency;
333
334 // Inner modifier (scientific notation)
335 if (macros.notation.fType == Notation::NTN_SCIENTIFIC) {
Frank Tangb8696612019-10-25 14:58:21 -0700336 auto newScientificHandler = new ScientificHandler(&macros.notation, fMicros.symbols, chain);
337 if (newScientificHandler == nullptr) {
338 status = U_MEMORY_ALLOCATION_ERROR;
339 return nullptr;
340 }
341 fScientificHandler.adoptInstead(newScientificHandler);
Jungshik Shinb3189662017-11-07 11:18:34 -0800342 chain = fScientificHandler.getAlias();
343 } else {
344 // No inner modifier required
345 fMicros.modInner = &fMicros.helpers.emptyStrongModifier;
346 }
347
348 // Middle modifier (patterns, positive/negative, currency symbols, percent)
349 auto patternModifier = new MutablePatternModifier(false);
Frank Tangb8696612019-10-25 14:58:21 -0700350 if (patternModifier == nullptr) {
351 status = U_MEMORY_ALLOCATION_ERROR;
352 return nullptr;
353 }
Jungshik Shinb3189662017-11-07 11:18:34 -0800354 fPatternModifier.adoptInstead(patternModifier);
Frank Tang3e05d9d2021-11-08 14:04:04 -0800355 const AffixPatternProvider* affixProvider =
Frank Tang1f164ee2022-11-08 12:31:27 -0800356 macros.affixProvider != nullptr && (
357 // For more information on this condition, see ICU-22073
358 !isCompactNotation || isCurrency == macros.affixProvider->hasCurrencySign())
Frank Tang3e05d9d2021-11-08 14:04:04 -0800359 ? macros.affixProvider
360 : static_cast<const AffixPatternProvider*>(fPatternInfo.getAlias());
361 patternModifier->setPatternInfo(affixProvider, kUndefinedField);
362 patternModifier->setPatternAttributes(fMicros.sign, isPermille, macros.approximately);
Jungshik Shinb3189662017-11-07 11:18:34 -0800363 if (patternModifier->needsPlurals()) {
364 patternModifier->setSymbols(
365 fMicros.symbols,
Frank Tangf2223962020-04-27 18:25:29 -0700366 currency,
Jungshik Shinb3189662017-11-07 11:18:34 -0800367 unitWidth,
Frank Tangf2223962020-04-27 18:25:29 -0700368 resolvePluralRules(macros.rules, macros.locale, status),
369 status);
Jungshik Shinb3189662017-11-07 11:18:34 -0800370 } else {
Frank Tangf2223962020-04-27 18:25:29 -0700371 patternModifier->setSymbols(fMicros.symbols, currency, unitWidth, nullptr, status);
Jungshik Shinb3189662017-11-07 11:18:34 -0800372 }
373 if (safe) {
Frank Tangf90543d2020-10-30 19:02:04 -0700374 fImmutablePatternModifier.adoptInsteadAndCheckErrorCode(patternModifier->createImmutable(status),
375 status);
Frank Tangf2223962020-04-27 18:25:29 -0700376 }
377 if (U_FAILURE(status)) {
378 return nullptr;
Jungshik Shinb3189662017-11-07 11:18:34 -0800379 }
380
Frank Tang3e05d9d2021-11-08 14:04:04 -0800381 // currencyAsDecimal
382 if (affixProvider->currencyAsDecimal()) {
383 fMicros.currencyAsDecimal = patternModifier->getCurrencySymbolForUnitWidth(status);
384 }
385
Jungshik Shinb3189662017-11-07 11:18:34 -0800386 // Outer modifier (CLDR units and currency long names)
387 if (isCldrUnit) {
Frank Tang7e7574b2021-04-13 21:19:13 -0700388 const char *unitDisplayCase = "";
389 if (macros.unitDisplayCase.isSet()) {
390 unitDisplayCase = macros.unitDisplayCase.fValue;
391 }
Frank Tangf90543d2020-10-30 19:02:04 -0700392 if (macros.usage.isSet()) {
393 fLongNameMultiplexer.adoptInsteadAndCheckErrorCode(
394 LongNameMultiplexer::forMeasureUnits(
Frank Tang7e7574b2021-04-13 21:19:13 -0700395 macros.locale, *fUsagePrefsHandler->getOutputUnits(), unitWidth, unitDisplayCase,
Frank Tangf90543d2020-10-30 19:02:04 -0700396 resolvePluralRules(macros.rules, macros.locale, status), chain, status),
397 status);
398 chain = fLongNameMultiplexer.getAlias();
399 } else if (isMixedUnit) {
400 fMixedUnitLongNameHandler.adoptInsteadAndCheckErrorCode(new MixedUnitLongNameHandler(),
401 status);
402 MixedUnitLongNameHandler::forMeasureUnit(
Frank Tang7e7574b2021-04-13 21:19:13 -0700403 macros.locale, macros.unit, unitWidth, unitDisplayCase,
Frank Tangf90543d2020-10-30 19:02:04 -0700404 resolvePluralRules(macros.rules, macros.locale, status), chain,
405 fMixedUnitLongNameHandler.getAlias(), status);
406 chain = fMixedUnitLongNameHandler.getAlias();
407 } else {
Frank Tang7e7574b2021-04-13 21:19:13 -0700408 MeasureUnit unit = macros.unit;
409 if (!utils::unitIsBaseUnit(macros.perUnit)) {
410 unit = unit.product(macros.perUnit.reciprocal(status), status);
411 // This isn't strictly necessary, but was what we specced out
412 // when perUnit became a backward-compatibility thing:
413 // unit/perUnit use case is only valid if both units are
414 // built-ins, or the product is a built-in.
415 if (uprv_strcmp(unit.getType(), "") == 0 &&
416 (uprv_strcmp(macros.unit.getType(), "") == 0 ||
417 uprv_strcmp(macros.perUnit.getType(), "") == 0)) {
418 status = U_UNSUPPORTED_ERROR;
419 return nullptr;
420 }
421 }
Frank Tangf90543d2020-10-30 19:02:04 -0700422 fLongNameHandler.adoptInsteadAndCheckErrorCode(new LongNameHandler(), status);
Frank Tang7e7574b2021-04-13 21:19:13 -0700423 LongNameHandler::forMeasureUnit(macros.locale, unit, unitWidth, unitDisplayCase,
Frank Tangf90543d2020-10-30 19:02:04 -0700424 resolvePluralRules(macros.rules, macros.locale, status),
425 chain, fLongNameHandler.getAlias(), status);
426 chain = fLongNameHandler.getAlias();
427 }
Jungshik Shinb3189662017-11-07 11:18:34 -0800428 } else if (isCurrency && unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) {
Frank Tangf90543d2020-10-30 19:02:04 -0700429 fLongNameHandler.adoptInsteadAndCheckErrorCode(
430 LongNameHandler::forCurrencyLongNames(
431 macros.locale, currency, resolvePluralRules(macros.rules, macros.locale, status), chain,
432 status),
433 status);
Jungshik Shinb3189662017-11-07 11:18:34 -0800434 chain = fLongNameHandler.getAlias();
435 } else {
436 // No outer modifier required
437 fMicros.modOuter = &fMicros.helpers.emptyWeakModifier;
438 }
Frank Tangf2223962020-04-27 18:25:29 -0700439 if (U_FAILURE(status)) {
440 return nullptr;
441 }
Jungshik Shinb3189662017-11-07 11:18:34 -0800442
443 // Compact notation
Frank Tangf90543d2020-10-30 19:02:04 -0700444 if (isCompactNotation) {
Jungshik Shinb3189662017-11-07 11:18:34 -0800445 CompactType compactType = (isCurrency && unitWidth != UNUM_UNIT_WIDTH_FULL_NAME)
446 ? CompactType::TYPE_CURRENCY : CompactType::TYPE_DECIMAL;
Frank Tangb8696612019-10-25 14:58:21 -0700447 auto newCompactHandler = new CompactHandler(
448 macros.notation.fUnion.compactStyle,
449 macros.locale,
450 nsName,
451 compactType,
452 resolvePluralRules(macros.rules, macros.locale, status),
Frank Tangf2223962020-04-27 18:25:29 -0700453 patternModifier,
454 safe,
Frank Tangb8696612019-10-25 14:58:21 -0700455 chain,
456 status);
Frank Tangf90543d2020-10-30 19:02:04 -0700457 if (U_FAILURE(status)) {
458 return nullptr;
459 }
Frank Tangb8696612019-10-25 14:58:21 -0700460 if (newCompactHandler == nullptr) {
461 status = U_MEMORY_ALLOCATION_ERROR;
462 return nullptr;
463 }
464 fCompactHandler.adoptInstead(newCompactHandler);
Jungshik Shinb3189662017-11-07 11:18:34 -0800465 chain = fCompactHandler.getAlias();
466 }
Frank Tangf2223962020-04-27 18:25:29 -0700467 if (U_FAILURE(status)) {
468 return nullptr;
469 }
470
471 // Always add the pattern modifier as the last element of the chain.
472 if (safe) {
473 fImmutablePatternModifier->addToChain(chain);
474 chain = fImmutablePatternModifier.getAlias();
475 } else {
476 patternModifier->addToChain(chain);
477 chain = patternModifier;
478 }
Jungshik Shinb3189662017-11-07 11:18:34 -0800479
480 return chain;
481}
482
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700483const PluralRules*
484NumberFormatterImpl::resolvePluralRules(const PluralRules* rulesPtr, const Locale& locale,
485 UErrorCode& status) {
Jungshik Shinb3189662017-11-07 11:18:34 -0800486 if (rulesPtr != nullptr) {
487 return rulesPtr;
488 }
489 // Lazily create PluralRules
490 if (fRules.isNull()) {
491 fRules.adoptInstead(PluralRules::forLocale(locale, status));
492 }
493 return fRules.getAlias();
494}
495
Frank Tangb8696612019-10-25 14:58:21 -0700496int32_t NumberFormatterImpl::writeAffixes(const MicroProps& micros, FormattedStringBuilder& string,
Jungshik Shin42d50272018-10-24 01:22:09 -0700497 int32_t start, int32_t end, UErrorCode& status) {
Frank Tangf90543d2020-10-30 19:02:04 -0700498 U_ASSERT(micros.modOuter != nullptr);
Jungshik Shinb3189662017-11-07 11:18:34 -0800499 // Always apply the inner modifier (which is "strong").
Jungshik Shin42d50272018-10-24 01:22:09 -0700500 int32_t length = micros.modInner->apply(string, start, end, status);
Jungshik Shinb3189662017-11-07 11:18:34 -0800501 if (micros.padding.isValid()) {
502 length += micros.padding
Jungshik Shin42d50272018-10-24 01:22:09 -0700503 .padAndApply(*micros.modMiddle, *micros.modOuter, string, start, length + end, status);
Jungshik Shinb3189662017-11-07 11:18:34 -0800504 } else {
Jungshik Shin42d50272018-10-24 01:22:09 -0700505 length += micros.modMiddle->apply(string, start, length + end, status);
506 length += micros.modOuter->apply(string, start, length + end, status);
Jungshik Shinb3189662017-11-07 11:18:34 -0800507 }
508 return length;
509}
510
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700511int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuantity& quantity,
Frank Tangb8696612019-10-25 14:58:21 -0700512 FormattedStringBuilder& string, int32_t index,
Jungshik Shin42d50272018-10-24 01:22:09 -0700513 UErrorCode& status) {
Jungshik Shinb3189662017-11-07 11:18:34 -0800514 int32_t length = 0;
515 if (quantity.isInfinite()) {
516 length += string.insert(
Jungshik Shin42d50272018-10-24 01:22:09 -0700517 length + index,
Jungshik Shinb3189662017-11-07 11:18:34 -0800518 micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kInfinitySymbol),
Frank Tangf2223962020-04-27 18:25:29 -0700519 {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD},
Jungshik Shinb3189662017-11-07 11:18:34 -0800520 status);
521
522 } else if (quantity.isNaN()) {
523 length += string.insert(
Jungshik Shin42d50272018-10-24 01:22:09 -0700524 length + index,
Jungshik Shinb3189662017-11-07 11:18:34 -0800525 micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kNaNSymbol),
Frank Tangf2223962020-04-27 18:25:29 -0700526 {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD},
Jungshik Shinb3189662017-11-07 11:18:34 -0800527 status);
528
529 } else {
530 // Add the integer digits
Jungshik Shin42d50272018-10-24 01:22:09 -0700531 length += writeIntegerDigits(micros, quantity, string, length + index, status);
Jungshik Shinb3189662017-11-07 11:18:34 -0800532
533 // Add the decimal point
534 if (quantity.getLowerDisplayMagnitude() < 0 || micros.decimal == UNUM_DECIMAL_SEPARATOR_ALWAYS) {
Frank Tang3e05d9d2021-11-08 14:04:04 -0800535 if (!micros.currencyAsDecimal.isBogus()) {
536 length += string.insert(
Jungshik Shin42d50272018-10-24 01:22:09 -0700537 length + index,
Frank Tang3e05d9d2021-11-08 14:04:04 -0800538 micros.currencyAsDecimal,
539 {UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD},
540 status);
541 } else if (micros.useCurrency) {
542 length += string.insert(
543 length + index,
544 micros.symbols->getSymbol(
545 DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol),
Frank Tangf2223962020-04-27 18:25:29 -0700546 {UFIELD_CATEGORY_NUMBER, UNUM_DECIMAL_SEPARATOR_FIELD},
Jungshik Shinb3189662017-11-07 11:18:34 -0800547 status);
Frank Tang3e05d9d2021-11-08 14:04:04 -0800548 } else {
549 length += string.insert(
550 length + index,
551 micros.symbols->getSymbol(
552 DecimalFormatSymbols::ENumberFormatSymbol::kDecimalSeparatorSymbol),
553 {UFIELD_CATEGORY_NUMBER, UNUM_DECIMAL_SEPARATOR_FIELD},
554 status);
555 }
Jungshik Shinb3189662017-11-07 11:18:34 -0800556 }
557
558 // Add the fraction digits
Jungshik Shin42d50272018-10-24 01:22:09 -0700559 length += writeFractionDigits(micros, quantity, string, length + index, status);
Frank Tangf2223962020-04-27 18:25:29 -0700560
561 if (length == 0) {
562 // Force output of the digit for value 0
563 length += utils::insertDigitFromSymbols(
564 string,
565 index,
566 0,
567 *micros.symbols,
568 {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD},
569 status);
570 }
Jungshik Shinb3189662017-11-07 11:18:34 -0800571 }
572
573 return length;
574}
575
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700576int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, DecimalQuantity& quantity,
Frank Tangb8696612019-10-25 14:58:21 -0700577 FormattedStringBuilder& string, int32_t index,
Jungshik Shin42d50272018-10-24 01:22:09 -0700578 UErrorCode& status) {
Jungshik Shinb3189662017-11-07 11:18:34 -0800579 int length = 0;
580 int integerCount = quantity.getUpperDisplayMagnitude() + 1;
581 for (int i = 0; i < integerCount; i++) {
582 // Add grouping separator
583 if (micros.grouping.groupAtPosition(i, quantity)) {
584 length += string.insert(
Jungshik Shin42d50272018-10-24 01:22:09 -0700585 index,
Jungshik Shinb3189662017-11-07 11:18:34 -0800586 micros.useCurrency ? micros.symbols->getSymbol(
587 DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol)
588 : micros.symbols->getSymbol(
589 DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol),
Frank Tangf2223962020-04-27 18:25:29 -0700590 {UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD},
Jungshik Shinb3189662017-11-07 11:18:34 -0800591 status);
592 }
593
594 // Get and append the next digit value
595 int8_t nextDigit = quantity.getDigit(i);
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700596 length += utils::insertDigitFromSymbols(
Frank Tangf2223962020-04-27 18:25:29 -0700597 string,
598 index,
599 nextDigit,
600 *micros.symbols,
601 {UFIELD_CATEGORY_NUMBER,
602 UNUM_INTEGER_FIELD},
603 status);
Jungshik Shinb3189662017-11-07 11:18:34 -0800604 }
605 return length;
606}
607
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700608int32_t NumberFormatterImpl::writeFractionDigits(const MicroProps& micros, DecimalQuantity& quantity,
Frank Tangb8696612019-10-25 14:58:21 -0700609 FormattedStringBuilder& string, int32_t index,
Jungshik Shin42d50272018-10-24 01:22:09 -0700610 UErrorCode& status) {
Jungshik Shinb3189662017-11-07 11:18:34 -0800611 int length = 0;
612 int fractionCount = -quantity.getLowerDisplayMagnitude();
613 for (int i = 0; i < fractionCount; i++) {
614 // Get and append the next digit value
615 int8_t nextDigit = quantity.getDigit(-i - 1);
Jungshik Shina9a2bd32018-07-07 03:36:01 -0700616 length += utils::insertDigitFromSymbols(
Frank Tangf2223962020-04-27 18:25:29 -0700617 string,
618 length + index,
619 nextDigit,
620 *micros.symbols,
621 {UFIELD_CATEGORY_NUMBER, UNUM_FRACTION_FIELD},
622 status);
Jungshik Shinb3189662017-11-07 11:18:34 -0800623 }
624 return length;
625}
626
627#endif /* #if !UCONFIG_NO_FORMATTING */