1 // © 2018 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 // Allow implicit conversion from char16_t* to UnicodeString for this file:
9 // Helpful in toString methods and elsewhere.
10 #define UNISTR_FROM_STRING_EXPLICIT
11
12 #include <cmath>
13 #include <cstdlib>
14 #include <stdlib.h>
15 #include "unicode/errorcode.h"
16 #include "unicode/decimfmt.h"
17 #include "number_decimalquantity.h"
18 #include "number_types.h"
19 #include "numparse_impl.h"
20 #include "number_mapper.h"
21 #include "number_patternstring.h"
22 #include "putilimp.h"
23 #include "number_utils.h"
24 #include "number_utypes.h"
25
26 using namespace icu;
27 using namespace icu::number;
28 using namespace icu::number::impl;
29 using namespace icu::numparse;
30 using namespace icu::numparse::impl;
31 using ERoundingMode = icu::DecimalFormat::ERoundingMode;
32 using EPadPosition = icu::DecimalFormat::EPadPosition;
33
34 // MSVC warns C4805 when comparing bool with UBool
35 // TODO: Move this macro into a better place?
36 #if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN
37 #define UBOOL_TO_BOOL(b) static_cast<bool>(b)
38 #else
39 #define UBOOL_TO_BOOL(b) b
40 #endif
41
42
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormat)43 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormat)
44
45
46 DecimalFormat::DecimalFormat(UErrorCode& status)
47 : DecimalFormat(nullptr, status) {
48 // Use the default locale and decimal pattern.
49 const char* localeName = Locale::getDefault().getName();
50 LocalPointer<NumberingSystem> ns(NumberingSystem::createInstance(status));
51 UnicodeString patternString = utils::getPatternForStyle(
52 localeName,
53 ns->getName(),
54 CLDR_PATTERN_STYLE_DECIMAL,
55 status);
56 setPropertiesFromPattern(patternString, IGNORE_ROUNDING_IF_CURRENCY, status);
57 touch(status);
58 }
59
DecimalFormat(const UnicodeString & pattern,UErrorCode & status)60 DecimalFormat::DecimalFormat(const UnicodeString& pattern, UErrorCode& status)
61 : DecimalFormat(nullptr, status) {
62 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status);
63 touch(status);
64 }
65
DecimalFormat(const UnicodeString & pattern,DecimalFormatSymbols * symbolsToAdopt,UErrorCode & status)66 DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt,
67 UErrorCode& status)
68 : DecimalFormat(symbolsToAdopt, status) {
69 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status);
70 touch(status);
71 }
72
DecimalFormat(const UnicodeString & pattern,DecimalFormatSymbols * symbolsToAdopt,UNumberFormatStyle style,UErrorCode & status)73 DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt,
74 UNumberFormatStyle style, UErrorCode& status)
75 : DecimalFormat(symbolsToAdopt, status) {
76 // If choice is a currency type, ignore the rounding information.
77 if (style == UNumberFormatStyle::UNUM_CURRENCY || style == UNumberFormatStyle::UNUM_CURRENCY_ISO ||
78 style == UNumberFormatStyle::UNUM_CURRENCY_ACCOUNTING ||
79 style == UNumberFormatStyle::UNUM_CASH_CURRENCY ||
80 style == UNumberFormatStyle::UNUM_CURRENCY_STANDARD ||
81 style == UNumberFormatStyle::UNUM_CURRENCY_PLURAL) {
82 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_ALWAYS, status);
83 } else {
84 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status);
85 }
86 // Note: in Java, CurrencyPluralInfo is set in NumberFormat.java, but in C++, it is not set there,
87 // so we have to set it here.
88 if (style == UNumberFormatStyle::UNUM_CURRENCY_PLURAL) {
89 LocalPointer<CurrencyPluralInfo> cpi(
90 new CurrencyPluralInfo(fields->symbols->getLocale(), status),
91 status);
92 if (U_FAILURE(status)) { return; }
93 fields->properties->currencyPluralInfo.fPtr.adoptInstead(cpi.orphan());
94 }
95 touch(status);
96 }
97
DecimalFormat(const DecimalFormatSymbols * symbolsToAdopt,UErrorCode & status)98 DecimalFormat::DecimalFormat(const DecimalFormatSymbols* symbolsToAdopt, UErrorCode& status) {
99 LocalPointer<const DecimalFormatSymbols> adoptedSymbols(symbolsToAdopt);
100 fields = new DecimalFormatFields();
101 if (U_FAILURE(status)) {
102 return;
103 }
104 if (fields == nullptr) {
105 status = U_MEMORY_ALLOCATION_ERROR;
106 return;
107 }
108 fields->properties.adoptInsteadAndCheckErrorCode(new DecimalFormatProperties(), status);
109 fields->exportedProperties.adoptInsteadAndCheckErrorCode(new DecimalFormatProperties(), status);
110 if (adoptedSymbols.isNull()) {
111 fields->symbols.adoptInsteadAndCheckErrorCode(new DecimalFormatSymbols(status), status);
112 } else {
113 fields->symbols.adoptInsteadAndCheckErrorCode(adoptedSymbols.orphan(), status);
114 }
115 }
116
117 #if UCONFIG_HAVE_PARSEALLINPUT
118
setParseAllInput(UNumberFormatAttributeValue value)119 void DecimalFormat::setParseAllInput(UNumberFormatAttributeValue value) {
120 if (value == fields->properties->parseAllInput) { return; }
121 fields->properties->parseAllInput = value;
122 }
123
124 #endif
125
126 DecimalFormat&
setAttribute(UNumberFormatAttribute attr,int32_t newValue,UErrorCode & status)127 DecimalFormat::setAttribute(UNumberFormatAttribute attr, int32_t newValue, UErrorCode& status) {
128 if (U_FAILURE(status)) { return *this; }
129
130 switch (attr) {
131 case UNUM_LENIENT_PARSE:
132 setLenient(newValue != 0);
133 break;
134
135 case UNUM_PARSE_INT_ONLY:
136 setParseIntegerOnly(newValue != 0);
137 break;
138
139 case UNUM_GROUPING_USED:
140 setGroupingUsed(newValue != 0);
141 break;
142
143 case UNUM_DECIMAL_ALWAYS_SHOWN:
144 setDecimalSeparatorAlwaysShown(newValue != 0);
145 break;
146
147 case UNUM_MAX_INTEGER_DIGITS:
148 setMaximumIntegerDigits(newValue);
149 break;
150
151 case UNUM_MIN_INTEGER_DIGITS:
152 setMinimumIntegerDigits(newValue);
153 break;
154
155 case UNUM_INTEGER_DIGITS:
156 setMinimumIntegerDigits(newValue);
157 setMaximumIntegerDigits(newValue);
158 break;
159
160 case UNUM_MAX_FRACTION_DIGITS:
161 setMaximumFractionDigits(newValue);
162 break;
163
164 case UNUM_MIN_FRACTION_DIGITS:
165 setMinimumFractionDigits(newValue);
166 break;
167
168 case UNUM_FRACTION_DIGITS:
169 setMinimumFractionDigits(newValue);
170 setMaximumFractionDigits(newValue);
171 break;
172
173 case UNUM_SIGNIFICANT_DIGITS_USED:
174 setSignificantDigitsUsed(newValue != 0);
175 break;
176
177 case UNUM_MAX_SIGNIFICANT_DIGITS:
178 setMaximumSignificantDigits(newValue);
179 break;
180
181 case UNUM_MIN_SIGNIFICANT_DIGITS:
182 setMinimumSignificantDigits(newValue);
183 break;
184
185 case UNUM_MULTIPLIER:
186 setMultiplier(newValue);
187 break;
188
189 case UNUM_SCALE:
190 setMultiplierScale(newValue);
191 break;
192
193 case UNUM_GROUPING_SIZE:
194 setGroupingSize(newValue);
195 break;
196
197 case UNUM_ROUNDING_MODE:
198 setRoundingMode((DecimalFormat::ERoundingMode) newValue);
199 break;
200
201 case UNUM_FORMAT_WIDTH:
202 setFormatWidth(newValue);
203 break;
204
205 case UNUM_PADDING_POSITION:
206 /** The position at which padding will take place. */
207 setPadPosition((DecimalFormat::EPadPosition) newValue);
208 break;
209
210 case UNUM_SECONDARY_GROUPING_SIZE:
211 setSecondaryGroupingSize(newValue);
212 break;
213
214 #if UCONFIG_HAVE_PARSEALLINPUT
215 case UNUM_PARSE_ALL_INPUT:
216 setParseAllInput((UNumberFormatAttributeValue) newValue);
217 break;
218 #endif
219
220 case UNUM_PARSE_NO_EXPONENT:
221 setParseNoExponent((UBool) newValue);
222 break;
223
224 case UNUM_PARSE_DECIMAL_MARK_REQUIRED:
225 setDecimalPatternMatchRequired((UBool) newValue);
226 break;
227
228 case UNUM_CURRENCY_USAGE:
229 setCurrencyUsage((UCurrencyUsage) newValue, &status);
230 break;
231
232 case UNUM_MINIMUM_GROUPING_DIGITS:
233 setMinimumGroupingDigits(newValue);
234 break;
235
236 case UNUM_PARSE_CASE_SENSITIVE:
237 setParseCaseSensitive(static_cast<UBool>(newValue));
238 break;
239
240 case UNUM_SIGN_ALWAYS_SHOWN:
241 setSignAlwaysShown(static_cast<UBool>(newValue));
242 break;
243
244 case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS:
245 setFormatFailIfMoreThanMaxDigits(static_cast<UBool>(newValue));
246 break;
247
248 default:
249 status = U_UNSUPPORTED_ERROR;
250 break;
251 }
252 return *this;
253 }
254
getAttribute(UNumberFormatAttribute attr,UErrorCode & status) const255 int32_t DecimalFormat::getAttribute(UNumberFormatAttribute attr, UErrorCode& status) const {
256 if (U_FAILURE(status)) { return -1; }
257 switch (attr) {
258 case UNUM_LENIENT_PARSE:
259 return isLenient();
260
261 case UNUM_PARSE_INT_ONLY:
262 return isParseIntegerOnly();
263
264 case UNUM_GROUPING_USED:
265 return isGroupingUsed();
266
267 case UNUM_DECIMAL_ALWAYS_SHOWN:
268 return isDecimalSeparatorAlwaysShown();
269
270 case UNUM_MAX_INTEGER_DIGITS:
271 return getMaximumIntegerDigits();
272
273 case UNUM_MIN_INTEGER_DIGITS:
274 return getMinimumIntegerDigits();
275
276 case UNUM_INTEGER_DIGITS:
277 // TBD: what should this return?
278 return getMinimumIntegerDigits();
279
280 case UNUM_MAX_FRACTION_DIGITS:
281 return getMaximumFractionDigits();
282
283 case UNUM_MIN_FRACTION_DIGITS:
284 return getMinimumFractionDigits();
285
286 case UNUM_FRACTION_DIGITS:
287 // TBD: what should this return?
288 return getMinimumFractionDigits();
289
290 case UNUM_SIGNIFICANT_DIGITS_USED:
291 return areSignificantDigitsUsed();
292
293 case UNUM_MAX_SIGNIFICANT_DIGITS:
294 return getMaximumSignificantDigits();
295
296 case UNUM_MIN_SIGNIFICANT_DIGITS:
297 return getMinimumSignificantDigits();
298
299 case UNUM_MULTIPLIER:
300 return getMultiplier();
301
302 case UNUM_SCALE:
303 return getMultiplierScale();
304
305 case UNUM_GROUPING_SIZE:
306 return getGroupingSize();
307
308 case UNUM_ROUNDING_MODE:
309 return getRoundingMode();
310
311 case UNUM_FORMAT_WIDTH:
312 return getFormatWidth();
313
314 case UNUM_PADDING_POSITION:
315 return getPadPosition();
316
317 case UNUM_SECONDARY_GROUPING_SIZE:
318 return getSecondaryGroupingSize();
319
320 case UNUM_PARSE_NO_EXPONENT:
321 return isParseNoExponent();
322
323 case UNUM_PARSE_DECIMAL_MARK_REQUIRED:
324 return isDecimalPatternMatchRequired();
325
326 case UNUM_CURRENCY_USAGE:
327 return getCurrencyUsage();
328
329 case UNUM_MINIMUM_GROUPING_DIGITS:
330 return getMinimumGroupingDigits();
331
332 case UNUM_PARSE_CASE_SENSITIVE:
333 return isParseCaseSensitive();
334
335 case UNUM_SIGN_ALWAYS_SHOWN:
336 return isSignAlwaysShown();
337
338 case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS:
339 return isFormatFailIfMoreThanMaxDigits();
340
341 default:
342 status = U_UNSUPPORTED_ERROR;
343 break;
344 }
345
346 return -1; /* undefined */
347 }
348
setGroupingUsed(UBool enabled)349 void DecimalFormat::setGroupingUsed(UBool enabled) {
350 if (UBOOL_TO_BOOL(enabled) == fields->properties->groupingUsed) { return; }
351 NumberFormat::setGroupingUsed(enabled); // to set field for compatibility
352 fields->properties->groupingUsed = enabled;
353 touchNoError();
354 }
355
setParseIntegerOnly(UBool value)356 void DecimalFormat::setParseIntegerOnly(UBool value) {
357 if (UBOOL_TO_BOOL(value) == fields->properties->parseIntegerOnly) { return; }
358 NumberFormat::setParseIntegerOnly(value); // to set field for compatibility
359 fields->properties->parseIntegerOnly = value;
360 touchNoError();
361 }
362
setLenient(UBool enable)363 void DecimalFormat::setLenient(UBool enable) {
364 ParseMode mode = enable ? PARSE_MODE_LENIENT : PARSE_MODE_STRICT;
365 if (!fields->properties->parseMode.isNull() && mode == fields->properties->parseMode.getNoError()) { return; }
366 NumberFormat::setLenient(enable); // to set field for compatibility
367 fields->properties->parseMode = mode;
368 touchNoError();
369 }
370
DecimalFormat(const UnicodeString & pattern,DecimalFormatSymbols * symbolsToAdopt,UParseError &,UErrorCode & status)371 DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt,
372 UParseError&, UErrorCode& status)
373 : DecimalFormat(symbolsToAdopt, status) {
374 // TODO: What is parseError for?
375 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status);
376 touch(status);
377 }
378
DecimalFormat(const UnicodeString & pattern,const DecimalFormatSymbols & symbols,UErrorCode & status)379 DecimalFormat::DecimalFormat(const UnicodeString& pattern, const DecimalFormatSymbols& symbols,
380 UErrorCode& status)
381 : DecimalFormat(new DecimalFormatSymbols(symbols), status) {
382 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status);
383 touch(status);
384 }
385
DecimalFormat(const DecimalFormat & source)386 DecimalFormat::DecimalFormat(const DecimalFormat& source) : NumberFormat(source) {
387 // Note: it is not safe to copy fields->formatter or fWarehouse directly because fields->formatter might have
388 // dangling pointers to fields inside fWarehouse. The safe thing is to re-construct fields->formatter from
389 // the property bag, despite being somewhat slower.
390 fields = new DecimalFormatFields();
391 if (fields == nullptr) {
392 return;
393 }
394 fields->properties.adoptInstead(new DecimalFormatProperties(*source.fields->properties));
395 fields->symbols.adoptInstead(new DecimalFormatSymbols(*source.fields->symbols));
396 fields->exportedProperties.adoptInstead(new DecimalFormatProperties());
397 if (fields->properties == nullptr || fields->symbols == nullptr || fields->exportedProperties == nullptr) {
398 return;
399 }
400 touchNoError();
401 }
402
operator =(const DecimalFormat & rhs)403 DecimalFormat& DecimalFormat::operator=(const DecimalFormat& rhs) {
404 *fields->properties = *rhs.fields->properties;
405 fields->exportedProperties->clear();
406 fields->symbols.adoptInstead(new DecimalFormatSymbols(*rhs.fields->symbols));
407 touchNoError();
408 return *this;
409 }
410
~DecimalFormat()411 DecimalFormat::~DecimalFormat() {
412 delete fields->atomicParser.exchange(nullptr);
413 delete fields->atomicCurrencyParser.exchange(nullptr);
414 delete fields;
415 }
416
clone() const417 Format* DecimalFormat::clone() const {
418 return new DecimalFormat(*this);
419 }
420
operator ==(const Format & other) const421 UBool DecimalFormat::operator==(const Format& other) const {
422 auto* otherDF = dynamic_cast<const DecimalFormat*>(&other);
423 if (otherDF == nullptr) {
424 return false;
425 }
426 return *fields->properties == *otherDF->fields->properties && *fields->symbols == *otherDF->fields->symbols;
427 }
428
format(double number,UnicodeString & appendTo,FieldPosition & pos) const429 UnicodeString& DecimalFormat::format(double number, UnicodeString& appendTo, FieldPosition& pos) const {
430 if (pos.getField() == FieldPosition::DONT_CARE && fastFormatDouble(number, appendTo)) {
431 return appendTo;
432 }
433 UErrorCode localStatus = U_ZERO_ERROR;
434 FormattedNumber output = fields->formatter->formatDouble(number, localStatus);
435 fieldPositionHelper(output, pos, appendTo.length(), localStatus);
436 auto appendable = UnicodeStringAppendable(appendTo);
437 output.appendTo(appendable);
438 return appendTo;
439 }
440
format(double number,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const441 UnicodeString& DecimalFormat::format(double number, UnicodeString& appendTo, FieldPosition& pos,
442 UErrorCode& status) const {
443 if (pos.getField() == FieldPosition::DONT_CARE && fastFormatDouble(number, appendTo)) {
444 return appendTo;
445 }
446 FormattedNumber output = fields->formatter->formatDouble(number, status);
447 fieldPositionHelper(output, pos, appendTo.length(), status);
448 auto appendable = UnicodeStringAppendable(appendTo);
449 output.appendTo(appendable);
450 return appendTo;
451 }
452
453 UnicodeString&
format(double number,UnicodeString & appendTo,FieldPositionIterator * posIter,UErrorCode & status) const454 DecimalFormat::format(double number, UnicodeString& appendTo, FieldPositionIterator* posIter,
455 UErrorCode& status) const {
456 if (posIter == nullptr && fastFormatDouble(number, appendTo)) {
457 return appendTo;
458 }
459 FormattedNumber output = fields->formatter->formatDouble(number, status);
460 fieldPositionIteratorHelper(output, posIter, appendTo.length(), status);
461 auto appendable = UnicodeStringAppendable(appendTo);
462 output.appendTo(appendable);
463 return appendTo;
464 }
465
format(int32_t number,UnicodeString & appendTo,FieldPosition & pos) const466 UnicodeString& DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPosition& pos) const {
467 return format(static_cast<int64_t> (number), appendTo, pos);
468 }
469
format(int32_t number,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const470 UnicodeString& DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPosition& pos,
471 UErrorCode& status) const {
472 return format(static_cast<int64_t> (number), appendTo, pos, status);
473 }
474
475 UnicodeString&
format(int32_t number,UnicodeString & appendTo,FieldPositionIterator * posIter,UErrorCode & status) const476 DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPositionIterator* posIter,
477 UErrorCode& status) const {
478 return format(static_cast<int64_t> (number), appendTo, posIter, status);
479 }
480
format(int64_t number,UnicodeString & appendTo,FieldPosition & pos) const481 UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& pos) const {
482 if (pos.getField() == FieldPosition::DONT_CARE && fastFormatInt64(number, appendTo)) {
483 return appendTo;
484 }
485 UErrorCode localStatus = U_ZERO_ERROR;
486 FormattedNumber output = fields->formatter->formatInt(number, localStatus);
487 fieldPositionHelper(output, pos, appendTo.length(), localStatus);
488 auto appendable = UnicodeStringAppendable(appendTo);
489 output.appendTo(appendable);
490 return appendTo;
491 }
492
format(int64_t number,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const493 UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& pos,
494 UErrorCode& status) const {
495 if (pos.getField() == FieldPosition::DONT_CARE && fastFormatInt64(number, appendTo)) {
496 return appendTo;
497 }
498 FormattedNumber output = fields->formatter->formatInt(number, status);
499 fieldPositionHelper(output, pos, appendTo.length(), status);
500 auto appendable = UnicodeStringAppendable(appendTo);
501 output.appendTo(appendable);
502 return appendTo;
503 }
504
505 UnicodeString&
format(int64_t number,UnicodeString & appendTo,FieldPositionIterator * posIter,UErrorCode & status) const506 DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPositionIterator* posIter,
507 UErrorCode& status) const {
508 if (posIter == nullptr && fastFormatInt64(number, appendTo)) {
509 return appendTo;
510 }
511 FormattedNumber output = fields->formatter->formatInt(number, status);
512 fieldPositionIteratorHelper(output, posIter, appendTo.length(), status);
513 auto appendable = UnicodeStringAppendable(appendTo);
514 output.appendTo(appendable);
515 return appendTo;
516 }
517
518 UnicodeString&
format(StringPiece number,UnicodeString & appendTo,FieldPositionIterator * posIter,UErrorCode & status) const519 DecimalFormat::format(StringPiece number, UnicodeString& appendTo, FieldPositionIterator* posIter,
520 UErrorCode& status) const {
521 FormattedNumber output = fields->formatter->formatDecimal(number, status);
522 fieldPositionIteratorHelper(output, posIter, appendTo.length(), status);
523 auto appendable = UnicodeStringAppendable(appendTo);
524 output.appendTo(appendable);
525 return appendTo;
526 }
527
format(const DecimalQuantity & number,UnicodeString & appendTo,FieldPositionIterator * posIter,UErrorCode & status) const528 UnicodeString& DecimalFormat::format(const DecimalQuantity& number, UnicodeString& appendTo,
529 FieldPositionIterator* posIter, UErrorCode& status) const {
530 FormattedNumber output = fields->formatter->formatDecimalQuantity(number, status);
531 fieldPositionIteratorHelper(output, posIter, appendTo.length(), status);
532 auto appendable = UnicodeStringAppendable(appendTo);
533 output.appendTo(appendable);
534 return appendTo;
535 }
536
537 UnicodeString&
format(const DecimalQuantity & number,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const538 DecimalFormat::format(const DecimalQuantity& number, UnicodeString& appendTo, FieldPosition& pos,
539 UErrorCode& status) const {
540 FormattedNumber output = fields->formatter->formatDecimalQuantity(number, status);
541 fieldPositionHelper(output, pos, appendTo.length(), status);
542 auto appendable = UnicodeStringAppendable(appendTo);
543 output.appendTo(appendable);
544 return appendTo;
545 }
546
parse(const UnicodeString & text,Formattable & output,ParsePosition & parsePosition) const547 void DecimalFormat::parse(const UnicodeString& text, Formattable& output,
548 ParsePosition& parsePosition) const {
549 if (parsePosition.getIndex() < 0 || parsePosition.getIndex() >= text.length()) {
550 return;
551 }
552
553 ErrorCode status;
554 ParsedNumber result;
555 // Note: if this is a currency instance, currencies will be matched despite the fact that we are not in the
556 // parseCurrency method (backwards compatibility)
557 int32_t startIndex = parsePosition.getIndex();
558 const NumberParserImpl* parser = getParser(status);
559 if (U_FAILURE(status)) { return; }
560 parser->parse(text, startIndex, true, result, status);
561 // TODO: Do we need to check for fImpl->properties->parseAllInput (UCONFIG_HAVE_PARSEALLINPUT) here?
562 if (result.success()) {
563 parsePosition.setIndex(result.charEnd);
564 result.populateFormattable(output, parser->getParseFlags());
565 } else {
566 parsePosition.setErrorIndex(startIndex + result.charEnd);
567 }
568 }
569
parseCurrency(const UnicodeString & text,ParsePosition & parsePosition) const570 CurrencyAmount* DecimalFormat::parseCurrency(const UnicodeString& text, ParsePosition& parsePosition) const {
571 if (parsePosition.getIndex() < 0 || parsePosition.getIndex() >= text.length()) {
572 return nullptr;
573 }
574
575 ErrorCode status;
576 ParsedNumber result;
577 // Note: if this is a currency instance, currencies will be matched despite the fact that we are not in the
578 // parseCurrency method (backwards compatibility)
579 int32_t startIndex = parsePosition.getIndex();
580 const NumberParserImpl* parser = getCurrencyParser(status);
581 if (U_FAILURE(status)) { return nullptr; }
582 parser->parse(text, startIndex, true, result, status);
583 // TODO: Do we need to check for fImpl->properties->parseAllInput (UCONFIG_HAVE_PARSEALLINPUT) here?
584 if (result.success()) {
585 parsePosition.setIndex(result.charEnd);
586 Formattable formattable;
587 result.populateFormattable(formattable, parser->getParseFlags());
588 return new CurrencyAmount(formattable, result.currencyCode, status);
589 } else {
590 parsePosition.setErrorIndex(startIndex + result.charEnd);
591 return nullptr;
592 }
593 }
594
getDecimalFormatSymbols(void) const595 const DecimalFormatSymbols* DecimalFormat::getDecimalFormatSymbols(void) const {
596 return fields->symbols.getAlias();
597 }
598
adoptDecimalFormatSymbols(DecimalFormatSymbols * symbolsToAdopt)599 void DecimalFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt) {
600 if (symbolsToAdopt == nullptr) {
601 return; // do not allow caller to set fields->symbols to NULL
602 }
603 fields->symbols.adoptInstead(symbolsToAdopt);
604 touchNoError();
605 }
606
setDecimalFormatSymbols(const DecimalFormatSymbols & symbols)607 void DecimalFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols) {
608 fields->symbols.adoptInstead(new DecimalFormatSymbols(symbols));
609 touchNoError();
610 }
611
getCurrencyPluralInfo(void) const612 const CurrencyPluralInfo* DecimalFormat::getCurrencyPluralInfo(void) const {
613 return fields->properties->currencyPluralInfo.fPtr.getAlias();
614 }
615
adoptCurrencyPluralInfo(CurrencyPluralInfo * toAdopt)616 void DecimalFormat::adoptCurrencyPluralInfo(CurrencyPluralInfo* toAdopt) {
617 fields->properties->currencyPluralInfo.fPtr.adoptInstead(toAdopt);
618 touchNoError();
619 }
620
setCurrencyPluralInfo(const CurrencyPluralInfo & info)621 void DecimalFormat::setCurrencyPluralInfo(const CurrencyPluralInfo& info) {
622 if (fields->properties->currencyPluralInfo.fPtr.isNull()) {
623 fields->properties->currencyPluralInfo.fPtr.adoptInstead(info.clone());
624 } else {
625 *fields->properties->currencyPluralInfo.fPtr = info; // copy-assignment operator
626 }
627 touchNoError();
628 }
629
getPositivePrefix(UnicodeString & result) const630 UnicodeString& DecimalFormat::getPositivePrefix(UnicodeString& result) const {
631 ErrorCode localStatus;
632 fields->formatter->getAffixImpl(true, false, result, localStatus);
633 return result;
634 }
635
setPositivePrefix(const UnicodeString & newValue)636 void DecimalFormat::setPositivePrefix(const UnicodeString& newValue) {
637 if (newValue == fields->properties->positivePrefix) { return; }
638 fields->properties->positivePrefix = newValue;
639 touchNoError();
640 }
641
getNegativePrefix(UnicodeString & result) const642 UnicodeString& DecimalFormat::getNegativePrefix(UnicodeString& result) const {
643 ErrorCode localStatus;
644 fields->formatter->getAffixImpl(true, true, result, localStatus);
645 return result;
646 }
647
setNegativePrefix(const UnicodeString & newValue)648 void DecimalFormat::setNegativePrefix(const UnicodeString& newValue) {
649 if (newValue == fields->properties->negativePrefix) { return; }
650 fields->properties->negativePrefix = newValue;
651 touchNoError();
652 }
653
getPositiveSuffix(UnicodeString & result) const654 UnicodeString& DecimalFormat::getPositiveSuffix(UnicodeString& result) const {
655 ErrorCode localStatus;
656 fields->formatter->getAffixImpl(false, false, result, localStatus);
657 return result;
658 }
659
setPositiveSuffix(const UnicodeString & newValue)660 void DecimalFormat::setPositiveSuffix(const UnicodeString& newValue) {
661 if (newValue == fields->properties->positiveSuffix) { return; }
662 fields->properties->positiveSuffix = newValue;
663 touchNoError();
664 }
665
getNegativeSuffix(UnicodeString & result) const666 UnicodeString& DecimalFormat::getNegativeSuffix(UnicodeString& result) const {
667 ErrorCode localStatus;
668 fields->formatter->getAffixImpl(false, true, result, localStatus);
669 return result;
670 }
671
setNegativeSuffix(const UnicodeString & newValue)672 void DecimalFormat::setNegativeSuffix(const UnicodeString& newValue) {
673 if (newValue == fields->properties->negativeSuffix) { return; }
674 fields->properties->negativeSuffix = newValue;
675 touchNoError();
676 }
677
isSignAlwaysShown() const678 UBool DecimalFormat::isSignAlwaysShown() const {
679 return fields->properties->signAlwaysShown;
680 }
681
setSignAlwaysShown(UBool value)682 void DecimalFormat::setSignAlwaysShown(UBool value) {
683 if (UBOOL_TO_BOOL(value) == fields->properties->signAlwaysShown) { return; }
684 fields->properties->signAlwaysShown = value;
685 touchNoError();
686 }
687
getMultiplier(void) const688 int32_t DecimalFormat::getMultiplier(void) const {
689 if (fields->properties->multiplier != 1) {
690 return fields->properties->multiplier;
691 } else if (fields->properties->magnitudeMultiplier != 0) {
692 return static_cast<int32_t>(uprv_pow10(fields->properties->magnitudeMultiplier));
693 } else {
694 return 1;
695 }
696 }
697
setMultiplier(int32_t multiplier)698 void DecimalFormat::setMultiplier(int32_t multiplier) {
699 if (multiplier == 0) {
700 multiplier = 1; // one being the benign default value for a multiplier.
701 }
702
703 // Try to convert to a magnitude multiplier first
704 int delta = 0;
705 int value = multiplier;
706 while (value != 1) {
707 delta++;
708 int temp = value / 10;
709 if (temp * 10 != value) {
710 delta = -1;
711 break;
712 }
713 value = temp;
714 }
715 if (delta != -1) {
716 fields->properties->magnitudeMultiplier = delta;
717 fields->properties->multiplier = 1;
718 } else {
719 fields->properties->magnitudeMultiplier = 0;
720 fields->properties->multiplier = multiplier;
721 }
722 touchNoError();
723 }
724
getMultiplierScale() const725 int32_t DecimalFormat::getMultiplierScale() const {
726 return fields->properties->multiplierScale;
727 }
728
setMultiplierScale(int32_t newValue)729 void DecimalFormat::setMultiplierScale(int32_t newValue) {
730 if (newValue == fields->properties->multiplierScale) { return; }
731 fields->properties->multiplierScale = newValue;
732 touchNoError();
733 }
734
getRoundingIncrement(void) const735 double DecimalFormat::getRoundingIncrement(void) const {
736 return fields->exportedProperties->roundingIncrement;
737 }
738
setRoundingIncrement(double newValue)739 void DecimalFormat::setRoundingIncrement(double newValue) {
740 if (newValue == fields->properties->roundingIncrement) { return; }
741 fields->properties->roundingIncrement = newValue;
742 touchNoError();
743 }
744
getRoundingMode(void) const745 ERoundingMode DecimalFormat::getRoundingMode(void) const {
746 // UNumberFormatRoundingMode and ERoundingMode have the same values.
747 return static_cast<ERoundingMode>(fields->exportedProperties->roundingMode.getNoError());
748 }
749
setRoundingMode(ERoundingMode roundingMode)750 void DecimalFormat::setRoundingMode(ERoundingMode roundingMode) {
751 auto uRoundingMode = static_cast<UNumberFormatRoundingMode>(roundingMode);
752 if (!fields->properties->roundingMode.isNull() && uRoundingMode == fields->properties->roundingMode.getNoError()) {
753 return;
754 }
755 NumberFormat::setMaximumIntegerDigits(roundingMode); // to set field for compatibility
756 fields->properties->roundingMode = uRoundingMode;
757 touchNoError();
758 }
759
getFormatWidth(void) const760 int32_t DecimalFormat::getFormatWidth(void) const {
761 return fields->properties->formatWidth;
762 }
763
setFormatWidth(int32_t width)764 void DecimalFormat::setFormatWidth(int32_t width) {
765 if (width == fields->properties->formatWidth) { return; }
766 fields->properties->formatWidth = width;
767 touchNoError();
768 }
769
getPadCharacterString() const770 UnicodeString DecimalFormat::getPadCharacterString() const {
771 if (fields->properties->padString.isBogus()) {
772 // Readonly-alias the static string kFallbackPaddingString
773 return {TRUE, kFallbackPaddingString, -1};
774 } else {
775 return fields->properties->padString;
776 }
777 }
778
setPadCharacter(const UnicodeString & padChar)779 void DecimalFormat::setPadCharacter(const UnicodeString& padChar) {
780 if (padChar == fields->properties->padString) { return; }
781 if (padChar.length() > 0) {
782 fields->properties->padString = UnicodeString(padChar.char32At(0));
783 } else {
784 fields->properties->padString.setToBogus();
785 }
786 touchNoError();
787 }
788
getPadPosition(void) const789 EPadPosition DecimalFormat::getPadPosition(void) const {
790 if (fields->properties->padPosition.isNull()) {
791 return EPadPosition::kPadBeforePrefix;
792 } else {
793 // UNumberFormatPadPosition and EPadPosition have the same values.
794 return static_cast<EPadPosition>(fields->properties->padPosition.getNoError());
795 }
796 }
797
setPadPosition(EPadPosition padPos)798 void DecimalFormat::setPadPosition(EPadPosition padPos) {
799 auto uPadPos = static_cast<UNumberFormatPadPosition>(padPos);
800 if (!fields->properties->padPosition.isNull() && uPadPos == fields->properties->padPosition.getNoError()) {
801 return;
802 }
803 fields->properties->padPosition = uPadPos;
804 touchNoError();
805 }
806
isScientificNotation(void) const807 UBool DecimalFormat::isScientificNotation(void) const {
808 return fields->properties->minimumExponentDigits != -1;
809 }
810
setScientificNotation(UBool useScientific)811 void DecimalFormat::setScientificNotation(UBool useScientific) {
812 int32_t minExp = useScientific ? 1 : -1;
813 if (fields->properties->minimumExponentDigits == minExp) { return; }
814 if (useScientific) {
815 fields->properties->minimumExponentDigits = 1;
816 } else {
817 fields->properties->minimumExponentDigits = -1;
818 }
819 touchNoError();
820 }
821
getMinimumExponentDigits(void) const822 int8_t DecimalFormat::getMinimumExponentDigits(void) const {
823 return static_cast<int8_t>(fields->properties->minimumExponentDigits);
824 }
825
setMinimumExponentDigits(int8_t minExpDig)826 void DecimalFormat::setMinimumExponentDigits(int8_t minExpDig) {
827 if (minExpDig == fields->properties->minimumExponentDigits) { return; }
828 fields->properties->minimumExponentDigits = minExpDig;
829 touchNoError();
830 }
831
isExponentSignAlwaysShown(void) const832 UBool DecimalFormat::isExponentSignAlwaysShown(void) const {
833 return fields->properties->exponentSignAlwaysShown;
834 }
835
setExponentSignAlwaysShown(UBool expSignAlways)836 void DecimalFormat::setExponentSignAlwaysShown(UBool expSignAlways) {
837 if (UBOOL_TO_BOOL(expSignAlways) == fields->properties->exponentSignAlwaysShown) { return; }
838 fields->properties->exponentSignAlwaysShown = expSignAlways;
839 touchNoError();
840 }
841
getGroupingSize(void) const842 int32_t DecimalFormat::getGroupingSize(void) const {
843 if (fields->properties->groupingSize < 0) {
844 return 0;
845 }
846 return fields->properties->groupingSize;
847 }
848
setGroupingSize(int32_t newValue)849 void DecimalFormat::setGroupingSize(int32_t newValue) {
850 if (newValue == fields->properties->groupingSize) { return; }
851 fields->properties->groupingSize = newValue;
852 touchNoError();
853 }
854
getSecondaryGroupingSize(void) const855 int32_t DecimalFormat::getSecondaryGroupingSize(void) const {
856 int grouping2 = fields->properties->secondaryGroupingSize;
857 if (grouping2 < 0) {
858 return 0;
859 }
860 return grouping2;
861 }
862
setSecondaryGroupingSize(int32_t newValue)863 void DecimalFormat::setSecondaryGroupingSize(int32_t newValue) {
864 if (newValue == fields->properties->secondaryGroupingSize) { return; }
865 fields->properties->secondaryGroupingSize = newValue;
866 touchNoError();
867 }
868
getMinimumGroupingDigits() const869 int32_t DecimalFormat::getMinimumGroupingDigits() const {
870 return fields->properties->minimumGroupingDigits;
871 }
872
setMinimumGroupingDigits(int32_t newValue)873 void DecimalFormat::setMinimumGroupingDigits(int32_t newValue) {
874 if (newValue == fields->properties->minimumGroupingDigits) { return; }
875 fields->properties->minimumGroupingDigits = newValue;
876 touchNoError();
877 }
878
isDecimalSeparatorAlwaysShown(void) const879 UBool DecimalFormat::isDecimalSeparatorAlwaysShown(void) const {
880 return fields->properties->decimalSeparatorAlwaysShown;
881 }
882
setDecimalSeparatorAlwaysShown(UBool newValue)883 void DecimalFormat::setDecimalSeparatorAlwaysShown(UBool newValue) {
884 if (UBOOL_TO_BOOL(newValue) == fields->properties->decimalSeparatorAlwaysShown) { return; }
885 fields->properties->decimalSeparatorAlwaysShown = newValue;
886 touchNoError();
887 }
888
isDecimalPatternMatchRequired(void) const889 UBool DecimalFormat::isDecimalPatternMatchRequired(void) const {
890 return fields->properties->decimalPatternMatchRequired;
891 }
892
setDecimalPatternMatchRequired(UBool newValue)893 void DecimalFormat::setDecimalPatternMatchRequired(UBool newValue) {
894 if (UBOOL_TO_BOOL(newValue) == fields->properties->decimalPatternMatchRequired) { return; }
895 fields->properties->decimalPatternMatchRequired = newValue;
896 touchNoError();
897 }
898
isParseNoExponent() const899 UBool DecimalFormat::isParseNoExponent() const {
900 return fields->properties->parseNoExponent;
901 }
902
setParseNoExponent(UBool value)903 void DecimalFormat::setParseNoExponent(UBool value) {
904 if (UBOOL_TO_BOOL(value) == fields->properties->parseNoExponent) { return; }
905 fields->properties->parseNoExponent = value;
906 touchNoError();
907 }
908
isParseCaseSensitive() const909 UBool DecimalFormat::isParseCaseSensitive() const {
910 return fields->properties->parseCaseSensitive;
911 }
912
setParseCaseSensitive(UBool value)913 void DecimalFormat::setParseCaseSensitive(UBool value) {
914 if (UBOOL_TO_BOOL(value) == fields->properties->parseCaseSensitive) { return; }
915 fields->properties->parseCaseSensitive = value;
916 touchNoError();
917 }
918
isFormatFailIfMoreThanMaxDigits() const919 UBool DecimalFormat::isFormatFailIfMoreThanMaxDigits() const {
920 return fields->properties->formatFailIfMoreThanMaxDigits;
921 }
922
setFormatFailIfMoreThanMaxDigits(UBool value)923 void DecimalFormat::setFormatFailIfMoreThanMaxDigits(UBool value) {
924 if (UBOOL_TO_BOOL(value) == fields->properties->formatFailIfMoreThanMaxDigits) { return; }
925 fields->properties->formatFailIfMoreThanMaxDigits = value;
926 touchNoError();
927 }
928
toPattern(UnicodeString & result) const929 UnicodeString& DecimalFormat::toPattern(UnicodeString& result) const {
930 // Pull some properties from exportedProperties and others from properties
931 // to keep affix patterns intact. In particular, pull rounding properties
932 // so that CurrencyUsage is reflected properly.
933 // TODO: Consider putting this logic in number_patternstring.cpp instead.
934 ErrorCode localStatus;
935 DecimalFormatProperties tprops(*fields->properties);
936 bool useCurrency = ((!tprops.currency.isNull()) || !tprops.currencyPluralInfo.fPtr.isNull() ||
937 !tprops.currencyUsage.isNull() || AffixUtils::hasCurrencySymbols(
938 tprops.positivePrefixPattern, localStatus) || AffixUtils::hasCurrencySymbols(
939 tprops.positiveSuffixPattern, localStatus) || AffixUtils::hasCurrencySymbols(
940 tprops.negativePrefixPattern, localStatus) || AffixUtils::hasCurrencySymbols(
941 tprops.negativeSuffixPattern, localStatus));
942 if (useCurrency) {
943 tprops.minimumFractionDigits = fields->exportedProperties->minimumFractionDigits;
944 tprops.maximumFractionDigits = fields->exportedProperties->maximumFractionDigits;
945 tprops.roundingIncrement = fields->exportedProperties->roundingIncrement;
946 }
947 result = PatternStringUtils::propertiesToPatternString(tprops, localStatus);
948 return result;
949 }
950
toLocalizedPattern(UnicodeString & result) const951 UnicodeString& DecimalFormat::toLocalizedPattern(UnicodeString& result) const {
952 ErrorCode localStatus;
953 result = toPattern(result);
954 result = PatternStringUtils::convertLocalized(result, *fields->symbols, true, localStatus);
955 return result;
956 }
957
applyPattern(const UnicodeString & pattern,UParseError &,UErrorCode & status)958 void DecimalFormat::applyPattern(const UnicodeString& pattern, UParseError&, UErrorCode& status) {
959 // TODO: What is parseError for?
960 applyPattern(pattern, status);
961 }
962
applyPattern(const UnicodeString & pattern,UErrorCode & status)963 void DecimalFormat::applyPattern(const UnicodeString& pattern, UErrorCode& status) {
964 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_NEVER, status);
965 touch(status);
966 }
967
applyLocalizedPattern(const UnicodeString & localizedPattern,UParseError &,UErrorCode & status)968 void DecimalFormat::applyLocalizedPattern(const UnicodeString& localizedPattern, UParseError&,
969 UErrorCode& status) {
970 // TODO: What is parseError for?
971 applyLocalizedPattern(localizedPattern, status);
972 }
973
applyLocalizedPattern(const UnicodeString & localizedPattern,UErrorCode & status)974 void DecimalFormat::applyLocalizedPattern(const UnicodeString& localizedPattern, UErrorCode& status) {
975 if (U_SUCCESS(status)) {
976 UnicodeString pattern = PatternStringUtils::convertLocalized(
977 localizedPattern, *fields->symbols, false, status);
978 applyPattern(pattern, status);
979 }
980 }
981
setMaximumIntegerDigits(int32_t newValue)982 void DecimalFormat::setMaximumIntegerDigits(int32_t newValue) {
983 if (newValue == fields->properties->maximumIntegerDigits) { return; }
984 // For backwards compatibility, conflicting min/max need to keep the most recent setting.
985 int32_t min = fields->properties->minimumIntegerDigits;
986 if (min >= 0 && min > newValue) {
987 fields->properties->minimumIntegerDigits = newValue;
988 }
989 fields->properties->maximumIntegerDigits = newValue;
990 touchNoError();
991 }
992
setMinimumIntegerDigits(int32_t newValue)993 void DecimalFormat::setMinimumIntegerDigits(int32_t newValue) {
994 if (newValue == fields->properties->minimumIntegerDigits) { return; }
995 // For backwards compatibility, conflicting min/max need to keep the most recent setting.
996 int32_t max = fields->properties->maximumIntegerDigits;
997 if (max >= 0 && max < newValue) {
998 fields->properties->maximumIntegerDigits = newValue;
999 }
1000 fields->properties->minimumIntegerDigits = newValue;
1001 touchNoError();
1002 }
1003
setMaximumFractionDigits(int32_t newValue)1004 void DecimalFormat::setMaximumFractionDigits(int32_t newValue) {
1005 if (newValue == fields->properties->maximumFractionDigits) { return; }
1006 // For backwards compatibility, conflicting min/max need to keep the most recent setting.
1007 int32_t min = fields->properties->minimumFractionDigits;
1008 if (min >= 0 && min > newValue) {
1009 fields->properties->minimumFractionDigits = newValue;
1010 }
1011 fields->properties->maximumFractionDigits = newValue;
1012 touchNoError();
1013 }
1014
setMinimumFractionDigits(int32_t newValue)1015 void DecimalFormat::setMinimumFractionDigits(int32_t newValue) {
1016 if (newValue == fields->properties->minimumFractionDigits) { return; }
1017 // For backwards compatibility, conflicting min/max need to keep the most recent setting.
1018 int32_t max = fields->properties->maximumFractionDigits;
1019 if (max >= 0 && max < newValue) {
1020 fields->properties->maximumFractionDigits = newValue;
1021 }
1022 fields->properties->minimumFractionDigits = newValue;
1023 touchNoError();
1024 }
1025
getMinimumSignificantDigits() const1026 int32_t DecimalFormat::getMinimumSignificantDigits() const {
1027 return fields->exportedProperties->minimumSignificantDigits;
1028 }
1029
getMaximumSignificantDigits() const1030 int32_t DecimalFormat::getMaximumSignificantDigits() const {
1031 return fields->exportedProperties->maximumSignificantDigits;
1032 }
1033
setMinimumSignificantDigits(int32_t value)1034 void DecimalFormat::setMinimumSignificantDigits(int32_t value) {
1035 if (value == fields->properties->minimumSignificantDigits) { return; }
1036 int32_t max = fields->properties->maximumSignificantDigits;
1037 if (max >= 0 && max < value) {
1038 fields->properties->maximumSignificantDigits = value;
1039 }
1040 fields->properties->minimumSignificantDigits = value;
1041 touchNoError();
1042 }
1043
setMaximumSignificantDigits(int32_t value)1044 void DecimalFormat::setMaximumSignificantDigits(int32_t value) {
1045 if (value == fields->properties->maximumSignificantDigits) { return; }
1046 int32_t min = fields->properties->minimumSignificantDigits;
1047 if (min >= 0 && min > value) {
1048 fields->properties->minimumSignificantDigits = value;
1049 }
1050 fields->properties->maximumSignificantDigits = value;
1051 touchNoError();
1052 }
1053
areSignificantDigitsUsed() const1054 UBool DecimalFormat::areSignificantDigitsUsed() const {
1055 return fields->properties->minimumSignificantDigits != -1 || fields->properties->maximumSignificantDigits != -1;
1056 }
1057
setSignificantDigitsUsed(UBool useSignificantDigits)1058 void DecimalFormat::setSignificantDigitsUsed(UBool useSignificantDigits) {
1059 // These are the default values from the old implementation.
1060 if (useSignificantDigits) {
1061 if (fields->properties->minimumSignificantDigits != -1 ||
1062 fields->properties->maximumSignificantDigits != -1) {
1063 return;
1064 }
1065 } else {
1066 if (fields->properties->minimumSignificantDigits == -1 &&
1067 fields->properties->maximumSignificantDigits == -1) {
1068 return;
1069 }
1070 }
1071 int32_t minSig = useSignificantDigits ? 1 : -1;
1072 int32_t maxSig = useSignificantDigits ? 6 : -1;
1073 fields->properties->minimumSignificantDigits = minSig;
1074 fields->properties->maximumSignificantDigits = maxSig;
1075 touchNoError();
1076 }
1077
setCurrency(const char16_t * theCurrency,UErrorCode & ec)1078 void DecimalFormat::setCurrency(const char16_t* theCurrency, UErrorCode& ec) {
1079 CurrencyUnit currencyUnit(theCurrency, ec);
1080 if (U_FAILURE(ec)) { return; }
1081 if (!fields->properties->currency.isNull() && fields->properties->currency.getNoError() == currencyUnit) {
1082 return;
1083 }
1084 NumberFormat::setCurrency(theCurrency, ec); // to set field for compatibility
1085 fields->properties->currency = currencyUnit;
1086 // TODO: Set values in fields->symbols, too?
1087 touchNoError();
1088 }
1089
setCurrency(const char16_t * theCurrency)1090 void DecimalFormat::setCurrency(const char16_t* theCurrency) {
1091 ErrorCode localStatus;
1092 setCurrency(theCurrency, localStatus);
1093 }
1094
setCurrencyUsage(UCurrencyUsage newUsage,UErrorCode * ec)1095 void DecimalFormat::setCurrencyUsage(UCurrencyUsage newUsage, UErrorCode* ec) {
1096 if (U_FAILURE(*ec)) {
1097 return;
1098 }
1099 if (!fields->properties->currencyUsage.isNull() && newUsage == fields->properties->currencyUsage.getNoError()) {
1100 return;
1101 }
1102 fields->properties->currencyUsage = newUsage;
1103 touch(*ec);
1104 }
1105
getCurrencyUsage() const1106 UCurrencyUsage DecimalFormat::getCurrencyUsage() const {
1107 // CurrencyUsage is not exported, so we have to get it from the input property bag.
1108 // TODO: Should we export CurrencyUsage instead?
1109 if (fields->properties->currencyUsage.isNull()) {
1110 return UCURR_USAGE_STANDARD;
1111 }
1112 return fields->properties->currencyUsage.getNoError();
1113 }
1114
1115 void
formatToDecimalQuantity(double number,DecimalQuantity & output,UErrorCode & status) const1116 DecimalFormat::formatToDecimalQuantity(double number, DecimalQuantity& output, UErrorCode& status) const {
1117 fields->formatter->formatDouble(number, status).getDecimalQuantity(output, status);
1118 }
1119
formatToDecimalQuantity(const Formattable & number,DecimalQuantity & output,UErrorCode & status) const1120 void DecimalFormat::formatToDecimalQuantity(const Formattable& number, DecimalQuantity& output,
1121 UErrorCode& status) const {
1122 UFormattedNumberData obj;
1123 number.populateDecimalQuantity(obj.quantity, status);
1124 fields->formatter->formatImpl(&obj, status);
1125 output = std::move(obj.quantity);
1126 }
1127
toNumberFormatter() const1128 const number::LocalizedNumberFormatter& DecimalFormat::toNumberFormatter() const {
1129 return *fields->formatter;
1130 }
1131
1132 /** Rebuilds the formatter object from the property bag. */
touch(UErrorCode & status)1133 void DecimalFormat::touch(UErrorCode& status) {
1134 if (fields->exportedProperties == nullptr) {
1135 // fields->exportedProperties is null only when the formatter is not ready yet.
1136 // The only time when this happens is during legacy deserialization.
1137 return;
1138 }
1139
1140 // In C++, fields->symbols is the source of truth for the locale.
1141 Locale locale = fields->symbols->getLocale();
1142
1143 // Note: The formatter is relatively cheap to create, and we need it to populate fields->exportedProperties,
1144 // so automatically compute it here. The parser is a bit more expensive and is not needed until the
1145 // parse method is called, so defer that until needed.
1146 // TODO: Only update the pieces that changed instead of re-computing the whole formatter?
1147 fields->formatter.adoptInstead(
1148 new LocalizedNumberFormatter(
1149 NumberPropertyMapper::create(
1150 *fields->properties, *fields->symbols, fields->warehouse, *fields->exportedProperties, status).locale(
1151 locale)));
1152
1153 // Do this after fields->exportedProperties are set up
1154 setupFastFormat();
1155
1156 // Delete the parsers if they were made previously
1157 delete fields->atomicParser.exchange(nullptr);
1158 delete fields->atomicCurrencyParser.exchange(nullptr);
1159
1160 // In order for the getters to work, we need to populate some fields in NumberFormat.
1161 NumberFormat::setCurrency(fields->exportedProperties->currency.get(status).getISOCurrency(), status);
1162 NumberFormat::setMaximumIntegerDigits(fields->exportedProperties->maximumIntegerDigits);
1163 NumberFormat::setMinimumIntegerDigits(fields->exportedProperties->minimumIntegerDigits);
1164 NumberFormat::setMaximumFractionDigits(fields->exportedProperties->maximumFractionDigits);
1165 NumberFormat::setMinimumFractionDigits(fields->exportedProperties->minimumFractionDigits);
1166 // fImpl->properties, not fields->exportedProperties, since this information comes from the pattern:
1167 NumberFormat::setGroupingUsed(fields->properties->groupingUsed);
1168 }
1169
touchNoError()1170 void DecimalFormat::touchNoError() {
1171 UErrorCode localStatus = U_ZERO_ERROR;
1172 touch(localStatus);
1173 }
1174
setPropertiesFromPattern(const UnicodeString & pattern,int32_t ignoreRounding,UErrorCode & status)1175 void DecimalFormat::setPropertiesFromPattern(const UnicodeString& pattern, int32_t ignoreRounding,
1176 UErrorCode& status) {
1177 if (U_SUCCESS(status)) {
1178 // Cast workaround to get around putting the enum in the public header file
1179 auto actualIgnoreRounding = static_cast<IgnoreRounding>(ignoreRounding);
1180 PatternParser::parseToExistingProperties(pattern, *fields->properties, actualIgnoreRounding, status);
1181 }
1182 }
1183
getParser(UErrorCode & status) const1184 const numparse::impl::NumberParserImpl* DecimalFormat::getParser(UErrorCode& status) const {
1185 // TODO: Move this into umutex.h? (similar logic also in numrange_fluent.cpp)
1186 // See ICU-20146
1187
1188 if (U_FAILURE(status)) {
1189 return nullptr;
1190 }
1191
1192 // First try to get the pre-computed parser
1193 auto* ptr = fields->atomicParser.load();
1194 if (ptr != nullptr) {
1195 return ptr;
1196 }
1197
1198 // Try computing the parser on our own
1199 auto* temp = NumberParserImpl::createParserFromProperties(*fields->properties, *fields->symbols, false, status);
1200 if (U_FAILURE(status)) {
1201 return nullptr;
1202 }
1203 if (temp == nullptr) {
1204 status = U_MEMORY_ALLOCATION_ERROR;
1205 return nullptr;
1206 }
1207
1208 // Note: ptr starts as nullptr; during compare_exchange,
1209 // it is set to what is actually stored in the atomic
1210 // if another thread beat us to computing the parser object.
1211 auto* nonConstThis = const_cast<DecimalFormat*>(this);
1212 if (!nonConstThis->fields->atomicParser.compare_exchange_strong(ptr, temp)) {
1213 // Another thread beat us to computing the parser
1214 delete temp;
1215 return ptr;
1216 } else {
1217 // Our copy of the parser got stored in the atomic
1218 return temp;
1219 }
1220 }
1221
getCurrencyParser(UErrorCode & status) const1222 const numparse::impl::NumberParserImpl* DecimalFormat::getCurrencyParser(UErrorCode& status) const {
1223 if (U_FAILURE(status)) { return nullptr; }
1224
1225 // First try to get the pre-computed parser
1226 auto* ptr = fields->atomicCurrencyParser.load();
1227 if (ptr != nullptr) {
1228 return ptr;
1229 }
1230
1231 // Try computing the parser on our own
1232 auto* temp = NumberParserImpl::createParserFromProperties(*fields->properties, *fields->symbols, true, status);
1233 if (temp == nullptr) {
1234 status = U_MEMORY_ALLOCATION_ERROR;
1235 // although we may still dereference, call sites should be guarded
1236 }
1237
1238 // Note: ptr starts as nullptr; during compare_exchange, it is set to what is actually stored in the
1239 // atomic if another thread beat us to computing the parser object.
1240 auto* nonConstThis = const_cast<DecimalFormat*>(this);
1241 if (!nonConstThis->fields->atomicCurrencyParser.compare_exchange_strong(ptr, temp)) {
1242 // Another thread beat us to computing the parser
1243 delete temp;
1244 return ptr;
1245 } else {
1246 // Our copy of the parser got stored in the atomic
1247 return temp;
1248 }
1249 }
1250
1251 void
fieldPositionHelper(const number::FormattedNumber & formatted,FieldPosition & fieldPosition,int32_t offset,UErrorCode & status)1252 DecimalFormat::fieldPositionHelper(const number::FormattedNumber& formatted, FieldPosition& fieldPosition,
1253 int32_t offset, UErrorCode& status) {
1254 // always return first occurrence:
1255 fieldPosition.setBeginIndex(0);
1256 fieldPosition.setEndIndex(0);
1257 bool found = formatted.nextFieldPosition(fieldPosition, status);
1258 if (found && offset != 0) {
1259 FieldPositionOnlyHandler fpoh(fieldPosition);
1260 fpoh.shiftLast(offset);
1261 }
1262 }
1263
1264 void
fieldPositionIteratorHelper(const number::FormattedNumber & formatted,FieldPositionIterator * fpi,int32_t offset,UErrorCode & status)1265 DecimalFormat::fieldPositionIteratorHelper(const number::FormattedNumber& formatted, FieldPositionIterator* fpi,
1266 int32_t offset, UErrorCode& status) {
1267 if (fpi != nullptr) {
1268 FieldPositionIteratorHandler fpih(fpi, status);
1269 fpih.setShift(offset);
1270 formatted.getAllFieldPositionsImpl(fpih, status);
1271 }
1272 }
1273
1274 // To debug fast-format, change void(x) to printf(x)
1275 #define trace(x) void(x)
1276
setupFastFormat()1277 void DecimalFormat::setupFastFormat() {
1278 // Check the majority of properties:
1279 if (!fields->properties->equalsDefaultExceptFastFormat()) {
1280 trace("no fast format: equality\n");
1281 fields->canUseFastFormat = false;
1282 return;
1283 }
1284
1285 // Now check the remaining properties.
1286 // Nontrivial affixes:
1287 UBool trivialPP = fields->properties->positivePrefixPattern.isEmpty();
1288 UBool trivialPS = fields->properties->positiveSuffixPattern.isEmpty();
1289 UBool trivialNP = fields->properties->negativePrefixPattern.isBogus() || (
1290 fields->properties->negativePrefixPattern.length() == 1 &&
1291 fields->properties->negativePrefixPattern.charAt(0) == u'-');
1292 UBool trivialNS = fields->properties->negativeSuffixPattern.isEmpty();
1293 if (!trivialPP || !trivialPS || !trivialNP || !trivialNS) {
1294 trace("no fast format: affixes\n");
1295 fields->canUseFastFormat = false;
1296 return;
1297 }
1298
1299 // Grouping (secondary grouping is forbidden in equalsDefaultExceptFastFormat):
1300 bool groupingUsed = fields->properties->groupingUsed;
1301 int32_t groupingSize = fields->properties->groupingSize;
1302 bool unusualGroupingSize = groupingSize > 0 && groupingSize != 3;
1303 const UnicodeString& groupingString = fields->symbols->getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol);
1304 if (groupingUsed && (unusualGroupingSize || groupingString.length() != 1)) {
1305 trace("no fast format: grouping\n");
1306 fields->canUseFastFormat = false;
1307 return;
1308 }
1309
1310 // Integer length:
1311 int32_t minInt = fields->exportedProperties->minimumIntegerDigits;
1312 int32_t maxInt = fields->exportedProperties->maximumIntegerDigits;
1313 // Fastpath supports up to only 10 digits (length of INT32_MIN)
1314 if (minInt > 10) {
1315 trace("no fast format: integer\n");
1316 fields->canUseFastFormat = false;
1317 return;
1318 }
1319
1320 // Fraction length (no fraction part allowed in fast path):
1321 int32_t minFrac = fields->exportedProperties->minimumFractionDigits;
1322 if (minFrac > 0) {
1323 trace("no fast format: fraction\n");
1324 fields->canUseFastFormat = false;
1325 return;
1326 }
1327
1328 // Other symbols:
1329 const UnicodeString& minusSignString = fields->symbols->getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol);
1330 UChar32 codePointZero = fields->symbols->getCodePointZero();
1331 if (minusSignString.length() != 1 || U16_LENGTH(codePointZero) != 1) {
1332 trace("no fast format: symbols\n");
1333 fields->canUseFastFormat = false;
1334 return;
1335 }
1336
1337 // Good to go!
1338 trace("can use fast format!\n");
1339 fields->canUseFastFormat = true;
1340 fields->fastData.cpZero = static_cast<char16_t>(codePointZero);
1341 fields->fastData.cpGroupingSeparator = groupingUsed && groupingSize == 3 ? groupingString.charAt(0) : 0;
1342 fields->fastData.cpMinusSign = minusSignString.charAt(0);
1343 fields->fastData.minInt = (minInt < 0 || minInt > 127) ? 0 : static_cast<int8_t>(minInt);
1344 fields->fastData.maxInt = (maxInt < 0 || maxInt > 127) ? 127 : static_cast<int8_t>(maxInt);
1345 }
1346
fastFormatDouble(double input,UnicodeString & output) const1347 bool DecimalFormat::fastFormatDouble(double input, UnicodeString& output) const {
1348 if (!fields->canUseFastFormat) {
1349 return false;
1350 }
1351 if (std::isnan(input)
1352 || std::trunc(input) != input
1353 || input <= INT32_MIN
1354 || input > INT32_MAX) {
1355 return false;
1356 }
1357 doFastFormatInt32(static_cast<int32_t>(input), std::signbit(input), output);
1358 return true;
1359 }
1360
fastFormatInt64(int64_t input,UnicodeString & output) const1361 bool DecimalFormat::fastFormatInt64(int64_t input, UnicodeString& output) const {
1362 if (!fields->canUseFastFormat) {
1363 return false;
1364 }
1365 if (input <= INT32_MIN || input > INT32_MAX) {
1366 return false;
1367 }
1368 doFastFormatInt32(static_cast<int32_t>(input), input < 0, output);
1369 return true;
1370 }
1371
doFastFormatInt32(int32_t input,bool isNegative,UnicodeString & output) const1372 void DecimalFormat::doFastFormatInt32(int32_t input, bool isNegative, UnicodeString& output) const {
1373 U_ASSERT(fields->canUseFastFormat);
1374 if (isNegative) {
1375 output.append(fields->fastData.cpMinusSign);
1376 U_ASSERT(input != INT32_MIN); // handled by callers
1377 input = -input;
1378 }
1379 // Cap at int32_t to make the buffer small and operations fast.
1380 // Longest string: "2,147,483,648" (13 chars in length)
1381 static constexpr int32_t localCapacity = 13;
1382 char16_t localBuffer[localCapacity];
1383 char16_t* ptr = localBuffer + localCapacity;
1384 int8_t group = 0;
1385 for (int8_t i = 0; i < fields->fastData.maxInt && (input != 0 || i < fields->fastData.minInt); i++) {
1386 if (group++ == 3 && fields->fastData.cpGroupingSeparator != 0) {
1387 *(--ptr) = fields->fastData.cpGroupingSeparator;
1388 group = 1;
1389 }
1390 std::div_t res = std::div(input, 10);
1391 *(--ptr) = static_cast<char16_t>(fields->fastData.cpZero + res.rem);
1392 input = res.quot;
1393 }
1394 int32_t len = localCapacity - static_cast<int32_t>(ptr - localBuffer);
1395 output.append(ptr, len);
1396 }
1397
1398
1399 #endif /* #if !UCONFIG_NO_FORMATTING */
1400