Imported Upstream version 58.1
[platform/upstream/icu.git] / source / i18n / digitformatter.cpp
1 // Copyright (C) 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4  * Copyright (C) 2015, International Business Machines
5  * Corporation and others.  All Rights Reserved.
6  *
7  * file name: digitformatter.cpp
8  */
9
10 #include "unicode/utypes.h"
11
12 #if !UCONFIG_NO_FORMATTING
13
14 #include "unicode/dcfmtsym.h"
15 #include "unicode/unum.h"
16
17 #include "digitformatter.h"
18 #include "digitgrouping.h"
19 #include "digitinterval.h"
20 #include "digitlst.h"
21 #include "fphdlimp.h"
22 #include "smallintformatter.h"
23 #include "unistrappender.h"
24 #include "visibledigits.h"
25
26 U_NAMESPACE_BEGIN
27
28 DigitFormatter::DigitFormatter()
29         : fGroupingSeparator(",", -1, US_INV), fDecimal(".", -1, US_INV),
30           fNegativeSign("-", -1, US_INV), fPositiveSign("+", -1, US_INV),
31           fIsStandardDigits(TRUE), fExponent("E", -1, US_INV) {
32     for (int32_t i = 0; i < 10; ++i) {
33         fLocalizedDigits[i] = (UChar32) (0x30 + i);
34     }
35     fInfinity.setTo(UnicodeString("Inf", -1, US_INV), UNUM_INTEGER_FIELD);
36     fNan.setTo(UnicodeString("Nan", -1, US_INV), UNUM_INTEGER_FIELD);
37 }
38
39 DigitFormatter::DigitFormatter(const DecimalFormatSymbols &symbols) {
40     setDecimalFormatSymbols(symbols);
41 }
42
43 void
44 DigitFormatter::setOtherDecimalFormatSymbols(
45         const DecimalFormatSymbols &symbols) {
46     fLocalizedDigits[0] = symbols.getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0);
47     fLocalizedDigits[1] = symbols.getConstSymbol(DecimalFormatSymbols::kOneDigitSymbol).char32At(0);
48     fLocalizedDigits[2] = symbols.getConstSymbol(DecimalFormatSymbols::kTwoDigitSymbol).char32At(0);
49     fLocalizedDigits[3] = symbols.getConstSymbol(DecimalFormatSymbols::kThreeDigitSymbol).char32At(0);
50     fLocalizedDigits[4] = symbols.getConstSymbol(DecimalFormatSymbols::kFourDigitSymbol).char32At(0);
51     fLocalizedDigits[5] = symbols.getConstSymbol(DecimalFormatSymbols::kFiveDigitSymbol).char32At(0);
52     fLocalizedDigits[6] = symbols.getConstSymbol(DecimalFormatSymbols::kSixDigitSymbol).char32At(0);
53     fLocalizedDigits[7] = symbols.getConstSymbol(DecimalFormatSymbols::kSevenDigitSymbol).char32At(0);
54     fLocalizedDigits[8] = symbols.getConstSymbol(DecimalFormatSymbols::kEightDigitSymbol).char32At(0);
55     fLocalizedDigits[9] = symbols.getConstSymbol(DecimalFormatSymbols::kNineDigitSymbol).char32At(0);
56     fIsStandardDigits = isStandardDigits();
57     fNegativeSign = symbols.getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol);
58     fPositiveSign = symbols.getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol);
59     fInfinity.setTo(symbols.getConstSymbol(DecimalFormatSymbols::kInfinitySymbol), UNUM_INTEGER_FIELD);
60     fNan.setTo(symbols.getConstSymbol(DecimalFormatSymbols::kNaNSymbol), UNUM_INTEGER_FIELD);
61     fExponent = symbols.getConstSymbol(DecimalFormatSymbols::kExponentialSymbol);
62 }
63
64 void
65 DigitFormatter::setDecimalFormatSymbolsForMonetary(
66         const DecimalFormatSymbols &symbols) {
67     setOtherDecimalFormatSymbols(symbols);
68     fGroupingSeparator = symbols.getConstSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol);
69     fDecimal = symbols.getConstSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol);
70 }
71
72 void
73 DigitFormatter::setDecimalFormatSymbols(
74         const DecimalFormatSymbols &symbols) {
75     setOtherDecimalFormatSymbols(symbols);
76     fGroupingSeparator = symbols.getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol);
77     fDecimal = symbols.getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol);
78 }
79
80 static void appendField(
81         int32_t fieldId,
82         const UnicodeString &value,
83         FieldPositionHandler &handler,
84         UnicodeString &appendTo) {
85     int32_t currentLength = appendTo.length();
86     appendTo.append(value);
87     handler.addAttribute(
88             fieldId,
89             currentLength,
90             appendTo.length());
91 }
92
93 int32_t DigitFormatter::countChar32(
94         const DigitGrouping &grouping,
95         const DigitInterval &interval,
96         const DigitFormatterOptions &options) const {
97     int32_t result = interval.length();
98
99     // We always emit '0' in lieu of no digits.
100     if (result == 0) {
101         result = 1;
102     }
103     if (options.fAlwaysShowDecimal || interval.getLeastSignificantInclusive() < 0) {
104         result += fDecimal.countChar32();
105     }
106     result += grouping.getSeparatorCount(interval.getIntDigitCount()) * fGroupingSeparator.countChar32();
107     return result;
108 }
109
110 int32_t
111 DigitFormatter::countChar32(
112         const VisibleDigits &digits,
113         const DigitGrouping &grouping,
114         const DigitFormatterOptions &options) const {
115     if (digits.isNaN()) {
116         return countChar32ForNaN();
117     }   
118     if (digits.isInfinite()) {
119         return countChar32ForInfinity();
120     }   
121     return countChar32(
122             grouping,
123             digits.getInterval(),
124             options);
125 }
126
127 int32_t
128 DigitFormatter::countChar32(
129         const VisibleDigitsWithExponent &digits,
130         const SciFormatterOptions &options) const {
131     if (digits.isNaN()) {
132         return countChar32ForNaN();
133     }
134     if (digits.isInfinite()) {
135         return countChar32ForInfinity();
136     }
137     const VisibleDigits *exponent = digits.getExponent();
138     if (exponent == NULL) {
139         DigitGrouping grouping;
140         return countChar32(
141                 grouping,
142                 digits.getMantissa().getInterval(),
143                 options.fMantissa);
144     }
145     return countChar32(
146             *exponent, digits.getMantissa().getInterval(), options);
147 }
148
149 int32_t
150 DigitFormatter::countChar32(
151         const VisibleDigits &exponent,
152         const DigitInterval &mantissaInterval,
153         const SciFormatterOptions &options) const {
154     DigitGrouping grouping;
155     int32_t count = countChar32(
156             grouping, mantissaInterval, options.fMantissa);
157     count += fExponent.countChar32();
158     count += countChar32ForExponent(
159             exponent, options.fExponent);
160     return count;
161 }
162
163 UnicodeString &DigitFormatter::format(
164         const VisibleDigits &digits,
165         const DigitGrouping &grouping,
166         const DigitFormatterOptions &options,
167         FieldPositionHandler &handler,
168         UnicodeString &appendTo) const {
169     if (digits.isNaN()) {
170         return formatNaN(handler, appendTo);
171     }
172     if (digits.isInfinite()) {
173         return formatInfinity(handler, appendTo);
174     }
175
176     const DigitInterval &interval = digits.getInterval();
177     int32_t digitsLeftOfDecimal = interval.getMostSignificantExclusive();
178     int32_t lastDigitPos = interval.getLeastSignificantInclusive();
179     int32_t intBegin = appendTo.length();
180     int32_t fracBegin;
181
182     // Emit "0" instead of empty string.
183     if (digitsLeftOfDecimal == 0 && lastDigitPos == 0) {
184         appendTo.append(fLocalizedDigits[0]);
185         handler.addAttribute(UNUM_INTEGER_FIELD, intBegin, appendTo.length());
186         if (options.fAlwaysShowDecimal) {
187             appendField(
188                     UNUM_DECIMAL_SEPARATOR_FIELD,
189                     fDecimal,
190                     handler,
191                     appendTo);
192         }
193         return appendTo;
194     }
195     {
196         UnicodeStringAppender appender(appendTo);
197         for (int32_t i = interval.getMostSignificantExclusive() - 1;
198                 i >= interval.getLeastSignificantInclusive(); --i) {
199             if (i == -1) {
200                 appender.flush();
201                 appendField(
202                         UNUM_DECIMAL_SEPARATOR_FIELD,
203                         fDecimal,
204                         handler,
205                         appendTo);
206                 fracBegin = appendTo.length();
207             }
208             appender.append(fLocalizedDigits[digits.getDigitByExponent(i)]);
209             if (grouping.isSeparatorAt(digitsLeftOfDecimal, i)) {
210                 appender.flush();
211                 appendField(
212                         UNUM_GROUPING_SEPARATOR_FIELD,
213                         fGroupingSeparator,
214                         handler,
215                         appendTo);
216             }
217             if (i == 0) {
218                 appender.flush();
219                 if (digitsLeftOfDecimal > 0) {
220                     handler.addAttribute(UNUM_INTEGER_FIELD, intBegin, appendTo.length());
221                 }
222             }
223         }
224         if (options.fAlwaysShowDecimal && lastDigitPos == 0) {
225             appender.flush();
226             appendField(
227                     UNUM_DECIMAL_SEPARATOR_FIELD,
228                     fDecimal,
229                     handler,
230                     appendTo);
231         }
232     }
233     // lastDigitPos is never > 0 so we are guaranteed that kIntegerField
234     // is already added.
235     if (lastDigitPos < 0) {
236         handler.addAttribute(UNUM_FRACTION_FIELD, fracBegin, appendTo.length());
237     }
238     return appendTo;
239 }
240
241 UnicodeString &
242 DigitFormatter::format(
243         const VisibleDigitsWithExponent &digits,
244         const SciFormatterOptions &options,
245         FieldPositionHandler &handler,
246         UnicodeString &appendTo) const {
247     DigitGrouping grouping;
248     format(
249             digits.getMantissa(),
250             grouping,
251             options.fMantissa,
252             handler,
253             appendTo);
254     const VisibleDigits *exponent = digits.getExponent();
255     if (exponent == NULL) {
256         return appendTo;
257     }
258     int32_t expBegin = appendTo.length();
259     appendTo.append(fExponent);
260     handler.addAttribute(
261             UNUM_EXPONENT_SYMBOL_FIELD, expBegin, appendTo.length());
262     return formatExponent(
263             *exponent,
264             options.fExponent,
265             UNUM_EXPONENT_SIGN_FIELD,
266             UNUM_EXPONENT_FIELD,
267             handler,
268             appendTo);
269 }
270
271 static int32_t formatInt(
272         int32_t value, uint8_t *digits) {
273     int32_t idx = 0;
274     while (value > 0) {
275         digits[idx++] = (uint8_t) (value % 10);
276         value /= 10;
277     }
278     return idx;
279 }
280
281 UnicodeString &
282 DigitFormatter::formatDigits(
283         const uint8_t *digits,
284         int32_t count,
285         const IntDigitCountRange &range,
286         int32_t intField,
287         FieldPositionHandler &handler,
288         UnicodeString &appendTo) const {
289     int32_t i = range.pin(count) - 1;
290     int32_t begin = appendTo.length();
291
292     // Always emit '0' as placeholder for empty string.
293     if (i == -1) {
294         appendTo.append(fLocalizedDigits[0]);
295         handler.addAttribute(intField, begin, appendTo.length());
296         return appendTo;
297     }
298     {
299         UnicodeStringAppender appender(appendTo);
300         for (; i >= count; --i) {
301             appender.append(fLocalizedDigits[0]);
302         }
303         for (; i >= 0; --i) {
304             appender.append(fLocalizedDigits[digits[i]]);
305         }
306     }
307     handler.addAttribute(intField, begin, appendTo.length());
308     return appendTo;
309 }
310
311 UnicodeString &
312 DigitFormatter::formatExponent(
313         const VisibleDigits &digits,
314         const DigitFormatterIntOptions &options,
315         int32_t signField,
316         int32_t intField,
317         FieldPositionHandler &handler,
318         UnicodeString &appendTo) const {
319     UBool neg = digits.isNegative();
320     if (neg || options.fAlwaysShowSign) {
321         appendField(
322                 signField,
323                 neg ? fNegativeSign : fPositiveSign,
324                 handler,
325                 appendTo);
326     }
327     int32_t begin = appendTo.length();
328     DigitGrouping grouping;
329     DigitFormatterOptions expOptions;
330     FieldPosition fpos(FieldPosition::DONT_CARE);
331     FieldPositionOnlyHandler noHandler(fpos);
332     format(
333             digits,
334             grouping,
335             expOptions,
336             noHandler,
337             appendTo);
338     handler.addAttribute(intField, begin, appendTo.length());
339     return appendTo;
340 }
341
342 int32_t
343 DigitFormatter::countChar32ForExponent(
344         const VisibleDigits &exponent,
345         const DigitFormatterIntOptions &options) const {
346     int32_t result = 0;
347     UBool neg = exponent.isNegative();
348     if (neg || options.fAlwaysShowSign) {
349         result += neg ? fNegativeSign.countChar32() : fPositiveSign.countChar32();
350     }
351     DigitGrouping grouping;
352     DigitFormatterOptions expOptions;
353     result += countChar32(grouping, exponent.getInterval(), expOptions);
354     return result;
355 }
356
357 UnicodeString &
358 DigitFormatter::formatPositiveInt32(
359         int32_t positiveValue,
360         const IntDigitCountRange &range,
361         FieldPositionHandler &handler,
362         UnicodeString &appendTo) const {
363     // super fast path
364     if (fIsStandardDigits && SmallIntFormatter::canFormat(positiveValue, range)) {
365         int32_t begin = appendTo.length();
366         SmallIntFormatter::format(positiveValue, range, appendTo);
367         handler.addAttribute(UNUM_INTEGER_FIELD, begin, appendTo.length());
368         return appendTo;
369     }
370     uint8_t digits[10];
371     int32_t count = formatInt(positiveValue, digits);
372     return formatDigits(
373             digits,
374             count,
375             range,
376             UNUM_INTEGER_FIELD,
377             handler,
378             appendTo);
379 }
380
381 UBool DigitFormatter::isStandardDigits() const {
382     UChar32 cdigit = 0x30;
383     for (int32_t i = 0; i < UPRV_LENGTHOF(fLocalizedDigits); ++i) {
384         if (fLocalizedDigits[i] != cdigit) {
385             return FALSE;
386         }
387         ++cdigit;
388     }
389     return TRUE;
390 }
391
392 UBool
393 DigitFormatter::equals(const DigitFormatter &rhs) const {
394     UBool result = (fGroupingSeparator == rhs.fGroupingSeparator) &&
395                    (fDecimal == rhs.fDecimal) &&
396                    (fNegativeSign == rhs.fNegativeSign) &&
397                    (fPositiveSign == rhs.fPositiveSign) &&
398                    (fInfinity.equals(rhs.fInfinity)) &&
399                    (fNan.equals(rhs.fNan)) &&
400                    (fIsStandardDigits == rhs.fIsStandardDigits) &&
401                    (fExponent == rhs.fExponent);
402
403     if (!result) {
404         return FALSE;
405     }
406     for (int32_t i = 0; i < UPRV_LENGTHOF(fLocalizedDigits); ++i) {
407         if (fLocalizedDigits[i] != rhs.fLocalizedDigits[i]) {
408             return FALSE;
409         }
410     }
411     return TRUE;
412 }
413
414
415 U_NAMESPACE_END
416
417 #endif /* #if !UCONFIG_NO_FORMATTING */