1// © 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
26U_NAMESPACE_BEGIN
27
28DigitFormatter::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
39DigitFormatter::DigitFormatter(const DecimalFormatSymbols &symbols) {
40    setDecimalFormatSymbols(symbols);
41}
42
43void
44DigitFormatter::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
64void
65DigitFormatter::setDecimalFormatSymbolsForMonetary(
66        const DecimalFormatSymbols &symbols) {
67    setOtherDecimalFormatSymbols(symbols);
68    fGroupingSeparator = symbols.getConstSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol);
69    fDecimal = symbols.getConstSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol);
70}
71
72void
73DigitFormatter::setDecimalFormatSymbols(
74        const DecimalFormatSymbols &symbols) {
75    setOtherDecimalFormatSymbols(symbols);
76    fGroupingSeparator = symbols.getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol);
77    fDecimal = symbols.getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol);
78}
79
80static 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
93int32_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
110int32_t
111DigitFormatter::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
127int32_t
128DigitFormatter::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
149int32_t
150DigitFormatter::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
163UnicodeString &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 = 0; /* initialize to avoid compiler warning */
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
241UnicodeString &
242DigitFormatter::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
271static 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
281UnicodeString &
282DigitFormatter::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
311UnicodeString &
312DigitFormatter::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
342int32_t
343DigitFormatter::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
357UnicodeString &
358DigitFormatter::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
381UBool 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
392UBool
393DigitFormatter::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
415U_NAMESPACE_END
416
417#endif /* #if !UCONFIG_NO_FORMATTING */
418