diff options
Diffstat (limited to 'intl/icu/source/i18n/digitformatter.cpp')
-rw-r--r-- | intl/icu/source/i18n/digitformatter.cpp | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/intl/icu/source/i18n/digitformatter.cpp b/intl/icu/source/i18n/digitformatter.cpp new file mode 100644 index 000000000..62569d571 --- /dev/null +++ b/intl/icu/source/i18n/digitformatter.cpp @@ -0,0 +1,417 @@ +// Copyright (C) 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + * Copyright (C) 2015, International Business Machines + * Corporation and others. All Rights Reserved. + * + * file name: digitformatter.cpp + */ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/dcfmtsym.h" +#include "unicode/unum.h" + +#include "digitformatter.h" +#include "digitgrouping.h" +#include "digitinterval.h" +#include "digitlst.h" +#include "fphdlimp.h" +#include "smallintformatter.h" +#include "unistrappender.h" +#include "visibledigits.h" + +U_NAMESPACE_BEGIN + +DigitFormatter::DigitFormatter() + : fGroupingSeparator(",", -1, US_INV), fDecimal(".", -1, US_INV), + fNegativeSign("-", -1, US_INV), fPositiveSign("+", -1, US_INV), + fIsStandardDigits(TRUE), fExponent("E", -1, US_INV) { + for (int32_t i = 0; i < 10; ++i) { + fLocalizedDigits[i] = (UChar32) (0x30 + i); + } + fInfinity.setTo(UnicodeString("Inf", -1, US_INV), UNUM_INTEGER_FIELD); + fNan.setTo(UnicodeString("Nan", -1, US_INV), UNUM_INTEGER_FIELD); +} + +DigitFormatter::DigitFormatter(const DecimalFormatSymbols &symbols) { + setDecimalFormatSymbols(symbols); +} + +void +DigitFormatter::setOtherDecimalFormatSymbols( + const DecimalFormatSymbols &symbols) { + fLocalizedDigits[0] = symbols.getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); + fLocalizedDigits[1] = symbols.getConstSymbol(DecimalFormatSymbols::kOneDigitSymbol).char32At(0); + fLocalizedDigits[2] = symbols.getConstSymbol(DecimalFormatSymbols::kTwoDigitSymbol).char32At(0); + fLocalizedDigits[3] = symbols.getConstSymbol(DecimalFormatSymbols::kThreeDigitSymbol).char32At(0); + fLocalizedDigits[4] = symbols.getConstSymbol(DecimalFormatSymbols::kFourDigitSymbol).char32At(0); + fLocalizedDigits[5] = symbols.getConstSymbol(DecimalFormatSymbols::kFiveDigitSymbol).char32At(0); + fLocalizedDigits[6] = symbols.getConstSymbol(DecimalFormatSymbols::kSixDigitSymbol).char32At(0); + fLocalizedDigits[7] = symbols.getConstSymbol(DecimalFormatSymbols::kSevenDigitSymbol).char32At(0); + fLocalizedDigits[8] = symbols.getConstSymbol(DecimalFormatSymbols::kEightDigitSymbol).char32At(0); + fLocalizedDigits[9] = symbols.getConstSymbol(DecimalFormatSymbols::kNineDigitSymbol).char32At(0); + fIsStandardDigits = isStandardDigits(); + fNegativeSign = symbols.getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); + fPositiveSign = symbols.getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); + fInfinity.setTo(symbols.getConstSymbol(DecimalFormatSymbols::kInfinitySymbol), UNUM_INTEGER_FIELD); + fNan.setTo(symbols.getConstSymbol(DecimalFormatSymbols::kNaNSymbol), UNUM_INTEGER_FIELD); + fExponent = symbols.getConstSymbol(DecimalFormatSymbols::kExponentialSymbol); +} + +void +DigitFormatter::setDecimalFormatSymbolsForMonetary( + const DecimalFormatSymbols &symbols) { + setOtherDecimalFormatSymbols(symbols); + fGroupingSeparator = symbols.getConstSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol); + fDecimal = symbols.getConstSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol); +} + +void +DigitFormatter::setDecimalFormatSymbols( + const DecimalFormatSymbols &symbols) { + setOtherDecimalFormatSymbols(symbols); + fGroupingSeparator = symbols.getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol); + fDecimal = symbols.getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); +} + +static void appendField( + int32_t fieldId, + const UnicodeString &value, + FieldPositionHandler &handler, + UnicodeString &appendTo) { + int32_t currentLength = appendTo.length(); + appendTo.append(value); + handler.addAttribute( + fieldId, + currentLength, + appendTo.length()); +} + +int32_t DigitFormatter::countChar32( + const DigitGrouping &grouping, + const DigitInterval &interval, + const DigitFormatterOptions &options) const { + int32_t result = interval.length(); + + // We always emit '0' in lieu of no digits. + if (result == 0) { + result = 1; + } + if (options.fAlwaysShowDecimal || interval.getLeastSignificantInclusive() < 0) { + result += fDecimal.countChar32(); + } + result += grouping.getSeparatorCount(interval.getIntDigitCount()) * fGroupingSeparator.countChar32(); + return result; +} + +int32_t +DigitFormatter::countChar32( + const VisibleDigits &digits, + const DigitGrouping &grouping, + const DigitFormatterOptions &options) const { + if (digits.isNaN()) { + return countChar32ForNaN(); + } + if (digits.isInfinite()) { + return countChar32ForInfinity(); + } + return countChar32( + grouping, + digits.getInterval(), + options); +} + +int32_t +DigitFormatter::countChar32( + const VisibleDigitsWithExponent &digits, + const SciFormatterOptions &options) const { + if (digits.isNaN()) { + return countChar32ForNaN(); + } + if (digits.isInfinite()) { + return countChar32ForInfinity(); + } + const VisibleDigits *exponent = digits.getExponent(); + if (exponent == NULL) { + DigitGrouping grouping; + return countChar32( + grouping, + digits.getMantissa().getInterval(), + options.fMantissa); + } + return countChar32( + *exponent, digits.getMantissa().getInterval(), options); +} + +int32_t +DigitFormatter::countChar32( + const VisibleDigits &exponent, + const DigitInterval &mantissaInterval, + const SciFormatterOptions &options) const { + DigitGrouping grouping; + int32_t count = countChar32( + grouping, mantissaInterval, options.fMantissa); + count += fExponent.countChar32(); + count += countChar32ForExponent( + exponent, options.fExponent); + return count; +} + +UnicodeString &DigitFormatter::format( + const VisibleDigits &digits, + const DigitGrouping &grouping, + const DigitFormatterOptions &options, + FieldPositionHandler &handler, + UnicodeString &appendTo) const { + if (digits.isNaN()) { + return formatNaN(handler, appendTo); + } + if (digits.isInfinite()) { + return formatInfinity(handler, appendTo); + } + + const DigitInterval &interval = digits.getInterval(); + int32_t digitsLeftOfDecimal = interval.getMostSignificantExclusive(); + int32_t lastDigitPos = interval.getLeastSignificantInclusive(); + int32_t intBegin = appendTo.length(); + int32_t fracBegin; + + // Emit "0" instead of empty string. + if (digitsLeftOfDecimal == 0 && lastDigitPos == 0) { + appendTo.append(fLocalizedDigits[0]); + handler.addAttribute(UNUM_INTEGER_FIELD, intBegin, appendTo.length()); + if (options.fAlwaysShowDecimal) { + appendField( + UNUM_DECIMAL_SEPARATOR_FIELD, + fDecimal, + handler, + appendTo); + } + return appendTo; + } + { + UnicodeStringAppender appender(appendTo); + for (int32_t i = interval.getMostSignificantExclusive() - 1; + i >= interval.getLeastSignificantInclusive(); --i) { + if (i == -1) { + appender.flush(); + appendField( + UNUM_DECIMAL_SEPARATOR_FIELD, + fDecimal, + handler, + appendTo); + fracBegin = appendTo.length(); + } + appender.append(fLocalizedDigits[digits.getDigitByExponent(i)]); + if (grouping.isSeparatorAt(digitsLeftOfDecimal, i)) { + appender.flush(); + appendField( + UNUM_GROUPING_SEPARATOR_FIELD, + fGroupingSeparator, + handler, + appendTo); + } + if (i == 0) { + appender.flush(); + if (digitsLeftOfDecimal > 0) { + handler.addAttribute(UNUM_INTEGER_FIELD, intBegin, appendTo.length()); + } + } + } + if (options.fAlwaysShowDecimal && lastDigitPos == 0) { + appender.flush(); + appendField( + UNUM_DECIMAL_SEPARATOR_FIELD, + fDecimal, + handler, + appendTo); + } + } + // lastDigitPos is never > 0 so we are guaranteed that kIntegerField + // is already added. + if (lastDigitPos < 0) { + handler.addAttribute(UNUM_FRACTION_FIELD, fracBegin, appendTo.length()); + } + return appendTo; +} + +UnicodeString & +DigitFormatter::format( + const VisibleDigitsWithExponent &digits, + const SciFormatterOptions &options, + FieldPositionHandler &handler, + UnicodeString &appendTo) const { + DigitGrouping grouping; + format( + digits.getMantissa(), + grouping, + options.fMantissa, + handler, + appendTo); + const VisibleDigits *exponent = digits.getExponent(); + if (exponent == NULL) { + return appendTo; + } + int32_t expBegin = appendTo.length(); + appendTo.append(fExponent); + handler.addAttribute( + UNUM_EXPONENT_SYMBOL_FIELD, expBegin, appendTo.length()); + return formatExponent( + *exponent, + options.fExponent, + UNUM_EXPONENT_SIGN_FIELD, + UNUM_EXPONENT_FIELD, + handler, + appendTo); +} + +static int32_t formatInt( + int32_t value, uint8_t *digits) { + int32_t idx = 0; + while (value > 0) { + digits[idx++] = (uint8_t) (value % 10); + value /= 10; + } + return idx; +} + +UnicodeString & +DigitFormatter::formatDigits( + const uint8_t *digits, + int32_t count, + const IntDigitCountRange &range, + int32_t intField, + FieldPositionHandler &handler, + UnicodeString &appendTo) const { + int32_t i = range.pin(count) - 1; + int32_t begin = appendTo.length(); + + // Always emit '0' as placeholder for empty string. + if (i == -1) { + appendTo.append(fLocalizedDigits[0]); + handler.addAttribute(intField, begin, appendTo.length()); + return appendTo; + } + { + UnicodeStringAppender appender(appendTo); + for (; i >= count; --i) { + appender.append(fLocalizedDigits[0]); + } + for (; i >= 0; --i) { + appender.append(fLocalizedDigits[digits[i]]); + } + } + handler.addAttribute(intField, begin, appendTo.length()); + return appendTo; +} + +UnicodeString & +DigitFormatter::formatExponent( + const VisibleDigits &digits, + const DigitFormatterIntOptions &options, + int32_t signField, + int32_t intField, + FieldPositionHandler &handler, + UnicodeString &appendTo) const { + UBool neg = digits.isNegative(); + if (neg || options.fAlwaysShowSign) { + appendField( + signField, + neg ? fNegativeSign : fPositiveSign, + handler, + appendTo); + } + int32_t begin = appendTo.length(); + DigitGrouping grouping; + DigitFormatterOptions expOptions; + FieldPosition fpos(FieldPosition::DONT_CARE); + FieldPositionOnlyHandler noHandler(fpos); + format( + digits, + grouping, + expOptions, + noHandler, + appendTo); + handler.addAttribute(intField, begin, appendTo.length()); + return appendTo; +} + +int32_t +DigitFormatter::countChar32ForExponent( + const VisibleDigits &exponent, + const DigitFormatterIntOptions &options) const { + int32_t result = 0; + UBool neg = exponent.isNegative(); + if (neg || options.fAlwaysShowSign) { + result += neg ? fNegativeSign.countChar32() : fPositiveSign.countChar32(); + } + DigitGrouping grouping; + DigitFormatterOptions expOptions; + result += countChar32(grouping, exponent.getInterval(), expOptions); + return result; +} + +UnicodeString & +DigitFormatter::formatPositiveInt32( + int32_t positiveValue, + const IntDigitCountRange &range, + FieldPositionHandler &handler, + UnicodeString &appendTo) const { + // super fast path + if (fIsStandardDigits && SmallIntFormatter::canFormat(positiveValue, range)) { + int32_t begin = appendTo.length(); + SmallIntFormatter::format(positiveValue, range, appendTo); + handler.addAttribute(UNUM_INTEGER_FIELD, begin, appendTo.length()); + return appendTo; + } + uint8_t digits[10]; + int32_t count = formatInt(positiveValue, digits); + return formatDigits( + digits, + count, + range, + UNUM_INTEGER_FIELD, + handler, + appendTo); +} + +UBool DigitFormatter::isStandardDigits() const { + UChar32 cdigit = 0x30; + for (int32_t i = 0; i < UPRV_LENGTHOF(fLocalizedDigits); ++i) { + if (fLocalizedDigits[i] != cdigit) { + return FALSE; + } + ++cdigit; + } + return TRUE; +} + +UBool +DigitFormatter::equals(const DigitFormatter &rhs) const { + UBool result = (fGroupingSeparator == rhs.fGroupingSeparator) && + (fDecimal == rhs.fDecimal) && + (fNegativeSign == rhs.fNegativeSign) && + (fPositiveSign == rhs.fPositiveSign) && + (fInfinity.equals(rhs.fInfinity)) && + (fNan.equals(rhs.fNan)) && + (fIsStandardDigits == rhs.fIsStandardDigits) && + (fExponent == rhs.fExponent); + + if (!result) { + return FALSE; + } + for (int32_t i = 0; i < UPRV_LENGTHOF(fLocalizedDigits); ++i) { + if (fLocalizedDigits[i] != rhs.fLocalizedDigits[i]) { + return FALSE; + } + } + return TRUE; +} + + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ |