]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/decimfmt.cpp
ICU-64232.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / decimfmt.cpp
CommitLineData
3d1f044b 1// © 2018 and later: Unicode, Inc. and others.
f3c0d7a5 2// License & terms of use: http://www.unicode.org/copyright.html
3d1f044b 3
b75a7d8f
A
4#include "unicode/utypes.h"
5
6#if !UCONFIG_NO_FORMATTING
7
3d1f044b
A
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 "unicode/ucurr.h"
18#include "number_decimalquantity.h"
19#include "number_types.h"
20#include "numparse_impl.h"
21#include "number_mapper.h"
22#include "number_patternstring.h"
23#include "putilimp.h"
24#include "number_utils.h"
25#include "number_utypes.h"
26
27using namespace icu;
28using namespace icu::number;
29using namespace icu::number::impl;
30using namespace icu::numparse;
31using namespace icu::numparse::impl;
32using ERoundingMode = icu::DecimalFormat::ERoundingMode;
33using EPadPosition = icu::DecimalFormat::EPadPosition;
34
35// MSVC VS2015 warns C4805 when comparing bool with UBool, VS2017 no longer emits this warning.
36// TODO: Move this macro into a better place?
37#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN
38#define UBOOL_TO_BOOL(b) static_cast<bool>(b)
57a6839d 39#else
3d1f044b 40#define UBOOL_TO_BOOL(b) b
57a6839d 41#endif
729e4ab9 42
b75a7d8f 43
374ca955 44UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormat)
b75a7d8f 45
51004dcb 46
3d1f044b
A
47DecimalFormat::DecimalFormat(UErrorCode& status)
48 : DecimalFormat(nullptr, status) {
49 if (U_FAILURE(status)) { return; }
50 // Use the default locale and decimal pattern.
51 const char* localeName = Locale::getDefault().getName();
52 LocalPointer<NumberingSystem> ns(NumberingSystem::createInstance(status));
53 UnicodeString patternString = utils::getPatternForStyle(
54 localeName,
55 ns->getName(),
56 CLDR_PATTERN_STYLE_DECIMAL,
57 status);
58 setPropertiesFromPattern(patternString, IGNORE_ROUNDING_IF_CURRENCY, status);
59 touch(status);
60}
61
62DecimalFormat::DecimalFormat(const UnicodeString& pattern, UErrorCode& status)
63 : DecimalFormat(nullptr, status) {
64 if (U_FAILURE(status)) { return; }
65 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status);
66 touch(status);
67}
68
69DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt,
70 UErrorCode& status)
71 : DecimalFormat(symbolsToAdopt, status) {
72 if (U_FAILURE(status)) { return; }
73 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status);
74 touch(status);
75}
76
77DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt,
78 UNumberFormatStyle style, UErrorCode& status)
79 : DecimalFormat(symbolsToAdopt, status) {
80 if (U_FAILURE(status)) { return; }
81 // If choice is a currency type, ignore the rounding information.
82 if (style == UNumberFormatStyle::UNUM_CURRENCY ||
83 style == UNumberFormatStyle::UNUM_CURRENCY_ISO ||
84 style == UNumberFormatStyle::UNUM_CURRENCY_ACCOUNTING ||
85 style == UNumberFormatStyle::UNUM_CASH_CURRENCY ||
86 style == UNumberFormatStyle::UNUM_CURRENCY_STANDARD ||
87 style == UNumberFormatStyle::UNUM_CURRENCY_PLURAL) {
88 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_ALWAYS, status);
89 } else {
90 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status);
91 }
92 // Note: in Java, CurrencyPluralInfo is set in NumberFormat.java, but in C++, it is not set there,
93 // so we have to set it here.
94 if (style == UNumberFormatStyle::UNUM_CURRENCY_PLURAL) {
95 LocalPointer<CurrencyPluralInfo> cpi(
96 new CurrencyPluralInfo(fields->symbols->getLocale(), status),
97 status);
98 if (U_FAILURE(status)) { return; }
99 fields->properties->currencyPluralInfo.fPtr.adoptInstead(cpi.orphan());
100 }
101 touch(status);
729e4ab9
A
102}
103
3d1f044b
A
104DecimalFormat::DecimalFormat(const DecimalFormatSymbols* symbolsToAdopt, UErrorCode& status) {
105 // we must take ownership of symbolsToAdopt, even in a failure case.
106 LocalPointer<const DecimalFormatSymbols> adoptedSymbols(symbolsToAdopt);
107 if (U_FAILURE(status)) {
b75a7d8f 108 return;
b75a7d8f 109 }
3d1f044b
A
110 fields = new DecimalFormatFields();
111 if (fields == nullptr) {
112 status = U_MEMORY_ALLOCATION_ERROR;
57a6839d
A
113 return;
114 }
3d1f044b
A
115 fields->formatter.adoptInsteadAndCheckErrorCode(new LocalizedNumberFormatter(), status);
116 fields->properties.adoptInsteadAndCheckErrorCode(new DecimalFormatProperties(), status);
117 fields->exportedProperties.adoptInsteadAndCheckErrorCode(new DecimalFormatProperties(), status);
118 if (adoptedSymbols.isNull()) {
119 fields->symbols.adoptInsteadAndCheckErrorCode(new DecimalFormatSymbols(status), status);
120 } else {
121 fields->symbols.adoptInsteadAndCheckErrorCode(adoptedSymbols.orphan(), status);
122 }
123 // In order to simplify error handling logic in the various getters/setters/etc, we do not allow
124 // any partially populated DecimalFormatFields object. We must have a fully complete fields object
125 // or else we set it to nullptr.
126 if (fields->formatter.isNull() || fields->properties.isNull() || fields->exportedProperties.isNull() || fields->symbols.isNull()) {
127 delete fields;
128 fields = nullptr;
129 status = U_MEMORY_ALLOCATION_ERROR;
130 }
131}
b75a7d8f 132
3d1f044b 133#if UCONFIG_HAVE_PARSEALLINPUT
2ca993e8 134
3d1f044b
A
135void DecimalFormat::setParseAllInput(UNumberFormatAttributeValue value) {
136 if (fields == nullptr) { return; }
137 if (value == fields->properties->parseAllInput) { return; }
138 fields->properties->parseAllInput = value;
139}
b75a7d8f 140
3d1f044b 141#endif
4388f060 142
3d1f044b
A
143DecimalFormat&
144DecimalFormat::setAttribute(UNumberFormatAttribute attr, int32_t newValue, UErrorCode& status) {
145 if (U_FAILURE(status)) { return *this; }
146
147 if (fields == nullptr) {
148 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
149 status = U_MEMORY_ALLOCATION_ERROR;
150 return *this;
b75a7d8f
A
151 }
152
3d1f044b
A
153 switch (attr) {
154 case UNUM_LENIENT_PARSE:
155 setLenient(newValue != 0);
156 break;
729e4ab9 157
3d1f044b
A
158 case UNUM_PARSE_INT_ONLY:
159 setParseIntegerOnly(newValue != 0);
160 break;
729e4ab9 161
3d1f044b
A
162 case UNUM_GROUPING_USED:
163 setGroupingUsed(newValue != 0);
164 break;
729e4ab9 165
3d1f044b
A
166 case UNUM_DECIMAL_ALWAYS_SHOWN:
167 setDecimalSeparatorAlwaysShown(newValue != 0);
168 break;
729e4ab9 169
3d1f044b
A
170 case UNUM_MAX_INTEGER_DIGITS:
171 setMaximumIntegerDigits(newValue);
172 break;
729e4ab9 173
3d1f044b
A
174 case UNUM_MIN_INTEGER_DIGITS:
175 setMinimumIntegerDigits(newValue);
176 break;
729e4ab9 177
3d1f044b
A
178 case UNUM_INTEGER_DIGITS:
179 setMinimumIntegerDigits(newValue);
180 setMaximumIntegerDigits(newValue);
181 break;
4388f060 182
3d1f044b
A
183 case UNUM_MAX_FRACTION_DIGITS:
184 setMaximumFractionDigits(newValue);
185 break;
729e4ab9 186
3d1f044b
A
187 case UNUM_MIN_FRACTION_DIGITS:
188 setMinimumFractionDigits(newValue);
189 break;
729e4ab9 190
3d1f044b
A
191 case UNUM_FRACTION_DIGITS:
192 setMinimumFractionDigits(newValue);
193 setMaximumFractionDigits(newValue);
194 break;
b75a7d8f 195
3d1f044b
A
196 case UNUM_SIGNIFICANT_DIGITS_USED:
197 setSignificantDigitsUsed(newValue != 0);
198 break;
b75a7d8f 199
3d1f044b
A
200 case UNUM_MAX_SIGNIFICANT_DIGITS:
201 setMaximumSignificantDigits(newValue);
202 break;
b75a7d8f 203
3d1f044b
A
204 case UNUM_MIN_SIGNIFICANT_DIGITS:
205 setMinimumSignificantDigits(newValue);
206 break;
b75a7d8f 207
3d1f044b
A
208 case UNUM_MULTIPLIER:
209 setMultiplier(newValue);
210 break;
b75a7d8f 211
3d1f044b
A
212 case UNUM_SCALE:
213 setMultiplierScale(newValue);
214 break;
b75a7d8f 215
3d1f044b
A
216 case UNUM_GROUPING_SIZE:
217 setGroupingSize(newValue);
218 break;
2ca993e8 219
3d1f044b
A
220 case UNUM_ROUNDING_MODE:
221 setRoundingMode((DecimalFormat::ERoundingMode) newValue);
222 break;
b75a7d8f 223
3d1f044b
A
224 case UNUM_FORMAT_WIDTH:
225 setFormatWidth(newValue);
226 break;
b75a7d8f 227
3d1f044b
A
228 case UNUM_PADDING_POSITION:
229 /** The position at which padding will take place. */
230 setPadPosition((DecimalFormat::EPadPosition) newValue);
231 break;
b75a7d8f 232
3d1f044b
A
233 case UNUM_SECONDARY_GROUPING_SIZE:
234 setSecondaryGroupingSize(newValue);
235 break;
b75a7d8f 236
3d1f044b
A
237#if UCONFIG_HAVE_PARSEALLINPUT
238 case UNUM_PARSE_ALL_INPUT:
239 setParseAllInput((UNumberFormatAttributeValue) newValue);
240 break;
241#endif
57a6839d 242
3d1f044b
A
243 case UNUM_PARSE_NO_EXPONENT:
244 setParseNoExponent((UBool) newValue);
245 break;
b75a7d8f 246
3d1f044b
A
247 case UNUM_PARSE_DECIMAL_MARK_REQUIRED:
248 setDecimalPatternMatchRequired((UBool) newValue);
249 break;
b75a7d8f 250
3d1f044b
A
251 case UNUM_CURRENCY_USAGE:
252 setCurrencyUsage((UCurrencyUsage) newValue, &status);
253 break;
b75a7d8f 254
3d1f044b
A
255 case UNUM_MINIMUM_GROUPING_DIGITS:
256 setMinimumGroupingDigits(newValue);
257 break;
57a6839d 258
3d1f044b
A
259 case UNUM_PARSE_CASE_SENSITIVE:
260 setParseCaseSensitive(static_cast<UBool>(newValue));
261 break;
57a6839d 262
3d1f044b
A
263 case UNUM_SIGN_ALWAYS_SHOWN:
264 setSignAlwaysShown(static_cast<UBool>(newValue));
265 break;
57a6839d 266
3d1f044b
A
267 case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS:
268 setFormatFailIfMoreThanMaxDigits(static_cast<UBool>(newValue));
269 break;
2ca993e8 270
3d1f044b
A
271 case UNUM_FORMAT_WITH_FULL_PRECISION: // Apple addition for <rdar://problem/39240173>
272 {
273 bool boolVal = UBOOL_TO_BOOL(static_cast<UBool>(newValue));
274 if (boolVal != fields->properties->formatFullPrecision) {
275 fields->properties->formatFullPrecision = boolVal;
276 touchNoError();
277 }
278 }
279 break;
57a6839d 280
3d1f044b
A
281 default:
282 status = U_UNSUPPORTED_ERROR;
283 break;
57a6839d 284 }
3d1f044b
A
285 return *this;
286}
57a6839d 287
3d1f044b
A
288int32_t DecimalFormat::getAttribute(UNumberFormatAttribute attr, UErrorCode& status) const {
289 if (U_FAILURE(status)) { return -1; }
290
291 if (fields == nullptr) {
292 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
293 status = U_MEMORY_ALLOCATION_ERROR;
294 return -1;
57a6839d 295 }
57a6839d 296
3d1f044b
A
297 switch (attr) {
298 case UNUM_LENIENT_PARSE:
299 return isLenient();
57a6839d 300
3d1f044b
A
301 case UNUM_PARSE_INT_ONLY:
302 return isParseIntegerOnly();
57a6839d 303
3d1f044b
A
304 case UNUM_GROUPING_USED:
305 return isGroupingUsed();
57a6839d 306
3d1f044b
A
307 case UNUM_DECIMAL_ALWAYS_SHOWN:
308 return isDecimalSeparatorAlwaysShown();
57a6839d 309
3d1f044b
A
310 case UNUM_MAX_INTEGER_DIGITS:
311 return getMaximumIntegerDigits();
729e4ab9 312
3d1f044b
A
313 case UNUM_MIN_INTEGER_DIGITS:
314 return getMinimumIntegerDigits();
374ca955 315
3d1f044b
A
316 case UNUM_INTEGER_DIGITS:
317 // TBD: what should this return?
318 return getMinimumIntegerDigits();
51004dcb 319
3d1f044b
A
320 case UNUM_MAX_FRACTION_DIGITS:
321 return getMaximumFractionDigits();
729e4ab9 322
3d1f044b
A
323 case UNUM_MIN_FRACTION_DIGITS:
324 return getMinimumFractionDigits();
51004dcb 325
3d1f044b
A
326 case UNUM_FRACTION_DIGITS:
327 // TBD: what should this return?
328 return getMinimumFractionDigits();
729e4ab9 329
3d1f044b
A
330 case UNUM_SIGNIFICANT_DIGITS_USED:
331 return areSignificantDigitsUsed();
51004dcb 332
3d1f044b
A
333 case UNUM_MAX_SIGNIFICANT_DIGITS:
334 return getMaximumSignificantDigits();
b75a7d8f 335
3d1f044b
A
336 case UNUM_MIN_SIGNIFICANT_DIGITS:
337 return getMinimumSignificantDigits();
729e4ab9 338
3d1f044b
A
339 case UNUM_MULTIPLIER:
340 return getMultiplier();
b75a7d8f 341
3d1f044b
A
342 case UNUM_SCALE:
343 return getMultiplierScale();
51004dcb 344
3d1f044b
A
345 case UNUM_GROUPING_SIZE:
346 return getGroupingSize();
b75a7d8f 347
3d1f044b
A
348 case UNUM_ROUNDING_MODE:
349 return getRoundingMode();
729e4ab9 350
3d1f044b
A
351 case UNUM_FORMAT_WIDTH:
352 return getFormatWidth();
729e4ab9 353
3d1f044b
A
354 case UNUM_PADDING_POSITION:
355 return getPadPosition();
729e4ab9 356
3d1f044b
A
357 case UNUM_SECONDARY_GROUPING_SIZE:
358 return getSecondaryGroupingSize();
729e4ab9 359
3d1f044b
A
360 case UNUM_PARSE_NO_EXPONENT:
361 return isParseNoExponent();
729e4ab9 362
3d1f044b
A
363 case UNUM_PARSE_DECIMAL_MARK_REQUIRED:
364 return isDecimalPatternMatchRequired();
729e4ab9 365
3d1f044b
A
366 case UNUM_CURRENCY_USAGE:
367 return getCurrencyUsage();
729e4ab9 368
3d1f044b
A
369 case UNUM_MINIMUM_GROUPING_DIGITS:
370 return getMinimumGroupingDigits();
b75a7d8f 371
3d1f044b
A
372 case UNUM_PARSE_CASE_SENSITIVE:
373 return isParseCaseSensitive();
b75a7d8f 374
3d1f044b
A
375 case UNUM_SIGN_ALWAYS_SHOWN:
376 return isSignAlwaysShown();
b75a7d8f 377
3d1f044b
A
378 case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS:
379 return isFormatFailIfMoreThanMaxDigits();
b75a7d8f 380
3d1f044b
A
381 case UNUM_FORMAT_WITH_FULL_PRECISION: // Apple addition for <rdar://problem/39240173>
382 return (UBool)fields->properties->formatFullPrecision;
b75a7d8f 383
3d1f044b
A
384 default:
385 status = U_UNSUPPORTED_ERROR;
386 break;
57a6839d 387 }
729e4ab9 388
3d1f044b
A
389 return -1; /* undefined */
390}
729e4ab9 391
3d1f044b
A
392void DecimalFormat::setGroupingUsed(UBool enabled) {
393 if (fields == nullptr) {
394 return;
b75a7d8f 395 }
3d1f044b
A
396 if (UBOOL_TO_BOOL(enabled) == fields->properties->groupingUsed) { return; }
397 NumberFormat::setGroupingUsed(enabled); // to set field for compatibility
398 fields->properties->groupingUsed = enabled;
399 touchNoError();
400}
729e4ab9 401
3d1f044b
A
402void DecimalFormat::setParseIntegerOnly(UBool value) {
403 if (fields == nullptr) {
404 return;
46f4442e 405 }
3d1f044b
A
406 if (UBOOL_TO_BOOL(value) == fields->properties->parseIntegerOnly) { return; }
407 NumberFormat::setParseIntegerOnly(value); // to set field for compatibility
408 fields->properties->parseIntegerOnly = value;
409 touchNoError();
410}
729e4ab9 411
3d1f044b
A
412void DecimalFormat::setLenient(UBool enable) {
413 if (fields == nullptr) {
b75a7d8f
A
414 return;
415 }
3d1f044b
A
416 ParseMode mode = enable ? PARSE_MODE_LENIENT : PARSE_MODE_STRICT;
417 if (!fields->properties->parseMode.isNull() && mode == fields->properties->parseMode.getNoError()) { return; }
418 NumberFormat::setLenient(enable); // to set field for compatibility
419 fields->properties->parseMode = mode;
420 touchNoError();
421}
729e4ab9 422
3d1f044b
A
423DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt,
424 UParseError&, UErrorCode& status)
425 : DecimalFormat(symbolsToAdopt, status) {
426 if (U_FAILURE(status)) { return; }
427 // TODO: What is parseError for?
428 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status);
429 touch(status);
430}
51004dcb 431
3d1f044b
A
432DecimalFormat::DecimalFormat(const UnicodeString& pattern, const DecimalFormatSymbols& symbols,
433 UErrorCode& status)
434 : DecimalFormat(nullptr, status) {
435 if (U_FAILURE(status)) { return; }
436 LocalPointer<DecimalFormatSymbols> dfs(new DecimalFormatSymbols(symbols), status);
437 if (U_FAILURE(status)) {
438 // If we failed to allocate DecimalFormatSymbols, then release fields and its members.
439 // We must have a fully complete fields object, we cannot have partially populated members.
440 delete fields;
441 fields = nullptr;
442 status = U_MEMORY_ALLOCATION_ERROR;
443 return;
729e4ab9 444 }
3d1f044b
A
445 fields->symbols.adoptInstead(dfs.orphan());
446 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status);
447 touch(status);
448}
b75a7d8f 449
3d1f044b
A
450DecimalFormat::DecimalFormat(const DecimalFormat& source) : NumberFormat(source) {
451 // If the object that we are copying from is invalid, no point in going further.
452 if (source.fields == nullptr) {
453 return;
b75a7d8f 454 }
3d1f044b
A
455 // Note: it is not safe to copy fields->formatter or fWarehouse directly because fields->formatter might have
456 // dangling pointers to fields inside fWarehouse. The safe thing is to re-construct fields->formatter from
457 // the property bag, despite being somewhat slower.
458 fields = new DecimalFormatFields();
459 if (fields == nullptr) {
460 return; // no way to report an error.
461 }
462 UErrorCode status = U_ZERO_ERROR;
463 fields->formatter.adoptInsteadAndCheckErrorCode(new LocalizedNumberFormatter(), status);
464 fields->properties.adoptInsteadAndCheckErrorCode(new DecimalFormatProperties(*source.fields->properties), status);
465 fields->symbols.adoptInsteadAndCheckErrorCode(new DecimalFormatSymbols(*source.fields->symbols), status);
466 fields->exportedProperties.adoptInsteadAndCheckErrorCode(new DecimalFormatProperties(), status);
467 // In order to simplify error handling logic in the various getters/setters/etc, we do not allow
468 // any partially populated DecimalFormatFields object. We must have a fully complete fields object
469 // or else we set it to nullptr.
470 if (fields->formatter.isNull() || fields->properties.isNull() || fields->exportedProperties.isNull() || fields->symbols.isNull()) {
471 delete fields;
472 fields = nullptr;
473 return;
b75a7d8f 474 }
3d1f044b
A
475 touch(status);
476}
b75a7d8f 477
3d1f044b
A
478DecimalFormat& DecimalFormat::operator=(const DecimalFormat& rhs) {
479 // guard against self-assignment
480 if (this == &rhs) {
481 return *this;
482 }
483 // Make sure both objects are valid.
484 if (fields == nullptr || rhs.fields == nullptr) {
485 return *this; // unfortunately, no way to report an error.
486 }
487 *fields->properties = *rhs.fields->properties;
488 fields->exportedProperties->clear();
489 UErrorCode status = U_ZERO_ERROR;
490 LocalPointer<DecimalFormatSymbols> dfs(new DecimalFormatSymbols(*rhs.fields->symbols), status);
491 if (U_FAILURE(status)) {
492 // We failed to allocate DecimalFormatSymbols, release fields and its members.
493 // We must have a fully complete fields object, we cannot have partially populated members.
494 delete fields;
495 fields = nullptr;
496 return *this;
497 }
498 fields->symbols.adoptInstead(dfs.orphan());
499 touch(status);
374ca955 500
3d1f044b
A
501 return *this;
502}
729e4ab9 503
3d1f044b
A
504DecimalFormat::~DecimalFormat() {
505 if (fields == nullptr) { return; }
51004dcb 506
3d1f044b
A
507 delete fields->atomicParser.exchange(nullptr);
508 delete fields->atomicCurrencyParser.exchange(nullptr);
509 delete fields;
510}
57a6839d 511
3d1f044b
A
512Format* DecimalFormat::clone() const {
513 // can only clone valid objects.
514 if (fields == nullptr) {
515 return nullptr;
729e4ab9 516 }
3d1f044b
A
517 LocalPointer<DecimalFormat> df(new DecimalFormat(*this));
518 if (df.isValid() && df->fields != nullptr) {
519 return df.orphan();
729e4ab9 520 }
3d1f044b
A
521 return nullptr;
522}
729e4ab9 523
3d1f044b
A
524UBool DecimalFormat::operator==(const Format& other) const {
525 auto* otherDF = dynamic_cast<const DecimalFormat*>(&other);
526 if (otherDF == nullptr) {
527 return false;
51004dcb 528 }
3d1f044b
A
529 // If either object is in an invalid state, prevent dereferencing nullptr below.
530 // Additionally, invalid objects should not be considered equal to anything.
531 if (fields == nullptr || otherDF->fields == nullptr) {
532 return false;
533 }
534 return *fields->properties == *otherDF->fields->properties && *fields->symbols == *otherDF->fields->symbols;
535}
51004dcb 536
3d1f044b
A
537UnicodeString& DecimalFormat::format(double number, UnicodeString& appendTo, FieldPosition& pos) const {
538 if (fields == nullptr) {
539 appendTo.setToBogus();
540 return appendTo;
b75a7d8f 541 }
3d1f044b
A
542 if (pos.getField() == FieldPosition::DONT_CARE && fastFormatDouble(number, appendTo)) {
543 return appendTo;
b75a7d8f 544 }
3d1f044b
A
545 UErrorCode localStatus = U_ZERO_ERROR;
546 FormattedNumber output = fields->formatter->formatDouble(number, localStatus);
547 fieldPositionHelper(output, pos, appendTo.length(), localStatus);
548 auto appendable = UnicodeStringAppendable(appendTo);
549 output.appendTo(appendable, localStatus);
550 return appendTo;
551}
b75a7d8f 552
3d1f044b
A
553UnicodeString& DecimalFormat::format(double number, UnicodeString& appendTo, FieldPosition& pos,
554 UErrorCode& status) const {
555 if (U_FAILURE(status)) {
556 return appendTo; // don't overwrite status if it's already a failure.
b75a7d8f 557 }
3d1f044b
A
558 if (fields == nullptr) {
559 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
560 status = U_MEMORY_ALLOCATION_ERROR;
561 appendTo.setToBogus();
562 return appendTo;
46f4442e 563 }
3d1f044b
A
564 if (pos.getField() == FieldPosition::DONT_CARE && fastFormatDouble(number, appendTo)) {
565 return appendTo;
566 }
567 FormattedNumber output = fields->formatter->formatDouble(number, status);
568 fieldPositionHelper(output, pos, appendTo.length(), status);
569 auto appendable = UnicodeStringAppendable(appendTo);
570 output.appendTo(appendable, status);
571 return appendTo;
572}
729e4ab9 573
3d1f044b
A
574UnicodeString&
575DecimalFormat::format(double number, UnicodeString& appendTo, FieldPositionIterator* posIter,
576 UErrorCode& status) const {
577 if (U_FAILURE(status)) {
578 return appendTo; // don't overwrite status if it's already a failure.
579 }
580 if (fields == nullptr) {
581 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
582 status = U_MEMORY_ALLOCATION_ERROR;
583 appendTo.setToBogus();
584 return appendTo;
585 }
586 if (posIter == nullptr && fastFormatDouble(number, appendTo)) {
587 return appendTo;
588 }
589 FormattedNumber output = fields->formatter->formatDouble(number, status);
590 fieldPositionIteratorHelper(output, posIter, appendTo.length(), status);
591 auto appendable = UnicodeStringAppendable(appendTo);
592 output.appendTo(appendable, status);
593 return appendTo;
594}
46f4442e 595
3d1f044b
A
596UnicodeString& DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPosition& pos) const {
597 return format(static_cast<int64_t> (number), appendTo, pos);
598}
729e4ab9 599
3d1f044b
A
600UnicodeString& DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPosition& pos,
601 UErrorCode& status) const {
602 return format(static_cast<int64_t> (number), appendTo, pos, status);
603}
729e4ab9 604
3d1f044b
A
605UnicodeString&
606DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPositionIterator* posIter,
607 UErrorCode& status) const {
608 return format(static_cast<int64_t> (number), appendTo, posIter, status);
609}
b75a7d8f 610
3d1f044b
A
611UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& pos) const {
612 if (fields == nullptr) {
613 appendTo.setToBogus();
614 return appendTo;
b75a7d8f 615 }
3d1f044b
A
616 if (pos.getField() == FieldPosition::DONT_CARE && fastFormatInt64(number, appendTo)) {
617 return appendTo;
b75a7d8f 618 }
3d1f044b
A
619 UErrorCode localStatus = U_ZERO_ERROR;
620 FormattedNumber output = fields->formatter->formatInt(number, localStatus);
621 fieldPositionHelper(output, pos, appendTo.length(), localStatus);
622 auto appendable = UnicodeStringAppendable(appendTo);
623 output.appendTo(appendable, localStatus);
624 return appendTo;
625}
b75a7d8f 626
3d1f044b
A
627UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& pos,
628 UErrorCode& status) const {
629 if (U_FAILURE(status)) {
630 return appendTo; // don't overwrite status if it's already a failure.
b75a7d8f 631 }
3d1f044b
A
632 if (fields == nullptr) {
633 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
634 status = U_MEMORY_ALLOCATION_ERROR;
635 appendTo.setToBogus();
636 return appendTo;
b75a7d8f 637 }
3d1f044b
A
638 if (pos.getField() == FieldPosition::DONT_CARE && fastFormatInt64(number, appendTo)) {
639 return appendTo;
b75a7d8f 640 }
3d1f044b
A
641 FormattedNumber output = fields->formatter->formatInt(number, status);
642 fieldPositionHelper(output, pos, appendTo.length(), status);
643 auto appendable = UnicodeStringAppendable(appendTo);
644 output.appendTo(appendable, status);
645 return appendTo;
646}
b75a7d8f 647
3d1f044b
A
648UnicodeString&
649DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPositionIterator* posIter,
650 UErrorCode& status) const {
651 if (U_FAILURE(status)) {
652 return appendTo; // don't overwrite status if it's already a failure.
51004dcb 653 }
3d1f044b
A
654 if (fields == nullptr) {
655 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
656 status = U_MEMORY_ALLOCATION_ERROR;
657 appendTo.setToBogus();
658 return appendTo;
b75a7d8f 659 }
3d1f044b
A
660 if (posIter == nullptr && fastFormatInt64(number, appendTo)) {
661 return appendTo;
b331163b 662 }
3d1f044b
A
663 FormattedNumber output = fields->formatter->formatInt(number, status);
664 fieldPositionIteratorHelper(output, posIter, appendTo.length(), status);
665 auto appendable = UnicodeStringAppendable(appendTo);
666 output.appendTo(appendable, status);
667 return appendTo;
668}
b331163b 669
3d1f044b
A
670UnicodeString&
671DecimalFormat::format(StringPiece number, UnicodeString& appendTo, FieldPositionIterator* posIter,
672 UErrorCode& status) const {
673 if (U_FAILURE(status)) {
674 return appendTo; // don't overwrite status if it's already a failure.
b75a7d8f 675 }
3d1f044b
A
676 if (fields == nullptr) {
677 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
678 status = U_MEMORY_ALLOCATION_ERROR;
679 appendTo.setToBogus();
680 return appendTo;
681 }
682 FormattedNumber output = fields->formatter->formatDecimal(number, status);
683 fieldPositionIteratorHelper(output, posIter, appendTo.length(), status);
684 auto appendable = UnicodeStringAppendable(appendTo);
685 output.appendTo(appendable, status);
686 return appendTo;
687}
729e4ab9 688
3d1f044b
A
689UnicodeString& DecimalFormat::format(const DecimalQuantity& number, UnicodeString& appendTo,
690 FieldPositionIterator* posIter, UErrorCode& status) const {
691 if (U_FAILURE(status)) {
692 return appendTo; // don't overwrite status if it's already a failure.
b75a7d8f 693 }
3d1f044b
A
694 if (fields == nullptr) {
695 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
696 status = U_MEMORY_ALLOCATION_ERROR;
697 appendTo.setToBogus();
698 return appendTo;
46f4442e 699 }
3d1f044b
A
700 FormattedNumber output = fields->formatter->formatDecimalQuantity(number, status);
701 fieldPositionIteratorHelper(output, posIter, appendTo.length(), status);
702 auto appendable = UnicodeStringAppendable(appendTo);
703 output.appendTo(appendable, status);
704 return appendTo;
b75a7d8f
A
705}
706
3d1f044b
A
707UnicodeString&
708DecimalFormat::format(const DecimalQuantity& number, UnicodeString& appendTo, FieldPosition& pos,
709 UErrorCode& status) const {
710 if (U_FAILURE(status)) {
711 return appendTo; // don't overwrite status if it's already a failure.
712 }
713 if (fields == nullptr) {
714 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
715 status = U_MEMORY_ALLOCATION_ERROR;
716 appendTo.setToBogus();
717 return appendTo;
57a6839d 718 }
3d1f044b
A
719 FormattedNumber output = fields->formatter->formatDecimalQuantity(number, status);
720 fieldPositionHelper(output, pos, appendTo.length(), status);
721 auto appendable = UnicodeStringAppendable(appendTo);
722 output.appendTo(appendable, status);
723 return appendTo;
57a6839d
A
724}
725
3d1f044b
A
726void DecimalFormat::parse(const UnicodeString& text, Formattable& output,
727 ParsePosition& parsePosition) const {
728 if (fields == nullptr) {
729 return;
730 }
731 if (parsePosition.getIndex() < 0 || parsePosition.getIndex() >= text.length()) {
732 if (parsePosition.getIndex() == text.length()) {
733 // If there is nothing to parse, it is an error
734 parsePosition.setErrorIndex(parsePosition.getIndex());
57a6839d 735 }
3d1f044b 736 return;
57a6839d 737 }
729e4ab9 738
3d1f044b
A
739 ErrorCode status;
740 ParsedNumber result;
741 // Note: if this is a currency instance, currencies will be matched despite the fact that we are not in the
742 // parseCurrency method (backwards compatibility)
743 int32_t startIndex = parsePosition.getIndex();
744 const NumberParserImpl* parser = getParser(status);
57a6839d 745 if (U_FAILURE(status)) {
3d1f044b 746 return; // unfortunately no way to report back the error.
57a6839d 747 }
3d1f044b
A
748 parser->parse(text, startIndex, true, result, status);
749 if (U_FAILURE(status)) {
750 return; // unfortunately no way to report back the error.
751 }
752 // TODO: Do we need to check for fImpl->properties->parseAllInput (UCONFIG_HAVE_PARSEALLINPUT) here?
753 if (result.success()) {
754 parsePosition.setIndex(result.charEnd);
755 result.populateFormattable(output, parser->getParseFlags());
729e4ab9 756 } else {
3d1f044b
A
757 parsePosition.setErrorIndex(startIndex + result.charEnd);
758 }
759}
729e4ab9 760
3d1f044b
A
761CurrencyAmount* DecimalFormat::parseCurrency(const UnicodeString& text, ParsePosition& parsePosition) const {
762 if (fields == nullptr) {
763 return nullptr;
764 }
765 if (parsePosition.getIndex() < 0 || parsePosition.getIndex() >= text.length()) {
766 return nullptr;
767 }
729e4ab9 768
3d1f044b
A
769 ErrorCode status;
770 ParsedNumber result;
771 // Note: if this is a currency instance, currencies will be matched despite the fact that we are not in the
772 // parseCurrency method (backwards compatibility)
773 int32_t startIndex = parsePosition.getIndex();
774 const NumberParserImpl* parser = getCurrencyParser(status);
775 if (U_FAILURE(status)) {
776 return nullptr;
777 }
778 parser->parse(text, startIndex, true, result, status);
779 if (U_FAILURE(status)) {
780 return nullptr;
781 }
782 // TODO: Do we need to check for fImpl->properties->parseAllInput (UCONFIG_HAVE_PARSEALLINPUT) here?
783 if (result.success()) {
784 parsePosition.setIndex(result.charEnd);
785 Formattable formattable;
786 result.populateFormattable(formattable, parser->getParseFlags());
787 LocalPointer<CurrencyAmount> currencyAmount(
788 new CurrencyAmount(formattable, result.currencyCode, status), status);
789 if (U_FAILURE(status)) {
790 return nullptr;
4388f060 791 }
3d1f044b
A
792 return currencyAmount.orphan();
793 } else {
794 parsePosition.setErrorIndex(startIndex + result.charEnd);
795 return nullptr;
729e4ab9 796 }
b75a7d8f
A
797}
798
3d1f044b
A
799const DecimalFormatSymbols* DecimalFormat::getDecimalFormatSymbols(void) const {
800 if (fields == nullptr) {
801 return nullptr;
802 }
803 return fields->symbols.getAlias();
b75a7d8f
A
804}
805
3d1f044b
A
806void DecimalFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt) {
807 if (symbolsToAdopt == nullptr) {
808 return; // do not allow caller to set fields->symbols to NULL
809 }
810 // we must take ownership of symbolsToAdopt, even in a failure case.
811 LocalPointer<DecimalFormatSymbols> dfs(symbolsToAdopt);
812 if (fields == nullptr) {
813 return;
b75a7d8f 814 }
3d1f044b
A
815 fields->symbols.adoptInstead(dfs.orphan());
816 touchNoError();
b75a7d8f
A
817}
818
3d1f044b
A
819void DecimalFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols) {
820 if (fields == nullptr) {
821 return;
57a6839d 822 }
3d1f044b
A
823 UErrorCode status = U_ZERO_ERROR;
824 LocalPointer<DecimalFormatSymbols> dfs(new DecimalFormatSymbols(symbols), status);
825 if (U_FAILURE(status)) {
826 // We failed to allocate DecimalFormatSymbols, release fields and its members.
827 // We must have a fully complete fields object, we cannot have partially populated members.
828 delete fields;
829 fields = nullptr;
830 return;
831 }
832 fields->symbols.adoptInstead(dfs.orphan());
833 touchNoError();
57a6839d
A
834}
835
3d1f044b
A
836const CurrencyPluralInfo* DecimalFormat::getCurrencyPluralInfo(void) const {
837 if (fields == nullptr) {
838 return nullptr;
839 }
840 return fields->properties->currencyPluralInfo.fPtr.getAlias();
841}
b75a7d8f 842
3d1f044b
A
843void DecimalFormat::adoptCurrencyPluralInfo(CurrencyPluralInfo* toAdopt) {
844 // TODO: should we guard against nullptr input, like in adoptDecimalFormatSymbols?
845 // we must take ownership of toAdopt, even in a failure case.
846 LocalPointer<CurrencyPluralInfo> cpi(toAdopt);
847 if (fields == nullptr) {
848 return;
b75a7d8f 849 }
3d1f044b
A
850 fields->properties->currencyPluralInfo.fPtr.adoptInstead(cpi.orphan());
851 touchNoError();
852}
853
854void DecimalFormat::setCurrencyPluralInfo(const CurrencyPluralInfo& info) {
855 if (fields == nullptr) {
856 return;
857 }
858 if (fields->properties->currencyPluralInfo.fPtr.isNull()) {
859 // Note: clone() can fail with OOM error, but we have no way to report it. :(
860 fields->properties->currencyPluralInfo.fPtr.adoptInstead(info.clone());
4388f060 861 } else {
3d1f044b 862 *fields->properties->currencyPluralInfo.fPtr = info; // copy-assignment operator
4388f060 863 }
3d1f044b 864 touchNoError();
4388f060
A
865}
866
3d1f044b
A
867UnicodeString& DecimalFormat::getPositivePrefix(UnicodeString& result) const {
868 if (fields == nullptr) {
869 result.setToBogus();
870 return result;
871 }
872 UErrorCode status = U_ZERO_ERROR;
873 fields->formatter->getAffixImpl(true, false, result, status);
874 if (U_FAILURE(status)) { result.setToBogus(); }
875 return result;
b75a7d8f
A
876}
877
3d1f044b
A
878void DecimalFormat::setPositivePrefix(const UnicodeString& newValue) {
879 if (fields == nullptr) {
880 return;
374ca955 881 }
3d1f044b
A
882 if (newValue == fields->properties->positivePrefix) { return; }
883 fields->properties->positivePrefix = newValue;
884 touchNoError();
b75a7d8f 885}
b75a7d8f 886
3d1f044b
A
887UnicodeString& DecimalFormat::getNegativePrefix(UnicodeString& result) const {
888 if (fields == nullptr) {
889 result.setToBogus();
890 return result;
891 }
892 UErrorCode status = U_ZERO_ERROR;
893 fields->formatter->getAffixImpl(true, true, result, status);
894 if (U_FAILURE(status)) { result.setToBogus(); }
895 return result;
b75a7d8f 896}
729e4ab9 897
3d1f044b
A
898void DecimalFormat::setNegativePrefix(const UnicodeString& newValue) {
899 if (fields == nullptr) {
900 return;
901 }
902 if (newValue == fields->properties->negativePrefix) { return; }
903 fields->properties->negativePrefix = newValue;
904 touchNoError();
729e4ab9
A
905}
906
3d1f044b
A
907UnicodeString& DecimalFormat::getPositiveSuffix(UnicodeString& result) const {
908 if (fields == nullptr) {
909 result.setToBogus();
910 return result;
729e4ab9 911 }
3d1f044b
A
912 UErrorCode status = U_ZERO_ERROR;
913 fields->formatter->getAffixImpl(false, false, result, status);
914 if (U_FAILURE(status)) { result.setToBogus(); }
915 return result;
729e4ab9
A
916}
917
3d1f044b
A
918void DecimalFormat::setPositiveSuffix(const UnicodeString& newValue) {
919 if (fields == nullptr) {
920 return;
921 }
922 if (newValue == fields->properties->positiveSuffix) { return; }
923 fields->properties->positiveSuffix = newValue;
924 touchNoError();
b75a7d8f
A
925}
926
3d1f044b
A
927UnicodeString& DecimalFormat::getNegativeSuffix(UnicodeString& result) const {
928 if (fields == nullptr) {
929 result.setToBogus();
930 return result;
931 }
932 UErrorCode status = U_ZERO_ERROR;
933 fields->formatter->getAffixImpl(false, true, result, status);
934 if (U_FAILURE(status)) { result.setToBogus(); }
935 return result;
b75a7d8f 936}
729e4ab9 937
3d1f044b
A
938void DecimalFormat::setNegativeSuffix(const UnicodeString& newValue) {
939 if (fields == nullptr) {
940 return;
941 }
942 if (newValue == fields->properties->negativeSuffix) { return; }
943 fields->properties->negativeSuffix = newValue;
944 touchNoError();
b75a7d8f
A
945}
946
3d1f044b
A
947UBool DecimalFormat::isSignAlwaysShown() const {
948 // Not much we can do to report an error.
949 if (fields == nullptr) {
950 return DecimalFormatProperties::getDefault().signAlwaysShown;
951 }
952 return fields->properties->signAlwaysShown;
b75a7d8f
A
953}
954
3d1f044b
A
955void DecimalFormat::setSignAlwaysShown(UBool value) {
956 if (fields == nullptr) { return; }
957 if (UBOOL_TO_BOOL(value) == fields->properties->signAlwaysShown) { return; }
958 fields->properties->signAlwaysShown = value;
959 touchNoError();
960}
b75a7d8f 961
3d1f044b
A
962int32_t DecimalFormat::getMultiplier(void) const {
963 const DecimalFormatProperties *dfp;
964 // Not much we can do to report an error.
965 if (fields == nullptr) {
966 // Fallback to using the default instance of DecimalFormatProperties.
967 dfp = &(DecimalFormatProperties::getDefault());
968 } else {
969 dfp = fields->properties.getAlias();
970 }
971 if (dfp->multiplier != 1) {
972 return dfp->multiplier;
973 } else if (dfp->magnitudeMultiplier != 0) {
974 return static_cast<int32_t>(uprv_pow10(dfp->magnitudeMultiplier));
975 } else {
976 return 1;
977 }
b75a7d8f
A
978}
979
3d1f044b
A
980void DecimalFormat::setMultiplier(int32_t multiplier) {
981 if (fields == nullptr) {
982 return;
983 }
984 if (multiplier == 0) {
985 multiplier = 1; // one being the benign default value for a multiplier.
986 }
b75a7d8f 987
3d1f044b
A
988 // Try to convert to a magnitude multiplier first
989 int delta = 0;
990 int value = multiplier;
991 while (value != 1) {
992 delta++;
993 int temp = value / 10;
994 if (temp * 10 != value) {
995 delta = -1;
996 break;
997 }
998 value = temp;
999 }
1000 if (delta != -1) {
1001 fields->properties->magnitudeMultiplier = delta;
1002 fields->properties->multiplier = 1;
1003 } else {
1004 fields->properties->magnitudeMultiplier = 0;
1005 fields->properties->multiplier = multiplier;
1006 }
1007 touchNoError();
b75a7d8f
A
1008}
1009
3d1f044b
A
1010int32_t DecimalFormat::getMultiplierScale() const {
1011 // Not much we can do to report an error.
1012 if (fields == nullptr) {
1013 // Fallback to using the default instance of DecimalFormatProperties.
1014 return DecimalFormatProperties::getDefault().multiplierScale;
1015 }
1016 return fields->properties->multiplierScale;
b75a7d8f
A
1017}
1018
3d1f044b
A
1019void DecimalFormat::setMultiplierScale(int32_t newValue) {
1020 if (fields == nullptr) { return; }
1021 if (newValue == fields->properties->multiplierScale) { return; }
1022 fields->properties->multiplierScale = newValue;
1023 touchNoError();
1024}
b75a7d8f 1025
3d1f044b
A
1026double DecimalFormat::getRoundingIncrement(void) const {
1027 // Not much we can do to report an error.
1028 if (fields == nullptr) {
1029 // Fallback to using the default instance of DecimalFormatProperties.
1030 return DecimalFormatProperties::getDefault().roundingIncrement;
1031 }
1032 return fields->exportedProperties->roundingIncrement;
b75a7d8f
A
1033}
1034
3d1f044b
A
1035void DecimalFormat::setRoundingIncrement(double newValue) {
1036 if (fields == nullptr) { return; }
1037 if (newValue == fields->properties->roundingIncrement) { return; }
1038 fields->properties->roundingIncrement = newValue;
1039 touchNoError();
1040}
b75a7d8f 1041
3d1f044b
A
1042ERoundingMode DecimalFormat::getRoundingMode(void) const {
1043 // Not much we can do to report an error.
1044 if (fields == nullptr) {
1045 // Fallback to using the default instance of DecimalFormatProperties.
1046 return static_cast<ERoundingMode>(DecimalFormatProperties::getDefault().roundingMode.getNoError());
1047 }
1048 // UNumberFormatRoundingMode and ERoundingMode have the same values.
1049 return static_cast<ERoundingMode>(fields->exportedProperties->roundingMode.getNoError());
b75a7d8f
A
1050}
1051
3d1f044b
A
1052void DecimalFormat::setRoundingMode(ERoundingMode roundingMode) {
1053 if (fields == nullptr) { return; }
1054 auto uRoundingMode = static_cast<UNumberFormatRoundingMode>(roundingMode);
1055 if (!fields->properties->roundingMode.isNull() && uRoundingMode == fields->properties->roundingMode.getNoError()) {
1056 return;
1057 }
1058 NumberFormat::setMaximumIntegerDigits(roundingMode); // to set field for compatibility
1059 fields->properties->roundingMode = uRoundingMode;
1060 touchNoError();
1061}
b75a7d8f 1062
3d1f044b
A
1063int32_t DecimalFormat::getFormatWidth(void) const {
1064 // Not much we can do to report an error.
1065 if (fields == nullptr) {
1066 // Fallback to using the default instance of DecimalFormatProperties.
1067 return DecimalFormatProperties::getDefault().formatWidth;
1068 }
1069 return fields->properties->formatWidth;
b75a7d8f
A
1070}
1071
b75a7d8f 1072void DecimalFormat::setFormatWidth(int32_t width) {
3d1f044b
A
1073 if (fields == nullptr) { return; }
1074 if (width == fields->properties->formatWidth) { return; }
1075 fields->properties->formatWidth = width;
1076 touchNoError();
b75a7d8f
A
1077}
1078
374ca955 1079UnicodeString DecimalFormat::getPadCharacterString() const {
3d1f044b
A
1080 if (fields == nullptr || fields->properties->padString.isBogus()) {
1081 // Readonly-alias the static string kFallbackPaddingString
1082 return {TRUE, kFallbackPaddingString, -1};
1083 } else {
1084 return fields->properties->padString;
1085 }
b75a7d8f
A
1086}
1087
3d1f044b
A
1088void DecimalFormat::setPadCharacter(const UnicodeString& padChar) {
1089 if (fields == nullptr) { return; }
1090 if (padChar == fields->properties->padString) { return; }
b75a7d8f 1091 if (padChar.length() > 0) {
3d1f044b
A
1092 fields->properties->padString = UnicodeString(padChar.char32At(0));
1093 } else {
1094 fields->properties->padString.setToBogus();
1095 }
1096 touchNoError();
b75a7d8f
A
1097}
1098
3d1f044b
A
1099EPadPosition DecimalFormat::getPadPosition(void) const {
1100 if (fields == nullptr || fields->properties->padPosition.isNull()) {
1101 return EPadPosition::kPadBeforePrefix;
1102 } else {
1103 // UNumberFormatPadPosition and EPadPosition have the same values.
1104 return static_cast<EPadPosition>(fields->properties->padPosition.getNoError());
1105 }
b75a7d8f 1106}
729e4ab9 1107
3d1f044b
A
1108void DecimalFormat::setPadPosition(EPadPosition padPos) {
1109 if (fields == nullptr) { return; }
1110 auto uPadPos = static_cast<UNumberFormatPadPosition>(padPos);
1111 if (!fields->properties->padPosition.isNull() && uPadPos == fields->properties->padPosition.getNoError()) {
1112 return;
1113 }
1114 fields->properties->padPosition = uPadPos;
1115 touchNoError();
b75a7d8f
A
1116}
1117
3d1f044b
A
1118UBool DecimalFormat::isScientificNotation(void) const {
1119 // Not much we can do to report an error.
1120 if (fields == nullptr) {
1121 // Fallback to using the default instance of DecimalFormatProperties.
1122 return (DecimalFormatProperties::getDefault().minimumExponentDigits != -1);
1123 }
1124 return (fields->properties->minimumExponentDigits != -1);
b75a7d8f
A
1125}
1126
3d1f044b
A
1127void DecimalFormat::setScientificNotation(UBool useScientific) {
1128 if (fields == nullptr) { return; }
1129 int32_t minExp = useScientific ? 1 : -1;
1130 if (fields->properties->minimumExponentDigits == minExp) { return; }
1131 if (useScientific) {
1132 fields->properties->minimumExponentDigits = 1;
1133 } else {
1134 fields->properties->minimumExponentDigits = -1;
1135 }
1136 touchNoError();
1137}
b75a7d8f 1138
3d1f044b
A
1139int8_t DecimalFormat::getMinimumExponentDigits(void) const {
1140 // Not much we can do to report an error.
1141 if (fields == nullptr) {
1142 // Fallback to using the default instance of DecimalFormatProperties.
1143 return static_cast<int8_t>(DecimalFormatProperties::getDefault().minimumExponentDigits);
1144 }
1145 return static_cast<int8_t>(fields->properties->minimumExponentDigits);
2ca993e8
A
1146}
1147
3d1f044b
A
1148void DecimalFormat::setMinimumExponentDigits(int8_t minExpDig) {
1149 if (fields == nullptr) { return; }
1150 if (minExpDig == fields->properties->minimumExponentDigits) { return; }
1151 fields->properties->minimumExponentDigits = minExpDig;
1152 touchNoError();
1153}
2ca993e8 1154
3d1f044b
A
1155UBool DecimalFormat::isExponentSignAlwaysShown(void) const {
1156 // Not much we can do to report an error.
1157 if (fields == nullptr) {
1158 // Fallback to using the default instance of DecimalFormatProperties.
1159 return DecimalFormatProperties::getDefault().exponentSignAlwaysShown;
1160 }
1161 return fields->properties->exponentSignAlwaysShown;
2ca993e8
A
1162}
1163
3d1f044b
A
1164void DecimalFormat::setExponentSignAlwaysShown(UBool expSignAlways) {
1165 if (fields == nullptr) { return; }
1166 if (UBOOL_TO_BOOL(expSignAlways) == fields->properties->exponentSignAlwaysShown) { return; }
1167 fields->properties->exponentSignAlwaysShown = expSignAlways;
1168 touchNoError();
1169}
1170
1171int32_t DecimalFormat::getGroupingSize(void) const {
1172 int32_t groupingSize;
1173 // Not much we can do to report an error.
1174 if (fields == nullptr) {
1175 // Fallback to using the default instance of DecimalFormatProperties.
1176 groupingSize = DecimalFormatProperties::getDefault().groupingSize;
1177 } else {
1178 groupingSize = fields->properties->groupingSize;
1179 }
1180 if (groupingSize < 0) {
1181 return 0;
1182 }
1183 return groupingSize;
1184}
2ca993e8 1185
3d1f044b
A
1186void DecimalFormat::setGroupingSize(int32_t newValue) {
1187 if (fields == nullptr) { return; }
1188 if (newValue == fields->properties->groupingSize) { return; }
1189 fields->properties->groupingSize = newValue;
1190 touchNoError();
b75a7d8f
A
1191}
1192
3d1f044b
A
1193int32_t DecimalFormat::getSecondaryGroupingSize(void) const {
1194 int32_t grouping2;
1195 // Not much we can do to report an error.
1196 if (fields == nullptr) {
1197 // Fallback to using the default instance of DecimalFormatProperties.
1198 grouping2 = DecimalFormatProperties::getDefault().secondaryGroupingSize;
1199 } else {
1200 grouping2 = fields->properties->secondaryGroupingSize;
1201 }
1202 if (grouping2 < 0) {
1203 return 0;
1204 }
1205 return grouping2;
1206}
b75a7d8f 1207
3d1f044b
A
1208void DecimalFormat::setSecondaryGroupingSize(int32_t newValue) {
1209 if (fields == nullptr) { return; }
1210 if (newValue == fields->properties->secondaryGroupingSize) { return; }
1211 fields->properties->secondaryGroupingSize = newValue;
1212 touchNoError();
b75a7d8f
A
1213}
1214
3d1f044b
A
1215int32_t DecimalFormat::getMinimumGroupingDigits() const {
1216 // Not much we can do to report an error.
1217 if (fields == nullptr) {
1218 // Fallback to using the default instance of DecimalFormatProperties.
1219 return DecimalFormatProperties::getDefault().minimumGroupingDigits;
1220 }
1221 return fields->properties->minimumGroupingDigits;
1222}
b75a7d8f 1223
3d1f044b
A
1224void DecimalFormat::setMinimumGroupingDigits(int32_t newValue) {
1225 if (fields == nullptr) { return; }
1226 if (newValue == fields->properties->minimumGroupingDigits) { return; }
1227 fields->properties->minimumGroupingDigits = newValue;
1228 touchNoError();
b75a7d8f
A
1229}
1230
3d1f044b
A
1231UBool DecimalFormat::isDecimalSeparatorAlwaysShown(void) const {
1232 // Not much we can do to report an error.
1233 if (fields == nullptr) {
1234 // Fallback to using the default instance of DecimalFormatProperties.
1235 return DecimalFormatProperties::getDefault().decimalSeparatorAlwaysShown;
1236 }
1237 return fields->properties->decimalSeparatorAlwaysShown;
b331163b
A
1238}
1239
3d1f044b
A
1240void DecimalFormat::setDecimalSeparatorAlwaysShown(UBool newValue) {
1241 if (fields == nullptr) { return; }
1242 if (UBOOL_TO_BOOL(newValue) == fields->properties->decimalSeparatorAlwaysShown) { return; }
1243 fields->properties->decimalSeparatorAlwaysShown = newValue;
1244 touchNoError();
b331163b
A
1245}
1246
3d1f044b
A
1247UBool DecimalFormat::isDecimalPatternMatchRequired(void) const {
1248 // Not much we can do to report an error.
1249 if (fields == nullptr) {
1250 // Fallback to using the default instance of DecimalFormatProperties.
1251 return DecimalFormatProperties::getDefault().decimalPatternMatchRequired;
1252 }
1253 return fields->properties->decimalPatternMatchRequired;
1254}
b331163b 1255
3d1f044b
A
1256void DecimalFormat::setDecimalPatternMatchRequired(UBool newValue) {
1257 if (fields == nullptr) { return; }
1258 if (UBOOL_TO_BOOL(newValue) == fields->properties->decimalPatternMatchRequired) { return; }
1259 fields->properties->decimalPatternMatchRequired = newValue;
1260 touchNoError();
1261}
b75a7d8f 1262
3d1f044b
A
1263UBool DecimalFormat::isParseNoExponent() const {
1264 // Not much we can do to report an error.
1265 if (fields == nullptr) {
1266 // Fallback to using the default instance of DecimalFormatProperties.
1267 return DecimalFormatProperties::getDefault().parseNoExponent;
1268 }
1269 return fields->properties->parseNoExponent;
b75a7d8f
A
1270}
1271
3d1f044b
A
1272void DecimalFormat::setParseNoExponent(UBool value) {
1273 if (fields == nullptr) { return; }
1274 if (UBOOL_TO_BOOL(value) == fields->properties->parseNoExponent) { return; }
1275 fields->properties->parseNoExponent = value;
1276 touchNoError();
1277}
b75a7d8f 1278
3d1f044b
A
1279UBool DecimalFormat::isParseCaseSensitive() const {
1280 // Not much we can do to report an error.
1281 if (fields == nullptr) {
1282 // Fallback to using the default instance of DecimalFormatProperties.
1283 return DecimalFormatProperties::getDefault().parseCaseSensitive;
1284 }
1285 return fields->properties->parseCaseSensitive;
b75a7d8f
A
1286}
1287
3d1f044b
A
1288void DecimalFormat::setParseCaseSensitive(UBool value) {
1289 if (fields == nullptr) { return; }
1290 if (UBOOL_TO_BOOL(value) == fields->properties->parseCaseSensitive) { return; }
1291 fields->properties->parseCaseSensitive = value;
1292 touchNoError();
1293}
b75a7d8f 1294
3d1f044b
A
1295UBool DecimalFormat::isFormatFailIfMoreThanMaxDigits() const {
1296 // Not much we can do to report an error.
1297 if (fields == nullptr) {
1298 // Fallback to using the default instance of DecimalFormatProperties.
1299 return DecimalFormatProperties::getDefault().formatFailIfMoreThanMaxDigits;
2ca993e8 1300 }
3d1f044b 1301 return fields->properties->formatFailIfMoreThanMaxDigits;
b75a7d8f
A
1302}
1303
3d1f044b
A
1304void DecimalFormat::setFormatFailIfMoreThanMaxDigits(UBool value) {
1305 if (fields == nullptr) { return; }
1306 if (UBOOL_TO_BOOL(value) == fields->properties->formatFailIfMoreThanMaxDigits) { return; }
1307 fields->properties->formatFailIfMoreThanMaxDigits = value;
1308 touchNoError();
1309}
b75a7d8f 1310
3d1f044b
A
1311UnicodeString& DecimalFormat::toPattern(UnicodeString& result) const {
1312 if (fields == nullptr) {
1313 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1314 result.setToBogus();
1315 return result;
1316 }
1317 // Pull some properties from exportedProperties and others from properties
1318 // to keep affix patterns intact. In particular, pull rounding properties
1319 // so that CurrencyUsage is reflected properly.
1320 // TODO: Consider putting this logic in number_patternstring.cpp instead.
1321 ErrorCode localStatus;
1322 DecimalFormatProperties tprops(*fields->properties);
1323 bool useCurrency = (
1324 !tprops.currency.isNull() ||
1325 !tprops.currencyPluralInfo.fPtr.isNull() ||
1326 !tprops.currencyUsage.isNull() ||
1327 AffixUtils::hasCurrencySymbols(tprops.positivePrefixPattern, localStatus) ||
1328 AffixUtils::hasCurrencySymbols(tprops.positiveSuffixPattern, localStatus) ||
1329 AffixUtils::hasCurrencySymbols(tprops.negativePrefixPattern, localStatus) ||
1330 AffixUtils::hasCurrencySymbols(tprops.negativeSuffixPattern, localStatus));
1331 if (useCurrency) {
1332 tprops.minimumFractionDigits = fields->exportedProperties->minimumFractionDigits;
1333 tprops.maximumFractionDigits = fields->exportedProperties->maximumFractionDigits;
1334 tprops.roundingIncrement = fields->exportedProperties->roundingIncrement;
2ca993e8 1335 }
3d1f044b
A
1336 result = PatternStringUtils::propertiesToPatternString(tprops, localStatus);
1337 return result;
b75a7d8f 1338}
b75a7d8f 1339
3d1f044b
A
1340UnicodeString& DecimalFormat::toLocalizedPattern(UnicodeString& result) const {
1341 if (fields == nullptr) {
1342 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1343 result.setToBogus();
1344 return result;
2ca993e8 1345 }
3d1f044b
A
1346 ErrorCode localStatus;
1347 result = toPattern(result);
1348 result = PatternStringUtils::convertLocalized(result, *fields->symbols, true, localStatus);
1349 return result;
b75a7d8f
A
1350}
1351
3d1f044b
A
1352void DecimalFormat::applyPattern(const UnicodeString& pattern, UParseError&, UErrorCode& status) {
1353 // TODO: What is parseError for?
1354 applyPattern(pattern, status);
1355}
b75a7d8f 1356
3d1f044b
A
1357void DecimalFormat::applyPattern(const UnicodeString& pattern, UErrorCode& status) {
1358 // don't overwrite status if it's already a failure.
1359 if (U_FAILURE(status)) { return; }
1360 if (fields == nullptr) {
1361 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1362 status = U_MEMORY_ALLOCATION_ERROR;
1363 return;
b75a7d8f 1364 }
3d1f044b
A
1365 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_NEVER, status);
1366 touch(status);
1367}
1368
1369void DecimalFormat::applyLocalizedPattern(const UnicodeString& localizedPattern, UParseError&,
1370 UErrorCode& status) {
1371 // TODO: What is parseError for?
1372 applyLocalizedPattern(localizedPattern, status);
b75a7d8f
A
1373}
1374
3d1f044b
A
1375void DecimalFormat::applyLocalizedPattern(const UnicodeString& localizedPattern, UErrorCode& status) {
1376 // don't overwrite status if it's already a failure.
1377 if (U_FAILURE(status)) { return; }
1378 if (fields == nullptr) {
1379 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1380 status = U_MEMORY_ALLOCATION_ERROR;
1381 return;
1382 }
1383 UnicodeString pattern = PatternStringUtils::convertLocalized(
1384 localizedPattern, *fields->symbols, false, status);
1385 applyPattern(pattern, status);
1386}
729e4ab9 1387
b75a7d8f 1388void DecimalFormat::setMaximumIntegerDigits(int32_t newValue) {
3d1f044b
A
1389 if (fields == nullptr) { return; }
1390 if (newValue == fields->properties->maximumIntegerDigits) { return; }
1391 // For backwards compatibility, conflicting min/max need to keep the most recent setting.
1392 int32_t min = fields->properties->minimumIntegerDigits;
1393 if (min >= 0 && min > newValue) {
1394 fields->properties->minimumIntegerDigits = newValue;
1395 }
1396 fields->properties->maximumIntegerDigits = newValue;
1397 touchNoError();
b75a7d8f
A
1398}
1399
b75a7d8f 1400void DecimalFormat::setMinimumIntegerDigits(int32_t newValue) {
3d1f044b
A
1401 if (fields == nullptr) { return; }
1402 if (newValue == fields->properties->minimumIntegerDigits) { return; }
1403 // For backwards compatibility, conflicting min/max need to keep the most recent setting.
1404 int32_t max = fields->properties->maximumIntegerDigits;
1405 if (max >= 0 && max < newValue) {
1406 fields->properties->maximumIntegerDigits = newValue;
1407 }
1408 fields->properties->minimumIntegerDigits = newValue;
1409 touchNoError();
b75a7d8f
A
1410}
1411
b75a7d8f 1412void DecimalFormat::setMaximumFractionDigits(int32_t newValue) {
3d1f044b
A
1413 if (fields == nullptr) { return; }
1414 if (newValue == fields->properties->maximumFractionDigits) { return; }
1415 // backward compatibility, limit to 340 <rdar://problem/50113359>
1416 if (newValue > 340) {
1417 newValue = 340;
1418 }
1419 // For backwards compatibility, conflicting min/max need to keep the most recent setting.
1420 int32_t min = fields->properties->minimumFractionDigits;
1421 if (min >= 0 && min > newValue) {
1422 fields->properties->minimumFractionDigits = newValue;
1423 }
1424 fields->properties->maximumFractionDigits = newValue;
1425 touchNoError();
b75a7d8f
A
1426}
1427
b75a7d8f 1428void DecimalFormat::setMinimumFractionDigits(int32_t newValue) {
3d1f044b
A
1429 if (fields == nullptr) { return; }
1430 if (newValue == fields->properties->minimumFractionDigits) { return; }
1431 // For backwards compatibility, conflicting min/max need to keep the most recent setting.
1432 int32_t max = fields->properties->maximumFractionDigits;
1433 if (max >= 0 && max < newValue) {
1434 fields->properties->maximumFractionDigits = newValue;
1435 }
1436 fields->properties->minimumFractionDigits = newValue;
1437 touchNoError();
b75a7d8f
A
1438}
1439
374ca955 1440int32_t DecimalFormat::getMinimumSignificantDigits() const {
3d1f044b
A
1441 // Not much we can do to report an error.
1442 if (fields == nullptr) {
1443 // Fallback to using the default instance of DecimalFormatProperties.
1444 return DecimalFormatProperties::getDefault().minimumSignificantDigits;
1445 }
1446 return fields->exportedProperties->minimumSignificantDigits;
374ca955
A
1447}
1448
1449int32_t DecimalFormat::getMaximumSignificantDigits() const {
3d1f044b
A
1450 // Not much we can do to report an error.
1451 if (fields == nullptr) {
1452 // Fallback to using the default instance of DecimalFormatProperties.
1453 return DecimalFormatProperties::getDefault().maximumSignificantDigits;
1454 }
1455 return fields->exportedProperties->maximumSignificantDigits;
374ca955
A
1456}
1457
3d1f044b
A
1458void DecimalFormat::setMinimumSignificantDigits(int32_t value) {
1459 if (fields == nullptr) { return; }
1460 if (value == fields->properties->minimumSignificantDigits) { return; }
1461 int32_t max = fields->properties->maximumSignificantDigits;
1462 if (max >= 0 && max < value) {
1463 fields->properties->maximumSignificantDigits = value;
374ca955 1464 }
3d1f044b
A
1465 fields->properties->minimumSignificantDigits = value;
1466 touchNoError();
374ca955
A
1467}
1468
3d1f044b
A
1469void DecimalFormat::setMaximumSignificantDigits(int32_t value) {
1470 if (fields == nullptr) { return; }
1471 if (value == fields->properties->maximumSignificantDigits) { return; }
1472 int32_t min = fields->properties->minimumSignificantDigits;
1473 if (min >= 0 && min > value) {
1474 fields->properties->minimumSignificantDigits = value;
374ca955 1475 }
3d1f044b
A
1476 fields->properties->maximumSignificantDigits = value;
1477 touchNoError();
374ca955
A
1478}
1479
1480UBool DecimalFormat::areSignificantDigitsUsed() const {
3d1f044b
A
1481 const DecimalFormatProperties* dfp;
1482 // Not much we can do to report an error.
1483 if (fields == nullptr) {
1484 // Fallback to using the default instance of DecimalFormatProperties.
1485 dfp = &(DecimalFormatProperties::getDefault());
1486 } else {
1487 dfp = fields->properties.getAlias();
1488 }
1489 return dfp->minimumSignificantDigits != -1 || dfp->maximumSignificantDigits != -1;
374ca955
A
1490}
1491
1492void DecimalFormat::setSignificantDigitsUsed(UBool useSignificantDigits) {
3d1f044b
A
1493 if (fields == nullptr) { return; }
1494
1495 // These are the default values from the old implementation.
1496 if (useSignificantDigits) {
1497 if (fields->properties->minimumSignificantDigits != -1 ||
1498 fields->properties->maximumSignificantDigits != -1) {
1499 return;
1500 }
1501 } else {
1502 if (fields->properties->minimumSignificantDigits == -1 &&
1503 fields->properties->maximumSignificantDigits == -1) {
1504 return;
1505 }
1506 }
1507 int32_t minSig = useSignificantDigits ? 1 : -1;
1508 int32_t maxSig = useSignificantDigits ? 6 : -1;
1509 fields->properties->minimumSignificantDigits = minSig;
1510 fields->properties->maximumSignificantDigits = maxSig;
1511 touchNoError();
729e4ab9
A
1512}
1513
3d1f044b
A
1514void DecimalFormat::setCurrency(const char16_t* theCurrency, UErrorCode& ec) {
1515 // don't overwrite ec if it's already a failure.
1516 if (U_FAILURE(ec)) { return; }
1517 if (fields == nullptr) {
1518 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1519 ec = U_MEMORY_ALLOCATION_ERROR;
1520 return;
1521 }
1522 // <rdar://problem/49544607> Restore behavior in which empty currency sets locale default
1523 UChar localeCurr[4];
1524 if (theCurrency==nullptr || theCurrency[0]==0) {
1525 UErrorCode getCurrStatus = U_ZERO_ERROR;
1526 int32_t currLen = ucurr_forLocale(fields->symbols->getLocale().getName(), localeCurr, UPRV_LENGTHOF(localeCurr), &getCurrStatus);
1527 if (U_SUCCESS(getCurrStatus) && currLen==3) {
1528 localeCurr[3] = 0;
1529 theCurrency = localeCurr;
1530 }
1531 }
1532 //
1533 CurrencyUnit currencyUnit(theCurrency, ec);
1534 if (U_FAILURE(ec)) { return; }
1535 if (!fields->properties->currency.isNull() && fields->properties->currency.getNoError() == currencyUnit) {
1536 return;
1537 }
1538 NumberFormat::setCurrency(theCurrency, ec); // to set field for compatibility
1539 fields->properties->currency = currencyUnit;
1540 // TODO: Set values in fields->symbols, too?
1541 touchNoError();
b75a7d8f
A
1542}
1543
3d1f044b
A
1544void DecimalFormat::setCurrency(const char16_t* theCurrency) {
1545 ErrorCode localStatus;
1546 setCurrency(theCurrency, localStatus);
1547}
1548
1549void DecimalFormat::setCurrencyUsage(UCurrencyUsage newUsage, UErrorCode* ec) {
1550 // don't overwrite ec if it's already a failure.
1551 if (U_FAILURE(*ec)) { return; }
1552 if (fields == nullptr) {
1553 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1554 *ec = U_MEMORY_ALLOCATION_ERROR;
1555 return;
1556 }
1557 if (!fields->properties->currencyUsage.isNull() && newUsage == fields->properties->currencyUsage.getNoError()) {
0f5d89e8
A
1558 return;
1559 }
3d1f044b
A
1560 fields->properties->currencyUsage = newUsage;
1561 touch(*ec);
b331163b
A
1562}
1563
1564UCurrencyUsage DecimalFormat::getCurrencyUsage() const {
3d1f044b
A
1565 // CurrencyUsage is not exported, so we have to get it from the input property bag.
1566 // TODO: Should we export CurrencyUsage instead?
1567 if (fields == nullptr || fields->properties->currencyUsage.isNull()) {
1568 return UCURR_USAGE_STANDARD;
1569 }
1570 return fields->properties->currencyUsage.getNoError();
b331163b
A
1571}
1572
3d1f044b
A
1573void
1574DecimalFormat::formatToDecimalQuantity(double number, DecimalQuantity& output, UErrorCode& status) const {
1575 // don't overwrite status if it's already a failure.
1576 if (U_FAILURE(status)) { return; }
1577 if (fields == nullptr) {
1578 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1579 status = U_MEMORY_ALLOCATION_ERROR;
1580 return;
1581 }
1582 fields->formatter->formatDouble(number, status).getDecimalQuantity(output, status);
374ca955
A
1583}
1584
3d1f044b
A
1585void DecimalFormat::formatToDecimalQuantity(const Formattable& number, DecimalQuantity& output,
1586 UErrorCode& status) const {
1587 // don't overwrite status if it's already a failure.
1588 if (U_FAILURE(status)) { return; }
1589 if (fields == nullptr) {
1590 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1591 status = U_MEMORY_ALLOCATION_ERROR;
46f4442e
A
1592 return;
1593 }
3d1f044b
A
1594 UFormattedNumberData obj;
1595 number.populateDecimalQuantity(obj.quantity, status);
1596 fields->formatter->formatImpl(&obj, status);
1597 output = std::move(obj.quantity);
1598}
1599
1600const number::LocalizedNumberFormatter* DecimalFormat::toNumberFormatter(UErrorCode& status) const {
1601 // We sometimes need to return nullptr here (see ICU-20380)
1602 if (U_FAILURE(status)) { return nullptr; }
1603 if (fields == nullptr) {
1604 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1605 status = U_MEMORY_ALLOCATION_ERROR;
1606 return nullptr;
1607 }
1608 return &*fields->formatter;
1609}
1610
1611const number::LocalizedNumberFormatter& DecimalFormat::toNumberFormatter() const {
1612 UErrorCode localStatus = U_ZERO_ERROR;
1613 return *toNumberFormatter(localStatus);
1614}
1615
1616// Apple <rdar://problem/49955427>
1617void DecimalFormat::setDFSShallowCopy(UBool shallow) {
1618 if (fields != nullptr && fields->formatter != nullptr) {
1619 fields->formatter->setDFSShallowCopy(shallow);
374ca955 1620 }
374ca955
A
1621}
1622
3d1f044b
A
1623/** Rebuilds the formatter object from the property bag. */
1624void DecimalFormat::touch(UErrorCode& status) {
1625 if (U_FAILURE(status)) {
1626 return;
729e4ab9 1627 }
3d1f044b
A
1628 if (fields == nullptr) {
1629 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1630 // For regular construction, the caller should have checked the status variable for errors.
1631 // For copy construction, there is unfortunately nothing to report the error, so we need to guard against
1632 // this possible bad state here and set the status to an error.
729e4ab9 1633 status = U_MEMORY_ALLOCATION_ERROR;
3d1f044b
A
1634 return;
1635 }
1636
1637 // In C++, fields->symbols is the source of truth for the locale.
1638 Locale locale = fields->symbols->getLocale();
1639
1640 // Note: The formatter is relatively cheap to create, and we need it to populate fields->exportedProperties,
1641 // so automatically recompute it here. The parser is a bit more expensive and is not needed until the
1642 // parse method is called, so defer that until needed.
1643 // TODO: Only update the pieces that changed instead of re-computing the whole formatter?
1644
1645 // Since memory has already been allocated for the formatter, we can move assign a stack-allocated object
1646 // and don't need to call new. (Which is slower and could possibly fail).
1647 *fields->formatter = NumberPropertyMapper::create(
1648 *fields->properties, *fields->symbols, fields->warehouse, *fields->exportedProperties, status).locale(
1649 locale);
1650
1651 // Do this after fields->exportedProperties are set up
1652 setupFastFormat();
1653
1654 // Delete the parsers if they were made previously
1655 delete fields->atomicParser.exchange(nullptr);
1656 delete fields->atomicCurrencyParser.exchange(nullptr);
1657
1658 // In order for the getters to work, we need to populate some fields in NumberFormat.
1659 const UChar* newCurr = u"";
1660 CurrencyUnit currency = fields->exportedProperties->currency.get(status);
1661 if (U_SUCCESS(status)) {
1662 // currency.getISOCurrency() is an inline that just returns a pointer to currency's
1663 // internal field char16_t isoCode[4], cannot be NULL if currency is valid:
1664 newCurr = (const UChar*)currency.getISOCurrency();
1665 // NumberFormat::getCurrency() just returns a pointer to the superclass's
1666 // internal field char16_t fCurrency[4], cannot be NULL:
1667 const UChar* haveCurr = (const UChar*)NumberFormat::getCurrency();
1668 if (u_strcmp(newCurr,u"XXX")==0 && u_strcmp(haveCurr,u"XXX")!=0) { // <rdar://51985640>
1669 // We did not get here via DecimalFormat::setCurrency(u"XXX", ...)
1670 newCurr = u"";
1671 }
729e4ab9 1672 }
3d1f044b
A
1673 NumberFormat::setCurrency(newCurr, status);
1674 NumberFormat::setMaximumIntegerDigits(fields->exportedProperties->maximumIntegerDigits);
1675 NumberFormat::setMinimumIntegerDigits(fields->exportedProperties->minimumIntegerDigits);
1676 NumberFormat::setMaximumFractionDigits(fields->exportedProperties->maximumFractionDigits);
1677 NumberFormat::setMinimumFractionDigits(fields->exportedProperties->minimumFractionDigits);
1678 // fImpl->properties, not fields->exportedProperties, since this information comes from the pattern:
1679 NumberFormat::setGroupingUsed(fields->properties->groupingUsed);
1680}
1681
1682void DecimalFormat::touchNoError() {
1683 UErrorCode localStatus = U_ZERO_ERROR;
1684 touch(localStatus);
1685}
1686
1687void DecimalFormat::setPropertiesFromPattern(const UnicodeString& pattern, int32_t ignoreRounding,
1688 UErrorCode& status) {
1689 if (U_SUCCESS(status)) {
1690 // Cast workaround to get around putting the enum in the public header file
1691 auto actualIgnoreRounding = static_cast<IgnoreRounding>(ignoreRounding);
1692 PatternParser::parseToExistingProperties(pattern, *fields->properties, actualIgnoreRounding, status);
729e4ab9 1693 }
729e4ab9
A
1694}
1695
3d1f044b
A
1696const numparse::impl::NumberParserImpl* DecimalFormat::getParser(UErrorCode& status) const {
1697 // TODO: Move this into umutex.h? (similar logic also in numrange_fluent.cpp)
1698 // See ICU-20146
1699
1700 if (U_FAILURE(status)) {
1701 return nullptr;
1702 }
1703
1704 // First try to get the pre-computed parser
1705 auto* ptr = fields->atomicParser.load();
1706 if (ptr != nullptr) {
1707 return ptr;
1708 }
1709
1710 // Try computing the parser on our own
1711 auto* temp = NumberParserImpl::createParserFromProperties(*fields->properties, *fields->symbols, false, status);
1712 if (U_FAILURE(status)) {
1713 return nullptr;
1714 }
1715 if (temp == nullptr) {
1716 status = U_MEMORY_ALLOCATION_ERROR;
1717 return nullptr;
729e4ab9 1718 }
3d1f044b
A
1719
1720 // Note: ptr starts as nullptr; during compare_exchange,
1721 // it is set to what is actually stored in the atomic
1722 // if another thread beat us to computing the parser object.
1723 auto* nonConstThis = const_cast<DecimalFormat*>(this);
1724 if (!nonConstThis->fields->atomicParser.compare_exchange_strong(ptr, temp)) {
1725 // Another thread beat us to computing the parser
1726 delete temp;
1727 return ptr;
1728 } else {
1729 // Our copy of the parser got stored in the atomic
1730 return temp;
729e4ab9 1731 }
729e4ab9
A
1732}
1733
3d1f044b
A
1734const numparse::impl::NumberParserImpl* DecimalFormat::getCurrencyParser(UErrorCode& status) const {
1735 if (U_FAILURE(status)) { return nullptr; }
729e4ab9 1736
3d1f044b
A
1737 // First try to get the pre-computed parser
1738 auto* ptr = fields->atomicCurrencyParser.load();
1739 if (ptr != nullptr) {
1740 return ptr;
729e4ab9 1741 }
3d1f044b
A
1742
1743 // Try computing the parser on our own
1744 auto* temp = NumberParserImpl::createParserFromProperties(*fields->properties, *fields->symbols, true, status);
1745 if (temp == nullptr) {
1746 status = U_MEMORY_ALLOCATION_ERROR;
1747 // although we may still dereference, call sites should be guarded
1748 }
1749
1750 // Note: ptr starts as nullptr; during compare_exchange, it is set to what is actually stored in the
1751 // atomic if another thread beat us to computing the parser object.
1752 auto* nonConstThis = const_cast<DecimalFormat*>(this);
1753 if (!nonConstThis->fields->atomicCurrencyParser.compare_exchange_strong(ptr, temp)) {
1754 // Another thread beat us to computing the parser
1755 delete temp;
1756 return ptr;
1757 } else {
1758 // Our copy of the parser got stored in the atomic
1759 return temp;
729e4ab9
A
1760 }
1761}
1762
57a6839d 1763void
3d1f044b
A
1764DecimalFormat::fieldPositionHelper(const number::FormattedNumber& formatted, FieldPosition& fieldPosition,
1765 int32_t offset, UErrorCode& status) {
1766 if (U_FAILURE(status)) { return; }
1767 // always return first occurrence:
1768 fieldPosition.setBeginIndex(0);
1769 fieldPosition.setEndIndex(0);
1770 bool found = formatted.nextFieldPosition(fieldPosition, status);
1771 if (found && offset != 0) {
1772 FieldPositionOnlyHandler fpoh(fieldPosition);
1773 fpoh.shiftLast(offset);
1774 }
57a6839d
A
1775}
1776
57a6839d 1777void
3d1f044b
A
1778DecimalFormat::fieldPositionIteratorHelper(const number::FormattedNumber& formatted, FieldPositionIterator* fpi,
1779 int32_t offset, UErrorCode& status) {
1780 if (U_SUCCESS(status) && (fpi != nullptr)) {
1781 FieldPositionIteratorHandler fpih(fpi, status);
1782 fpih.setShift(offset);
1783 formatted.getAllFieldPositionsImpl(fpih, status);
1784 }
57a6839d
A
1785}
1786
3d1f044b
A
1787// To debug fast-format, change void(x) to printf(x)
1788#define trace(x) void(x)
51004dcb 1789
3d1f044b
A
1790void DecimalFormat::setupFastFormat() {
1791 // Check the majority of properties:
1792 if (!fields->properties->equalsDefaultExceptFastFormat()) {
1793 trace("no fast format: equality\n");
1794 fields->canUseFastFormat = false;
1795 return;
1796 }
1797
1798 // Now check the remaining properties.
1799 // Nontrivial affixes:
1800 UBool trivialPP = fields->properties->positivePrefixPattern.isEmpty();
1801 UBool trivialPS = fields->properties->positiveSuffixPattern.isEmpty();
1802 UBool trivialNP = fields->properties->negativePrefixPattern.isBogus() || (
1803 fields->properties->negativePrefixPattern.length() == 1 &&
1804 fields->properties->negativePrefixPattern.charAt(0) == u'-');
1805 UBool trivialNS = fields->properties->negativeSuffixPattern.isEmpty();
1806 if (!trivialPP || !trivialPS || !trivialNP || !trivialNS) {
1807 trace("no fast format: affixes\n");
1808 fields->canUseFastFormat = false;
1809 return;
1810 }
1811
1812 // Grouping (secondary grouping is forbidden in equalsDefaultExceptFastFormat):
1813 bool groupingUsed = fields->properties->groupingUsed;
1814 int32_t groupingSize = fields->properties->groupingSize;
1815 bool unusualGroupingSize = groupingSize > 0 && groupingSize != 3;
1816 const UnicodeString& groupingString = fields->symbols->getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol);
1817 if (groupingUsed && (unusualGroupingSize || groupingString.length() != 1)) {
1818 trace("no fast format: grouping\n");
1819 fields->canUseFastFormat = false;
1820 return;
1821 }
1822
1823 // Integer length:
1824 int32_t minInt = fields->exportedProperties->minimumIntegerDigits;
1825 int32_t maxInt = fields->exportedProperties->maximumIntegerDigits;
1826 // Fastpath supports up to only 10 digits (length of INT32_MIN)
1827 if (minInt > 10) {
1828 trace("no fast format: integer\n");
1829 fields->canUseFastFormat = false;
1830 return;
1831 }
1832
1833 // Fraction length (no fraction part allowed in fast path):
1834 int32_t minFrac = fields->exportedProperties->minimumFractionDigits;
1835 if (minFrac > 0) {
1836 trace("no fast format: fraction\n");
1837 fields->canUseFastFormat = false;
1838 return;
1839 }
1840
1841 // Other symbols:
1842 const UnicodeString& minusSignString = fields->symbols->getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol);
1843 UChar32 codePointZero = fields->symbols->getCodePointZero();
1844 if (minusSignString.length() != 1 || U16_LENGTH(codePointZero) != 1) {
1845 trace("no fast format: symbols\n");
1846 fields->canUseFastFormat = false;
1847 return;
1848 }
51004dcb 1849
3d1f044b
A
1850 // Good to go!
1851 trace("can use fast format!\n");
1852 fields->canUseFastFormat = true;
1853 fields->fastData.cpZero = static_cast<char16_t>(codePointZero);
1854 fields->fastData.cpGroupingSeparator = groupingUsed && groupingSize == 3 ? groupingString.charAt(0) : 0;
1855 fields->fastData.cpMinusSign = minusSignString.charAt(0);
1856 fields->fastData.minInt = (minInt < 0 || minInt > 127) ? 0 : static_cast<int8_t>(minInt);
1857 fields->fastData.maxInt = (maxInt < 0 || maxInt > 127) ? 127 : static_cast<int8_t>(maxInt);
51004dcb
A
1858}
1859
3d1f044b
A
1860bool DecimalFormat::fastFormatDouble(double input, UnicodeString& output) const {
1861 if (!fields->canUseFastFormat) {
1862 return false;
1863 }
1864 if (std::isnan(input)
1865 || std::trunc(input) != input
1866 || input <= INT32_MIN
1867 || input > INT32_MAX) {
1868 return false;
1869 }
1870 doFastFormatInt32(static_cast<int32_t>(input), std::signbit(input), output);
1871 return true;
1872}
1873
1874bool DecimalFormat::fastFormatInt64(int64_t input, UnicodeString& output) const {
1875 if (!fields->canUseFastFormat) {
1876 return false;
1877 }
1878 if (input <= INT32_MIN || input > INT32_MAX) {
1879 return false;
1880 }
1881 doFastFormatInt32(static_cast<int32_t>(input), input < 0, output);
1882 return true;
1883}
1884
1885void DecimalFormat::doFastFormatInt32(int32_t input, bool isNegative, UnicodeString& output) const {
1886 U_ASSERT(fields->canUseFastFormat);
1887 if (isNegative) {
1888 output.append(fields->fastData.cpMinusSign);
1889 U_ASSERT(input != INT32_MIN); // handled by callers
1890 input = -input;
1891 }
1892 // Cap at int32_t to make the buffer small and operations fast.
1893 // Longest string: "2,147,483,648" (13 chars in length)
1894 static constexpr int32_t localCapacity = 13;
1895 char16_t localBuffer[localCapacity];
1896 char16_t* ptr = localBuffer + localCapacity;
1897 int8_t group = 0;
1898 for (int8_t i = 0; i < fields->fastData.maxInt && (input != 0 || i < fields->fastData.minInt); i++) {
1899 if (group++ == 3 && fields->fastData.cpGroupingSeparator != 0) {
1900 *(--ptr) = fields->fastData.cpGroupingSeparator;
1901 group = 1;
1902 }
1903 std::div_t res = std::div(input, 10);
1904 *(--ptr) = static_cast<char16_t>(fields->fastData.cpZero + res.rem);
1905 input = res.quot;
1906 }
1907 int32_t len = localCapacity - static_cast<int32_t>(ptr - localBuffer);
1908 output.append(ptr, len);
51004dcb 1909}
729e4ab9 1910
b75a7d8f
A
1911
1912#endif /* #if !UCONFIG_NO_FORMATTING */