blob: 71360f4a70cbef0f29ec16944b56966440938912 [file] [log] [blame]
/*
* 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 "starboard/client_porting/poem/string_poem.h"
#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 */