mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-06 22:15:31 +00:00
ICU-9543 C++ CompactDecimalFormat moved to trunk.
X-SVN-Rev: 32819
This commit is contained in:
parent
caf814a5db
commit
161183009a
13 changed files with 1794 additions and 73 deletions
|
@ -86,7 +86,7 @@ tmunit.o tmutamt.o tmutfmt.o colldata.o bmsearch.o bms.o currpinf.o \
|
|||
uspoof.o uspoof_impl.o uspoof_build.o uspoof_conf.o uspoof_wsconf.o decfmtst.o smpdtfst.o \
|
||||
ztrans.o zrule.o vzone.o fphdlimp.o fpositer.o locdspnm.o \
|
||||
decNumber.o decContext.o alphaindex.o tznames.o tznames_impl.o tzgnames.o \
|
||||
tzfmt.o gender.o
|
||||
tzfmt.o compactdecimalformat.o gender.o
|
||||
|
||||
## Header files to install
|
||||
HEADERS = $(srcdir)/unicode/*.h
|
||||
|
|
964
icu4c/source/i18n/compactdecimalformat.cpp
Normal file
964
icu4c/source/i18n/compactdecimalformat.cpp
Normal file
|
@ -0,0 +1,964 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 1997-2012, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*
|
||||
* File COMPACTDECIMALFORMAT.CPP
|
||||
*
|
||||
********************************************************************************
|
||||
*/
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#include "charstr.h"
|
||||
#include "cstring.h"
|
||||
#include "digitlst.h"
|
||||
#include "mutex.h"
|
||||
#include "unicode/compactdecimalformat.h"
|
||||
#include "unicode/numsys.h"
|
||||
#include "unicode/plurrule.h"
|
||||
#include "unicode/ures.h"
|
||||
#include "ucln_in.h"
|
||||
#include "uhash.h"
|
||||
#include "umutex.h"
|
||||
#include "unicode/ures.h"
|
||||
#include "uresimp.h"
|
||||
|
||||
#define LENGTHOF(array) (int32_t)(sizeof(array) / sizeof((array)[0]))
|
||||
|
||||
// Maps locale name to CDFLocaleData struct.
|
||||
static UHashtable* gCompactDecimalData = NULL;
|
||||
static UMutex gCompactDecimalMetaLock = U_MUTEX_INITIALIZER;
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
static const int32_t MAX_DIGITS = 15;
|
||||
static const char gOther[] = "other";
|
||||
static const char gLatnTag[] = "latn";
|
||||
static const char gNumberElementsTag[] = "NumberElements";
|
||||
static const char gDecimalFormatTag[] = "decimalFormat";
|
||||
static const char gPatternsShort[] = "patternsShort";
|
||||
static const char gPatternsLong[] = "patternsLong";
|
||||
static const char gRoot[] = "root";
|
||||
|
||||
static const UChar u_0 = 0x30;
|
||||
static const UChar u_apos = 0x27;
|
||||
|
||||
static const UChar kZero[] = {u_0};
|
||||
|
||||
// Used to unescape single quotes.
|
||||
enum QuoteState {
|
||||
OUTSIDE,
|
||||
INSIDE_EMPTY,
|
||||
INSIDE_FULL
|
||||
};
|
||||
|
||||
enum DataLocation {
|
||||
LOCAL_LOC,
|
||||
LATIN_LOC,
|
||||
ROOT_LOC
|
||||
};
|
||||
|
||||
enum FallbackFlags {
|
||||
ANY = 0,
|
||||
MUST = 1,
|
||||
NOT_ROOT = 2
|
||||
// Next one will be 4 then 6 etc.
|
||||
};
|
||||
|
||||
|
||||
// CDFUnit represents a prefix-suffix pair for a particular variant
|
||||
// and log10 value.
|
||||
struct CDFUnit : public UMemory {
|
||||
UnicodeString prefix;
|
||||
UnicodeString suffix;
|
||||
inline CDFUnit() : prefix(), suffix() {
|
||||
prefix.setToBogus();
|
||||
}
|
||||
inline ~CDFUnit() {}
|
||||
inline UBool isSet() const {
|
||||
return !prefix.isBogus();
|
||||
}
|
||||
inline void markAsSet() {
|
||||
prefix.remove();
|
||||
}
|
||||
};
|
||||
|
||||
// CDFLocaleStyleData contains formatting data for a particular locale
|
||||
// and style.
|
||||
class CDFLocaleStyleData : public UMemory {
|
||||
public:
|
||||
// What to divide by for each log10 value when formatting. These values
|
||||
// will be powers of 10. For English, would be:
|
||||
// 1, 1, 1, 1000, 1000, 1000, 1000000, 1000000, 1000000, 1000000000 ...
|
||||
double divisors[MAX_DIGITS];
|
||||
// Maps plural variants to CDFUnit[MAX_DIGITS] arrays.
|
||||
// To format a number x,
|
||||
// first compute log10(x). Compute displayNum = (x / divisors[log10(x)]).
|
||||
// Compute the plural variant for displayNum
|
||||
// (e.g zero, one, two, few, many, other).
|
||||
// Compute cdfUnits = unitsByVariant[pluralVariant].
|
||||
// Prefix and suffix to use at cdfUnits[log10(x)]
|
||||
UHashtable* unitsByVariant;
|
||||
inline CDFLocaleStyleData() : unitsByVariant(NULL) {}
|
||||
~CDFLocaleStyleData();
|
||||
// Init initializes this object.
|
||||
void Init(UErrorCode& status);
|
||||
inline UBool isBogus() const {
|
||||
return unitsByVariant == NULL;
|
||||
}
|
||||
void setToBogus();
|
||||
private:
|
||||
CDFLocaleStyleData(const CDFLocaleStyleData&);
|
||||
CDFLocaleStyleData& operator=(const CDFLocaleStyleData&);
|
||||
};
|
||||
|
||||
// CDFLocaleData contains formatting data for a particular locale.
|
||||
struct CDFLocaleData : public UMemory {
|
||||
CDFLocaleStyleData shortData;
|
||||
CDFLocaleStyleData longData;
|
||||
inline CDFLocaleData() : shortData(), longData() { }
|
||||
inline ~CDFLocaleData() { }
|
||||
// Init initializes this object.
|
||||
void Init(UErrorCode& status);
|
||||
};
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
U_CDECL_BEGIN
|
||||
|
||||
static UBool U_CALLCONV cdf_cleanup(void) {
|
||||
if (gCompactDecimalData != NULL) {
|
||||
uhash_close(gCompactDecimalData);
|
||||
gCompactDecimalData = NULL;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void U_CALLCONV deleteCDFUnits(void* ptr) {
|
||||
delete [] (icu::CDFUnit*) ptr;
|
||||
}
|
||||
|
||||
static void U_CALLCONV deleteCDFLocaleData(void* ptr) {
|
||||
delete (icu::CDFLocaleData*) ptr;
|
||||
}
|
||||
|
||||
U_CDECL_END
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
static UBool divisors_equal(const double* lhs, const double* rhs);
|
||||
static const CDFLocaleStyleData* getCDFLocaleStyleData(const Locale& inLocale, UNumberCompactStyle style, UErrorCode& status);
|
||||
|
||||
static const CDFLocaleStyleData* extractDataByStyleEnum(const CDFLocaleData& data, UNumberCompactStyle style, UErrorCode& status);
|
||||
static CDFLocaleData* loadCDFLocaleData(const Locale& inLocale, UErrorCode& status);
|
||||
static void initCDFLocaleData(const Locale& inLocale, CDFLocaleData* result, UErrorCode& status);
|
||||
static UResourceBundle* tryGetDecimalFallback(const UResourceBundle* numberSystemResource, const char* style, UResourceBundle** fillIn, FallbackFlags flags, UErrorCode& status);
|
||||
static UResourceBundle* tryGetByKeyWithFallback(const UResourceBundle* rb, const char* path, UResourceBundle** fillIn, FallbackFlags flags, UErrorCode& status);
|
||||
static UBool isRoot(const UResourceBundle* rb, UErrorCode& status);
|
||||
static void initCDFLocaleStyleData(const UResourceBundle* decimalFormatBundle, CDFLocaleStyleData* result, UErrorCode& status);
|
||||
static void populatePower10(const UResourceBundle* power10Bundle, CDFLocaleStyleData* result, UErrorCode& status);
|
||||
static int32_t populatePrefixSuffix(const char* variant, int32_t log10Value, const UnicodeString& formatStr, UHashtable* result, UErrorCode& status);
|
||||
static UBool onlySpaces(UnicodeString u);
|
||||
static void fixQuotes(UnicodeString& s);
|
||||
static void fillInMissing(CDFLocaleStyleData* result);
|
||||
static int32_t computeLog10(double x, UBool inRange);
|
||||
static CDFUnit* createCDFUnit(const char* variant, int32_t log10Value, UHashtable* table, UErrorCode& status);
|
||||
static const CDFUnit* getCDFUnitFallback(const UHashtable* table, const UnicodeString& variant, int32_t log10Value);
|
||||
|
||||
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CompactDecimalFormat)
|
||||
|
||||
CompactDecimalFormat::CompactDecimalFormat(
|
||||
const DecimalFormat& decimalFormat,
|
||||
const UHashtable* unitsByVariant,
|
||||
const double* divisors,
|
||||
PluralRules* pluralRules)
|
||||
: DecimalFormat(decimalFormat), _unitsByVariant(unitsByVariant), _divisors(divisors), _pluralRules(pluralRules) {
|
||||
}
|
||||
|
||||
CompactDecimalFormat::CompactDecimalFormat(const CompactDecimalFormat& source)
|
||||
: DecimalFormat(source), _unitsByVariant(source._unitsByVariant), _divisors(source._divisors), _pluralRules(source._pluralRules->clone()) {
|
||||
}
|
||||
|
||||
CompactDecimalFormat* U_EXPORT2
|
||||
CompactDecimalFormat::createInstance(
|
||||
const Locale& inLocale, UNumberCompactStyle style, UErrorCode& status) {
|
||||
LocalPointer<DecimalFormat> decfmt((DecimalFormat*) NumberFormat::makeInstance(inLocale, UNUM_DECIMAL, TRUE, status));
|
||||
if (U_FAILURE(status)) {
|
||||
return NULL;
|
||||
}
|
||||
LocalPointer<PluralRules> pluralRules(PluralRules::forLocale(inLocale, status));
|
||||
if (U_FAILURE(status)) {
|
||||
return NULL;
|
||||
}
|
||||
const CDFLocaleStyleData* data = getCDFLocaleStyleData(inLocale, style, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return NULL;
|
||||
}
|
||||
CompactDecimalFormat* result =
|
||||
new CompactDecimalFormat(*decfmt, data->unitsByVariant, data->divisors, pluralRules.getAlias());
|
||||
if (result == NULL) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return NULL;
|
||||
}
|
||||
pluralRules.orphan();
|
||||
result->setMaximumSignificantDigits(3);
|
||||
result->setSignificantDigitsUsed(TRUE);
|
||||
result->setGroupingUsed(FALSE);
|
||||
return result;
|
||||
}
|
||||
|
||||
CompactDecimalFormat&
|
||||
CompactDecimalFormat::operator=(const CompactDecimalFormat& rhs) {
|
||||
if (this != &rhs) {
|
||||
DecimalFormat::operator=(rhs);
|
||||
_unitsByVariant = rhs._unitsByVariant;
|
||||
_divisors = rhs._divisors;
|
||||
delete _pluralRules;
|
||||
_pluralRules = rhs._pluralRules->clone();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
CompactDecimalFormat::~CompactDecimalFormat() {
|
||||
delete _pluralRules;
|
||||
}
|
||||
|
||||
|
||||
Format*
|
||||
CompactDecimalFormat::clone(void) const {
|
||||
return new CompactDecimalFormat(*this);
|
||||
}
|
||||
|
||||
UBool
|
||||
CompactDecimalFormat::operator==(const Format& that) const {
|
||||
if (this == &that) {
|
||||
return TRUE;
|
||||
}
|
||||
return (DecimalFormat::operator==(that) && eqHelper((const CompactDecimalFormat&) that));
|
||||
}
|
||||
|
||||
UBool
|
||||
CompactDecimalFormat::eqHelper(const CompactDecimalFormat& that) const {
|
||||
return uhash_equals(_unitsByVariant, that._unitsByVariant) && divisors_equal(_divisors, that._divisors) && (*_pluralRules == *that._pluralRules);
|
||||
}
|
||||
|
||||
UnicodeString&
|
||||
CompactDecimalFormat::format(
|
||||
double number,
|
||||
UnicodeString& appendTo,
|
||||
FieldPosition& pos) const {
|
||||
DigitList orig, rounded;
|
||||
orig.set(number);
|
||||
UBool isNegative;
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
_round(orig, rounded, isNegative, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
double roundedDouble = rounded.getDouble();
|
||||
if (isNegative) {
|
||||
roundedDouble = -roundedDouble;
|
||||
}
|
||||
int32_t baseIdx = computeLog10(roundedDouble, TRUE);
|
||||
double numberToFormat = roundedDouble / _divisors[baseIdx];
|
||||
UnicodeString variant = _pluralRules->select(numberToFormat);
|
||||
if (isNegative) {
|
||||
numberToFormat = -numberToFormat;
|
||||
}
|
||||
const CDFUnit* unit = getCDFUnitFallback(_unitsByVariant, variant, baseIdx);
|
||||
appendTo += unit->prefix;
|
||||
DecimalFormat::format(numberToFormat, appendTo, pos);
|
||||
appendTo += unit->suffix;
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
UnicodeString&
|
||||
CompactDecimalFormat::format(
|
||||
double /* number */,
|
||||
UnicodeString& appendTo,
|
||||
FieldPositionIterator* /* posIter */,
|
||||
UErrorCode& status) const {
|
||||
status = U_UNSUPPORTED_ERROR;
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
UnicodeString&
|
||||
CompactDecimalFormat::format(
|
||||
int64_t number,
|
||||
UnicodeString& appendTo,
|
||||
FieldPosition& pos) const {
|
||||
return format((double) number, appendTo, pos);
|
||||
}
|
||||
|
||||
UnicodeString&
|
||||
CompactDecimalFormat::format(
|
||||
int64_t /* number */,
|
||||
UnicodeString& appendTo,
|
||||
FieldPositionIterator* /* posIter */,
|
||||
UErrorCode& status) const {
|
||||
status = U_UNSUPPORTED_ERROR;
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
UnicodeString&
|
||||
CompactDecimalFormat::format(
|
||||
const StringPiece& /* number */,
|
||||
UnicodeString& appendTo,
|
||||
FieldPositionIterator* /* posIter */,
|
||||
UErrorCode& status) const {
|
||||
status = U_UNSUPPORTED_ERROR;
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
UnicodeString&
|
||||
CompactDecimalFormat::format(
|
||||
const DigitList& /* number */,
|
||||
UnicodeString& appendTo,
|
||||
FieldPositionIterator* /* posIter */,
|
||||
UErrorCode& status) const {
|
||||
status = U_UNSUPPORTED_ERROR;
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
UnicodeString&
|
||||
CompactDecimalFormat::format(const DigitList& /* number */,
|
||||
UnicodeString& appendTo,
|
||||
FieldPosition& /* pos */,
|
||||
UErrorCode& status) const {
|
||||
status = U_UNSUPPORTED_ERROR;
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
void
|
||||
CompactDecimalFormat::parse(
|
||||
const UnicodeString& /* text */,
|
||||
Formattable& /* result */,
|
||||
ParsePosition& /* parsePosition */) const {
|
||||
}
|
||||
|
||||
void
|
||||
CompactDecimalFormat::parse(
|
||||
const UnicodeString& /* text */,
|
||||
Formattable& /* result */,
|
||||
UErrorCode& status) const {
|
||||
status = U_UNSUPPORTED_ERROR;
|
||||
}
|
||||
|
||||
CurrencyAmount*
|
||||
CompactDecimalFormat::parseCurrency(
|
||||
const UnicodeString& /* text */,
|
||||
ParsePosition& /* pos */) const {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CDFLocaleStyleData::Init(UErrorCode& status) {
|
||||
if (unitsByVariant != NULL) {
|
||||
return;
|
||||
}
|
||||
unitsByVariant = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
uhash_setKeyDeleter(unitsByVariant, uprv_free);
|
||||
uhash_setValueDeleter(unitsByVariant, deleteCDFUnits);
|
||||
}
|
||||
|
||||
CDFLocaleStyleData::~CDFLocaleStyleData() {
|
||||
setToBogus();
|
||||
}
|
||||
|
||||
void CDFLocaleStyleData::setToBogus() {
|
||||
if (unitsByVariant != NULL) {
|
||||
uhash_close(unitsByVariant);
|
||||
unitsByVariant = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CDFLocaleData::Init(UErrorCode& status) {
|
||||
shortData.Init(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
longData.Init(status);
|
||||
}
|
||||
|
||||
// Helper method for operator=
|
||||
static UBool divisors_equal(const double* lhs, const double* rhs) {
|
||||
for (int32_t i = 0; i < MAX_DIGITS; i++) {
|
||||
if (lhs[i] != rhs[i]) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// getCDFLocaleStyleData returns pointer to formatting data for given locale and
|
||||
// style within the global cache. On cache miss, getCDFLocaleStyleData loads
|
||||
// the data from CLDR into the global cache before returning the pointer. If a
|
||||
// UNUM_LONG data is requested for a locale, and that locale does not have
|
||||
// UNUM_LONG data, getCDFLocaleStyleData will fall back to UNUM_SHORT data for
|
||||
// that locale.
|
||||
static const CDFLocaleStyleData* getCDFLocaleStyleData(const Locale& inLocale, UNumberCompactStyle style, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return NULL;
|
||||
}
|
||||
CDFLocaleData* result = NULL;
|
||||
const char* key = inLocale.getName();
|
||||
{
|
||||
Mutex lock(&gCompactDecimalMetaLock);
|
||||
if (gCompactDecimalData == NULL) {
|
||||
gCompactDecimalData = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
return NULL;
|
||||
}
|
||||
uhash_setKeyDeleter(gCompactDecimalData, uprv_free);
|
||||
uhash_setValueDeleter(gCompactDecimalData, deleteCDFLocaleData);
|
||||
ucln_i18n_registerCleanup(UCLN_I18N_CDFINFO, cdf_cleanup);
|
||||
} else {
|
||||
result = (CDFLocaleData*) uhash_get(gCompactDecimalData, key);
|
||||
}
|
||||
}
|
||||
if (result != NULL) {
|
||||
return extractDataByStyleEnum(*result, style, status);
|
||||
}
|
||||
|
||||
result = loadCDFLocaleData(inLocale, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
{
|
||||
Mutex lock(&gCompactDecimalMetaLock);
|
||||
CDFLocaleData* temp = (CDFLocaleData*) uhash_get(gCompactDecimalData, key);
|
||||
if (temp != NULL) {
|
||||
delete result;
|
||||
result = temp;
|
||||
} else {
|
||||
uhash_put(gCompactDecimalData, uprv_strdup(key), (void*) result, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return extractDataByStyleEnum(*result, style, status);
|
||||
}
|
||||
|
||||
static const CDFLocaleStyleData* extractDataByStyleEnum(const CDFLocaleData& data, UNumberCompactStyle style, UErrorCode& status) {
|
||||
switch (style) {
|
||||
case UNUM_SHORT:
|
||||
return &data.shortData;
|
||||
case UNUM_LONG:
|
||||
if (!data.longData.isBogus()) {
|
||||
return &data.longData;
|
||||
}
|
||||
return &data.shortData;
|
||||
default:
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// loadCDFLocaleData loads formatting data from CLDR for a given locale. The
|
||||
// caller owns the returned pointer.
|
||||
static CDFLocaleData* loadCDFLocaleData(const Locale& inLocale, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return NULL;
|
||||
}
|
||||
CDFLocaleData* result = new CDFLocaleData;
|
||||
if (result == NULL) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return NULL;
|
||||
}
|
||||
result->Init(status);
|
||||
if (U_FAILURE(status)) {
|
||||
delete result;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
initCDFLocaleData(inLocale, result, status);
|
||||
if (U_FAILURE(status)) {
|
||||
delete result;
|
||||
return NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// initCDFLocaleData initializes result with data from CLDR.
|
||||
// inLocale is the locale, the CLDR data is stored in result.
|
||||
// First we load the UNUM_SHORT data looking first in local numbering
|
||||
// system and not including root locale in fallback. Next we try in the latn
|
||||
// numbering system where we fallback all the way to root. So we find the
|
||||
// short data in one of 3 places: the local numbering system, the latn
|
||||
// numbering system non root, latn numbering system root locale.
|
||||
// Next we look for the UNUM_LONG data in the same way except that if we don't
|
||||
// find the UNUM_LONG data before we get to where we found the UNUM_SHORT data
|
||||
// we mark our UNUM_LONG data bogus so that it will fallback to what we have
|
||||
// for UNUM_SHORT.
|
||||
static void initCDFLocaleData(const Locale& inLocale, CDFLocaleData* result, UErrorCode& status) {
|
||||
LocalPointer<NumberingSystem> ns(NumberingSystem::createInstance(inLocale, status));
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
const char* numberingSystemName = ns->getName();
|
||||
UResourceBundle* rb = ures_open(NULL, inLocale.getName(), &status);
|
||||
rb = ures_getByKeyWithFallback(rb, gNumberElementsTag, rb, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
ures_close(rb);
|
||||
return;
|
||||
}
|
||||
LocalUResourceBundlePointer localResource;
|
||||
LocalUResourceBundlePointer latnResource;
|
||||
UResourceBundle* dataFillIn = NULL;
|
||||
UResourceBundle* data = NULL;
|
||||
|
||||
// Look in local numbering system first for UNUM_SHORT if it is not latn
|
||||
DataLocation shortLocation = LOCAL_LOC;
|
||||
if (uprv_strcmp(numberingSystemName, gLatnTag) != 0) {
|
||||
localResource.adoptInstead(tryGetByKeyWithFallback(rb, numberingSystemName, NULL, NOT_ROOT, status));
|
||||
data = tryGetDecimalFallback(localResource.getAlias(), gPatternsShort, &dataFillIn, NOT_ROOT, status);
|
||||
}
|
||||
// If we haven't found UNUM_SHORT look in latn numbering system. We must
|
||||
// succeed at finding UNUM_SHORT here.
|
||||
if (data == NULL) {
|
||||
latnResource.adoptInstead(tryGetByKeyWithFallback(rb, gLatnTag, NULL, MUST, status));
|
||||
data = tryGetDecimalFallback(latnResource.getAlias(), gPatternsShort, &dataFillIn, MUST, status);
|
||||
shortLocation = isRoot(data, status) ? ROOT_LOC : LATIN_LOC;
|
||||
}
|
||||
initCDFLocaleStyleData(data, &result->shortData, status);
|
||||
if (U_FAILURE(status)) {
|
||||
ures_close(dataFillIn);
|
||||
ures_close(rb);
|
||||
return;
|
||||
}
|
||||
data = NULL;
|
||||
|
||||
// Look for UNUM_LONG data in local numbering system first.
|
||||
data = tryGetDecimalFallback(localResource.getAlias(), gPatternsLong, &dataFillIn, NOT_ROOT, status);
|
||||
|
||||
// If we haven't found UNUM_LONG and we found the UNUM_SHORT data in the latn
|
||||
// Numbering system, continue. If we find UNUM_LONG in the latin numbering
|
||||
// system, we have to be sure that we didn't find it after where we found
|
||||
// UNUM_SHORT.
|
||||
if (data == NULL && shortLocation != LOCAL_LOC) {
|
||||
data = tryGetDecimalFallback(latnResource.getAlias(), gPatternsLong, &dataFillIn, ANY, status);
|
||||
if (data != NULL) {
|
||||
if (shortLocation == LATIN_LOC && isRoot(data, status)) {
|
||||
data = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data == NULL) {
|
||||
result->longData.setToBogus();
|
||||
} else {
|
||||
initCDFLocaleStyleData(data, &result->longData, status);
|
||||
}
|
||||
ures_close(dataFillIn);
|
||||
ures_close(rb);
|
||||
}
|
||||
|
||||
/**
|
||||
* tryGetDecimalFallback attempts to fetch the "decimalFormat" resource bundle
|
||||
* with a particular style. style is either "patternsShort" or "patternsLong."
|
||||
* FillIn, flags, and status work in the same way as in tryGetByKeyWithFallback.
|
||||
*/
|
||||
static UResourceBundle* tryGetDecimalFallback(const UResourceBundle* numberSystemResource, const char* style, UResourceBundle** fillIn, FallbackFlags flags, UErrorCode& status) {
|
||||
UResourceBundle* first = tryGetByKeyWithFallback(numberSystemResource, style, fillIn, flags, status);
|
||||
UResourceBundle* second = tryGetByKeyWithFallback(first, gDecimalFormatTag, fillIn, flags, status);
|
||||
if (fillIn == NULL) {
|
||||
ures_close(first);
|
||||
}
|
||||
return second;
|
||||
}
|
||||
|
||||
// tryGetByKeyWithFallback returns a sub-resource bundle that matches given
|
||||
// criteria or NULL if none found. rb is the resource bundle that we are
|
||||
// searching. If rb == NULL then this function behaves as if no sub-resource
|
||||
// is found; path is the key of the sub-resource,
|
||||
// (i.e "foo" but not "foo/bar"); If fillIn is NULL, caller must always call
|
||||
// ures_close() on returned resource. See below for example when fillIn is
|
||||
// not NULL. flags is ANY or NOT_ROOT. Optionally, these values
|
||||
// can be ored with MUST. MUST by itself is the same as ANY | MUST.
|
||||
// The locale of the returned sub-resource will either match the
|
||||
// flags or the returned sub-resouce will be NULL. If MUST is included in
|
||||
// flags, and not suitable sub-resource is found then in addition to returning
|
||||
// NULL, this function also sets status to U_MISSING_RESOURCE_ERROR. If MUST
|
||||
// is not included in flags, then this function just returns NULL if no
|
||||
// such sub-resource is found and will never set status to
|
||||
// U_MISSING_RESOURCE_ERROR.
|
||||
//
|
||||
// Example: This code first searches for "foo/bar" sub-resource without falling
|
||||
// back to ROOT. Then searches for "baz" sub-resource as last resort.
|
||||
//
|
||||
// UResourcebundle* fillIn = NULL;
|
||||
// UResourceBundle* data = tryGetByKeyWithFallback(rb, "foo", &fillIn, NON_ROOT, status);
|
||||
// data = tryGetByKeyWithFallback(data, "bar", &fillIn, NON_ROOT, status);
|
||||
// if (!data) {
|
||||
// data = tryGetbyKeyWithFallback(rb, "baz", &fillIn, MUST, status);
|
||||
// }
|
||||
// if (U_FAILURE(status)) {
|
||||
// ures_close(fillIn);
|
||||
// return;
|
||||
// }
|
||||
// doStuffWithNonNullSubresource(data);
|
||||
//
|
||||
// /* Wrong! don't do the following as it can leak memory if fillIn gets set
|
||||
// to NULL. */
|
||||
// fillIn = tryGetByKeyWithFallback(rb, "wrong", &fillIn, ANY, status);
|
||||
//
|
||||
// ures_close(fillIn);
|
||||
//
|
||||
static UResourceBundle* tryGetByKeyWithFallback(const UResourceBundle* rb, const char* path, UResourceBundle** fillIn, FallbackFlags flags, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return NULL;
|
||||
}
|
||||
UBool must = (flags & MUST);
|
||||
if (rb == NULL) {
|
||||
if (must) {
|
||||
status = U_MISSING_RESOURCE_ERROR;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
UResourceBundle* result = NULL;
|
||||
UResourceBundle* ownedByUs = NULL;
|
||||
if (fillIn == NULL) {
|
||||
ownedByUs = ures_getByKeyWithFallback(rb, path, NULL, &status);
|
||||
result = ownedByUs;
|
||||
} else {
|
||||
*fillIn = ures_getByKeyWithFallback(rb, path, *fillIn, &status);
|
||||
result = *fillIn;
|
||||
}
|
||||
if (U_FAILURE(status)) {
|
||||
ures_close(ownedByUs);
|
||||
if (status == U_MISSING_RESOURCE_ERROR && !must) {
|
||||
status = U_ZERO_ERROR;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
flags = (FallbackFlags) (flags & ~MUST);
|
||||
switch (flags) {
|
||||
case NOT_ROOT:
|
||||
{
|
||||
UBool bRoot = isRoot(result, status);
|
||||
if (bRoot || U_FAILURE(status)) {
|
||||
ures_close(ownedByUs);
|
||||
if (must && (status == U_ZERO_ERROR)) {
|
||||
status = U_MISSING_RESOURCE_ERROR;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
case ANY:
|
||||
return result;
|
||||
default:
|
||||
ures_close(ownedByUs);
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static UBool isRoot(const UResourceBundle* rb, UErrorCode& status) {
|
||||
const char* actualLocale = ures_getLocaleByType(
|
||||
rb, ULOC_ACTUAL_LOCALE, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
return FALSE;
|
||||
}
|
||||
return uprv_strcmp(actualLocale, gRoot) == 0;
|
||||
}
|
||||
|
||||
|
||||
// initCDFLocaleStyleData loads formatting data for a particular style.
|
||||
// decimalFormatBundle is the "decimalFormat" resource bundle in CLDR.
|
||||
// Loaded data stored in result.
|
||||
static void initCDFLocaleStyleData(const UResourceBundle* decimalFormatBundle, CDFLocaleStyleData* result, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
// Iterate through all the powers of 10.
|
||||
int32_t size = ures_getSize(decimalFormatBundle);
|
||||
UResourceBundle* power10 = NULL;
|
||||
for (int32_t i = 0; i < size; ++i) {
|
||||
power10 = ures_getByIndex(decimalFormatBundle, i, power10, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
ures_close(power10);
|
||||
return;
|
||||
}
|
||||
populatePower10(power10, result, status);
|
||||
if (U_FAILURE(status)) {
|
||||
ures_close(power10);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ures_close(power10);
|
||||
fillInMissing(result);
|
||||
}
|
||||
|
||||
// populatePower10 grabs data for a particular power of 10 from CLDR.
|
||||
// The loaded data is stored in result.
|
||||
static void populatePower10(const UResourceBundle* power10Bundle, CDFLocaleStyleData* result, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
char* endPtr = NULL;
|
||||
double power10 = uprv_strtod(ures_getKey(power10Bundle), &endPtr);
|
||||
if (*endPtr != 0) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return;
|
||||
}
|
||||
int32_t log10Value = computeLog10(power10, FALSE);
|
||||
// Silently ignore divisors that are too big.
|
||||
if (log10Value == MAX_DIGITS) {
|
||||
return;
|
||||
}
|
||||
int32_t size = ures_getSize(power10Bundle);
|
||||
int32_t numZeros = 0;
|
||||
UBool otherVariantDefined = FALSE;
|
||||
UResourceBundle* variantBundle = NULL;
|
||||
// Iterate over all the plural variants for the power of 10
|
||||
for (int i = 0; i < size; ++i) {
|
||||
variantBundle = ures_getByIndex(power10Bundle, i, variantBundle, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
ures_close(variantBundle);
|
||||
return;
|
||||
}
|
||||
const char* variant = ures_getKey(variantBundle);
|
||||
int32_t resLen;
|
||||
const UChar* formatStrP = ures_getString(variantBundle, &resLen, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
ures_close(variantBundle);
|
||||
return;
|
||||
}
|
||||
UnicodeString formatStr(false, formatStrP, resLen);
|
||||
if (uprv_strcmp(variant, gOther) == 0) {
|
||||
otherVariantDefined = TRUE;
|
||||
}
|
||||
int nz = populatePrefixSuffix(
|
||||
variant, log10Value, formatStr, result->unitsByVariant, status);
|
||||
if (U_FAILURE(status)) {
|
||||
ures_close(variantBundle);
|
||||
return;
|
||||
}
|
||||
if (nz != numZeros) {
|
||||
// We expect all format strings to have the same number of 0's
|
||||
// left of the decimal point.
|
||||
if (numZeros != 0) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
ures_close(variantBundle);
|
||||
return;
|
||||
}
|
||||
numZeros = nz;
|
||||
}
|
||||
}
|
||||
ures_close(variantBundle);
|
||||
// We expect to find an OTHER variant for each power of 10.
|
||||
if (!otherVariantDefined) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return;
|
||||
}
|
||||
long divisor = power10;
|
||||
for (int i = 1; i < numZeros; i++) {
|
||||
divisor /= 10.0;
|
||||
}
|
||||
result->divisors[log10Value] = divisor;
|
||||
}
|
||||
|
||||
// populatePrefixSuffix Adds a specific prefix-suffix pair to result for a
|
||||
// given variant and log10 value.
|
||||
// variant is 'zero', 'one', 'two', 'few', 'many', or 'other'.
|
||||
// formatStr is the format string from which the prefix and suffix are
|
||||
// extracted. It is usually of form 'Pefix 000 suffix'.
|
||||
// populatePrefixSuffix returns the number of 0's found in formatStr
|
||||
// before the decimal point.
|
||||
// In the special case that formatStr contains only spaces for prefix
|
||||
// and suffix, populatePrefixSuffix returns log10Value + 1.
|
||||
static int32_t populatePrefixSuffix(
|
||||
const char* variant, int32_t log10Value, const UnicodeString& formatStr, UHashtable* result, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
int32_t firstIdx = formatStr.indexOf(kZero, LENGTHOF(kZero), 0);
|
||||
// We must have 0's in format string.
|
||||
if (firstIdx == -1) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return 0;
|
||||
}
|
||||
int32_t lastIdx = formatStr.lastIndexOf(kZero, LENGTHOF(kZero), firstIdx);
|
||||
CDFUnit* unit = createCDFUnit(variant, log10Value, result, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
// Everything up to first 0 is the prefix
|
||||
unit->prefix = formatStr.tempSubString(0, firstIdx);
|
||||
fixQuotes(unit->prefix);
|
||||
// Everything beyond the last 0 is the suffix
|
||||
unit->suffix = formatStr.tempSubString(lastIdx + 1);
|
||||
fixQuotes(unit->suffix);
|
||||
|
||||
// If there is effectively no prefix or suffix, ignore the actual number of
|
||||
// 0's and act as if the number of 0's matches the size of the number.
|
||||
if (onlySpaces(unit->prefix) && onlySpaces(unit->suffix)) {
|
||||
return log10Value + 1;
|
||||
}
|
||||
|
||||
// Calculate number of zeros before decimal point
|
||||
int32_t idx = firstIdx + 1;
|
||||
while (idx <= lastIdx && formatStr.charAt(idx) == u_0) {
|
||||
++idx;
|
||||
}
|
||||
return (idx - firstIdx);
|
||||
}
|
||||
|
||||
static UBool onlySpaces(UnicodeString u) {
|
||||
return u.trim().length() == 0;
|
||||
}
|
||||
|
||||
// fixQuotes unescapes single quotes. Don''t -> Don't. Letter 'j' -> Letter j.
|
||||
// Modifies s in place.
|
||||
static void fixQuotes(UnicodeString& s) {
|
||||
QuoteState state = OUTSIDE;
|
||||
int32_t len = s.length();
|
||||
int32_t dest = 0;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
UChar ch = s.charAt(i);
|
||||
if (ch == u_apos) {
|
||||
if (state == INSIDE_EMPTY) {
|
||||
s.setCharAt(dest, ch);
|
||||
++dest;
|
||||
}
|
||||
} else {
|
||||
s.setCharAt(dest, ch);
|
||||
++dest;
|
||||
}
|
||||
|
||||
// Update state
|
||||
switch (state) {
|
||||
case OUTSIDE:
|
||||
state = ch == u_apos ? INSIDE_EMPTY : OUTSIDE;
|
||||
break;
|
||||
case INSIDE_EMPTY:
|
||||
case INSIDE_FULL:
|
||||
state = ch == u_apos ? OUTSIDE : INSIDE_FULL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
s.truncate(dest);
|
||||
}
|
||||
|
||||
// fillInMissing ensures that the data in result is complete.
|
||||
// result data is complete if for each variant in result, there exists
|
||||
// a prefix-suffix pair for each log10 value and there also exists
|
||||
// a divisor for each log10 value.
|
||||
//
|
||||
// First this function figures out for which log10 values, the other
|
||||
// variant already had data. These are the same log10 values defined
|
||||
// in CLDR.
|
||||
//
|
||||
// For each log10 value not defined in CLDR, it uses the divisor for
|
||||
// the last defined log10 value or 1.
|
||||
//
|
||||
// Then for each variant, it does the following. For each log10
|
||||
// value not defined in CLDR, copy the prefix-suffix pair from the
|
||||
// previous log10 value. If log10 value is defined in CLDR but is
|
||||
// missing from given variant, copy the prefix-suffix pair for that
|
||||
// log10 value from the 'other' variant.
|
||||
static void fillInMissing(CDFLocaleStyleData* result) {
|
||||
const CDFUnit* otherUnits =
|
||||
(const CDFUnit*) uhash_get(result->unitsByVariant, gOther);
|
||||
UBool definedInCLDR[MAX_DIGITS];
|
||||
double lastDivisor = 1.0;
|
||||
for (int i = 0; i < MAX_DIGITS; ++i) {
|
||||
if (!otherUnits[i].isSet()) {
|
||||
result->divisors[i] = lastDivisor;
|
||||
definedInCLDR[i] = FALSE;
|
||||
} else {
|
||||
lastDivisor = result->divisors[i];
|
||||
definedInCLDR[i] = TRUE;
|
||||
}
|
||||
}
|
||||
// Iterate over each variant.
|
||||
int32_t pos = -1;
|
||||
const UHashElement* element = uhash_nextElement(result->unitsByVariant, &pos);
|
||||
for (;element != NULL; element = uhash_nextElement(result->unitsByVariant, &pos)) {
|
||||
CDFUnit* units = (CDFUnit*) element->value.pointer;
|
||||
for (int32_t i = 0; i < MAX_DIGITS; ++i) {
|
||||
if (definedInCLDR[i]) {
|
||||
if (!units[i].isSet()) {
|
||||
units[i] = otherUnits[i];
|
||||
}
|
||||
} else {
|
||||
if (i == 0) {
|
||||
units[0].markAsSet();
|
||||
} else {
|
||||
units[i] = units[i - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// computeLog10 computes floor(log10(x)). If inRange is TRUE, the biggest
|
||||
// value computeLog10 will return MAX_DIGITS -1 even for
|
||||
// numbers > 10^MAX_DIGITS. If inRange is FALSE, computeLog10 will return
|
||||
// up to MAX_DIGITS.
|
||||
static int32_t computeLog10(double x, UBool inRange) {
|
||||
int32_t result = 0;
|
||||
int32_t max = inRange ? MAX_DIGITS - 1 : MAX_DIGITS;
|
||||
while (x >= 10.0) {
|
||||
x /= 10.0;
|
||||
++result;
|
||||
if (result == max) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// createCDFUnit returns a pointer to the prefix-suffix pair for a given
|
||||
// variant and log10 value within table. If no such prefix-suffix pair is
|
||||
// stored in table, one is created within table before returning pointer.
|
||||
static CDFUnit* createCDFUnit(const char* variant, int32_t log10Value, UHashtable* table, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return NULL;
|
||||
}
|
||||
CDFUnit *cdfUnit = (CDFUnit*) uhash_get(table, variant);
|
||||
if (cdfUnit == NULL) {
|
||||
cdfUnit = new CDFUnit[MAX_DIGITS];
|
||||
if (cdfUnit == NULL) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return NULL;
|
||||
}
|
||||
uhash_put(table, uprv_strdup(variant), cdfUnit, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
CDFUnit* result = &cdfUnit[log10Value];
|
||||
result->markAsSet();
|
||||
return result;
|
||||
}
|
||||
|
||||
// getCDFUnitFallback returns a pointer to the prefix-suffix pair for a given
|
||||
// variant and log10 value within table. If the given variant doesn't exist, it
|
||||
// falls back to the OTHER variant. Therefore, this method will always return
|
||||
// some non-NULL value.
|
||||
static const CDFUnit* getCDFUnitFallback(const UHashtable* table, const UnicodeString& variant, int32_t log10Value) {
|
||||
CharString cvariant;
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
const CDFUnit *cdfUnit = NULL;
|
||||
cvariant.appendInvariantChars(variant, status);
|
||||
if (!U_FAILURE(status)) {
|
||||
cdfUnit = (const CDFUnit*) uhash_get(table, cvariant.data());
|
||||
}
|
||||
if (cdfUnit == NULL) {
|
||||
cdfUnit = (const CDFUnit*) uhash_get(table, gOther);
|
||||
}
|
||||
return &cdfUnit[log10Value];
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
||||
#endif
|
|
@ -1319,7 +1319,74 @@ DecimalFormat::format(const DigitList &number,
|
|||
return appendTo;
|
||||
}
|
||||
|
||||
DigitList&
|
||||
DecimalFormat::_round(const DigitList &number, DigitList &adjustedNum, UBool& isNegative, UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return adjustedNum;
|
||||
}
|
||||
adjustedNum = number;
|
||||
isNegative = false;
|
||||
if (number.isNaN()) {
|
||||
return adjustedNum;
|
||||
}
|
||||
|
||||
// Do this BEFORE checking to see if value is infinite or negative! Sets the
|
||||
// begin and end index to be length of the string composed of
|
||||
// localized name of Infinite and the positive/negative localized
|
||||
// signs.
|
||||
|
||||
adjustedNum.setRoundingMode(fRoundingMode);
|
||||
if (fMultiplier != NULL) {
|
||||
adjustedNum.mult(*fMultiplier, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return adjustedNum;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: sign is important for zero as well as non-zero numbers.
|
||||
* Proper detection of -0.0 is needed to deal with the
|
||||
* issues raised by bugs 4106658, 4106667, and 4147706. Liu 7/6/98.
|
||||
*/
|
||||
isNegative = !adjustedNum.isPositive();
|
||||
|
||||
// Apply rounding after multiplier
|
||||
|
||||
adjustedNum.fContext.status &= ~DEC_Inexact;
|
||||
if (fRoundingIncrement != NULL) {
|
||||
adjustedNum.div(*fRoundingIncrement, status);
|
||||
adjustedNum.toIntegralValue();
|
||||
adjustedNum.mult(*fRoundingIncrement, status);
|
||||
adjustedNum.trim();
|
||||
if (U_FAILURE(status)) {
|
||||
return adjustedNum;
|
||||
}
|
||||
}
|
||||
if (fRoundingMode == kRoundUnnecessary && (adjustedNum.fContext.status & DEC_Inexact)) {
|
||||
status = U_FORMAT_INEXACT_ERROR;
|
||||
return adjustedNum;
|
||||
}
|
||||
|
||||
if (adjustedNum.isInfinite()) {
|
||||
return adjustedNum;
|
||||
}
|
||||
|
||||
if (fUseExponentialNotation || areSignificantDigitsUsed()) {
|
||||
int32_t sigDigits = precision();
|
||||
if (sigDigits > 0) {
|
||||
adjustedNum.round(sigDigits);
|
||||
}
|
||||
} else {
|
||||
// Fixed point format. Round to a set number of fraction digits.
|
||||
int32_t numFractionDigits = precision();
|
||||
adjustedNum.roundFixedPoint(numFractionDigits);
|
||||
}
|
||||
if (fRoundingMode == kRoundUnnecessary && (adjustedNum.fContext.status & DEC_Inexact)) {
|
||||
status = U_FORMAT_INEXACT_ERROR;
|
||||
return adjustedNum;
|
||||
}
|
||||
return adjustedNum;
|
||||
}
|
||||
|
||||
UnicodeString&
|
||||
DecimalFormat::_format(const DigitList &number,
|
||||
|
@ -1327,6 +1394,10 @@ DecimalFormat::_format(const DigitList &number,
|
|||
FieldPositionHandler& handler,
|
||||
UErrorCode &status) const
|
||||
{
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
// Special case for NaN, sets the begin and end index to be the
|
||||
// the string length of localized name of NaN.
|
||||
if (number.isNaN())
|
||||
|
@ -1340,38 +1411,12 @@ DecimalFormat::_format(const DigitList &number,
|
|||
return appendTo;
|
||||
}
|
||||
|
||||
// Do this BEFORE checking to see if value is infinite or negative! Sets the
|
||||
// begin and end index to be length of the string composed of
|
||||
// localized name of Infinite and the positive/negative localized
|
||||
// signs.
|
||||
|
||||
DigitList adjustedNum(number); // Copy, so we do not alter the original.
|
||||
adjustedNum.setRoundingMode(fRoundingMode);
|
||||
if (fMultiplier != NULL) {
|
||||
adjustedNum.mult(*fMultiplier, status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: sign is important for zero as well as non-zero numbers.
|
||||
* Proper detection of -0.0 is needed to deal with the
|
||||
* issues raised by bugs 4106658, 4106667, and 4147706. Liu 7/6/98.
|
||||
*/
|
||||
UBool isNegative = !adjustedNum.isPositive();
|
||||
|
||||
// Apply rounding after multiplier
|
||||
|
||||
adjustedNum.fContext.status &= ~DEC_Inexact;
|
||||
if (fRoundingIncrement != NULL) {
|
||||
adjustedNum.div(*fRoundingIncrement, status);
|
||||
adjustedNum.toIntegralValue();
|
||||
adjustedNum.mult(*fRoundingIncrement, status);
|
||||
adjustedNum.trim();
|
||||
}
|
||||
if (fRoundingMode == kRoundUnnecessary && (adjustedNum.fContext.status & DEC_Inexact)) {
|
||||
status = U_FORMAT_INEXACT_ERROR;
|
||||
DigitList adjustedNum;
|
||||
UBool isNegative;
|
||||
_round(number, adjustedNum, isNegative, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
|
||||
// Special case for INFINITE,
|
||||
if (adjustedNum.isInfinite()) {
|
||||
|
@ -1387,26 +1432,9 @@ DecimalFormat::_format(const DigitList &number,
|
|||
addPadding(appendTo, handler, prefixLen, suffixLen);
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
if (fUseExponentialNotation || areSignificantDigitsUsed()) {
|
||||
int32_t sigDigits = precision();
|
||||
if (sigDigits > 0) {
|
||||
adjustedNum.round(sigDigits);
|
||||
}
|
||||
} else {
|
||||
// Fixed point format. Round to a set number of fraction digits.
|
||||
int32_t numFractionDigits = precision();
|
||||
adjustedNum.roundFixedPoint(numFractionDigits);
|
||||
}
|
||||
if (fRoundingMode == kRoundUnnecessary && (adjustedNum.fContext.status & DEC_Inexact)) {
|
||||
status = U_FORMAT_INEXACT_ERROR;
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
return subformat(appendTo, handler, adjustedNum, FALSE, status);
|
||||
}
|
||||
|
||||
|
||||
UnicodeString&
|
||||
DecimalFormat::format( const Formattable& obj,
|
||||
UnicodeString& appendTo,
|
||||
|
|
|
@ -1140,8 +1140,15 @@ NumberFormat::isStyleSupported(UNumberFormatStyle style) {
|
|||
NumberFormat*
|
||||
NumberFormat::makeInstance(const Locale& desiredLocale,
|
||||
UNumberFormatStyle style,
|
||||
UErrorCode& status)
|
||||
{
|
||||
UErrorCode& status) {
|
||||
return makeInstance(desiredLocale, style, false, status);
|
||||
}
|
||||
|
||||
NumberFormat*
|
||||
NumberFormat::makeInstance(const Locale& desiredLocale,
|
||||
UNumberFormatStyle style,
|
||||
UBool mustBeDecimalFormat,
|
||||
UErrorCode& status) {
|
||||
if (U_FAILURE(status)) return NULL;
|
||||
|
||||
if (style < 0 || style >= UNUM_FORMAT_STYLE_COUNT) {
|
||||
|
@ -1161,33 +1168,34 @@ NumberFormat::makeInstance(const Locale& desiredLocale,
|
|||
}
|
||||
|
||||
#if U_PLATFORM_USES_ONLY_WIN32_API
|
||||
char buffer[8];
|
||||
int32_t count = desiredLocale.getKeywordValue("compat", buffer, sizeof(buffer), status);
|
||||
if (!mustBeDecimalFormat) {
|
||||
char buffer[8];
|
||||
int32_t count = desiredLocale.getKeywordValue("compat", buffer, sizeof(buffer), status);
|
||||
|
||||
// if the locale has "@compat=host", create a host-specific NumberFormat
|
||||
if (U_SUCCESS(status) && count > 0 && uprv_strcmp(buffer, "host") == 0) {
|
||||
Win32NumberFormat *f = NULL;
|
||||
UBool curr = TRUE;
|
||||
// if the locale has "@compat=host", create a host-specific NumberFormat
|
||||
if (U_SUCCESS(status) && count > 0 && uprv_strcmp(buffer, "host") == 0) {
|
||||
Win32NumberFormat *f = NULL;
|
||||
UBool curr = TRUE;
|
||||
|
||||
switch (style) {
|
||||
case UNUM_DECIMAL:
|
||||
curr = FALSE;
|
||||
// fall-through
|
||||
switch (style) {
|
||||
case UNUM_DECIMAL:
|
||||
curr = FALSE;
|
||||
// fall-through
|
||||
|
||||
case UNUM_CURRENCY:
|
||||
case UNUM_CURRENCY_ISO: // do not support plural formatting here
|
||||
case UNUM_CURRENCY_PLURAL:
|
||||
f = new Win32NumberFormat(desiredLocale, curr, status);
|
||||
case UNUM_CURRENCY:
|
||||
case UNUM_CURRENCY_ISO: // do not support plural formatting here
|
||||
case UNUM_CURRENCY_PLURAL:
|
||||
f = new Win32NumberFormat(desiredLocale, curr, status);
|
||||
|
||||
if (U_SUCCESS(status)) {
|
||||
return f;
|
||||
if (U_SUCCESS(status)) {
|
||||
return f;
|
||||
}
|
||||
|
||||
delete f;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
delete f;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -1245,6 +1253,11 @@ NumberFormat::makeInstance(const Locale& desiredLocale,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (mustBeDecimalFormat && ns->isAlgorithmic()) {
|
||||
status = U_UNSUPPORTED_ERROR;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LocalPointer<DecimalFormatSymbols> symbolsToAdopt;
|
||||
UnicodeString pattern;
|
||||
LocalUResourceBundlePointer ownedResource(ures_open(NULL, desiredLocale.getName(), &status));
|
||||
|
|
|
@ -51,6 +51,7 @@ typedef enum ECleanupI18NType {
|
|||
UCLN_I18N_COLL_DATA,
|
||||
UCLN_I18N_INDEX_CHARACTERS,
|
||||
UCLN_I18N_GENDERINFO,
|
||||
UCLN_I18N_CDFINFO,
|
||||
UCLN_I18N_COUNT /* This must be last */
|
||||
} ECleanupI18NType;
|
||||
|
||||
|
|
330
icu4c/source/i18n/unicode/compactdecimalformat.h
Normal file
330
icu4c/source/i18n/unicode/compactdecimalformat.h
Normal file
|
@ -0,0 +1,330 @@
|
|||
/*
|
||||
********************************************************************************
|
||||
* Copyright (C) 2012, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
********************************************************************************
|
||||
*
|
||||
* File COMPACTDECIMALFORMAT.H
|
||||
********************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef __COMPACT_DECIMAL_FORMAT_H__
|
||||
#define __COMPACT_DECIMAL_FORMAT_H__
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
/**
|
||||
* \file
|
||||
* \brief C++ API: Formats decimal numbers in compact form.
|
||||
*/
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#include "unicode/decimfmt.h"
|
||||
|
||||
struct UHashtable;
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
class PluralRules;
|
||||
|
||||
/**
|
||||
* The CompactDecimalFormat produces abbreviated numbers, suitable for display in
|
||||
* environments will limited real estate. For example, 'Hits: 1.2B' instead of
|
||||
* 'Hits: 1,200,000,000'. The format will be appropriate for the given language,
|
||||
* such as "1,2 Mrd." for German.
|
||||
* <p>
|
||||
* For numbers under 1000 trillion (under 10^15, such as 123,456,789,012,345),
|
||||
* the result will be short for supported languages. However, the result may
|
||||
* sometimes exceed 7 characters, such as when there are combining marks or thin
|
||||
* characters. In such cases, the visual width in fonts should still be short.
|
||||
* <p>
|
||||
* By default, there are 3 significant digits. After creation, if more than
|
||||
* three significant digits are set (with setMaximumSignificantDigits), or if a
|
||||
* fixed number of digits are set (with setMaximumIntegerDigits or
|
||||
* setMaximumFractionDigits), then result may be wider.
|
||||
* <p>
|
||||
* At this time, parsing is not supported, and will produce a U_UNSUPPORTED_ERROR.
|
||||
* Resetting the pattern prefixes or suffixes is not supported; the method calls
|
||||
* are ignored.
|
||||
* <p>
|
||||
*/
|
||||
class U_I18N_API CompactDecimalFormat : public DecimalFormat {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Returns a compact decimal instance for specified locale.
|
||||
* @param inLocale the given locale.
|
||||
* @param style whether to use short or long style.
|
||||
* @param status error code returned here.
|
||||
* @draft ICU 51
|
||||
*/
|
||||
static CompactDecimalFormat* U_EXPORT2 createInstance(
|
||||
const Locale& inLocale, UNumberCompactStyle style, UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
*
|
||||
* @param source the DecimalFormat object to be copied from.
|
||||
* @draft ICU 51
|
||||
*/
|
||||
CompactDecimalFormat(const CompactDecimalFormat& source);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
* @draft ICU 51
|
||||
*/
|
||||
virtual ~CompactDecimalFormat();
|
||||
|
||||
/**
|
||||
* Assignment operator.
|
||||
*
|
||||
* @param rhs the DecimalFormat object to be copied.
|
||||
* @draft ICU 51
|
||||
*/
|
||||
CompactDecimalFormat& operator=(const CompactDecimalFormat& rhs);
|
||||
|
||||
/**
|
||||
* Clone this Format object polymorphically. The caller owns the
|
||||
* result and should delete it when done.
|
||||
*
|
||||
* @return a polymorphic copy of this CompactDecimalFormat.
|
||||
* @draft ICU 51
|
||||
*/
|
||||
virtual Format* clone() const;
|
||||
|
||||
/**
|
||||
* Return TRUE if the given Format objects are semantically equal.
|
||||
* Objects of different subclasses are considered unequal.
|
||||
*
|
||||
* @param other the object to be compared with.
|
||||
* @return TRUE if the given Format objects are semantically equal.
|
||||
* @draft ICU 51
|
||||
*/
|
||||
virtual UBool operator==(const Format& other) const;
|
||||
|
||||
|
||||
using DecimalFormat::format;
|
||||
|
||||
/**
|
||||
* Format a double or long number using base-10 representation.
|
||||
*
|
||||
* @param number The value to be formatted.
|
||||
* @param appendTo Output parameter to receive result.
|
||||
* Result is appended to existing contents.
|
||||
* @param pos On input: an alignment field, if desired.
|
||||
* On output: the offsets of the alignment field.
|
||||
* @return Reference to 'appendTo' parameter.
|
||||
* @draft ICU 51
|
||||
*/
|
||||
virtual UnicodeString& format(double number,
|
||||
UnicodeString& appendTo,
|
||||
FieldPosition& pos) const;
|
||||
|
||||
/**
|
||||
* Format a double or long number using base-10 representation.
|
||||
* Currently sets status to U_UNSUPPORTED_ERROR.
|
||||
*
|
||||
* @param number The value to be formatted.
|
||||
* @param appendTo Output parameter to receive result.
|
||||
* Result is appended to existing contents.
|
||||
* @param posIter On return, can be used to iterate over positions
|
||||
* of fields generated by this format call.
|
||||
* Can be NULL.
|
||||
* @param status Output param filled with success/failure status.
|
||||
* @return Reference to 'appendTo' parameter.
|
||||
* @internal
|
||||
*/
|
||||
virtual UnicodeString& format(double number,
|
||||
UnicodeString& appendTo,
|
||||
FieldPositionIterator* posIter,
|
||||
UErrorCode& status) const;
|
||||
|
||||
/**
|
||||
* Format an int64 number using base-10 representation.
|
||||
*
|
||||
* @param number The value to be formatted.
|
||||
* @param appendTo Output parameter to receive result.
|
||||
* Result is appended to existing contents.
|
||||
* @param pos On input: an alignment field, if desired.
|
||||
* On output: the offsets of the alignment field.
|
||||
* @return Reference to 'appendTo' parameter.
|
||||
* @draft ICU 51
|
||||
*/
|
||||
virtual UnicodeString& format(int64_t number,
|
||||
UnicodeString& appendTo,
|
||||
FieldPosition& pos) const;
|
||||
|
||||
/**
|
||||
* Format an int64 number using base-10 representation.
|
||||
* Currently sets status to U_UNSUPPORTED_ERROR
|
||||
*
|
||||
* @param number The value to be formatted.
|
||||
* @param appendTo Output parameter to receive result.
|
||||
* Result is appended to existing contents.
|
||||
* @param posIter On return, can be used to iterate over positions
|
||||
* of fields generated by this format call.
|
||||
* Can be NULL.
|
||||
* @param status Output param filled with success/failure status.
|
||||
* @return Reference to 'appendTo' parameter.
|
||||
* @internal
|
||||
*/
|
||||
virtual UnicodeString& format(int64_t number,
|
||||
UnicodeString& appendTo,
|
||||
FieldPositionIterator* posIter,
|
||||
UErrorCode& status) const;
|
||||
|
||||
/**
|
||||
* Format a decimal number. Currently sets status to U_UNSUPPORTED_ERROR
|
||||
* The syntax of the unformatted number is a "numeric string"
|
||||
* as defined in the Decimal Arithmetic Specification, available at
|
||||
* http://speleotrove.com/decimal
|
||||
*
|
||||
* @param number The unformatted number, as a string.
|
||||
* @param appendTo Output parameter to receive result.
|
||||
* Result is appended to existing contents.
|
||||
* @param posIter On return, can be used to iterate over positions
|
||||
* of fields generated by this format call.
|
||||
* Can be NULL.
|
||||
* @param status Output param filled with success/failure status.
|
||||
* @return Reference to 'appendTo' parameter.
|
||||
* @internal
|
||||
*/
|
||||
virtual UnicodeString& format(const StringPiece &number,
|
||||
UnicodeString& appendTo,
|
||||
FieldPositionIterator* posIter,
|
||||
UErrorCode& status) const;
|
||||
|
||||
/**
|
||||
* Format a decimal number. Currently sets status to U_UNSUPPORTED_ERROR
|
||||
* The number is a DigitList wrapper onto a floating point decimal number.
|
||||
* The default implementation in NumberFormat converts the decimal number
|
||||
* to a double and formats that.
|
||||
*
|
||||
* @param number The number, a DigitList format Decimal Floating Point.
|
||||
* @param appendTo Output parameter to receive result.
|
||||
* Result is appended to existing contents.
|
||||
* @param posIter On return, can be used to iterate over positions
|
||||
* of fields generated by this format call.
|
||||
* @param status Output param filled with success/failure status.
|
||||
* @return Reference to 'appendTo' parameter.
|
||||
* @internal
|
||||
*/
|
||||
virtual UnicodeString& format(const DigitList &number,
|
||||
UnicodeString& appendTo,
|
||||
FieldPositionIterator* posIter,
|
||||
UErrorCode& status) const;
|
||||
|
||||
/**
|
||||
* Format a decimal number. Currently sets status to U_UNSUPPORTED_ERROR.
|
||||
* The number is a DigitList wrapper onto a floating point decimal number.
|
||||
* The default implementation in NumberFormat converts the decimal number
|
||||
* to a double and formats that.
|
||||
*
|
||||
* @param number The number, a DigitList format Decimal Floating Point.
|
||||
* @param appendTo Output parameter to receive result.
|
||||
* Result is appended to existing contents.
|
||||
* @param pos On input: an alignment field, if desired.
|
||||
* On output: the offsets of the alignment field.
|
||||
* @param status Output param filled with success/failure status.
|
||||
* @return Reference to 'appendTo' parameter.
|
||||
* @internal
|
||||
*/
|
||||
virtual UnicodeString& format(const DigitList &number,
|
||||
UnicodeString& appendTo,
|
||||
FieldPosition& pos,
|
||||
UErrorCode& status) const;
|
||||
|
||||
/**
|
||||
* CompactDecimalFormat does not support parsing. This implementation
|
||||
* does nothing.
|
||||
* @param text Unused.
|
||||
* @param result Does not change.
|
||||
* @param parsePosition Does not change.
|
||||
* @see Formattable
|
||||
* @draft ICU 51
|
||||
*/
|
||||
virtual void parse(const UnicodeString& text,
|
||||
Formattable& result,
|
||||
ParsePosition& parsePosition) const;
|
||||
|
||||
/**
|
||||
* CompactDecimalFormat does not support parsing. This implementation
|
||||
* sets status to U_UNSUPPORTED_ERROR
|
||||
*
|
||||
* @param text Unused.
|
||||
* @param result Does not change.
|
||||
* @param status Always set to U_UNSUPPORTED_ERROR.
|
||||
* @draft ICU 51
|
||||
*/
|
||||
virtual void parse(const UnicodeString& text,
|
||||
Formattable& result,
|
||||
UErrorCode& status) const;
|
||||
|
||||
/* Cannot use #ifndef U_HIDE_DRAFT_API for the following draft method since it is virtual */
|
||||
/**
|
||||
* Parses text from the given string as a currency amount. Unlike
|
||||
* the parse() method, this method will attempt to parse a generic
|
||||
* currency name, searching for a match of this object's locale's
|
||||
* currency display names, or for a 3-letter ISO currency code.
|
||||
* This method will fail if this format is not a currency format,
|
||||
* that is, if it does not contain the currency pattern symbol
|
||||
* (U+00A4) in its prefix or suffix. This implementation always returns
|
||||
* NULL.
|
||||
*
|
||||
* @param text the string to parse
|
||||
* @param pos input-output position; on input, the position within text
|
||||
* to match; must have 0 <= pos.getIndex() < text.length();
|
||||
* on output, the position after the last matched character.
|
||||
* If the parse fails, the position in unchanged upon output.
|
||||
* @return if parse succeeds, a pointer to a newly-created CurrencyAmount
|
||||
* object (owned by the caller) containing information about
|
||||
* the parsed currency; if parse fails, this is NULL.
|
||||
* @internal
|
||||
*/
|
||||
virtual CurrencyAmount* parseCurrency(const UnicodeString& text,
|
||||
ParsePosition& pos) const;
|
||||
|
||||
/**
|
||||
* Return the class ID for this class. This is useful only for
|
||||
* comparing to a return value from getDynamicClassID(). For example:
|
||||
* <pre>
|
||||
* . Base* polymorphic_pointer = createPolymorphicObject();
|
||||
* . if (polymorphic_pointer->getDynamicClassID() ==
|
||||
* . Derived::getStaticClassID()) ...
|
||||
* </pre>
|
||||
* @return The class ID for all objects of this class.
|
||||
* @draft ICU 51
|
||||
*/
|
||||
static UClassID U_EXPORT2 getStaticClassID();
|
||||
|
||||
/**
|
||||
* Returns a unique class ID POLYMORPHICALLY. Pure virtual override.
|
||||
* This method is to implement a simple version of RTTI, since not all
|
||||
* C++ compilers support genuine RTTI. Polymorphic operator==() and
|
||||
* clone() methods call this method.
|
||||
*
|
||||
* @return The class ID for this object. All objects of a
|
||||
* given class have the same class ID. Objects of
|
||||
* other classes have different class IDs.
|
||||
* @draft ICU 51
|
||||
*/
|
||||
virtual UClassID getDynamicClassID() const;
|
||||
|
||||
private:
|
||||
|
||||
const UHashtable* _unitsByVariant;
|
||||
const double* _divisors;
|
||||
PluralRules* _pluralRules;
|
||||
|
||||
// Default constructor not implemented.
|
||||
CompactDecimalFormat(const DecimalFormat &, const UHashtable* unitsByVariant, const double* divisors, PluralRules* pluralRules);
|
||||
|
||||
UBool eqHelper(const CompactDecimalFormat& that) const;
|
||||
};
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif // __COMPACT_DECIMAL_FORMAT_H__
|
||||
//eof
|
|
@ -2403,6 +2403,12 @@ private:
|
|||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Rounds a value according to the rules of this object.
|
||||
* @internal
|
||||
*/
|
||||
DigitList& _round(const DigitList& number, DigitList& adjustedNum, UBool& isNegative, UErrorCode& status) const;
|
||||
|
||||
/**
|
||||
* Returns the currency in effect for this formatter. Subclasses
|
||||
* should override this method as needed. Unlike getCurrency(),
|
||||
|
|
|
@ -71,6 +71,9 @@ class StringEnumeration;
|
|||
* cout << " Example 1: " << myString << endl;
|
||||
* \endcode
|
||||
* </pre>
|
||||
* Note that there are additional factory methods within subclasses of
|
||||
* NumberFormat.
|
||||
* <P>
|
||||
* If you are formatting multiple numbers, it is more efficient to get
|
||||
* the format and use it multiple times so that the system doesn't
|
||||
* have to fetch the information about the local language and country
|
||||
|
@ -983,6 +986,17 @@ protected:
|
|||
*/
|
||||
virtual void getEffectiveCurrency(UChar* result, UErrorCode& ec) const;
|
||||
|
||||
/**
|
||||
* Creates the specified number format style of the desired locale.
|
||||
* If mustBeDecimalFormat is TRUE, then the returned pointer is
|
||||
* either a DecimalFormat or it is NULL.
|
||||
* @internal
|
||||
*/
|
||||
static NumberFormat* makeInstance(const Locale& desiredLocale,
|
||||
UNumberFormatStyle style,
|
||||
UBool mustBeDecimalFormat,
|
||||
UErrorCode& errorCode);
|
||||
|
||||
private:
|
||||
|
||||
static UBool isStyleSupported(UNumberFormatStyle style);
|
||||
|
|
|
@ -250,6 +250,18 @@ typedef enum UNumberFormatPadPosition {
|
|||
UNUM_PAD_AFTER_SUFFIX
|
||||
} UNumberFormatPadPosition;
|
||||
|
||||
/**
|
||||
* Constants for specifying short or long format.
|
||||
* @draft ICU 51
|
||||
*/
|
||||
typedef enum UNumberCompactStyle {
|
||||
/** @draft ICU 51 */
|
||||
UNUM_SHORT,
|
||||
/** @draft ICU 51 */
|
||||
UNUM_LONG
|
||||
/** @draft ICU 51 */
|
||||
} UNumberCompactStyle;
|
||||
|
||||
/**
|
||||
* Constants for specifying currency spacing
|
||||
* @stable ICU 4.8
|
||||
|
|
|
@ -55,7 +55,7 @@ itrbnf.o itrbnfrt.o itrbnfp.o ucaconf.o icusvtst.o \
|
|||
uobjtest.o idnaref.o idnaconf.o nptrans.o punyref.o testidn.o testidna.o uts46test.o \
|
||||
incaltst.o calcasts.o v32test.o uvectest.o textfile.o tokiter.o utxttest.o \
|
||||
windttst.o winnmtst.o winutil.o csdetest.o tzrulets.o tzoffloc.o tzfmttst.o ssearch.o dtifmtts.o \
|
||||
tufmtts.o itspoof.o simplethread.o bidiconf.o locnmtst.o dcfmtest.o alphaindextst.o listformattertest.o genderinfotest.o
|
||||
tufmtts.o itspoof.o simplethread.o bidiconf.o locnmtst.o dcfmtest.o alphaindextst.o listformattertest.o genderinfotest.o compactdecimalformattest.o
|
||||
|
||||
DEPS = $(OBJECTS:.o=.d)
|
||||
|
||||
|
|
342
icu4c/source/test/intltest/compactdecimalformattest.cpp
Normal file
342
icu4c/source/test/intltest/compactdecimalformattest.cpp
Normal file
|
@ -0,0 +1,342 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 1997-2012, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*
|
||||
* File COMPACTDECIMALFORMATTEST.CPP
|
||||
*
|
||||
********************************************************************************
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "intltest.h"
|
||||
#include "unicode/compactdecimalformat.h"
|
||||
#include "unicode/unum.h"
|
||||
|
||||
#define LENGTHOF(array) (int32_t)(sizeof(array) / sizeof((array)[0]))
|
||||
|
||||
typedef struct ExpectedResult {
|
||||
double value;
|
||||
const char *expected;
|
||||
} ExpectedResult;
|
||||
|
||||
static const char *kShortStr = "Short";
|
||||
static const char *kLongStr = "Long";
|
||||
|
||||
static ExpectedResult kEnglishShort[] = {
|
||||
{0.0, "0.0"},
|
||||
{0.17, "0.17"},
|
||||
{1, "1"},
|
||||
{1234, "1.2K"},
|
||||
{12345, "12K"},
|
||||
{123456, "120K"},
|
||||
{1234567, "1.2M"},
|
||||
{12345678, "12M"},
|
||||
{123456789, "120M"},
|
||||
{1234567890, "1.2B"},
|
||||
{12345678901, "12B"},
|
||||
{123456789012, "120B"},
|
||||
{1234567890123, "1.2T"},
|
||||
{12345678901234, "12T"},
|
||||
{123456789012345, "120T"},
|
||||
{1234567890123456, "1200T"}};
|
||||
|
||||
static ExpectedResult kSerbianShort[] = {
|
||||
{1234, "1200"},
|
||||
{12345, "12K"},
|
||||
{20789, "21\\u00a0\\u0445\\u0438\\u0459"},
|
||||
{123456, "120\\u00a0\\u0445\\u0438\\u0459"},
|
||||
{1234567, "1,2\\u00A0\\u043C\\u0438\\u043B"},
|
||||
{12345678, "12\\u00A0\\u043C\\u0438\\u043B"},
|
||||
{123456789, "120\\u00A0\\u043C\\u0438\\u043B"},
|
||||
{1234567890, "1,2\\u00A0\\u043C\\u043B\\u0440\\u0434"},
|
||||
{12345678901, "12\\u00A0\\u043C\\u043B\\u0440\\u0434"},
|
||||
{123456789012, "120\\u00A0\\u043C\\u043B\\u0440\\u0434"},
|
||||
{1234567890123, "1,2\\u00A0\\u0431\\u0438\\u043B"},
|
||||
{12345678901234, "12\\u00A0\\u0431\\u0438\\u043B"},
|
||||
{123456789012345, "120\\u00A0\\u0431\\u0438\\u043B"},
|
||||
{1234567890123456, "1200\\u00A0\\u0431\\u0438\\u043B"}};
|
||||
|
||||
static ExpectedResult kSerbianLong[] = {
|
||||
{1234, "1,2 \\u0445\\u0438\\u0459\\u0430\\u0434\\u0430"},
|
||||
{12345, "12 \\u0445\\u0438\\u0459\\u0430\\u0434\\u0430"},
|
||||
{21789, "22 \\u0445\\u0438\\u0459\\u0430\\u0434\\u0435"},
|
||||
{123456, "120 \\u0445\\u0438\\u0459\\u0430\\u0434\\u0430"},
|
||||
{999999, "1 \\u043C\\u0438\\u043B\\u0438\\u043E\\u043D"},
|
||||
{1234567, "1,2 \\u043C\\u0438\\u043B\\u0438\\u043E\\u043D\\u0430"},
|
||||
{12345678, "12 \\u043C\\u0438\\u043B\\u0438\\u043E\\u043D\\u0430"},
|
||||
{123456789, "120 \\u043C\\u0438\\u043B\\u0438\\u043E\\u043D\\u0430"},
|
||||
{1234567890, "1,2 \\u043C\\u0438\\u043B\\u0438\\u0458\\u0430\\u0440\\u0434\\u0438"},
|
||||
{12345678901, "12 \\u043C\\u0438\\u043B\\u0438\\u0458\\u0430\\u0440\\u0434\\u0438"},
|
||||
{20890123456, "21 \\u043C\\u0438\\u043B\\u0438\\u0458\\u0430\\u0440\\u0434\\u0430"},
|
||||
{21890123456, "22 \\u043C\\u0438\\u043B\\u0438\\u0458\\u0430\\u0440\\u0434\\u0435"},
|
||||
{123456789012, "120 \\u043C\\u0438\\u043B\\u0438\\u0458\\u0430\\u0440\\u0434\\u0438"},
|
||||
{1234567890123, "1,2 \\u0442\\u0440\\u0438\\u043B\\u0438\\u043E\\u043D\\u0430"},
|
||||
{12345678901234, "12 \\u0442\\u0440\\u0438\\u043B\\u0438\\u043E\\u043D\\u0430"},
|
||||
{123456789012345, "120 \\u0442\\u0440\\u0438\\u043B\\u0438\\u043E\\u043D\\u0430"},
|
||||
{1234567890123456, "1200 \\u0442\\u0440\\u0438\\u043B\\u0438\\u043E\\u043D\\u0430"}};
|
||||
|
||||
static ExpectedResult kSerbianLongNegative[] = {
|
||||
{-1234, "-1,2 \\u0445\\u0438\\u0459\\u0430\\u0434\\u0430"},
|
||||
{-12345, "-12 \\u0445\\u0438\\u0459\\u0430\\u0434\\u0430"},
|
||||
{-21789, "-22 \\u0445\\u0438\\u0459\\u0430\\u0434\\u0435"},
|
||||
{-123456, "-120 \\u0445\\u0438\\u0459\\u0430\\u0434\\u0430"},
|
||||
{-999999, "-1 \\u043C\\u0438\\u043B\\u0438\\u043E\\u043D"},
|
||||
{-1234567, "-1,2 \\u043C\\u0438\\u043B\\u0438\\u043E\\u043D\\u0430"},
|
||||
{-12345678, "-12 \\u043C\\u0438\\u043B\\u0438\\u043E\\u043D\\u0430"},
|
||||
{-123456789, "-120 \\u043C\\u0438\\u043B\\u0438\\u043E\\u043D\\u0430"},
|
||||
{-1234567890, "-1,2 \\u043C\\u0438\\u043B\\u0438\\u0458\\u0430\\u0440\\u0434\\u0438"},
|
||||
{-12345678901, "-12 \\u043C\\u0438\\u043B\\u0438\\u0458\\u0430\\u0440\\u0434\\u0438"},
|
||||
{-20890123456, "-21 \\u043C\\u0438\\u043B\\u0438\\u0458\\u0430\\u0440\\u0434\\u0430"},
|
||||
{-21890123456, "-22 \\u043C\\u0438\\u043B\\u0438\\u0458\\u0430\\u0440\\u0434\\u0435"},
|
||||
{-123456789012, "-120 \\u043C\\u0438\\u043B\\u0438\\u0458\\u0430\\u0440\\u0434\\u0438"},
|
||||
{-1234567890123, "-1,2 \\u0442\\u0440\\u0438\\u043B\\u0438\\u043E\\u043D\\u0430"},
|
||||
{-12345678901234, "-12 \\u0442\\u0440\\u0438\\u043B\\u0438\\u043E\\u043D\\u0430"},
|
||||
{-123456789012345, "-120 \\u0442\\u0440\\u0438\\u043B\\u0438\\u043E\\u043D\\u0430"},
|
||||
{-1234567890123456, "-1200 \\u0442\\u0440\\u0438\\u043B\\u0438\\u043E\\u043D\\u0430"}};
|
||||
|
||||
static ExpectedResult kJapaneseShort[] = {
|
||||
{1234, "1.2\\u5343"},
|
||||
{12345, "1.2\\u4E07"},
|
||||
{123456, "12\\u4E07"},
|
||||
{1234567, "120\\u4E07"},
|
||||
{12345678, "1200\\u4E07"},
|
||||
{123456789, "1.2\\u5104"},
|
||||
{1234567890, "12\\u5104"},
|
||||
{12345678901, "120\\u5104"},
|
||||
{123456789012, "1200\\u5104"},
|
||||
{1234567890123, "1.2\\u5146"},
|
||||
{12345678901234, "12\\u5146"},
|
||||
{123456789012345, "120\\u5146"}};
|
||||
|
||||
static ExpectedResult kSwahiliShort[] = {
|
||||
{1234, "elfu\\u00a01.2"},
|
||||
{12345, "elfu\\u00a012"},
|
||||
{123456, "laki1.2"},
|
||||
{1234567, "M1.2"},
|
||||
{12345678, "M12"},
|
||||
{123456789, "M120"},
|
||||
{1234567890, "B1.2"},
|
||||
{12345678901, "B12"},
|
||||
{123456789012, "B120"},
|
||||
{1234567890123, "T1.2"},
|
||||
{12345678901234, "T12"},
|
||||
{1234567890123456, "T1200"}};
|
||||
|
||||
static ExpectedResult kCsShort[] = {
|
||||
{1000, "1\\u00a0tis."},
|
||||
{1500, "1,5\\u00a0tis."},
|
||||
{5000, "5\\u00a0tis."},
|
||||
{23000, "23\\u00a0tis."},
|
||||
{127123, "130\\u00a0tis."},
|
||||
{1271234, "1,3\\u00a0mil."},
|
||||
{12712345, "13\\u00a0mil."},
|
||||
{127123456, "130\\u00a0mil."},
|
||||
{1271234567, "1,3\\u00a0mld."},
|
||||
{12712345678, "13\\u00a0mld."},
|
||||
{127123456789, "130\\u00a0mld."},
|
||||
{1271234567890, "1,3\\u00a0bil."},
|
||||
{12712345678901, "13\\u00a0bil."},
|
||||
{127123456789012, "130\\u00a0bil."}};
|
||||
|
||||
static ExpectedResult kSkLong[] = {
|
||||
{1000, "1 tis\\u00edc"},
|
||||
{1572, "1,6 tis\\u00edc"},
|
||||
{5184, "5,2 tis\\u00edc"}};
|
||||
|
||||
static ExpectedResult kSwahiliShortNegative[] = {
|
||||
{-1234, "elfu\\u00a0-1.2"},
|
||||
{-12345, "elfu\\u00a0-12"},
|
||||
{-123456, "laki-1.2"},
|
||||
{-1234567, "M-1.2"},
|
||||
{-12345678, "M-12"},
|
||||
{-123456789, "M-120"},
|
||||
{-1234567890, "B-1.2"},
|
||||
{-12345678901, "B-12"},
|
||||
{-123456789012, "B-120"},
|
||||
{-1234567890123, "T-1.2"},
|
||||
{-12345678901234, "T-12"},
|
||||
{-1234567890123456, "T-1200"}};
|
||||
|
||||
static ExpectedResult kArabicLong[] = {
|
||||
{-5300, "\\u0665\\u066B\\u0663- \\u0623\\u0644\\u0641"}};
|
||||
|
||||
|
||||
class CompactDecimalFormatTest : public IntlTest {
|
||||
public:
|
||||
CompactDecimalFormatTest() {
|
||||
}
|
||||
|
||||
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=0);
|
||||
private:
|
||||
void TestEnglishShort();
|
||||
void TestSerbianShort();
|
||||
void TestSerbianLong();
|
||||
void TestSerbianLongNegative();
|
||||
void TestJapaneseShort();
|
||||
void TestSwahiliShort();
|
||||
void TestCsShort();
|
||||
void TestSkLong();
|
||||
void TestSwahiliShortNegative();
|
||||
void TestArabicLong();
|
||||
void TestFieldPosition();
|
||||
void TestSignificantDigits();
|
||||
void CheckLocale(
|
||||
const Locale& locale, UNumberCompactStyle style,
|
||||
const ExpectedResult* expectedResult, int32_t expectedResultLength);
|
||||
void CheckExpectedResult(
|
||||
const CompactDecimalFormat* cdf, const ExpectedResult* expectedResult,
|
||||
const char* description);
|
||||
CompactDecimalFormat* createCDFInstance(const Locale& locale, UNumberCompactStyle style, UErrorCode& status);
|
||||
static const char *StyleStr(UNumberCompactStyle style);
|
||||
};
|
||||
|
||||
void CompactDecimalFormatTest::runIndexedTest(
|
||||
int32_t index, UBool exec, const char *&name, char *) {
|
||||
if (exec) {
|
||||
logln("TestSuite CompactDecimalFormatTest: ");
|
||||
}
|
||||
TESTCASE_AUTO_BEGIN;
|
||||
TESTCASE_AUTO(TestEnglishShort);
|
||||
TESTCASE_AUTO(TestSerbianShort);
|
||||
TESTCASE_AUTO(TestSerbianLong);
|
||||
TESTCASE_AUTO(TestSerbianLongNegative);
|
||||
TESTCASE_AUTO(TestJapaneseShort);
|
||||
TESTCASE_AUTO(TestSwahiliShort);
|
||||
TESTCASE_AUTO(TestCsShort);
|
||||
TESTCASE_AUTO(TestSkLong);
|
||||
TESTCASE_AUTO(TestSwahiliShortNegative);
|
||||
TESTCASE_AUTO(TestArabicLong);
|
||||
TESTCASE_AUTO(TestFieldPosition);
|
||||
TESTCASE_AUTO(TestSignificantDigits);
|
||||
TESTCASE_AUTO_END;
|
||||
}
|
||||
|
||||
void CompactDecimalFormatTest::TestEnglishShort() {
|
||||
CheckLocale("en", UNUM_SHORT, kEnglishShort, LENGTHOF(kEnglishShort));
|
||||
}
|
||||
|
||||
void CompactDecimalFormatTest::TestSerbianShort() {
|
||||
CheckLocale("sr", UNUM_SHORT, kSerbianShort, LENGTHOF(kSerbianShort));
|
||||
}
|
||||
|
||||
void CompactDecimalFormatTest::TestSerbianLong() {
|
||||
CheckLocale("sr", UNUM_LONG, kSerbianLong, LENGTHOF(kSerbianLong));
|
||||
}
|
||||
|
||||
void CompactDecimalFormatTest::TestSerbianLongNegative() {
|
||||
CheckLocale("sr", UNUM_LONG, kSerbianLongNegative, LENGTHOF(kSerbianLongNegative));
|
||||
}
|
||||
|
||||
void CompactDecimalFormatTest::TestJapaneseShort() {
|
||||
CheckLocale(Locale::getJapan(), UNUM_SHORT, kJapaneseShort, LENGTHOF(kJapaneseShort));
|
||||
}
|
||||
|
||||
void CompactDecimalFormatTest::TestSwahiliShort() {
|
||||
CheckLocale("sw", UNUM_SHORT, kSwahiliShort, LENGTHOF(kSwahiliShort));
|
||||
}
|
||||
|
||||
void CompactDecimalFormatTest::TestFieldPosition() {
|
||||
// Swahili uses prefixes which forces offsets in field position to change
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
LocalPointer<CompactDecimalFormat> cdf(createCDFInstance("sw", UNUM_SHORT, status));
|
||||
if (U_FAILURE(status)) {
|
||||
errln("Unable to create format object - %s", u_errorName(status));
|
||||
}
|
||||
FieldPosition fp(UNUM_INTEGER_FIELD);
|
||||
UnicodeString result;
|
||||
cdf->format(1234567.0, result, fp);
|
||||
UnicodeString subString = result.tempSubString(fp.getBeginIndex(), fp.getEndIndex() - fp.getBeginIndex());
|
||||
if (subString != UnicodeString("1", -1, US_INV)) {
|
||||
errln(UnicodeString("Expected 1, got ") + subString);
|
||||
}
|
||||
}
|
||||
|
||||
void CompactDecimalFormatTest::TestCsShort() {
|
||||
CheckLocale("cs", UNUM_SHORT, kCsShort, LENGTHOF(kCsShort));
|
||||
}
|
||||
|
||||
void CompactDecimalFormatTest::TestSkLong() {
|
||||
// In CLDR we have:
|
||||
// 1000 {
|
||||
// few{"0"}
|
||||
// one{"0"}
|
||||
// other{"0"}
|
||||
CheckLocale("sk", UNUM_LONG, kSkLong, LENGTHOF(kSkLong));
|
||||
}
|
||||
|
||||
void CompactDecimalFormatTest::TestSwahiliShortNegative() {
|
||||
CheckLocale("sw", UNUM_SHORT, kSwahiliShortNegative, LENGTHOF(kSwahiliShortNegative));
|
||||
}
|
||||
|
||||
void CompactDecimalFormatTest::TestArabicLong() {
|
||||
CheckLocale("ar", UNUM_LONG, kArabicLong, LENGTHOF(kArabicLong));
|
||||
}
|
||||
|
||||
void CompactDecimalFormatTest::TestSignificantDigits() {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
LocalPointer<CompactDecimalFormat> cdf(CompactDecimalFormat::createInstance("en", UNUM_SHORT, status));
|
||||
if (U_FAILURE(status)) {
|
||||
errln("Unable to create format object - %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
UnicodeString actual;
|
||||
cdf->format(123456.0, actual);
|
||||
// We expect 3 significant digits by default
|
||||
UnicodeString expected("123K", -1, US_INV);
|
||||
if (actual != expected) {
|
||||
errln(UnicodeString("Fail: Expected: ") + expected + UnicodeString(" Got: ") + actual);
|
||||
}
|
||||
}
|
||||
|
||||
void CompactDecimalFormatTest::CheckLocale(const Locale& locale, UNumberCompactStyle style, const ExpectedResult* expectedResults, int32_t expectedResultLength) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
LocalPointer<CompactDecimalFormat> cdf(createCDFInstance(locale, style, status));
|
||||
if (U_FAILURE(status)) {
|
||||
errln("Unable to create format object - %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
char description[256];
|
||||
sprintf(description,"%s - %s", locale.getName(), StyleStr(style));
|
||||
for (int32_t i = 0; i < expectedResultLength; i++) {
|
||||
CheckExpectedResult(cdf.getAlias(), &expectedResults[i], description);
|
||||
}
|
||||
}
|
||||
|
||||
void CompactDecimalFormatTest::CheckExpectedResult(
|
||||
const CompactDecimalFormat* cdf, const ExpectedResult* expectedResult, const char* description) {
|
||||
UnicodeString actual;
|
||||
cdf->format(expectedResult->value, actual);
|
||||
UnicodeString expected(expectedResult->expected, -1, US_INV);
|
||||
expected = expected.unescape();
|
||||
if (actual != expected) {
|
||||
errln(UnicodeString("Fail: Expected: ") + expected
|
||||
+ UnicodeString(" Got: ") + actual
|
||||
+ UnicodeString(" for: ") + UnicodeString(description));
|
||||
}
|
||||
}
|
||||
|
||||
CompactDecimalFormat*
|
||||
CompactDecimalFormatTest::createCDFInstance(const Locale& locale, UNumberCompactStyle style, UErrorCode& status) {
|
||||
CompactDecimalFormat* result = CompactDecimalFormat::createInstance(locale, style, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return NULL;
|
||||
}
|
||||
// All tests are written for two significant digits, so we explicitly set here
|
||||
// in case default significant digits change.
|
||||
result->setMaximumSignificantDigits(2);
|
||||
return result;
|
||||
}
|
||||
|
||||
const char *CompactDecimalFormatTest::StyleStr(UNumberCompactStyle style) {
|
||||
if (style == UNUM_SHORT) {
|
||||
return kShortStr;
|
||||
}
|
||||
return kLongStr;
|
||||
}
|
||||
|
||||
extern IntlTest *createCompactDecimalFormatTest() {
|
||||
return new CompactDecimalFormatTest();
|
||||
}
|
|
@ -281,6 +281,7 @@
|
|||
<ClCompile Include="calregts.cpp" />
|
||||
<ClCompile Include="caltest.cpp" />
|
||||
<ClCompile Include="caltztst.cpp" />
|
||||
<ClCompile Include="compactdecimalformattest.cpp" />
|
||||
<ClCompile Include="dadrcal.cpp" />
|
||||
<ClCompile Include="dadrfmt.cpp" />
|
||||
<ClCompile Include="dcfmapts.cpp" />
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
#include "dcfmtest.h" // DecimalFormatTest
|
||||
#include "listformattertest.h" // ListFormatterTest
|
||||
|
||||
extern IntlTest *createCompactDecimalFormatTest();
|
||||
extern IntlTest *createGenderInfoTest();
|
||||
|
||||
#define TESTCLASS(id, TestClass) \
|
||||
|
@ -145,6 +146,15 @@ void IntlTestFormat::runIndexedTest( int32_t index, UBool exec, const char* &nam
|
|||
callTest(*test, par);
|
||||
}
|
||||
break;
|
||||
case 44:
|
||||
name = "CompactDecimalFormatTest";
|
||||
if (exec) {
|
||||
logln("CompactDecimalFormatTest test---");
|
||||
logln((UnicodeString)"");
|
||||
LocalPointer<IntlTest> test(createCompactDecimalFormatTest());
|
||||
callTest(*test, par);
|
||||
}
|
||||
break;
|
||||
default: name = ""; break; //needed to end loop
|
||||
}
|
||||
if (exec) {
|
||||
|
|
Loading…
Add table
Reference in a new issue