summaryrefslogtreecommitdiffstats
path: root/intl/icu/source/i18n/digitformatter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'intl/icu/source/i18n/digitformatter.cpp')
-rw-r--r--intl/icu/source/i18n/digitformatter.cpp417
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 */