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