Coverage Report

Created: 2018-09-25 14:53

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