* Copyright 2003, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2010-2011, Oliver Tappe, zooey@hirschkaefer.de.
* Copyright 2012, John Scipione, jscipione@gmail.com
* Copyright 2017, Adrien Destugues, pulkomandy@pulkomandy.tk
* Copyright 2021, Andrew Lindesay, apl@lindesay.co.nz
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include <unicode/uversion.h>
#include <NumberFormat.h>
#include <AutoDeleter.h>
#include <Autolock.h>
#include <FormattingConventionsPrivate.h>
#include <ICUWrapper.h>
#include <unicode/dcfmtsym.h>
#include <unicode/decimfmt.h>
#include <unicode/numfmt.h>
U_NAMESPACE_USE
class BNumberFormatImpl {
public:
BNumberFormatImpl();
~BNumberFormatImpl();
NumberFormat* GetInteger(BFormattingConventions* convention);
NumberFormat* GetFloat(BFormattingConventions* convention);
NumberFormat* GetCurrency(BFormattingConventions* convention);
NumberFormat* GetPercent(BFormattingConventions* convention);
ssize_t ApplyFormatter(NumberFormat* formatter, char* string,
size_t maxSize, const double value);
status_t ApplyFormatter(NumberFormat* formatter, BString& string,
const double value);
private:
NumberFormat* fIntegerFormat;
NumberFormat* fFloatFormat;
NumberFormat* fCurrencyFormat;
NumberFormat* fPercentFormat;
};
BNumberFormatImpl::BNumberFormatImpl()
{
fIntegerFormat = NULL;
fFloatFormat = NULL;
fCurrencyFormat = NULL;
fPercentFormat = NULL;
}
BNumberFormatImpl::~BNumberFormatImpl()
{
delete fIntegerFormat;
delete fFloatFormat;
delete fCurrencyFormat;
delete fPercentFormat;
}
NumberFormat*
BNumberFormatImpl::GetInteger(BFormattingConventions* convention)
{
if (fIntegerFormat == NULL) {
UErrorCode err = U_ZERO_ERROR;
fIntegerFormat = NumberFormat::createInstance(
*BFormattingConventions::Private(convention).ICULocale(),
UNUM_DECIMAL, err);
if (fIntegerFormat == NULL)
return NULL;
if (U_FAILURE(err)) {
delete fIntegerFormat;
fIntegerFormat = NULL;
return NULL;
}
}
return fIntegerFormat;
}
NumberFormat*
BNumberFormatImpl::GetFloat(BFormattingConventions* convention)
{
if (fFloatFormat == NULL) {
UErrorCode err = U_ZERO_ERROR;
fFloatFormat = NumberFormat::createInstance(
*BFormattingConventions::Private(convention).ICULocale(),
UNUM_DECIMAL, err);
if (fFloatFormat == NULL)
return NULL;
if (U_FAILURE(err)) {
delete fFloatFormat;
fFloatFormat = NULL;
return NULL;
}
}
return fFloatFormat;
}
NumberFormat*
BNumberFormatImpl::GetCurrency(BFormattingConventions* convention)
{
if (fCurrencyFormat == NULL) {
UErrorCode err = U_ZERO_ERROR;
fCurrencyFormat = NumberFormat::createCurrencyInstance(
*BFormattingConventions::Private(convention).ICULocale(),
err);
if (fCurrencyFormat == NULL)
return NULL;
if (U_FAILURE(err)) {
delete fCurrencyFormat;
fCurrencyFormat = NULL;
return NULL;
}
}
return fCurrencyFormat;
}
NumberFormat*
BNumberFormatImpl::GetPercent(BFormattingConventions* convention)
{
if (fPercentFormat == NULL) {
UErrorCode err = U_ZERO_ERROR;
fPercentFormat = NumberFormat::createInstance(
*BFormattingConventions::Private(convention).ICULocale(),
UNUM_PERCENT, err);
if (fPercentFormat == NULL)
return NULL;
if (U_FAILURE(err)) {
delete fPercentFormat;
fPercentFormat = NULL;
return NULL;
}
}
return fPercentFormat;
}
ssize_t
BNumberFormatImpl::ApplyFormatter(NumberFormat* formatter, char* string,
size_t maxSize, const double value)
{
BString fullString;
status_t status = ApplyFormatter(formatter, fullString, value);
if (status != B_OK)
return status;
return strlcpy(string, fullString.String(), maxSize);
}
status_t
BNumberFormatImpl::ApplyFormatter(NumberFormat* formatter, BString& string,
const double value)
{
if (formatter == NULL)
return B_NO_MEMORY;
UnicodeString icuString;
formatter->format(value, icuString);
string.Truncate(0);
BStringByteSink stringConverter(&string);
icuString.toUTF8(stringConverter);
return B_OK;
}
BNumberFormat::BNumberFormat()
: BFormat()
{
fPrivateData = new BNumberFormatImpl();
}
BNumberFormat::BNumberFormat(const BLocale* locale)
: BFormat(locale)
{
fPrivateData = new BNumberFormatImpl();
}
BNumberFormat::BNumberFormat(const BNumberFormat &other)
: BFormat(other)
{
fPrivateData = new BNumberFormatImpl(*other.fPrivateData);
}
BNumberFormat::~BNumberFormat()
{
delete fPrivateData;
}
ssize_t
BNumberFormat::Format(char* string, size_t maxSize, const double value)
{
BString fullString;
status_t status = Format(fullString, value);
if (status != B_OK)
return status;
return strlcpy(string, fullString.String(), maxSize);
}
status_t
BNumberFormat::Format(BString& string, const double value)
{
NumberFormat* formatter = fPrivateData->GetFloat(&fConventions);
if (formatter == NULL)
return B_NO_MEMORY;
UnicodeString icuString;
formatter->format(value, icuString);
string.Truncate(0);
BStringByteSink stringConverter(&string);
icuString.toUTF8(stringConverter);
return B_OK;
}
ssize_t
BNumberFormat::Format(char* string, size_t maxSize, const int32 value)
{
BString fullString;
status_t status = Format(fullString, value);
if (status != B_OK)
return status;
return strlcpy(string, fullString.String(), maxSize);
}
status_t
BNumberFormat::Format(BString& string, const int32 value)
{
NumberFormat* formatter = fPrivateData->GetInteger(&fConventions);
if (formatter == NULL)
return B_NO_MEMORY;
UnicodeString icuString;
formatter->format((int32_t)value, icuString);
string.Truncate(0);
BStringByteSink stringConverter(&string);
icuString.toUTF8(stringConverter);
return B_OK;
}
status_t
BNumberFormat::SetPrecision(int precision)
{
NumberFormat* decimalFormatter = fPrivateData->GetFloat(&fConventions);
NumberFormat* currencyFormatter = fPrivateData->GetCurrency(&fConventions);
NumberFormat* percentFormatter = fPrivateData->GetPercent(&fConventions);
if ((decimalFormatter == NULL) || (currencyFormatter == NULL) || (percentFormatter == NULL))
return B_ERROR;
decimalFormatter->setMinimumFractionDigits(precision);
decimalFormatter->setMaximumFractionDigits(precision);
currencyFormatter->setMinimumFractionDigits(precision);
currencyFormatter->setMaximumFractionDigits(precision);
percentFormatter->setMinimumFractionDigits(precision);
percentFormatter->setMaximumFractionDigits(precision);
return B_OK;
}
ssize_t
BNumberFormat::FormatMonetary(char* string, size_t maxSize, const double value)
{
return fPrivateData->ApplyFormatter(
fPrivateData->GetCurrency(&fConventions), string, maxSize, value);
}
status_t
BNumberFormat::FormatMonetary(BString& string, const double value)
{
return fPrivateData->ApplyFormatter(
fPrivateData->GetCurrency(&fConventions), string, value);
}
ssize_t
BNumberFormat::FormatPercent(char* string, size_t maxSize, const double value)
{
return fPrivateData->ApplyFormatter(
fPrivateData->GetPercent(&fConventions), string, maxSize, value);
}
status_t
BNumberFormat::FormatPercent(BString& string, const double value)
{
return fPrivateData->ApplyFormatter(
fPrivateData->GetPercent(&fConventions), string, value);
}
status_t
BNumberFormat::Parse(const BString& string, double& value)
{
NumberFormat* parser = fPrivateData->GetFloat(&fConventions);
if (parser == NULL)
return B_NO_MEMORY;
UnicodeString unicode(string.String());
Formattable result(0);
UErrorCode err = U_ZERO_ERROR;
parser->parse(unicode, result, err);
if (err != U_ZERO_ERROR)
return B_BAD_DATA;
value = result.getDouble();
return B_OK;
}
BString
BNumberFormat::GetSeparator(BNumberElement element)
{
DecimalFormatSymbols::ENumberFormatSymbol symbol;
BString result;
switch(element) {
case B_DECIMAL_SEPARATOR:
symbol = DecimalFormatSymbols::kDecimalSeparatorSymbol;
break;
case B_GROUPING_SEPARATOR:
symbol = DecimalFormatSymbols::kGroupingSeparatorSymbol;
break;
default:
return result;
}
NumberFormat* format = fPrivateData->GetFloat(&fConventions);
DecimalFormat* decimal = dynamic_cast<DecimalFormat*>(format);
if (decimal == NULL)
return result;
const DecimalFormatSymbols* symbols = decimal->getDecimalFormatSymbols();
UnicodeString string = symbols->getSymbol(symbol);
BStringByteSink stringConverter(&result);
string.toUTF8(stringConverter);
return result;
}