mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-10 15:42:14 +00:00
ICU-11740 DecimalFormat refactor.
X-SVN-Rev: 37821
This commit is contained in:
parent
57ac300668
commit
2860cd48c4
65 changed files with 14292 additions and 3149 deletions
2
.gitattributes
vendored
2
.gitattributes
vendored
|
@ -89,6 +89,8 @@ icu4c/source/data/region/pool.res -text
|
|||
icu4c/source/data/unit/pool.res -text
|
||||
icu4c/source/data/zone/pool.res -text
|
||||
icu4c/source/extra/uconv/uconv.vcxproj -text
|
||||
icu4c/source/i18n/visibledigits.cpp -text
|
||||
icu4c/source/i18n/visibledigits.h -text
|
||||
icu4c/source/samples/break/break.vcxproj -text
|
||||
icu4c/source/samples/case/case.vcxproj -text
|
||||
icu4c/source/samples/citer/citer.vcxproj -text
|
||||
|
|
|
@ -105,7 +105,8 @@ serv.o servnotf.o servls.o servlk.o servlkf.o servrbf.o servslkf.o \
|
|||
uidna.o usprep.o uts46.o punycode.o \
|
||||
util.o util_props.o parsepos.o locbased.o cwchar.o wintz.o dtintrv.o ucnvsel.o propsvec.o \
|
||||
ulist.o uloc_tag.o icudataver.o icuplug.o listformatter.o ulistformatter.o \
|
||||
sharedobject.o simplepatternformatter.o unifiedcache.o uloc_keytype.o
|
||||
sharedobject.o simplepatternformatter.o unifiedcache.o uloc_keytype.o \
|
||||
pluralmap.o
|
||||
|
||||
## Header files to install
|
||||
HEADERS = $(srcdir)/unicode/*.h
|
||||
|
|
|
@ -244,6 +244,7 @@
|
|||
<ClCompile Include="brkiter.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="dictbe.cpp" />
|
||||
<ClCompile Include="pluralmap.cpp" />
|
||||
<ClCompile Include="rbbi.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="rbbidata.cpp">
|
||||
|
@ -487,6 +488,7 @@
|
|||
</CustomBuild>
|
||||
<ClInclude Include="localsvc.h" />
|
||||
<ClInclude Include="msvcres.h" />
|
||||
<ClInclude Include="pluralmap.h" />
|
||||
<ClInclude Include="propname_data.h" />
|
||||
<ClInclude Include="ubidi_props.h" />
|
||||
<ClInclude Include="ubidiimp.h" />
|
||||
|
@ -576,6 +578,7 @@
|
|||
<ClInclude Include="uchar_props_data.h" />
|
||||
<ClInclude Include="ucol_data.h" />
|
||||
<ClInclude Include="ucol_swp.h" />
|
||||
<ClInclude Include="unistrappender.h" />
|
||||
<ClInclude Include="hash.h" />
|
||||
<ClInclude Include="propsvec.h" />
|
||||
<CustomBuild Include="unicode\strenum.h">
|
||||
|
@ -1771,4 +1774,4 @@
|
|||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -103,6 +103,9 @@
|
|||
<ClCompile Include="ucol_swp.cpp">
|
||||
<Filter>collation</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pluralmap.cpp">
|
||||
<Filter>collections</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="propsvec.c">
|
||||
<Filter>collections</Filter>
|
||||
</ClCompile>
|
||||
|
@ -625,6 +628,9 @@
|
|||
<ClInclude Include="hash.h">
|
||||
<Filter>collections</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="pluralmap.h">
|
||||
<Filter>collections</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="propsvec.h">
|
||||
<Filter>collections</Filter>
|
||||
</ClInclude>
|
||||
|
@ -829,6 +835,9 @@
|
|||
<ClInclude Include="uinvchar.h">
|
||||
<Filter>strings</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="unistrappender.h">
|
||||
<Filter>strings</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ustr_cnv.h">
|
||||
<Filter>strings</Filter>
|
||||
</ClInclude>
|
||||
|
@ -1131,4 +1140,4 @@
|
|||
<Filter>collections</Filter>
|
||||
</CustomBuild>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
42
icu4c/source/common/pluralmap.cpp
Normal file
42
icu4c/source/common/pluralmap.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2015, International Business Machines Corporation and
|
||||
* others. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include "unicode/unistr.h"
|
||||
#include "charstr.h"
|
||||
#include "cstring.h"
|
||||
#include "pluralmap.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
static const char * const gPluralForms[] = {
|
||||
"other", "zero", "one", "two", "few", "many"};
|
||||
|
||||
PluralMapBase::Category
|
||||
PluralMapBase::toCategory(const char *pluralForm) {
|
||||
for (int32_t i = 0; i < UPRV_LENGTHOF(gPluralForms); ++i) {
|
||||
if (uprv_strcmp(pluralForm, gPluralForms[i]) == 0) {
|
||||
return static_cast<Category>(i);
|
||||
}
|
||||
}
|
||||
return NONE;
|
||||
}
|
||||
|
||||
PluralMapBase::Category
|
||||
PluralMapBase::toCategory(const UnicodeString &pluralForm) {
|
||||
CharString cCategory;
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
cCategory.appendInvariantChars(pluralForm, status);
|
||||
return U_FAILURE(status) ? NONE : toCategory(cCategory.data());
|
||||
}
|
||||
|
||||
const char *PluralMapBase::getCategoryName(Category c) {
|
||||
int32_t index = c;
|
||||
return (index < 0 || index >= UPRV_LENGTHOF(gPluralForms)) ?
|
||||
NULL : gPluralForms[index];
|
||||
}
|
||||
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
290
icu4c/source/common/pluralmap.h
Normal file
290
icu4c/source/common/pluralmap.h
Normal file
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
******************************************************************************
|
||||
* Copyright (C) 2015, International Business Machines Corporation and
|
||||
* others. All Rights Reserved.
|
||||
******************************************************************************
|
||||
*
|
||||
* File pluralmap.h - PluralMap class that maps plural categories to values.
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef __PLURAL_MAP_H__
|
||||
#define __PLURAL_MAP_H__
|
||||
|
||||
#include "unicode/uobject.h"
|
||||
#include "cmemory.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
class UnicodeString;
|
||||
|
||||
class U_COMMON_API PluralMapBase : public UMemory {
|
||||
public:
|
||||
/**
|
||||
* The names of all the plural categories. NONE is not an actual plural
|
||||
* category, but rather represents the absense of a plural category.
|
||||
*/
|
||||
enum Category {
|
||||
NONE = -1,
|
||||
OTHER,
|
||||
ZERO,
|
||||
ONE,
|
||||
TWO,
|
||||
FEW,
|
||||
MANY,
|
||||
CATEGORY_COUNT
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a category name such as "zero", "one", "two", "few", "many"
|
||||
* or "other" to a category enum. Returns NONE for an unrecognized
|
||||
* category name.
|
||||
*/
|
||||
static Category toCategory(const char *categoryName);
|
||||
|
||||
/**
|
||||
* Converts a category name such as "zero", "one", "two", "few", "many"
|
||||
* or "other" to a category enum. Returns NONE for urecongized
|
||||
* category name.
|
||||
*/
|
||||
static Category toCategory(const UnicodeString &categoryName);
|
||||
|
||||
/**
|
||||
* Converts a category to a name.
|
||||
* Passing NONE or CATEGORY_COUNT for category returns NULL.
|
||||
*/
|
||||
static const char *getCategoryName(Category category);
|
||||
};
|
||||
|
||||
/**
|
||||
* A Map of plural categories to values. It maintains ownership of the
|
||||
* values.
|
||||
*
|
||||
* Type T is the value type. T must provide the followng:
|
||||
* 1) Default constructor
|
||||
* 2) Copy constructor
|
||||
* 3) Assignment operator
|
||||
* 4) Must extend UMemory
|
||||
*/
|
||||
template<typename T>
|
||||
class PluralMap : public PluralMapBase {
|
||||
public:
|
||||
/**
|
||||
* Other category is maps to a copy of the default value.
|
||||
*/
|
||||
PluralMap() : fOtherVariant() {
|
||||
initializeNew();
|
||||
}
|
||||
|
||||
/**
|
||||
* Other category is mapped to otherVariant.
|
||||
*/
|
||||
PluralMap(const T &otherVariant) : fOtherVariant(otherVariant) {
|
||||
initializeNew();
|
||||
}
|
||||
|
||||
PluralMap(const PluralMap<T> &other) : fOtherVariant(other.fOtherVariant) {
|
||||
fVariants[0] = &fOtherVariant;
|
||||
for (int32_t i = 1; i < UPRV_LENGTHOF(fVariants); ++i) {
|
||||
fVariants[i] = other.fVariants[i] ?
|
||||
new T(*other.fVariants[i]) : NULL;
|
||||
}
|
||||
}
|
||||
|
||||
PluralMap<T> &operator=(const PluralMap<T> &other) {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
for (int32_t i = 0; i < UPRV_LENGTHOF(fVariants); ++i) {
|
||||
if (fVariants[i] != NULL && other.fVariants[i] != NULL) {
|
||||
*fVariants[i] = *other.fVariants[i];
|
||||
} else if (fVariants[i] != NULL) {
|
||||
delete fVariants[i];
|
||||
fVariants[i] = NULL;
|
||||
} else if (other.fVariants[i] != NULL) {
|
||||
fVariants[i] = new T(*other.fVariants[i]);
|
||||
} else {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~PluralMap() {
|
||||
for (int32_t i = 1; i < UPRV_LENGTHOF(fVariants); ++i) {
|
||||
delete fVariants[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all mappings and makes 'other' point to the default value.
|
||||
*/
|
||||
void clear() {
|
||||
*fVariants[0] = T();
|
||||
for (int32_t i = 1; i < UPRV_LENGTHOF(fVariants); ++i) {
|
||||
delete fVariants[i];
|
||||
fVariants[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through the mappings in this instance, set index to NONE
|
||||
* prior to using. Call next repeatedly to get the values until it
|
||||
* returns NULL. Each time next returns, caller may pass index
|
||||
* to getCategoryName() to get the name of the plural category.
|
||||
* When this function returns NULL, index is CATEGORY_COUNT
|
||||
*/
|
||||
const T *next(Category &index) const {
|
||||
int32_t idx = index;
|
||||
++idx;
|
||||
for (; idx < UPRV_LENGTHOF(fVariants); ++idx) {
|
||||
if (fVariants[idx] != NULL) {
|
||||
index = static_cast<Category>(idx);
|
||||
return fVariants[idx];
|
||||
}
|
||||
}
|
||||
index = static_cast<Category>(idx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* non const version of next.
|
||||
*/
|
||||
T *nextMutable(Category &index) {
|
||||
const T *result = next(index);
|
||||
return const_cast<T *>(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 'other' variant.
|
||||
* Same as calling get(OTHER).
|
||||
*/
|
||||
const T &getOther() const {
|
||||
return get(OTHER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value associated with a category.
|
||||
* If no value found, or v is NONE or CATEGORY_COUNT, falls
|
||||
* back to returning the value for the 'other' category.
|
||||
*/
|
||||
const T &get(Category v) const {
|
||||
int32_t index = v;
|
||||
if (index < 0 || index >= UPRV_LENGTHOF(fVariants) || fVariants[index] == NULL) {
|
||||
return *fVariants[0];
|
||||
}
|
||||
return *fVariants[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience routine to get the value by category name. Otherwise
|
||||
* works just like get(Category).
|
||||
*/
|
||||
const T &get(const char *category) const {
|
||||
return get(toCategory(category));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience routine to get the value by category name as a
|
||||
* UnicodeString. Otherwise works just like get(category).
|
||||
*/
|
||||
const T &get(const UnicodeString &category) const {
|
||||
return get(toCategory(category));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pointer to the value associated with a category
|
||||
* that caller can safely modify. If the value was defaulting to the 'other'
|
||||
* variant because no explicit value was stored, this method creates a
|
||||
* new value using the default constructor at the returned pointer.
|
||||
*
|
||||
* @param category the category with the value to change.
|
||||
* @param status error returned here if index is NONE or CATEGORY_COUNT
|
||||
* or memory could not be allocated, or any other error happens.
|
||||
*/
|
||||
T *getMutable(
|
||||
Category category,
|
||||
UErrorCode &status) {
|
||||
return getMutable(category, NULL, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience routine to get a mutable pointer to a value by category name.
|
||||
* Otherwise works just like getMutable(Category, UErrorCode &).
|
||||
* reports an error if the category name is invalid.
|
||||
*/
|
||||
T *getMutable(
|
||||
const char *category,
|
||||
UErrorCode &status) {
|
||||
return getMutable(toCategory(category), NULL, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Just like getMutable(Category, UErrorCode &) but copies defaultValue to
|
||||
* returned pointer if it was defaulting to the 'other' variant
|
||||
* because no explicit value was stored.
|
||||
*/
|
||||
T *getMutableWithDefault(
|
||||
Category category,
|
||||
const T &defaultValue,
|
||||
UErrorCode &status) {
|
||||
return getMutable(category, &defaultValue, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if this object equals rhs.
|
||||
*/
|
||||
UBool equals(
|
||||
const PluralMap<T> &rhs,
|
||||
UBool (*eqFunc)(const T &, const T &)) const {
|
||||
for (int32_t i = 0; i < UPRV_LENGTHOF(fVariants); ++i) {
|
||||
if (fVariants[i] == rhs.fVariants[i]) {
|
||||
continue;
|
||||
}
|
||||
if (fVariants[i] == NULL || rhs.fVariants[i] == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
if (!eqFunc(*fVariants[i], *rhs.fVariants[i])) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
private:
|
||||
T fOtherVariant;
|
||||
T* fVariants[6];
|
||||
|
||||
T *getMutable(
|
||||
Category category,
|
||||
const T *defaultValue,
|
||||
UErrorCode &status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return NULL;
|
||||
}
|
||||
int32_t index = category;
|
||||
if (index < 0 || index >= UPRV_LENGTHOF(fVariants)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return NULL;
|
||||
}
|
||||
if (fVariants[index] == NULL) {
|
||||
fVariants[index] = defaultValue == NULL ?
|
||||
new T() : new T(*defaultValue);
|
||||
}
|
||||
if (!fVariants[index]) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
}
|
||||
return fVariants[index];
|
||||
}
|
||||
|
||||
void initializeNew() {
|
||||
fVariants[0] = &fOtherVariant;
|
||||
for (int32_t i = 1; i < UPRV_LENGTHOF(fVariants); ++i) {
|
||||
fVariants[i] = NULL;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif
|
88
icu4c/source/common/unistrappender.h
Normal file
88
icu4c/source/common/unistrappender.h
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
******************************************************************************
|
||||
* Copyright (C) 2015, International Business Machines Corporation and
|
||||
* others. All Rights Reserved.
|
||||
******************************************************************************
|
||||
*
|
||||
* File unistrappender.h
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef __UNISTRAPPENDER_H__
|
||||
#define __UNISTRAPPENDER_H__
|
||||
|
||||
#include "unicode/unistr.h"
|
||||
#include "unicode/uobject.h"
|
||||
#include "unicode/utf16.h"
|
||||
#include "unicode/utypes.h"
|
||||
#include "cmemory.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
/**
|
||||
* An optimization for the slowness of calling UnicodeString::append()
|
||||
* one character at a time in a loop. It stores appends in a buffer while
|
||||
* never actually calling append on the unicode string unless the buffer
|
||||
* fills up or is flushed.
|
||||
*
|
||||
* proper usage:
|
||||
* {
|
||||
* UnicodeStringAppender appender(astring);
|
||||
* for (int32_t i = 0; i < 100; ++i) {
|
||||
* appender.append((UChar) i);
|
||||
* }
|
||||
* // appender flushed automatically when it goes out of scope.
|
||||
* }
|
||||
*/
|
||||
class UnicodeStringAppender : public UMemory {
|
||||
public:
|
||||
|
||||
/**
|
||||
* dest is the UnicodeString being appended to. It must always
|
||||
* exist while this instance exists.
|
||||
*/
|
||||
UnicodeStringAppender(UnicodeString &dest) : fDest(&dest), fIdx(0) { }
|
||||
|
||||
inline void append(UChar x) {
|
||||
if (fIdx == UPRV_LENGTHOF(fBuffer)) {
|
||||
fDest->append(fBuffer, 0, fIdx);
|
||||
fIdx = 0;
|
||||
}
|
||||
fBuffer[fIdx++] = x;
|
||||
}
|
||||
|
||||
inline void append(UChar32 x) {
|
||||
if (fIdx >= UPRV_LENGTHOF(fBuffer) - 1) {
|
||||
fDest->append(fBuffer, 0, fIdx);
|
||||
fIdx = 0;
|
||||
}
|
||||
U16_APPEND_UNSAFE(fBuffer, fIdx, x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that all appended characters have been written out to dest.
|
||||
*/
|
||||
inline void flush() {
|
||||
if (fIdx) {
|
||||
fDest->append(fBuffer, 0, fIdx);
|
||||
}
|
||||
fIdx = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* flush the buffer when we go out of scope.
|
||||
*/
|
||||
~UnicodeStringAppender() {
|
||||
flush();
|
||||
}
|
||||
private:
|
||||
UnicodeString *fDest;
|
||||
int32_t fIdx;
|
||||
UChar fBuffer[32];
|
||||
UnicodeStringAppender(const UnicodeStringAppender &other);
|
||||
UnicodeStringAppender &operator=(const UnicodeStringAppender &other);
|
||||
};
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif
|
|
@ -95,7 +95,11 @@ ztrans.o zrule.o vzone.o fphdlimp.o fpositer.o ufieldpositer.o locdspnm.o \
|
|||
decNumber.o decContext.o alphaindex.o tznames.o tznames_impl.o tzgnames.o \
|
||||
tzfmt.o compactdecimalformat.o gender.o region.o scriptset.o identifier_info.o \
|
||||
uregion.o reldatefmt.o quantityformatter.o measunit.o \
|
||||
sharedbreakiterator.o scientificnumberformatter.o
|
||||
sharedbreakiterator.o scientificnumberformatter.o digitgrouping.o \
|
||||
digitinterval.o digitformatter.o digitaffix.o valueformatter.o \
|
||||
digitaffixesandpadding.o pluralaffix.o precision.o \
|
||||
affixpatternparser.o smallintformatter.o decimfmtimpl.o \
|
||||
visibledigits.o
|
||||
|
||||
## Header files to install
|
||||
HEADERS = $(srcdir)/unicode/*.h
|
||||
|
|
689
icu4c/source/i18n/affixpatternparser.cpp
Normal file
689
icu4c/source/i18n/affixpatternparser.cpp
Normal file
|
@ -0,0 +1,689 @@
|
|||
/*
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*
|
||||
* file name: affixpatternparser.cpp
|
||||
*/
|
||||
|
||||
|
||||
#include "unicode/dcfmtsym.h"
|
||||
#include "unicode/plurrule.h"
|
||||
#include "unicode/ucurr.h"
|
||||
#include "affixpatternparser.h"
|
||||
#include "charstr.h"
|
||||
#include "precision.h"
|
||||
#include "uassert.h"
|
||||
#include "unistrappender.h"
|
||||
|
||||
static UChar gDefaultSymbols[] = {0xa4, 0xa4, 0xa4};
|
||||
|
||||
static UChar gPercent = 0x25;
|
||||
static UChar gPerMill = 0x2030;
|
||||
static UChar gNegative = 0x2D;
|
||||
static UChar gPositive = 0x2B;
|
||||
|
||||
#define PACK_TOKEN_AND_LENGTH(t, l) ((UChar) (((t) << 8) | (l & 0xFF)))
|
||||
|
||||
#define UNPACK_TOKEN(c) ((AffixPattern::ETokenType) (((c) >> 8) & 0x7F))
|
||||
|
||||
#define UNPACK_LONG(c) (((c) >> 8) & 0x80)
|
||||
|
||||
#define UNPACK_LENGTH(c) ((c) & 0xFF)
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
static int32_t
|
||||
nextToken(const UChar *buffer, int32_t idx, int32_t len, UChar *token) {
|
||||
if (buffer[idx] != 0x27 || idx + 1 == len) {
|
||||
*token = buffer[idx];
|
||||
return 1;
|
||||
}
|
||||
*token = buffer[idx + 1];
|
||||
if (buffer[idx + 1] == 0xA4) {
|
||||
int32_t i = 2;
|
||||
for (; idx + i < len && i < 4 && buffer[idx + i] == buffer[idx + 1]; ++i);
|
||||
return i;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int32_t
|
||||
nextUserToken(const UChar *buffer, int32_t idx, int32_t len, UChar *token) {
|
||||
*token = buffer[idx];
|
||||
int32_t max;
|
||||
switch (buffer[idx]) {
|
||||
case 0x27:
|
||||
max = 2;
|
||||
break;
|
||||
case 0xA4:
|
||||
max = 3;
|
||||
break;
|
||||
default:
|
||||
max = 1;
|
||||
break;
|
||||
}
|
||||
int32_t i = 1;
|
||||
for (; idx + i < len && i < max && buffer[idx + i] == buffer[idx]; ++i);
|
||||
return i;
|
||||
}
|
||||
|
||||
CurrencyAffixInfo::CurrencyAffixInfo()
|
||||
: fSymbol(gDefaultSymbols, 1),
|
||||
fISO(gDefaultSymbols, 2),
|
||||
fLong(DigitAffix(gDefaultSymbols, 3)),
|
||||
fIsDefault(TRUE) {
|
||||
}
|
||||
|
||||
void
|
||||
CurrencyAffixInfo::set(
|
||||
const char *locale,
|
||||
const PluralRules *rules,
|
||||
const UChar *currency,
|
||||
UErrorCode &status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
fIsDefault = FALSE;
|
||||
if (currency == NULL) {
|
||||
fSymbol.setTo(gDefaultSymbols, 1);
|
||||
fISO.setTo(gDefaultSymbols, 2);
|
||||
fLong.remove();
|
||||
fLong.append(gDefaultSymbols, 3);
|
||||
fIsDefault = TRUE;
|
||||
return;
|
||||
}
|
||||
int32_t len;
|
||||
UBool unusedIsChoice;
|
||||
const UChar *symbol = ucurr_getName(
|
||||
currency, locale, UCURR_SYMBOL_NAME, &unusedIsChoice,
|
||||
&len, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
fSymbol.setTo(symbol, len);
|
||||
fISO.setTo(currency, u_strlen(currency));
|
||||
fLong.remove();
|
||||
StringEnumeration* keywords = rules->getKeywords(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
const UnicodeString* pluralCount;
|
||||
while ((pluralCount = keywords->snext(status)) != NULL) {
|
||||
CharString pCount;
|
||||
pCount.appendInvariantChars(*pluralCount, status);
|
||||
const UChar *pluralName = ucurr_getPluralName(
|
||||
currency, locale, &unusedIsChoice, pCount.data(),
|
||||
&len, &status);
|
||||
fLong.setVariant(pCount.data(), UnicodeString(pluralName, len), status);
|
||||
}
|
||||
delete keywords;
|
||||
}
|
||||
|
||||
void
|
||||
CurrencyAffixInfo::adjustPrecision(
|
||||
const UChar *currency, const UCurrencyUsage usage,
|
||||
FixedPrecision &precision, UErrorCode &status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t digitCount = ucurr_getDefaultFractionDigitsForUsage(
|
||||
currency, usage, &status);
|
||||
precision.fMin.setFracDigitCount(digitCount);
|
||||
precision.fMax.setFracDigitCount(digitCount);
|
||||
double increment = ucurr_getRoundingIncrementForUsage(
|
||||
currency, usage, &status);
|
||||
if (increment == 0.0) {
|
||||
precision.fRoundingIncrement.clear();
|
||||
} else {
|
||||
precision.fRoundingIncrement.set(increment);
|
||||
// guard against round-off error
|
||||
precision.fRoundingIncrement.round(6);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AffixPattern::addLiteral(
|
||||
const UChar *literal, int32_t start, int32_t len) {
|
||||
char32Count += u_countChar32(literal + start, len);
|
||||
literals.append(literal, start, len);
|
||||
int32_t tlen = tokens.length();
|
||||
// Takes 4 UChars to encode maximum literal length.
|
||||
UChar *tokenChars = tokens.getBuffer(tlen + 4);
|
||||
|
||||
// find start of literal size. May be tlen if there is no literal.
|
||||
// While finding start of literal size, compute literal length
|
||||
int32_t literalLength = 0;
|
||||
int32_t tLiteralStart = tlen;
|
||||
while (tLiteralStart > 0 && UNPACK_TOKEN(tokenChars[tLiteralStart - 1]) == kLiteral) {
|
||||
tLiteralStart--;
|
||||
literalLength <<= 8;
|
||||
literalLength |= UNPACK_LENGTH(tokenChars[tLiteralStart]);
|
||||
}
|
||||
// Add number of chars we just added to literal
|
||||
literalLength += len;
|
||||
|
||||
// Now encode the new length starting at tLiteralStart
|
||||
tlen = tLiteralStart;
|
||||
tokenChars[tlen++] = PACK_TOKEN_AND_LENGTH(kLiteral, literalLength & 0xFF);
|
||||
literalLength >>= 8;
|
||||
while (literalLength) {
|
||||
tokenChars[tlen++] = PACK_TOKEN_AND_LENGTH(kLiteral | 0x80, literalLength & 0xFF);
|
||||
literalLength >>= 8;
|
||||
}
|
||||
tokens.releaseBuffer(tlen);
|
||||
}
|
||||
|
||||
void
|
||||
AffixPattern::add(ETokenType t) {
|
||||
add(t, 1);
|
||||
}
|
||||
|
||||
void
|
||||
AffixPattern::addCurrency(uint8_t count) {
|
||||
add(kCurrency, count);
|
||||
}
|
||||
|
||||
void
|
||||
AffixPattern::add(ETokenType t, uint8_t count) {
|
||||
U_ASSERT(t != kLiteral);
|
||||
char32Count += count;
|
||||
switch (t) {
|
||||
case kCurrency:
|
||||
hasCurrencyToken = TRUE;
|
||||
break;
|
||||
case kPercent:
|
||||
hasPercentToken = TRUE;
|
||||
break;
|
||||
case kPerMill:
|
||||
hasPermillToken = TRUE;
|
||||
break;
|
||||
default:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
tokens.append(PACK_TOKEN_AND_LENGTH(t, count));
|
||||
}
|
||||
|
||||
AffixPattern &
|
||||
AffixPattern::append(const AffixPattern &other) {
|
||||
AffixPatternIterator iter;
|
||||
other.iterator(iter);
|
||||
UnicodeString literal;
|
||||
while (iter.nextToken()) {
|
||||
switch (iter.getTokenType()) {
|
||||
case kLiteral:
|
||||
iter.getLiteral(literal);
|
||||
addLiteral(literal.getBuffer(), 0, literal.length());
|
||||
break;
|
||||
case kCurrency:
|
||||
addCurrency(iter.getTokenLength());
|
||||
break;
|
||||
default:
|
||||
add(iter.getTokenType());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
AffixPattern::remove() {
|
||||
tokens.remove();
|
||||
literals.remove();
|
||||
hasCurrencyToken = FALSE;
|
||||
hasPercentToken = FALSE;
|
||||
hasPermillToken = FALSE;
|
||||
char32Count = 0;
|
||||
}
|
||||
|
||||
// escapes literals for strings where special characters are NOT escaped
|
||||
// except for apostrophe.
|
||||
static void escapeApostropheInLiteral(
|
||||
const UnicodeString &literal, UnicodeStringAppender &appender) {
|
||||
int32_t len = literal.length();
|
||||
const UChar *buffer = literal.getBuffer();
|
||||
for (int32_t i = 0; i < len; ++i) {
|
||||
UChar ch = buffer[i];
|
||||
switch (ch) {
|
||||
case 0x27:
|
||||
appender.append((UChar) 0x27);
|
||||
appender.append((UChar) 0x27);
|
||||
break;
|
||||
default:
|
||||
appender.append(ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// escapes literals for user strings where special characters in literals
|
||||
// are escaped with apostrophe.
|
||||
static void escapeLiteral(
|
||||
const UnicodeString &literal, UnicodeStringAppender &appender) {
|
||||
int32_t len = literal.length();
|
||||
const UChar *buffer = literal.getBuffer();
|
||||
for (int32_t i = 0; i < len; ++i) {
|
||||
UChar ch = buffer[i];
|
||||
switch (ch) {
|
||||
case 0x27:
|
||||
appender.append((UChar) 0x27);
|
||||
appender.append((UChar) 0x27);
|
||||
break;
|
||||
case 0x25:
|
||||
appender.append((UChar) 0x27);
|
||||
appender.append((UChar) 0x25);
|
||||
appender.append((UChar) 0x27);
|
||||
break;
|
||||
case 0x2030:
|
||||
appender.append((UChar) 0x27);
|
||||
appender.append((UChar) 0x2030);
|
||||
appender.append((UChar) 0x27);
|
||||
break;
|
||||
case 0xA4:
|
||||
appender.append((UChar) 0x27);
|
||||
appender.append((UChar) 0xA4);
|
||||
appender.append((UChar) 0x27);
|
||||
break;
|
||||
case 0x2D:
|
||||
appender.append((UChar) 0x27);
|
||||
appender.append((UChar) 0x2D);
|
||||
appender.append((UChar) 0x27);
|
||||
break;
|
||||
case 0x2B:
|
||||
appender.append((UChar) 0x27);
|
||||
appender.append((UChar) 0x2B);
|
||||
appender.append((UChar) 0x27);
|
||||
break;
|
||||
default:
|
||||
appender.append(ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnicodeString &
|
||||
AffixPattern::toString(UnicodeString &appendTo) const {
|
||||
AffixPatternIterator iter;
|
||||
iterator(iter);
|
||||
UnicodeStringAppender appender(appendTo);
|
||||
UnicodeString literal;
|
||||
while (iter.nextToken()) {
|
||||
switch (iter.getTokenType()) {
|
||||
case kLiteral:
|
||||
escapeApostropheInLiteral(iter.getLiteral(literal), appender);
|
||||
break;
|
||||
case kPercent:
|
||||
appender.append((UChar) 0x27);
|
||||
appender.append((UChar) 0x25);
|
||||
break;
|
||||
case kPerMill:
|
||||
appender.append((UChar) 0x27);
|
||||
appender.append((UChar) 0x2030);
|
||||
break;
|
||||
case kCurrency:
|
||||
{
|
||||
appender.append((UChar) 0x27);
|
||||
int32_t cl = iter.getTokenLength();
|
||||
for (int32_t i = 0; i < cl; ++i) {
|
||||
appender.append((UChar) 0xA4);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kNegative:
|
||||
appender.append((UChar) 0x27);
|
||||
appender.append((UChar) 0x2D);
|
||||
break;
|
||||
case kPositive:
|
||||
appender.append((UChar) 0x27);
|
||||
appender.append((UChar) 0x2B);
|
||||
break;
|
||||
default:
|
||||
U_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
UnicodeString &
|
||||
AffixPattern::toUserString(UnicodeString &appendTo) const {
|
||||
AffixPatternIterator iter;
|
||||
iterator(iter);
|
||||
UnicodeStringAppender appender(appendTo);
|
||||
UnicodeString literal;
|
||||
while (iter.nextToken()) {
|
||||
switch (iter.getTokenType()) {
|
||||
case kLiteral:
|
||||
escapeLiteral(iter.getLiteral(literal), appender);
|
||||
break;
|
||||
case kPercent:
|
||||
appender.append((UChar) 0x25);
|
||||
break;
|
||||
case kPerMill:
|
||||
appender.append((UChar) 0x2030);
|
||||
break;
|
||||
case kCurrency:
|
||||
{
|
||||
int32_t cl = iter.getTokenLength();
|
||||
for (int32_t i = 0; i < cl; ++i) {
|
||||
appender.append((UChar) 0xA4);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kNegative:
|
||||
appender.append((UChar) 0x2D);
|
||||
break;
|
||||
case kPositive:
|
||||
appender.append((UChar) 0x2B);
|
||||
break;
|
||||
default:
|
||||
U_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
class AffixPatternAppender : public UMemory {
|
||||
public:
|
||||
AffixPatternAppender(AffixPattern &dest) : fDest(&dest), fIdx(0) { }
|
||||
|
||||
inline void append(UChar x) {
|
||||
if (fIdx == UPRV_LENGTHOF(fBuffer)) {
|
||||
fDest->addLiteral(fBuffer, 0, fIdx);
|
||||
fIdx = 0;
|
||||
}
|
||||
fBuffer[fIdx++] = x;
|
||||
}
|
||||
|
||||
inline void append(UChar32 x) {
|
||||
if (fIdx >= UPRV_LENGTHOF(fBuffer) - 1) {
|
||||
fDest->addLiteral(fBuffer, 0, fIdx);
|
||||
fIdx = 0;
|
||||
}
|
||||
U16_APPEND_UNSAFE(fBuffer, fIdx, x);
|
||||
}
|
||||
|
||||
inline void flush() {
|
||||
if (fIdx) {
|
||||
fDest->addLiteral(fBuffer, 0, fIdx);
|
||||
}
|
||||
fIdx = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* flush the buffer when we go out of scope.
|
||||
*/
|
||||
~AffixPatternAppender() {
|
||||
flush();
|
||||
}
|
||||
private:
|
||||
AffixPattern *fDest;
|
||||
int32_t fIdx;
|
||||
UChar fBuffer[32];
|
||||
AffixPatternAppender(const AffixPatternAppender &other);
|
||||
AffixPatternAppender &operator=(const AffixPatternAppender &other);
|
||||
};
|
||||
|
||||
|
||||
AffixPattern &
|
||||
AffixPattern::parseUserAffixString(
|
||||
const UnicodeString &affixStr,
|
||||
AffixPattern &appendTo,
|
||||
UErrorCode &status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
int32_t len = affixStr.length();
|
||||
const UChar *buffer = affixStr.getBuffer();
|
||||
// 0 = not quoted; 1 = quoted.
|
||||
int32_t state = 0;
|
||||
AffixPatternAppender appender(appendTo);
|
||||
for (int32_t i = 0; i < len; ) {
|
||||
UChar token;
|
||||
int32_t tokenSize = nextUserToken(buffer, i, len, &token);
|
||||
i += tokenSize;
|
||||
if (token == 0x27 && tokenSize == 1) { // quote
|
||||
state = 1 - state;
|
||||
continue;
|
||||
}
|
||||
if (state == 0) {
|
||||
switch (token) {
|
||||
case 0x25:
|
||||
appender.flush();
|
||||
appendTo.add(kPercent, 1);
|
||||
break;
|
||||
case 0x27: // double quote
|
||||
appender.append((UChar) 0x27);
|
||||
break;
|
||||
case 0x2030:
|
||||
appender.flush();
|
||||
appendTo.add(kPerMill, 1);
|
||||
break;
|
||||
case 0x2D:
|
||||
appender.flush();
|
||||
appendTo.add(kNegative, 1);
|
||||
break;
|
||||
case 0x2B:
|
||||
appender.flush();
|
||||
appendTo.add(kPositive, 1);
|
||||
break;
|
||||
case 0xA4:
|
||||
appender.flush();
|
||||
appendTo.add(kCurrency, tokenSize);
|
||||
break;
|
||||
default:
|
||||
appender.append(token);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (token) {
|
||||
case 0x27: // double quote
|
||||
appender.append((UChar) 0x27);
|
||||
break;
|
||||
case 0xA4: // included b/c tokenSize can be > 1
|
||||
for (int32_t j = 0; j < tokenSize; ++j) {
|
||||
appender.append((UChar) 0xA4);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
appender.append(token);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
AffixPattern &
|
||||
AffixPattern::parseAffixString(
|
||||
const UnicodeString &affixStr,
|
||||
AffixPattern &appendTo,
|
||||
UErrorCode &status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
int32_t len = affixStr.length();
|
||||
const UChar *buffer = affixStr.getBuffer();
|
||||
for (int32_t i = 0; i < len; ) {
|
||||
UChar token;
|
||||
int32_t tokenSize = nextToken(buffer, i, len, &token);
|
||||
if (tokenSize == 1) {
|
||||
int32_t literalStart = i;
|
||||
++i;
|
||||
while (i < len && (tokenSize = nextToken(buffer, i, len, &token)) == 1) {
|
||||
++i;
|
||||
}
|
||||
appendTo.addLiteral(buffer, literalStart, i - literalStart);
|
||||
|
||||
// If we reached end of string, we are done
|
||||
if (i == len) {
|
||||
return appendTo;
|
||||
}
|
||||
}
|
||||
i += tokenSize;
|
||||
switch (token) {
|
||||
case 0x25:
|
||||
appendTo.add(kPercent, 1);
|
||||
break;
|
||||
case 0x2030:
|
||||
appendTo.add(kPerMill, 1);
|
||||
break;
|
||||
case 0x2D:
|
||||
appendTo.add(kNegative, 1);
|
||||
break;
|
||||
case 0x2B:
|
||||
appendTo.add(kPositive, 1);
|
||||
break;
|
||||
case 0xA4:
|
||||
{
|
||||
if (tokenSize - 1 > 3) {
|
||||
status = U_PARSE_ERROR;
|
||||
return appendTo;
|
||||
}
|
||||
appendTo.add(kCurrency, tokenSize - 1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
appendTo.addLiteral(&token, 0, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
AffixPatternIterator &
|
||||
AffixPattern::iterator(AffixPatternIterator &result) const {
|
||||
result.nextLiteralIndex = 0;
|
||||
result.lastLiteralLength = 0;
|
||||
result.nextTokenIndex = 0;
|
||||
result.tokens = &tokens;
|
||||
result.literals = &literals;
|
||||
return result;
|
||||
}
|
||||
|
||||
UBool
|
||||
AffixPatternIterator::nextToken() {
|
||||
int32_t tlen = tokens->length();
|
||||
if (nextTokenIndex == tlen) {
|
||||
return FALSE;
|
||||
}
|
||||
++nextTokenIndex;
|
||||
const UChar *tokenBuffer = tokens->getBuffer();
|
||||
if (UNPACK_TOKEN(tokenBuffer[nextTokenIndex - 1]) ==
|
||||
AffixPattern::kLiteral) {
|
||||
while (nextTokenIndex < tlen &&
|
||||
UNPACK_LONG(tokenBuffer[nextTokenIndex])) {
|
||||
++nextTokenIndex;
|
||||
}
|
||||
lastLiteralLength = 0;
|
||||
int32_t i = nextTokenIndex - 1;
|
||||
for (; UNPACK_LONG(tokenBuffer[i]); --i) {
|
||||
lastLiteralLength <<= 8;
|
||||
lastLiteralLength |= UNPACK_LENGTH(tokenBuffer[i]);
|
||||
}
|
||||
lastLiteralLength <<= 8;
|
||||
lastLiteralLength |= UNPACK_LENGTH(tokenBuffer[i]);
|
||||
nextLiteralIndex += lastLiteralLength;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
AffixPattern::ETokenType
|
||||
AffixPatternIterator::getTokenType() const {
|
||||
return UNPACK_TOKEN(tokens->charAt(nextTokenIndex - 1));
|
||||
}
|
||||
|
||||
UnicodeString &
|
||||
AffixPatternIterator::getLiteral(UnicodeString &result) const {
|
||||
const UChar *buffer = literals->getBuffer();
|
||||
result.setTo(buffer + (nextLiteralIndex - lastLiteralLength), lastLiteralLength);
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t
|
||||
AffixPatternIterator::getTokenLength() const {
|
||||
const UChar *tokenBuffer = tokens->getBuffer();
|
||||
AffixPattern::ETokenType type = UNPACK_TOKEN(tokenBuffer[nextTokenIndex - 1]);
|
||||
return type == AffixPattern::kLiteral ? lastLiteralLength : UNPACK_LENGTH(tokenBuffer[nextTokenIndex - 1]);
|
||||
}
|
||||
|
||||
AffixPatternParser::AffixPatternParser()
|
||||
: fPercent(gPercent), fPermill(gPerMill), fNegative(gNegative), fPositive(gPositive) {
|
||||
}
|
||||
|
||||
AffixPatternParser::AffixPatternParser(
|
||||
const DecimalFormatSymbols &symbols) {
|
||||
setDecimalFormatSymbols(symbols);
|
||||
}
|
||||
|
||||
void
|
||||
AffixPatternParser::setDecimalFormatSymbols(
|
||||
const DecimalFormatSymbols &symbols) {
|
||||
fPercent = symbols.getConstSymbol(DecimalFormatSymbols::kPercentSymbol);
|
||||
fPermill = symbols.getConstSymbol(DecimalFormatSymbols::kPerMillSymbol);
|
||||
fNegative = symbols.getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol);
|
||||
fPositive = symbols.getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol);
|
||||
}
|
||||
|
||||
PluralAffix &
|
||||
AffixPatternParser::parse(
|
||||
const AffixPattern &affixPattern,
|
||||
const CurrencyAffixInfo ¤cyAffixInfo,
|
||||
PluralAffix &appendTo,
|
||||
UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
AffixPatternIterator iter;
|
||||
affixPattern.iterator(iter);
|
||||
UnicodeString literal;
|
||||
while (iter.nextToken()) {
|
||||
switch (iter.getTokenType()) {
|
||||
case AffixPattern::kPercent:
|
||||
appendTo.append(fPercent, UNUM_PERCENT_FIELD);
|
||||
break;
|
||||
case AffixPattern::kPerMill:
|
||||
appendTo.append(fPermill, UNUM_PERMILL_FIELD);
|
||||
break;
|
||||
case AffixPattern::kNegative:
|
||||
appendTo.append(fNegative, UNUM_SIGN_FIELD);
|
||||
break;
|
||||
case AffixPattern::kPositive:
|
||||
appendTo.append(fPositive, UNUM_SIGN_FIELD);
|
||||
break;
|
||||
case AffixPattern::kCurrency:
|
||||
switch (iter.getTokenLength()) {
|
||||
case 1:
|
||||
appendTo.append(
|
||||
currencyAffixInfo.getSymbol(), UNUM_CURRENCY_FIELD);
|
||||
break;
|
||||
case 2:
|
||||
appendTo.append(
|
||||
currencyAffixInfo.getISO(), UNUM_CURRENCY_FIELD);
|
||||
break;
|
||||
case 3:
|
||||
appendTo.append(
|
||||
currencyAffixInfo.getLong(), UNUM_CURRENCY_FIELD, status);
|
||||
break;
|
||||
default:
|
||||
U_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AffixPattern::kLiteral:
|
||||
appendTo.append(iter.getLiteral(literal));
|
||||
break;
|
||||
default:
|
||||
U_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
397
icu4c/source/i18n/affixpatternparser.h
Normal file
397
icu4c/source/i18n/affixpatternparser.h
Normal file
|
@ -0,0 +1,397 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
* affixpatternparser.h
|
||||
*
|
||||
* created on: 2015jan06
|
||||
* created by: Travis Keep
|
||||
*/
|
||||
|
||||
#ifndef __AFFIX_PATTERN_PARSER_H__
|
||||
#define __AFFIX_PATTERN_PARSER_H__
|
||||
|
||||
#include "unicode/unistr.h"
|
||||
#include "unicode/uobject.h"
|
||||
#include "unicode/utypes.h"
|
||||
#include "pluralaffix.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
class PluralRules;
|
||||
class FixedPrecision;
|
||||
class DecimalFormatSymbols;
|
||||
|
||||
/**
|
||||
* A representation of the various forms of a particular currency according
|
||||
* to some locale and usage context.
|
||||
*
|
||||
* Includes the symbol, ISO code form, and long form(s) of the currency name
|
||||
* for each plural variation.
|
||||
*/
|
||||
class U_I18N_API CurrencyAffixInfo : public UMemory {
|
||||
public:
|
||||
/**
|
||||
* Symbol is \u00a4; ISO form is \u00a4\u00a4;
|
||||
* long form is \u00a4\u00a4\u00a4.
|
||||
*/
|
||||
CurrencyAffixInfo();
|
||||
|
||||
const UnicodeString &getSymbol() const { return fSymbol; }
|
||||
const UnicodeString &getISO() const { return fISO; }
|
||||
const PluralAffix &getLong() const { return fLong; }
|
||||
void setSymbol(const UnicodeString &symbol) {
|
||||
fSymbol = symbol;
|
||||
fIsDefault = FALSE;
|
||||
}
|
||||
void setISO(const UnicodeString &iso) {
|
||||
fISO = iso;
|
||||
fIsDefault = FALSE;
|
||||
}
|
||||
UBool
|
||||
equals(const CurrencyAffixInfo &other) const {
|
||||
return (fSymbol == other.fSymbol)
|
||||
&& (fISO == other.fISO)
|
||||
&& (fLong.equals(other.fLong))
|
||||
&& (fIsDefault == other.fIsDefault);
|
||||
}
|
||||
|
||||
/**
|
||||
* Intializes this instance.
|
||||
*
|
||||
* @param locale the locale for the currency forms.
|
||||
* @param rules The plural rules for the locale.
|
||||
* @param currency the null terminated, 3 character ISO code of the
|
||||
* currency. If NULL, resets this instance as if it were just created.
|
||||
* In this case, the first 2 parameters may be NULL as well.
|
||||
* @param status any error returned here.
|
||||
*/
|
||||
void set(
|
||||
const char *locale, const PluralRules *rules,
|
||||
const UChar *currency, UErrorCode &status);
|
||||
|
||||
/**
|
||||
* Returns true if this instance is the default. That is has no real
|
||||
* currency. For instance never initialized with set()
|
||||
* or reset with set(NULL, NULL, NULL, status).
|
||||
*/
|
||||
UBool isDefault() const { return fIsDefault; }
|
||||
|
||||
/**
|
||||
* Adjusts the precision used for a particular currency.
|
||||
* @param currency the null terminated, 3 character ISO code of the
|
||||
* currency.
|
||||
* @param usage the usage of the currency
|
||||
* @param precision min/max fraction digits and rounding increment
|
||||
* adjusted.
|
||||
* @params status any error reported here.
|
||||
*/
|
||||
static void adjustPrecision(
|
||||
const UChar *currency, const UCurrencyUsage usage,
|
||||
FixedPrecision &precision, UErrorCode &status);
|
||||
|
||||
private:
|
||||
/**
|
||||
* The symbol form of the currency.
|
||||
*/
|
||||
UnicodeString fSymbol;
|
||||
|
||||
/**
|
||||
* The ISO form of the currency, usually three letter abbreviation.
|
||||
*/
|
||||
UnicodeString fISO;
|
||||
|
||||
/**
|
||||
* The long forms of the currency keyed by plural variation.
|
||||
*/
|
||||
PluralAffix fLong;
|
||||
|
||||
UBool fIsDefault;
|
||||
|
||||
};
|
||||
|
||||
class AffixPatternIterator;
|
||||
|
||||
/**
|
||||
* A locale agnostic representation of an affix pattern.
|
||||
*/
|
||||
class U_I18N_API AffixPattern : public UMemory {
|
||||
public:
|
||||
|
||||
/**
|
||||
* The token types that can appear in an affix pattern.
|
||||
*/
|
||||
enum ETokenType {
|
||||
kLiteral,
|
||||
kPercent,
|
||||
kPerMill,
|
||||
kCurrency,
|
||||
kNegative,
|
||||
kPositive,
|
||||
};
|
||||
|
||||
/**
|
||||
* An empty affix pattern.
|
||||
*/
|
||||
AffixPattern()
|
||||
: tokens(), literals(), hasCurrencyToken(FALSE),
|
||||
hasPercentToken(FALSE), hasPermillToken(FALSE), char32Count(0) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a string literal to this affix pattern.
|
||||
*/
|
||||
void addLiteral(const UChar *, int32_t start, int32_t len);
|
||||
|
||||
/**
|
||||
* Adds a token to this affix pattern. t must not be kLiteral as
|
||||
* the addLiteral() method adds literals.
|
||||
* @param t the token type to add
|
||||
*/
|
||||
void add(ETokenType t);
|
||||
|
||||
/**
|
||||
* Adds a currency token with specific count to this affix pattern.
|
||||
* @param count the token count. Used to distinguish between
|
||||
* one, two, or three currency symbols. Note that adding a currency
|
||||
* token with count=2 (Use ISO code) is different than adding two
|
||||
* currency tokens each with count=1 (two currency symbols).
|
||||
*/
|
||||
void addCurrency(uint8_t count);
|
||||
|
||||
/**
|
||||
* Makes this instance be an empty affix pattern.
|
||||
*/
|
||||
void remove();
|
||||
|
||||
/**
|
||||
* Provides an iterator over the tokens in this instance.
|
||||
* @param result this is initialized to point just before the
|
||||
* first token of this instance. Caller must call nextToken()
|
||||
* on the iterator once it is set up to have it actually point
|
||||
* to the first token. This first call to nextToken() will return
|
||||
* FALSE if the AffixPattern being iterated over is empty.
|
||||
* @return result
|
||||
*/
|
||||
AffixPatternIterator &iterator(AffixPatternIterator &result) const;
|
||||
|
||||
/**
|
||||
* Returns TRUE if this instance has currency tokens in it.
|
||||
*/
|
||||
UBool usesCurrency() const {
|
||||
return hasCurrencyToken;
|
||||
}
|
||||
|
||||
UBool usesPercent() const {
|
||||
return hasPercentToken;
|
||||
}
|
||||
|
||||
UBool usesPermill() const {
|
||||
return hasPermillToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of code points a string of this instance
|
||||
* would have if none of the special tokens were escaped.
|
||||
* Used to compute the padding size.
|
||||
*/
|
||||
int32_t countChar32() const {
|
||||
return char32Count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends other to this instance mutating this instance in place.
|
||||
* @param other The pattern appended to the end of this one.
|
||||
* @return a reference to this instance for chaining.
|
||||
*/
|
||||
AffixPattern &append(const AffixPattern &other);
|
||||
|
||||
/**
|
||||
* Converts this AffixPattern back into a user string.
|
||||
* It is the inverse of parseUserAffixString.
|
||||
*/
|
||||
UnicodeString &toUserString(UnicodeString &appendTo) const;
|
||||
|
||||
/**
|
||||
* Converts this AffixPattern back into a string.
|
||||
* It is the inverse of parseAffixString.
|
||||
*/
|
||||
UnicodeString &toString(UnicodeString &appendTo) const;
|
||||
|
||||
/**
|
||||
* Parses an affix pattern string appending it to an AffixPattern.
|
||||
* Parses affix pattern strings produced from using
|
||||
* DecimalFormatPatternParser to parse a format pattern. Affix patterns
|
||||
* include the positive prefix and suffix and the negative prefix
|
||||
* and suffix. This method expects affix patterns strings to be in the
|
||||
* same format that DecimalFormatPatternParser produces. Namely special
|
||||
* characters in the affix that correspond to a field type must be
|
||||
* prefixed with an apostrophe ('). These special character sequences
|
||||
* inluce minus (-), percent (%), permile (U+2030), plus (+),
|
||||
* short currency (U+00a4), medium currency (u+00a4 * 2),
|
||||
* long currency (u+a4 * 3), and apostrophe (')
|
||||
* (apostrophe does not correspond to a field type but has to be escaped
|
||||
* because it itself is the escape character).
|
||||
* Since the expansion of these special character
|
||||
* sequences is locale dependent, these sequences are not expanded in
|
||||
* an AffixPattern instance.
|
||||
* If these special characters are not prefixed with an apostrophe in
|
||||
* the affix pattern string, then they are treated verbatim just as
|
||||
* any other character. If an apostrophe prefixes a non special
|
||||
* character in the affix pattern, the apostrophe is simply ignored.
|
||||
*
|
||||
* @param affixStr the string from DecimalFormatPatternParser
|
||||
* @param appendTo parsed result appended here.
|
||||
* @param status any error parsing returned here.
|
||||
*/
|
||||
static AffixPattern &parseAffixString(
|
||||
const UnicodeString &affixStr,
|
||||
AffixPattern &appendTo,
|
||||
UErrorCode &status);
|
||||
|
||||
/**
|
||||
* Parses an affix pattern string appending it to an AffixPattern.
|
||||
* Parses affix pattern strings as the user would supply them.
|
||||
* In this function, quoting makes special characters like normal
|
||||
* characters whereas in parseAffixString, quoting makes special
|
||||
* characters special.
|
||||
*
|
||||
* @param affixStr the string from the user
|
||||
* @param appendTo parsed result appended here.
|
||||
* @param status any error parsing returned here.
|
||||
*/
|
||||
static AffixPattern &parseUserAffixString(
|
||||
const UnicodeString &affixStr,
|
||||
AffixPattern &appendTo,
|
||||
UErrorCode &status);
|
||||
|
||||
UBool equals(const AffixPattern &other) const {
|
||||
return (tokens == other.tokens)
|
||||
&& (literals == other.literals)
|
||||
&& (hasCurrencyToken == other.hasCurrencyToken)
|
||||
&& (hasPercentToken == other.hasPercentToken)
|
||||
&& (hasPermillToken == other.hasPermillToken)
|
||||
&& (char32Count == other.char32Count);
|
||||
}
|
||||
|
||||
private:
|
||||
/*
|
||||
* Tokens stored here. Each UChar generally stands for one token. A
|
||||
* Each token is of form 'etttttttllllllll' llllllll is the length of
|
||||
* the token and ranges from 0-255. ttttttt is the token type and ranges
|
||||
* from 0-127. If e is set it means this is an extendo token (to be
|
||||
* described later). To accomodate token lengths above 255, each normal
|
||||
* token (e=0) can be followed by 0 or more extendo tokens (e=1) with
|
||||
* the same type. Right now only kLiteral Tokens have extendo tokens.
|
||||
* Each extendo token provides the next 8 higher bits for the length.
|
||||
* If a kLiteral token is followed by 2 extendo tokens then, then the
|
||||
* llllllll of the next extendo token contains bits 8-15 of the length
|
||||
* and the last extendo token contains bits 16-23 of the length.
|
||||
*/
|
||||
UnicodeString tokens;
|
||||
|
||||
/*
|
||||
* The characters of the kLiteral tokens are concatenated together here.
|
||||
* The first characters go with the first kLiteral token, the next
|
||||
* characters go with the next kLiteral token etc.
|
||||
*/
|
||||
UnicodeString literals;
|
||||
UBool hasCurrencyToken;
|
||||
UBool hasPercentToken;
|
||||
UBool hasPermillToken;
|
||||
int32_t char32Count;
|
||||
void add(ETokenType t, uint8_t count);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* An iterator over the tokens in an AffixPattern instance.
|
||||
*/
|
||||
class U_I18N_API AffixPatternIterator : public UMemory {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Using an iterator without first calling iterator on an AffixPattern
|
||||
* instance to initialize the iterator results in
|
||||
* undefined behavior.
|
||||
*/
|
||||
AffixPatternIterator() : nextLiteralIndex(0), lastLiteralLength(0), nextTokenIndex(0), tokens(NULL), literals(NULL) { }
|
||||
/**
|
||||
* Advances this iterator to the next token. Returns FALSE when there
|
||||
* are no more tokens. Calling the other methods after nextToken()
|
||||
* returns FALSE results in undefined behavior.
|
||||
*/
|
||||
UBool nextToken();
|
||||
|
||||
/**
|
||||
* Returns the type of token.
|
||||
*/
|
||||
AffixPattern::ETokenType getTokenType() const;
|
||||
|
||||
/**
|
||||
* For literal tokens, returns the literal string. Calling this for
|
||||
* other token types results in undefined behavior.
|
||||
* @param result replaced with a read-only alias to the literal string.
|
||||
* @return result
|
||||
*/
|
||||
UnicodeString &getLiteral(UnicodeString &result) const;
|
||||
|
||||
/**
|
||||
* Returns the token length. Usually 1, but for currency tokens may
|
||||
* be 2 for ISO code and 3 for long form.
|
||||
*/
|
||||
int32_t getTokenLength() const;
|
||||
private:
|
||||
int32_t nextLiteralIndex;
|
||||
int32_t lastLiteralLength;
|
||||
int32_t nextTokenIndex;
|
||||
const UnicodeString *tokens;
|
||||
const UnicodeString *literals;
|
||||
friend class AffixPattern;
|
||||
AffixPatternIterator(const AffixPatternIterator &);
|
||||
AffixPatternIterator &operator=(const AffixPatternIterator &);
|
||||
};
|
||||
|
||||
/**
|
||||
* A locale aware class that converts locale independent AffixPattern
|
||||
* instances into locale dependent PluralAffix instances.
|
||||
*/
|
||||
class U_I18N_API AffixPatternParser : public UMemory {
|
||||
public:
|
||||
AffixPatternParser();
|
||||
AffixPatternParser(const DecimalFormatSymbols &symbols);
|
||||
void setDecimalFormatSymbols(const DecimalFormatSymbols &symbols);
|
||||
|
||||
/**
|
||||
* Parses affixPattern appending the result to appendTo.
|
||||
* @param affixPattern The affix pattern.
|
||||
* @param currencyAffixInfo contains the currency forms.
|
||||
* @param appendTo The result of parsing affixPattern is appended here.
|
||||
* @param status any error returned here.
|
||||
* @return appendTo.
|
||||
*/
|
||||
PluralAffix &parse(
|
||||
const AffixPattern &affixPattern,
|
||||
const CurrencyAffixInfo ¤cyAffixInfo,
|
||||
PluralAffix &appendTo,
|
||||
UErrorCode &status) const;
|
||||
|
||||
UBool equals(const AffixPatternParser &other) const {
|
||||
return (fPercent == other.fPercent)
|
||||
&& (fPermill == other.fPermill)
|
||||
&& (fNegative == other.fNegative)
|
||||
&& (fPositive == other.fPositive);
|
||||
}
|
||||
|
||||
private:
|
||||
UnicodeString fPercent;
|
||||
UnicodeString fPermill;
|
||||
UnicodeString fNegative;
|
||||
UnicodeString fPositive;
|
||||
};
|
||||
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif // __AFFIX_PATTERN_PARSER_H__
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 1997-2014, International Business Machines Corporation and
|
||||
* Copyright (C) 1997-2015, International Business Machines Corporation and
|
||||
* others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
*
|
||||
|
@ -126,6 +126,8 @@ DecimalFormatSymbols::operator=(const DecimalFormatSymbols& rhs)
|
|||
locale = rhs.locale;
|
||||
uprv_strcpy(validLocale, rhs.validLocale);
|
||||
uprv_strcpy(actualLocale, rhs.actualLocale);
|
||||
fIsCustomCurrencySymbol = rhs.fIsCustomCurrencySymbol;
|
||||
fIsCustomIntlCurrencySymbol = rhs.fIsCustomIntlCurrencySymbol;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
@ -138,6 +140,12 @@ DecimalFormatSymbols::operator==(const DecimalFormatSymbols& that) const
|
|||
if (this == &that) {
|
||||
return TRUE;
|
||||
}
|
||||
if (fIsCustomCurrencySymbol != that.fIsCustomCurrencySymbol) {
|
||||
return FALSE;
|
||||
}
|
||||
if (fIsCustomIntlCurrencySymbol != that.fIsCustomIntlCurrencySymbol) {
|
||||
return FALSE;
|
||||
}
|
||||
for(int32_t i = 0; i < (int32_t)kFormatSymbolCount; ++i) {
|
||||
if(fSymbols[(ENumberFormatSymbol)i] != that.fSymbols[(ENumberFormatSymbol)i]) {
|
||||
return FALSE;
|
||||
|
@ -422,6 +430,9 @@ DecimalFormatSymbols::initialize() {
|
|||
fSymbols[kSignificantDigitSymbol] = (UChar)0x0040; // '@' significant digit
|
||||
fSymbols[kMonetaryGroupingSeparatorSymbol].remove(); //
|
||||
fSymbols[kExponentMultiplicationSymbol] = (UChar)0xd7; // 'x' multiplication symbol for exponents
|
||||
fIsCustomCurrencySymbol = FALSE;
|
||||
fIsCustomIntlCurrencySymbol = FALSE;
|
||||
|
||||
}
|
||||
|
||||
Locale
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include "unicode/dcfmtsym.h"
|
||||
#include "unicode/format.h"
|
||||
#include "unicode/utf16.h"
|
||||
#include "decimalformatpatternimpl.h"
|
||||
|
||||
|
||||
#ifdef FMT_DEBUG
|
||||
#define debug(x) printf("%s:%d: %s\n", __FILE__,__LINE__, x);
|
||||
|
@ -20,23 +22,6 @@
|
|||
#define debug(x)
|
||||
#endif
|
||||
|
||||
#define kPatternZeroDigit ((UChar)0x0030) /*'0'*/
|
||||
#define kPatternSignificantDigit ((UChar)0x0040) /*'@'*/
|
||||
#define kPatternGroupingSeparator ((UChar)0x002C) /*','*/
|
||||
#define kPatternDecimalSeparator ((UChar)0x002E) /*'.'*/
|
||||
#define kPatternPerMill ((UChar)0x2030)
|
||||
#define kPatternPercent ((UChar)0x0025) /*'%'*/
|
||||
#define kPatternDigit ((UChar)0x0023) /*'#'*/
|
||||
#define kPatternSeparator ((UChar)0x003B) /*';'*/
|
||||
#define kPatternExponent ((UChar)0x0045) /*'E'*/
|
||||
#define kPatternPlus ((UChar)0x002B) /*'+'*/
|
||||
#define kPatternMinus ((UChar)0x002D) /*'-'*/
|
||||
#define kPatternPadEscape ((UChar)0x002A) /*'*'*/
|
||||
#define kQuote ((UChar)0x0027) /*'\''*/
|
||||
|
||||
#define kCurrencySign ((UChar)0x00A4)
|
||||
#define kDefaultPad ((UChar)0x0020) /* */
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
// TODO: Travis Keep: Copied from numfmt.cpp
|
||||
|
@ -651,6 +636,15 @@ DecimalFormatPatternParser::applyPatternWithoutExpandAffix(
|
|||
out.fNegPrefixPattern.append(kQuote).append(kPatternMinus)
|
||||
.append(out.fPosPrefixPattern);
|
||||
}
|
||||
// TODO: Deprecate/Remove out.fNegSuffixPattern and 3 other fields.
|
||||
AffixPattern::parseAffixString(
|
||||
out.fNegSuffixPattern, out.fNegSuffixAffix, status);
|
||||
AffixPattern::parseAffixString(
|
||||
out.fPosSuffixPattern, out.fPosSuffixAffix, status);
|
||||
AffixPattern::parseAffixString(
|
||||
out.fNegPrefixPattern, out.fNegPrefixAffix, status);
|
||||
AffixPattern::parseAffixString(
|
||||
out.fPosPrefixPattern, out.fPosPrefixAffix, status);
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 1997-2014, International Business Machines Corporation and *
|
||||
* Copyright (C) 1997-2015, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
@ -14,6 +14,7 @@
|
|||
#include "unicode/uobject.h"
|
||||
#include "unicode/unistr.h"
|
||||
#include "digitlst.h"
|
||||
#include "affixpatternparser.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
|
@ -63,6 +64,10 @@ struct DecimalFormatPattern : UMemory {
|
|||
UnicodeString fNegSuffixPattern;
|
||||
UnicodeString fPosPrefixPattern;
|
||||
UnicodeString fPosSuffixPattern;
|
||||
AffixPattern fNegPrefixAffix;
|
||||
AffixPattern fNegSuffixAffix;
|
||||
AffixPattern fPosPrefixAffix;
|
||||
AffixPattern fPosSuffixAffix;
|
||||
EPadPosition fPadPosition;
|
||||
};
|
||||
|
||||
|
|
33
icu4c/source/i18n/decimalformatpatternimpl.h
Normal file
33
icu4c/source/i18n/decimalformatpatternimpl.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
********************************************************************************
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
********************************************************************************
|
||||
*
|
||||
* File decimalformatpatternimpl.h
|
||||
********************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef DECIMALFORMATPATTERNIMPL_H
|
||||
#define DECIMALFORMATPATTERNIMPL_H
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#define kPatternZeroDigit ((UChar)0x0030) /*'0'*/
|
||||
#define kPatternSignificantDigit ((UChar)0x0040) /*'@'*/
|
||||
#define kPatternGroupingSeparator ((UChar)0x002C) /*','*/
|
||||
#define kPatternDecimalSeparator ((UChar)0x002E) /*'.'*/
|
||||
#define kPatternPerMill ((UChar)0x2030)
|
||||
#define kPatternPercent ((UChar)0x0025) /*'%'*/
|
||||
#define kPatternDigit ((UChar)0x0023) /*'#'*/
|
||||
#define kPatternSeparator ((UChar)0x003B) /*';'*/
|
||||
#define kPatternExponent ((UChar)0x0045) /*'E'*/
|
||||
#define kPatternPlus ((UChar)0x002B) /*'+'*/
|
||||
#define kPatternMinus ((UChar)0x002D) /*'-'*/
|
||||
#define kPatternPadEscape ((UChar)0x002A) /*'*'*/
|
||||
#define kQuote ((UChar)0x0027) /*'\''*/
|
||||
|
||||
#define kCurrencySign ((UChar)0x00A4)
|
||||
#define kDefaultPad ((UChar)0x0020) /* */
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load diff
1590
icu4c/source/i18n/decimfmtimpl.cpp
Normal file
1590
icu4c/source/i18n/decimfmtimpl.cpp
Normal file
File diff suppressed because it is too large
Load diff
424
icu4c/source/i18n/decimfmtimpl.h
Normal file
424
icu4c/source/i18n/decimfmtimpl.h
Normal file
|
@ -0,0 +1,424 @@
|
|||
/*
|
||||
********************************************************************************
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
********************************************************************************
|
||||
*
|
||||
* File decimfmtimpl.h
|
||||
********************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef DECIMFMTIMPL_H
|
||||
#define DECIMFMTIMPL_H
|
||||
|
||||
#include "unicode/decimfmt.h"
|
||||
#include "unicode/uobject.h"
|
||||
#include "unicode/utypes.h"
|
||||
#include "affixpatternparser.h"
|
||||
#include "digitaffixesandpadding.h"
|
||||
#include "digitformatter.h"
|
||||
#include "digitgrouping.h"
|
||||
#include "precision.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
class UnicodeString;
|
||||
class FieldPosition;
|
||||
class ValueFormatter;
|
||||
class FieldPositionHandler;
|
||||
class FixedDecimal;
|
||||
|
||||
class DecimalFormatImpl : public UObject {
|
||||
public:
|
||||
|
||||
DecimalFormatImpl(
|
||||
NumberFormat *super,
|
||||
const Locale &locale,
|
||||
const UnicodeString &pattern,
|
||||
UErrorCode &status);
|
||||
DecimalFormatImpl(
|
||||
NumberFormat *super,
|
||||
const UnicodeString &pattern,
|
||||
DecimalFormatSymbols *symbolsToAdopt,
|
||||
UParseError &parseError,
|
||||
UErrorCode &status);
|
||||
DecimalFormatImpl(
|
||||
NumberFormat *super,
|
||||
const DecimalFormatImpl &other,
|
||||
UErrorCode &status);
|
||||
DecimalFormatImpl &assign(
|
||||
const DecimalFormatImpl &other, UErrorCode &status);
|
||||
virtual ~DecimalFormatImpl();
|
||||
void adoptDecimalFormatSymbols(DecimalFormatSymbols *symbolsToAdopt);
|
||||
const DecimalFormatSymbols &getDecimalFormatSymbols() const {
|
||||
return *fSymbols;
|
||||
}
|
||||
UnicodeString &format(
|
||||
int32_t number,
|
||||
UnicodeString &appendTo,
|
||||
FieldPosition &pos,
|
||||
UErrorCode &status) const;
|
||||
UnicodeString &format(
|
||||
int32_t number,
|
||||
UnicodeString &appendTo,
|
||||
FieldPositionIterator *posIter,
|
||||
UErrorCode &status) const;
|
||||
UnicodeString &format(
|
||||
int64_t number,
|
||||
UnicodeString &appendTo,
|
||||
FieldPosition &pos,
|
||||
UErrorCode &status) const;
|
||||
UnicodeString &format(
|
||||
double number,
|
||||
UnicodeString &appendTo,
|
||||
FieldPosition &pos,
|
||||
UErrorCode &status) const;
|
||||
UnicodeString &format(
|
||||
const DigitList &number,
|
||||
UnicodeString &appendTo,
|
||||
FieldPosition &pos,
|
||||
UErrorCode &status) const;
|
||||
UnicodeString &format(
|
||||
int64_t number,
|
||||
UnicodeString &appendTo,
|
||||
FieldPositionIterator *posIter,
|
||||
UErrorCode &status) const;
|
||||
UnicodeString &format(
|
||||
double number,
|
||||
UnicodeString &appendTo,
|
||||
FieldPositionIterator *posIter,
|
||||
UErrorCode &status) const;
|
||||
UnicodeString &format(
|
||||
const DigitList &number,
|
||||
UnicodeString &appendTo,
|
||||
FieldPositionIterator *posIter,
|
||||
UErrorCode &status) const;
|
||||
UnicodeString &format(
|
||||
const StringPiece &number,
|
||||
UnicodeString &appendTo,
|
||||
FieldPositionIterator *posIter,
|
||||
UErrorCode &status) const;
|
||||
UnicodeString &format(
|
||||
const VisibleDigitsWithExponent &digits,
|
||||
UnicodeString &appendTo,
|
||||
FieldPosition &pos,
|
||||
UErrorCode &status) const;
|
||||
UnicodeString &format(
|
||||
const VisibleDigitsWithExponent &digits,
|
||||
UnicodeString &appendTo,
|
||||
FieldPositionIterator *posIter,
|
||||
UErrorCode &status) const;
|
||||
|
||||
UBool operator==(const DecimalFormatImpl &) const;
|
||||
|
||||
UBool operator!=(const DecimalFormatImpl &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
void setRoundingMode(DecimalFormat::ERoundingMode mode) {
|
||||
fRoundingMode = mode;
|
||||
fEffPrecision.fMantissa.fExactOnly = (fRoundingMode == DecimalFormat::kRoundUnnecessary);
|
||||
fEffPrecision.fMantissa.fRoundingMode = mode;
|
||||
}
|
||||
DecimalFormat::ERoundingMode getRoundingMode() const {
|
||||
return fRoundingMode;
|
||||
}
|
||||
void setFailIfMoreThanMaxDigits(UBool b) {
|
||||
fEffPrecision.fMantissa.fFailIfOverMax = b;
|
||||
}
|
||||
UBool isFailIfMoreThanMaxDigits() const { return fEffPrecision.fMantissa.fFailIfOverMax; }
|
||||
void setMinimumSignificantDigits(int32_t newValue);
|
||||
void setMaximumSignificantDigits(int32_t newValue);
|
||||
void setMinMaxSignificantDigits(int32_t min, int32_t max);
|
||||
void setScientificNotation(UBool newValue);
|
||||
void setSignificantDigitsUsed(UBool newValue);
|
||||
|
||||
int32_t getMinimumSignificantDigits() const {
|
||||
return fMinSigDigits; }
|
||||
int32_t getMaximumSignificantDigits() const {
|
||||
return fMaxSigDigits; }
|
||||
UBool isScientificNotation() const { return fUseScientific; }
|
||||
UBool areSignificantDigitsUsed() const { return fUseSigDigits; }
|
||||
void setGroupingSize(int32_t newValue);
|
||||
void setSecondaryGroupingSize(int32_t newValue);
|
||||
void setMinimumGroupingDigits(int32_t newValue);
|
||||
int32_t getGroupingSize() const { return fGrouping.fGrouping; }
|
||||
int32_t getSecondaryGroupingSize() const { return fGrouping.fGrouping2; }
|
||||
int32_t getMinimumGroupingDigits() const { return fGrouping.fMinGrouping; }
|
||||
void applyPattern(const UnicodeString &pattern, UErrorCode &status);
|
||||
void applyPatternFavorCurrencyPrecision(
|
||||
const UnicodeString &pattern, UErrorCode &status);
|
||||
void applyPattern(
|
||||
const UnicodeString &pattern, UParseError &perror, UErrorCode &status);
|
||||
void applyLocalizedPattern(const UnicodeString &pattern, UErrorCode &status);
|
||||
void applyLocalizedPattern(
|
||||
const UnicodeString &pattern, UParseError &perror, UErrorCode &status);
|
||||
void setCurrencyUsage(UCurrencyUsage usage, UErrorCode &status);
|
||||
UCurrencyUsage getCurrencyUsage() const { return fCurrencyUsage; }
|
||||
void setRoundingIncrement(double d);
|
||||
double getRoundingIncrement() const;
|
||||
int32_t getMultiplier() const;
|
||||
void setMultiplier(int32_t m);
|
||||
UChar32 getPadCharacter() const { return fAffixes.fPadChar; }
|
||||
void setPadCharacter(UChar32 c) { fAffixes.fPadChar = c; }
|
||||
int32_t getFormatWidth() const { return fAffixes.fWidth; }
|
||||
void setFormatWidth(int32_t x) { fAffixes.fWidth = x; }
|
||||
DigitAffixesAndPadding::EPadPosition getPadPosition() const {
|
||||
return fAffixes.fPadPosition;
|
||||
}
|
||||
void setPadPosition(DigitAffixesAndPadding::EPadPosition x) {
|
||||
fAffixes.fPadPosition = x;
|
||||
}
|
||||
int32_t getMinimumExponentDigits() const {
|
||||
return fEffPrecision.fMinExponentDigits;
|
||||
}
|
||||
void setMinimumExponentDigits(int32_t x) {
|
||||
fEffPrecision.fMinExponentDigits = x;
|
||||
}
|
||||
UBool isExponentSignAlwaysShown() const {
|
||||
return fOptions.fExponent.fAlwaysShowSign;
|
||||
}
|
||||
void setExponentSignAlwaysShown(UBool x) {
|
||||
fOptions.fExponent.fAlwaysShowSign = x;
|
||||
}
|
||||
UBool isDecimalSeparatorAlwaysShown() const {
|
||||
return fOptions.fMantissa.fAlwaysShowDecimal;
|
||||
}
|
||||
void setDecimalSeparatorAlwaysShown(UBool x) {
|
||||
fOptions.fMantissa.fAlwaysShowDecimal = x;
|
||||
}
|
||||
UnicodeString &getPositivePrefix(UnicodeString &result) const;
|
||||
UnicodeString &getPositiveSuffix(UnicodeString &result) const;
|
||||
UnicodeString &getNegativePrefix(UnicodeString &result) const;
|
||||
UnicodeString &getNegativeSuffix(UnicodeString &result) const;
|
||||
void setPositivePrefix(const UnicodeString &str);
|
||||
void setPositiveSuffix(const UnicodeString &str);
|
||||
void setNegativePrefix(const UnicodeString &str);
|
||||
void setNegativeSuffix(const UnicodeString &str);
|
||||
UnicodeString &toPattern(UnicodeString& result) const;
|
||||
FixedDecimal &getFixedDecimal(double value, FixedDecimal &result, UErrorCode &status) const;
|
||||
FixedDecimal &getFixedDecimal(DigitList &number, FixedDecimal &result, UErrorCode &status) const;
|
||||
DigitList &round(DigitList &number, UErrorCode &status) const;
|
||||
|
||||
VisibleDigitsWithExponent &
|
||||
initVisibleDigitsWithExponent(
|
||||
int64_t number,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const;
|
||||
VisibleDigitsWithExponent &
|
||||
initVisibleDigitsWithExponent(
|
||||
double number,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const;
|
||||
VisibleDigitsWithExponent &
|
||||
initVisibleDigitsWithExponent(
|
||||
DigitList &number,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const;
|
||||
|
||||
void updatePrecision();
|
||||
void updateGrouping();
|
||||
void updateCurrency(UErrorCode &status);
|
||||
|
||||
|
||||
private:
|
||||
// Disallow copy and assign
|
||||
DecimalFormatImpl(const DecimalFormatImpl &other);
|
||||
DecimalFormatImpl &operator=(const DecimalFormatImpl &other);
|
||||
NumberFormat *fSuper;
|
||||
DigitList fMultiplier;
|
||||
int32_t fScale;
|
||||
|
||||
DecimalFormat::ERoundingMode fRoundingMode;
|
||||
|
||||
// These fields include what the user can see and set.
|
||||
// When the user updates these fields, it triggers automatic updates of
|
||||
// other fields that may be invisible to user
|
||||
|
||||
// Updating any of the following fields triggers an update to
|
||||
// fEffPrecision.fMantissa.fMin,
|
||||
// fEffPrecision.fMantissa.fMax,
|
||||
// fEffPrecision.fMantissa.fSignificant fields
|
||||
// We have this two phase update because of backward compatibility.
|
||||
// DecimalFormat has to remember all settings even if those settings are
|
||||
// invalid or disabled.
|
||||
int32_t fMinSigDigits;
|
||||
int32_t fMaxSigDigits;
|
||||
UBool fUseScientific;
|
||||
UBool fUseSigDigits;
|
||||
// In addition to these listed above, changes to min/max int digits and
|
||||
// min/max frac digits from fSuper also trigger an update.
|
||||
|
||||
// Updating any of the following fields triggers an update to
|
||||
// fEffGrouping field Again we do it this way because original
|
||||
// grouping settings have to be retained if grouping is turned off.
|
||||
DigitGrouping fGrouping;
|
||||
// In addition to these listed above, changes to isGroupingUsed in
|
||||
// fSuper also triggers an update to fEffGrouping.
|
||||
|
||||
// Updating any of the following fields triggers updates on the following:
|
||||
// fMonetary, fRules, fAffixParser, fCurrencyAffixInfo,
|
||||
// fFormatter, fAffixes.fPositivePrefiix, fAffixes.fPositiveSuffix,
|
||||
// fAffixes.fNegativePrefiix, fAffixes.fNegativeSuffix
|
||||
// We do this two phase update because localizing the affix patterns
|
||||
// and formatters can be expensive. Better to do it once with the setters
|
||||
// than each time within format.
|
||||
AffixPattern fPositivePrefixPattern;
|
||||
AffixPattern fNegativePrefixPattern;
|
||||
AffixPattern fPositiveSuffixPattern;
|
||||
AffixPattern fNegativeSuffixPattern;
|
||||
DecimalFormatSymbols *fSymbols;
|
||||
UCurrencyUsage fCurrencyUsage;
|
||||
// In addition to these listed above, changes to getCurrency() in
|
||||
// fSuper also triggers an update.
|
||||
|
||||
// Optional may be NULL
|
||||
PluralRules *fRules;
|
||||
|
||||
// These fields are totally hidden from user and are used to derive the affixes
|
||||
// in fAffixes below from the four affix patterns above.
|
||||
UBool fMonetary;
|
||||
AffixPatternParser fAffixParser;
|
||||
CurrencyAffixInfo fCurrencyAffixInfo;
|
||||
|
||||
// The actual precision used when formatting
|
||||
ScientificPrecision fEffPrecision;
|
||||
|
||||
// The actual grouping used when formatting
|
||||
DigitGrouping fEffGrouping;
|
||||
SciFormatterOptions fOptions; // Encapsulates fixed precision options
|
||||
DigitFormatter fFormatter;
|
||||
DigitAffixesAndPadding fAffixes;
|
||||
|
||||
UnicodeString &formatInt32(
|
||||
int32_t number,
|
||||
UnicodeString &appendTo,
|
||||
FieldPositionHandler &handler,
|
||||
UErrorCode &status) const;
|
||||
|
||||
UnicodeString &formatInt64(
|
||||
int64_t number,
|
||||
UnicodeString &appendTo,
|
||||
FieldPositionHandler &handler,
|
||||
UErrorCode &status) const;
|
||||
|
||||
UnicodeString &formatDouble(
|
||||
double number,
|
||||
UnicodeString &appendTo,
|
||||
FieldPositionHandler &handler,
|
||||
UErrorCode &status) const;
|
||||
|
||||
// Scales for precent or permille symbols
|
||||
UnicodeString &formatDigitList(
|
||||
DigitList &number,
|
||||
UnicodeString &appendTo,
|
||||
FieldPositionHandler &handler,
|
||||
UErrorCode &status) const;
|
||||
|
||||
// Does not scale for precent or permille symbols
|
||||
UnicodeString &formatAdjustedDigitList(
|
||||
DigitList &number,
|
||||
UnicodeString &appendTo,
|
||||
FieldPositionHandler &handler,
|
||||
UErrorCode &status) const;
|
||||
|
||||
UnicodeString &formatVisibleDigitsWithExponent(
|
||||
const VisibleDigitsWithExponent &number,
|
||||
UnicodeString &appendTo,
|
||||
FieldPositionHandler &handler,
|
||||
UErrorCode &status) const;
|
||||
|
||||
VisibleDigitsWithExponent &
|
||||
initVisibleDigitsFromAdjusted(
|
||||
DigitList &number,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const;
|
||||
|
||||
template<class T>
|
||||
UBool maybeFormatWithDigitList(
|
||||
T number,
|
||||
UnicodeString &appendTo,
|
||||
FieldPositionHandler &handler,
|
||||
UErrorCode &status) const;
|
||||
|
||||
template<class T>
|
||||
UBool maybeInitVisibleDigitsFromDigitList(
|
||||
T number,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const;
|
||||
|
||||
DigitList &adjustDigitList(DigitList &number, UErrorCode &status) const;
|
||||
|
||||
void applyPattern(
|
||||
const UnicodeString &pattern,
|
||||
UBool localized, UParseError &perror, UErrorCode &status);
|
||||
|
||||
ValueFormatter &prepareValueFormatter(ValueFormatter &vf) const;
|
||||
void setMultiplierScale(int32_t s);
|
||||
int32_t getPatternScale() const;
|
||||
void setScale(int32_t s) { fScale = s; }
|
||||
int32_t getScale() const { return fScale; }
|
||||
|
||||
// Updates everything
|
||||
void updateAll(UErrorCode &status);
|
||||
void updateAll(
|
||||
int32_t formattingFlags,
|
||||
UBool updatePrecisionBasedOnCurrency,
|
||||
UErrorCode &status);
|
||||
|
||||
// Updates from formatting pattern changes
|
||||
void updateForApplyPattern(UErrorCode &status);
|
||||
void updateForApplyPatternFavorCurrencyPrecision(UErrorCode &status);
|
||||
|
||||
// Updates from changes to third group of attributes
|
||||
void updateFormatting(int32_t changedFormattingFields, UErrorCode &status);
|
||||
void updateFormatting(
|
||||
int32_t changedFormattingFields,
|
||||
UBool updatePrecisionBasedOnCurrency,
|
||||
UErrorCode &status);
|
||||
|
||||
// Helper functions for updatePrecision
|
||||
void updatePrecisionForScientific();
|
||||
void updatePrecisionForFixed();
|
||||
void extractMinMaxDigits(DigitInterval &min, DigitInterval &max) const;
|
||||
void extractSigDigits(SignificantDigitInterval &sig) const;
|
||||
|
||||
// Helper functions for updateFormatting
|
||||
void updateFormattingUsesCurrency(int32_t &changedFormattingFields);
|
||||
void updateFormattingPluralRules(
|
||||
int32_t &changedFormattingFields, UErrorCode &status);
|
||||
void updateFormattingAffixParser(int32_t &changedFormattingFields);
|
||||
void updateFormattingCurrencyAffixInfo(
|
||||
int32_t &changedFormattingFields,
|
||||
UBool updatePrecisionBasedOnCurrency,
|
||||
UErrorCode &status);
|
||||
void updateFormattingFixedPointFormatter(
|
||||
int32_t &changedFormattingFields);
|
||||
void updateFormattingLocalizedPositivePrefix(
|
||||
int32_t &changedFormattingFields, UErrorCode &status);
|
||||
void updateFormattingLocalizedPositiveSuffix(
|
||||
int32_t &changedFormattingFields, UErrorCode &status);
|
||||
void updateFormattingLocalizedNegativePrefix(
|
||||
int32_t &changedFormattingFields, UErrorCode &status);
|
||||
void updateFormattingLocalizedNegativeSuffix(
|
||||
int32_t &changedFormattingFields, UErrorCode &status);
|
||||
|
||||
int32_t computeExponentPatternLength() const;
|
||||
int32_t countFractionDigitAndDecimalPatternLength(int32_t fracDigitCount) const;
|
||||
UnicodeString &toNumberPattern(
|
||||
UBool hasPadding, int32_t minimumLength, UnicodeString& result) const;
|
||||
|
||||
int32_t getOldFormatWidth() const;
|
||||
const UnicodeString &getConstSymbol(
|
||||
DecimalFormatSymbols::ENumberFormatSymbol symbol) const;
|
||||
UBool isParseFastpath() const;
|
||||
|
||||
friend class DecimalFormat;
|
||||
|
||||
};
|
||||
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif // DECIMFMTIMPL_H
|
||||
//eof
|
104
icu4c/source/i18n/digitaffix.cpp
Normal file
104
icu4c/source/i18n/digitaffix.cpp
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*
|
||||
* file name: digitaffix.cpp
|
||||
*/
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#include "digitaffix.h"
|
||||
#include "fphdlimp.h"
|
||||
#include "uassert.h"
|
||||
#include "unistrappender.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
DigitAffix::DigitAffix() : fAffix(), fAnnotations() {
|
||||
}
|
||||
|
||||
DigitAffix::DigitAffix(
|
||||
const UChar *value, int32_t charCount, int32_t fieldId)
|
||||
: fAffix(value, charCount),
|
||||
fAnnotations(charCount, (UChar) fieldId, charCount) {
|
||||
}
|
||||
|
||||
void
|
||||
DigitAffix::remove() {
|
||||
fAffix.remove();
|
||||
fAnnotations.remove();
|
||||
}
|
||||
|
||||
void
|
||||
DigitAffix::appendUChar(UChar value, int32_t fieldId) {
|
||||
fAffix.append(value);
|
||||
fAnnotations.append((UChar) fieldId);
|
||||
}
|
||||
|
||||
void
|
||||
DigitAffix::append(const UnicodeString &value, int32_t fieldId) {
|
||||
fAffix.append(value);
|
||||
{
|
||||
UnicodeStringAppender appender(fAnnotations);
|
||||
int32_t len = value.length();
|
||||
for (int32_t i = 0; i < len; ++i) {
|
||||
appender.append((UChar) fieldId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DigitAffix::setTo(const UnicodeString &value, int32_t fieldId) {
|
||||
fAffix = value;
|
||||
fAnnotations.remove();
|
||||
{
|
||||
UnicodeStringAppender appender(fAnnotations);
|
||||
int32_t len = value.length();
|
||||
for (int32_t i = 0; i < len; ++i) {
|
||||
appender.append((UChar) fieldId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DigitAffix::append(const UChar *value, int32_t charCount, int32_t fieldId) {
|
||||
fAffix.append(value, charCount);
|
||||
{
|
||||
UnicodeStringAppender appender(fAnnotations);
|
||||
for (int32_t i = 0; i < charCount; ++i) {
|
||||
appender.append((UChar) fieldId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnicodeString &
|
||||
DigitAffix::format(FieldPositionHandler &handler, UnicodeString &appendTo) const {
|
||||
int32_t len = fAffix.length();
|
||||
if (len == 0) {
|
||||
return appendTo;
|
||||
}
|
||||
if (!handler.isRecording()) {
|
||||
return appendTo.append(fAffix);
|
||||
}
|
||||
U_ASSERT(fAffix.length() == fAnnotations.length());
|
||||
int32_t appendToStart = appendTo.length();
|
||||
int32_t lastId = (int32_t) fAnnotations.charAt(0);
|
||||
int32_t lastIdStart = 0;
|
||||
for (int32_t i = 1; i < len; ++i) {
|
||||
int32_t id = (int32_t) fAnnotations.charAt(i);
|
||||
if (id != lastId) {
|
||||
if (lastId != UNUM_FIELD_COUNT) {
|
||||
handler.addAttribute(lastId, appendToStart + lastIdStart, appendToStart + i);
|
||||
}
|
||||
lastId = id;
|
||||
lastIdStart = i;
|
||||
}
|
||||
}
|
||||
if (lastId != UNUM_FIELD_COUNT) {
|
||||
handler.addAttribute(lastId, appendToStart + lastIdStart, appendToStart + len);
|
||||
}
|
||||
return appendTo.append(fAffix);
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
99
icu4c/source/i18n/digitaffix.h
Normal file
99
icu4c/source/i18n/digitaffix.h
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
* digitaffix.h
|
||||
*
|
||||
* created on: 2015jan06
|
||||
* created by: Travis Keep
|
||||
*/
|
||||
|
||||
#ifndef __DIGITAFFIX_H__
|
||||
#define __DIGITAFFIX_H__
|
||||
|
||||
#include "unicode/unistr.h"
|
||||
#include "unicode/unum.h"
|
||||
#include "unicode/uobject.h"
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
class FieldPositionHandler;
|
||||
|
||||
/**
|
||||
* A prefix or suffix of a formatted number.
|
||||
*/
|
||||
class U_I18N_API DigitAffix : public UMemory {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Creates an empty DigitAffix.
|
||||
*/
|
||||
DigitAffix();
|
||||
|
||||
/**
|
||||
* Creates a DigitAffix containing given UChars where all of it has
|
||||
* a field type of fieldId.
|
||||
*/
|
||||
DigitAffix(
|
||||
const UChar *value,
|
||||
int32_t charCount,
|
||||
int32_t fieldId=UNUM_FIELD_COUNT);
|
||||
|
||||
/**
|
||||
* Makes this affix be the empty string.
|
||||
*/
|
||||
void remove();
|
||||
|
||||
/**
|
||||
* Append value to this affix. If fieldId is present, the appended
|
||||
* string is considered to be the type fieldId.
|
||||
*/
|
||||
void appendUChar(UChar value, int32_t fieldId=UNUM_FIELD_COUNT);
|
||||
|
||||
/**
|
||||
* Append value to this affix. If fieldId is present, the appended
|
||||
* string is considered to be the type fieldId.
|
||||
*/
|
||||
void append(const UnicodeString &value, int32_t fieldId=UNUM_FIELD_COUNT);
|
||||
|
||||
/**
|
||||
* Sets this affix to given string. The entire string
|
||||
* is considered to be the type fieldId.
|
||||
*/
|
||||
void setTo(const UnicodeString &value, int32_t fieldId=UNUM_FIELD_COUNT);
|
||||
|
||||
/**
|
||||
* Append value to this affix. If fieldId is present, the appended
|
||||
* string is considered to be the type fieldId.
|
||||
*/
|
||||
void append(const UChar *value, int32_t charCount, int32_t fieldId=UNUM_FIELD_COUNT);
|
||||
|
||||
/**
|
||||
* Formats this affix.
|
||||
*/
|
||||
UnicodeString &format(
|
||||
FieldPositionHandler &handler, UnicodeString &appendTo) const;
|
||||
int32_t countChar32() const { return fAffix.countChar32(); }
|
||||
|
||||
/**
|
||||
* Returns this affix as a unicode string.
|
||||
*/
|
||||
const UnicodeString & toString() const { return fAffix; }
|
||||
|
||||
/**
|
||||
* Returns TRUE if this object equals rhs.
|
||||
*/
|
||||
UBool equals(const DigitAffix &rhs) const {
|
||||
return ((fAffix == rhs.fAffix) && (fAnnotations == rhs.fAnnotations));
|
||||
}
|
||||
private:
|
||||
UnicodeString fAffix;
|
||||
UnicodeString fAnnotations;
|
||||
};
|
||||
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif // __DIGITAFFIX_H__
|
170
icu4c/source/i18n/digitaffixesandpadding.cpp
Normal file
170
icu4c/source/i18n/digitaffixesandpadding.cpp
Normal file
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*
|
||||
* file name: digitaffixesandpadding.cpp
|
||||
*/
|
||||
|
||||
|
||||
#include "unicode/plurrule.h"
|
||||
#include "charstr.h"
|
||||
#include "digitaffix.h"
|
||||
#include "digitaffixesandpadding.h"
|
||||
#include "digitlst.h"
|
||||
#include "uassert.h"
|
||||
#include "valueformatter.h"
|
||||
#include "visibledigits.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
UBool
|
||||
DigitAffixesAndPadding::needsPluralRules() const {
|
||||
return (
|
||||
fPositivePrefix.hasMultipleVariants() ||
|
||||
fPositiveSuffix.hasMultipleVariants() ||
|
||||
fNegativePrefix.hasMultipleVariants() ||
|
||||
fNegativeSuffix.hasMultipleVariants());
|
||||
}
|
||||
|
||||
UnicodeString &
|
||||
DigitAffixesAndPadding::formatInt32(
|
||||
int32_t value,
|
||||
const ValueFormatter &formatter,
|
||||
FieldPositionHandler &handler,
|
||||
const PluralRules *optPluralRules,
|
||||
UnicodeString &appendTo,
|
||||
UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
if (optPluralRules != NULL || fWidth > 0 || !formatter.isFastFormattable(value)) {
|
||||
VisibleDigitsWithExponent digits;
|
||||
formatter.toVisibleDigitsWithExponent(
|
||||
(int64_t) value, digits, status);
|
||||
return format(
|
||||
digits,
|
||||
formatter,
|
||||
handler,
|
||||
optPluralRules,
|
||||
appendTo,
|
||||
status);
|
||||
}
|
||||
UBool bPositive = value >= 0;
|
||||
const DigitAffix *prefix = bPositive ? &fPositivePrefix.getOtherVariant() : &fNegativePrefix.getOtherVariant();
|
||||
const DigitAffix *suffix = bPositive ? &fPositiveSuffix.getOtherVariant() : &fNegativeSuffix.getOtherVariant();
|
||||
if (value < 0) {
|
||||
value = -value;
|
||||
}
|
||||
prefix->format(handler, appendTo);
|
||||
formatter.formatInt32(value, handler, appendTo);
|
||||
return suffix->format(handler, appendTo);
|
||||
}
|
||||
|
||||
static UnicodeString &
|
||||
formatAffix(
|
||||
const DigitAffix *affix,
|
||||
FieldPositionHandler &handler,
|
||||
UnicodeString &appendTo) {
|
||||
if (affix) {
|
||||
affix->format(handler, appendTo);
|
||||
}
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
static int32_t
|
||||
countAffixChar32(const DigitAffix *affix) {
|
||||
if (affix) {
|
||||
return affix->countChar32();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
UnicodeString &
|
||||
DigitAffixesAndPadding::format(
|
||||
const VisibleDigitsWithExponent &digits,
|
||||
const ValueFormatter &formatter,
|
||||
FieldPositionHandler &handler,
|
||||
const PluralRules *optPluralRules,
|
||||
UnicodeString &appendTo,
|
||||
UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
const DigitAffix *prefix = NULL;
|
||||
const DigitAffix *suffix = NULL;
|
||||
if (!digits.isNaN()) {
|
||||
UBool bPositive = !digits.isNegative();
|
||||
const PluralAffix *pluralPrefix = bPositive ? &fPositivePrefix : &fNegativePrefix;
|
||||
const PluralAffix *pluralSuffix = bPositive ? &fPositiveSuffix : &fNegativeSuffix;
|
||||
if (optPluralRules == NULL || digits.isInfinite()) {
|
||||
prefix = &pluralPrefix->getOtherVariant();
|
||||
suffix = &pluralSuffix->getOtherVariant();
|
||||
} else {
|
||||
UnicodeString count(optPluralRules->select(digits));
|
||||
prefix = &pluralPrefix->getByCategory(count);
|
||||
suffix = &pluralSuffix->getByCategory(count);
|
||||
}
|
||||
}
|
||||
if (fWidth <= 0) {
|
||||
formatAffix(prefix, handler, appendTo);
|
||||
formatter.format(digits, handler, appendTo);
|
||||
return formatAffix(suffix, handler, appendTo);
|
||||
}
|
||||
int32_t codePointCount = countAffixChar32(prefix) + formatter.countChar32(digits) + countAffixChar32(suffix);
|
||||
int32_t paddingCount = fWidth - codePointCount;
|
||||
switch (fPadPosition) {
|
||||
case kPadBeforePrefix:
|
||||
appendPadding(paddingCount, appendTo);
|
||||
formatAffix(prefix, handler, appendTo);
|
||||
formatter.format(digits, handler, appendTo);
|
||||
return formatAffix(suffix, handler, appendTo);
|
||||
case kPadAfterPrefix:
|
||||
formatAffix(prefix, handler, appendTo);
|
||||
appendPadding(paddingCount, appendTo);
|
||||
formatter.format(digits, handler, appendTo);
|
||||
return formatAffix(suffix, handler, appendTo);
|
||||
case kPadBeforeSuffix:
|
||||
formatAffix(prefix, handler, appendTo);
|
||||
formatter.format(digits, handler, appendTo);
|
||||
appendPadding(paddingCount, appendTo);
|
||||
return formatAffix(suffix, handler, appendTo);
|
||||
case kPadAfterSuffix:
|
||||
formatAffix(prefix, handler, appendTo);
|
||||
formatter.format(digits, handler, appendTo);
|
||||
formatAffix(suffix, handler, appendTo);
|
||||
return appendPadding(paddingCount, appendTo);
|
||||
default:
|
||||
U_ASSERT(FALSE);
|
||||
return appendTo;
|
||||
}
|
||||
}
|
||||
|
||||
UnicodeString &
|
||||
DigitAffixesAndPadding::format(
|
||||
DigitList &value,
|
||||
const ValueFormatter &formatter,
|
||||
FieldPositionHandler &handler,
|
||||
const PluralRules *optPluralRules,
|
||||
UnicodeString &appendTo,
|
||||
UErrorCode &status) const {
|
||||
VisibleDigitsWithExponent digits;
|
||||
formatter.toVisibleDigitsWithExponent(
|
||||
value, digits, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
return format(
|
||||
digits, formatter, handler, optPluralRules, appendTo, status);
|
||||
}
|
||||
|
||||
UnicodeString &
|
||||
DigitAffixesAndPadding::appendPadding(int32_t paddingCount, UnicodeString &appendTo) const {
|
||||
for (int32_t i = 0; i < paddingCount; ++i) {
|
||||
appendTo.append(fPadChar);
|
||||
}
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
174
icu4c/source/i18n/digitaffixesandpadding.h
Normal file
174
icu4c/source/i18n/digitaffixesandpadding.h
Normal file
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
* digitaffixesandpadding.h
|
||||
*
|
||||
* created on: 2015jan06
|
||||
* created by: Travis Keep
|
||||
*/
|
||||
|
||||
#ifndef __DIGITAFFIXESANDPADDING_H__
|
||||
#define __DIGITAFFIXESANDPADDING_H__
|
||||
|
||||
#include "unicode/uobject.h"
|
||||
#include "unicode/utypes.h"
|
||||
#include "pluralaffix.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
class DigitList;
|
||||
class ValueFormatter;
|
||||
class UnicodeString;
|
||||
class FieldPositionHandler;
|
||||
class PluralRules;
|
||||
class VisibleDigitsWithExponent;
|
||||
|
||||
/**
|
||||
* A formatter of numbers. This class can format any numerical value
|
||||
* except for not a number (NaN), positive infinity, and negative infinity.
|
||||
* This class manages prefixes, suffixes, and padding but delegates the
|
||||
* formatting of actual positive values to a ValueFormatter.
|
||||
*/
|
||||
class U_I18N_API DigitAffixesAndPadding : public UMemory {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Equivalent to DecimalFormat EPadPosition, but redeclared here to prevent
|
||||
* depending on DecimalFormat which would cause a circular dependency.
|
||||
*/
|
||||
enum EPadPosition {
|
||||
kPadBeforePrefix,
|
||||
kPadAfterPrefix,
|
||||
kPadBeforeSuffix,
|
||||
kPadAfterSuffix
|
||||
};
|
||||
|
||||
/**
|
||||
* The positive prefix
|
||||
*/
|
||||
PluralAffix fPositivePrefix;
|
||||
|
||||
/**
|
||||
* The positive suffix
|
||||
*/
|
||||
PluralAffix fPositiveSuffix;
|
||||
|
||||
/**
|
||||
* The negative suffix
|
||||
*/
|
||||
PluralAffix fNegativePrefix;
|
||||
|
||||
/**
|
||||
* The negative suffix
|
||||
*/
|
||||
PluralAffix fNegativeSuffix;
|
||||
|
||||
/**
|
||||
* The padding position
|
||||
*/
|
||||
EPadPosition fPadPosition;
|
||||
|
||||
/**
|
||||
* The padding character.
|
||||
*/
|
||||
UChar32 fPadChar;
|
||||
|
||||
/**
|
||||
* The field width in code points. The format method inserts instances of
|
||||
* the padding character as needed in the desired padding position so that
|
||||
* the entire formatted string contains this many code points. If the
|
||||
* formatted string already exceeds this many code points, the format method
|
||||
* inserts no padding.
|
||||
*/
|
||||
int32_t fWidth;
|
||||
|
||||
/**
|
||||
* Pad position is before prefix; padding character is '*' field width is 0.
|
||||
* The affixes are all the empty string with no annotated fields with just
|
||||
* the 'other' plural variation.
|
||||
*/
|
||||
DigitAffixesAndPadding()
|
||||
: fPadPosition(kPadBeforePrefix), fPadChar(0x2a), fWidth(0) { }
|
||||
|
||||
/**
|
||||
* Returns TRUE if this object is equal to rhs.
|
||||
*/
|
||||
UBool equals(const DigitAffixesAndPadding &rhs) const {
|
||||
return (fPositivePrefix.equals(rhs.fPositivePrefix) &&
|
||||
fPositiveSuffix.equals(rhs.fPositiveSuffix) &&
|
||||
fNegativePrefix.equals(rhs.fNegativePrefix) &&
|
||||
fNegativeSuffix.equals(rhs.fNegativeSuffix) &&
|
||||
fPadPosition == rhs.fPadPosition &&
|
||||
fWidth == rhs.fWidth &&
|
||||
fPadChar == rhs.fPadChar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if a plural rules instance is needed to complete the
|
||||
* formatting by detecting if any of the affixes have multiple plural
|
||||
* variations.
|
||||
*/
|
||||
UBool needsPluralRules() const;
|
||||
|
||||
/**
|
||||
* Formats value and appends to appendTo.
|
||||
*
|
||||
* @param value the value to format. May be NaN or ininite.
|
||||
* @param formatter handles the details of formatting the actual value.
|
||||
* @param handler records field positions
|
||||
* @param optPluralRules the plural rules, but may be NULL if
|
||||
* needsPluralRules returns FALSE.
|
||||
* @appendTo formatted string appended here.
|
||||
* @status any error returned here.
|
||||
*/
|
||||
UnicodeString &format(
|
||||
const VisibleDigitsWithExponent &value,
|
||||
const ValueFormatter &formatter,
|
||||
FieldPositionHandler &handler,
|
||||
const PluralRules *optPluralRules,
|
||||
UnicodeString &appendTo,
|
||||
UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* For testing only.
|
||||
*/
|
||||
UnicodeString &format(
|
||||
DigitList &value,
|
||||
const ValueFormatter &formatter,
|
||||
FieldPositionHandler &handler,
|
||||
const PluralRules *optPluralRules,
|
||||
UnicodeString &appendTo,
|
||||
UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Formats a 32-bit integer and appends to appendTo. When formatting an
|
||||
* integer, this method is preferred to plain format as it can run
|
||||
* several times faster under certain conditions.
|
||||
*
|
||||
* @param value the value to format.
|
||||
* @param formatter handles the details of formatting the actual value.
|
||||
* @param handler records field positions
|
||||
* @param optPluralRules the plural rules, but may be NULL if
|
||||
* needsPluralRules returns FALSE.
|
||||
* @appendTo formatted string appended here.
|
||||
* @status any error returned here.
|
||||
*/
|
||||
UnicodeString &formatInt32(
|
||||
int32_t value,
|
||||
const ValueFormatter &formatter,
|
||||
FieldPositionHandler &handler,
|
||||
const PluralRules *optPluralRules,
|
||||
UnicodeString &appendTo,
|
||||
UErrorCode &status) const;
|
||||
|
||||
private:
|
||||
UnicodeString &appendPadding(int32_t paddingCount, UnicodeString &appendTo) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif // __DIGITAFFIXANDPADDING_H__
|
411
icu4c/source/i18n/digitformatter.cpp
Normal file
411
icu4c/source/i18n/digitformatter.cpp
Normal file
|
@ -0,0 +1,411 @@
|
|||
/*
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*
|
||||
* file name: digitformatter.cpp
|
||||
*/
|
||||
|
||||
#include "unicode/dcfmtsym.h"
|
||||
#include "unicode/unum.h"
|
||||
#include "unicode/utypes.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
|
||||
|
283
icu4c/source/i18n/digitformatter.h
Normal file
283
icu4c/source/i18n/digitformatter.h
Normal file
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
* digitformatter.h
|
||||
*
|
||||
* created on: 2015jan06
|
||||
* created by: Travis Keep
|
||||
*/
|
||||
|
||||
#ifndef __DIGITFORMATTER_H__
|
||||
#define __DIGITFORMATTER_H__
|
||||
|
||||
#include "unicode/unistr.h"
|
||||
#include "unicode/uobject.h"
|
||||
#include "unicode/utypes.h"
|
||||
#include "digitaffix.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
class DecimalFormatSymbols;
|
||||
class DigitList;
|
||||
class DigitGrouping;
|
||||
class DigitInterval;
|
||||
class UnicodeString;
|
||||
class FieldPositionHandler;
|
||||
class IntDigitCountRange;
|
||||
class VisibleDigits;
|
||||
class VisibleDigitsWithExponent;
|
||||
|
||||
/**
|
||||
* Various options for formatting in fixed point.
|
||||
*/
|
||||
class U_I18N_API DigitFormatterOptions : public UMemory {
|
||||
public:
|
||||
DigitFormatterOptions() : fAlwaysShowDecimal(FALSE) { }
|
||||
|
||||
/**
|
||||
* Returns TRUE if this object equals rhs.
|
||||
*/
|
||||
UBool equals(const DigitFormatterOptions &rhs) const {
|
||||
return (
|
||||
fAlwaysShowDecimal == rhs.fAlwaysShowDecimal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if these options allow for fast formatting of
|
||||
* integers.
|
||||
*/
|
||||
UBool isFastFormattable() const {
|
||||
return (fAlwaysShowDecimal == FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* If TRUE, show the decimal separator even when there are no fraction
|
||||
* digits. default is FALSE.
|
||||
*/
|
||||
UBool fAlwaysShowDecimal;
|
||||
};
|
||||
|
||||
/**
|
||||
* Various options for formatting an integer.
|
||||
*/
|
||||
class U_I18N_API DigitFormatterIntOptions : public UMemory {
|
||||
public:
|
||||
DigitFormatterIntOptions() : fAlwaysShowSign(FALSE) { }
|
||||
|
||||
/**
|
||||
* Returns TRUE if this object equals rhs.
|
||||
*/
|
||||
UBool equals(const DigitFormatterIntOptions &rhs) const {
|
||||
return (fAlwaysShowSign == rhs.fAlwaysShowSign);
|
||||
}
|
||||
|
||||
/**
|
||||
* If TRUE, always prefix the integer with its sign even if the number is
|
||||
* positive. Default is FALSE.
|
||||
*/
|
||||
UBool fAlwaysShowSign;
|
||||
};
|
||||
|
||||
/**
|
||||
* Options for formatting in scientific notation.
|
||||
*/
|
||||
class U_I18N_API SciFormatterOptions : public UMemory {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Returns TRUE if this object equals rhs.
|
||||
*/
|
||||
UBool equals(const SciFormatterOptions &rhs) const {
|
||||
return (fMantissa.equals(rhs.fMantissa) &&
|
||||
fExponent.equals(rhs.fExponent));
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for formatting the mantissa.
|
||||
*/
|
||||
DigitFormatterOptions fMantissa;
|
||||
|
||||
/**
|
||||
* Options for formatting the exponent.
|
||||
*/
|
||||
DigitFormatterIntOptions fExponent;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Does fixed point formatting.
|
||||
*
|
||||
* This class only does fixed point formatting. It does no rounding before
|
||||
* formatting.
|
||||
*/
|
||||
class U_I18N_API DigitFormatter : public UMemory {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Decimal separator is period (.), Plus sign is plus (+),
|
||||
* minus sign is minus (-), grouping separator is comma (,), digits are 0-9.
|
||||
*/
|
||||
DigitFormatter();
|
||||
|
||||
/**
|
||||
* Let symbols determine the digits, decimal separator,
|
||||
* plus and mius sign, grouping separator, and possibly other settings.
|
||||
*/
|
||||
DigitFormatter(const DecimalFormatSymbols &symbols);
|
||||
|
||||
/**
|
||||
* Change what this instance uses for digits, decimal separator,
|
||||
* plus and mius sign, grouping separator, and possibly other settings
|
||||
* according to symbols.
|
||||
*/
|
||||
void setDecimalFormatSymbols(const DecimalFormatSymbols &symbols);
|
||||
|
||||
/**
|
||||
* Change what this instance uses for digits, decimal separator,
|
||||
* plus and mius sign, grouping separator, and possibly other settings
|
||||
* according to symbols in the context of monetary amounts.
|
||||
*/
|
||||
void setDecimalFormatSymbolsForMonetary(const DecimalFormatSymbols &symbols);
|
||||
|
||||
/**
|
||||
* Fixed point formatting.
|
||||
*
|
||||
* @param positiveDigits the value to format
|
||||
* Negative sign can be present, but it won't show.
|
||||
* @param grouping controls how digit grouping is done
|
||||
* @param options formatting options
|
||||
* @param handler records field positions
|
||||
* @param appendTo formatted value appended here.
|
||||
* @return appendTo
|
||||
*/
|
||||
UnicodeString &format(
|
||||
const VisibleDigits &positiveDigits,
|
||||
const DigitGrouping &grouping,
|
||||
const DigitFormatterOptions &options,
|
||||
FieldPositionHandler &handler,
|
||||
UnicodeString &appendTo) const;
|
||||
|
||||
/**
|
||||
* formats in scientifc notation.
|
||||
* @param positiveDigits the value to format.
|
||||
* Negative sign can be present, but it won't show.
|
||||
* @param options formatting options
|
||||
* @param handler records field positions.
|
||||
* @param appendTo formatted value appended here.
|
||||
*/
|
||||
UnicodeString &format(
|
||||
const VisibleDigitsWithExponent &positiveDigits,
|
||||
const SciFormatterOptions &options,
|
||||
FieldPositionHandler &handler,
|
||||
UnicodeString &appendTo) const;
|
||||
|
||||
/**
|
||||
* Fixed point formatting of integers.
|
||||
* Always performed with no grouping and no decimal point.
|
||||
*
|
||||
* @param positiveValue the value to format must be positive.
|
||||
* @param range specifies minimum and maximum number of digits.
|
||||
* @param handler records field positions
|
||||
* @param appendTo formatted value appended here.
|
||||
* @return appendTo
|
||||
*/
|
||||
UnicodeString &formatPositiveInt32(
|
||||
int32_t positiveValue,
|
||||
const IntDigitCountRange &range,
|
||||
FieldPositionHandler &handler,
|
||||
UnicodeString &appendTo) const;
|
||||
|
||||
/**
|
||||
* Counts how many code points are needed for fixed formatting.
|
||||
* If digits is negative, the negative sign is not included in the count.
|
||||
*/
|
||||
int32_t countChar32(
|
||||
const VisibleDigits &digits,
|
||||
const DigitGrouping &grouping,
|
||||
const DigitFormatterOptions &options) const;
|
||||
|
||||
/**
|
||||
* Counts how many code points are needed for scientific formatting.
|
||||
* If digits is negative, the negative sign is not included in the count.
|
||||
*/
|
||||
int32_t countChar32(
|
||||
const VisibleDigitsWithExponent &digits,
|
||||
const SciFormatterOptions &options) const;
|
||||
|
||||
/**
|
||||
* Returns TRUE if this object equals rhs.
|
||||
*/
|
||||
UBool equals(const DigitFormatter &rhs) const;
|
||||
|
||||
private:
|
||||
UChar32 fLocalizedDigits[10];
|
||||
UnicodeString fGroupingSeparator;
|
||||
UnicodeString fDecimal;
|
||||
UnicodeString fNegativeSign;
|
||||
UnicodeString fPositiveSign;
|
||||
DigitAffix fInfinity;
|
||||
DigitAffix fNan;
|
||||
UBool fIsStandardDigits;
|
||||
UnicodeString fExponent;
|
||||
UBool isStandardDigits() const;
|
||||
|
||||
UnicodeString &formatDigits(
|
||||
const uint8_t *digits,
|
||||
int32_t count,
|
||||
const IntDigitCountRange &range,
|
||||
int32_t intField,
|
||||
FieldPositionHandler &handler,
|
||||
UnicodeString &appendTo) const;
|
||||
|
||||
void setOtherDecimalFormatSymbols(const DecimalFormatSymbols &symbols);
|
||||
|
||||
int32_t countChar32(
|
||||
const VisibleDigits &exponent,
|
||||
const DigitInterval &mantissaInterval,
|
||||
const SciFormatterOptions &options) const;
|
||||
|
||||
UnicodeString &formatNaN(
|
||||
FieldPositionHandler &handler,
|
||||
UnicodeString &appendTo) const {
|
||||
return fNan.format(handler, appendTo);
|
||||
}
|
||||
|
||||
int32_t countChar32ForNaN() const {
|
||||
return fNan.toString().countChar32();
|
||||
}
|
||||
|
||||
UnicodeString &formatInfinity(
|
||||
FieldPositionHandler &handler,
|
||||
UnicodeString &appendTo) const {
|
||||
return fInfinity.format(handler, appendTo);
|
||||
}
|
||||
|
||||
int32_t countChar32ForInfinity() const {
|
||||
return fInfinity.toString().countChar32();
|
||||
}
|
||||
|
||||
UnicodeString &formatExponent(
|
||||
const VisibleDigits &digits,
|
||||
const DigitFormatterIntOptions &options,
|
||||
int32_t signField,
|
||||
int32_t intField,
|
||||
FieldPositionHandler &handler,
|
||||
UnicodeString &appendTo) const;
|
||||
|
||||
int32_t countChar32(
|
||||
const DigitGrouping &grouping,
|
||||
const DigitInterval &interval,
|
||||
const DigitFormatterOptions &options) const;
|
||||
|
||||
int32_t countChar32ForExponent(
|
||||
const VisibleDigits &exponent,
|
||||
const DigitFormatterIntOptions &options) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif // __DIGITFORMATTER_H__
|
57
icu4c/source/i18n/digitgrouping.cpp
Normal file
57
icu4c/source/i18n/digitgrouping.cpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*
|
||||
* file name: digitgrouping.cpp
|
||||
*/
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#include "digitgrouping.h"
|
||||
#include "smallintformatter.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
UBool DigitGrouping::isSeparatorAt(
|
||||
int32_t digitsLeftOfDecimal, int32_t digitPos) const {
|
||||
if (!isGroupingEnabled(digitsLeftOfDecimal) || digitPos < fGrouping) {
|
||||
return FALSE;
|
||||
}
|
||||
return ((digitPos - fGrouping) % getGrouping2() == 0);
|
||||
}
|
||||
|
||||
int32_t DigitGrouping::getSeparatorCount(int32_t digitsLeftOfDecimal) const {
|
||||
if (!isGroupingEnabled(digitsLeftOfDecimal)) {
|
||||
return 0;
|
||||
}
|
||||
return (digitsLeftOfDecimal - 1 - fGrouping) / getGrouping2() + 1;
|
||||
}
|
||||
|
||||
UBool DigitGrouping::isGroupingEnabled(int32_t digitsLeftOfDecimal) const {
|
||||
return (isGroupingUsed()
|
||||
&& digitsLeftOfDecimal >= fGrouping + getMinGrouping());
|
||||
}
|
||||
|
||||
UBool DigitGrouping::isNoGrouping(
|
||||
int32_t positiveValue, const IntDigitCountRange &range) const {
|
||||
return getSeparatorCount(
|
||||
SmallIntFormatter::estimateDigitCount(positiveValue, range)) == 0;
|
||||
}
|
||||
|
||||
int32_t DigitGrouping::getGrouping2() const {
|
||||
return (fGrouping2 > 0 ? fGrouping2 : fGrouping);
|
||||
}
|
||||
|
||||
int32_t DigitGrouping::getMinGrouping() const {
|
||||
return (fMinGrouping > 0 ? fMinGrouping : 1);
|
||||
}
|
||||
|
||||
void
|
||||
DigitGrouping::clear() {
|
||||
fMinGrouping = 0;
|
||||
fGrouping = 0;
|
||||
fGrouping2 = 0;
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
110
icu4c/source/i18n/digitgrouping.h
Normal file
110
icu4c/source/i18n/digitgrouping.h
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
* digitgrouping.h
|
||||
*
|
||||
* created on: 2015jan6
|
||||
* created by: Travis Keep
|
||||
*/
|
||||
|
||||
#ifndef __DIGITGROUPING_H__
|
||||
#define __DIGITGROUPING_H__
|
||||
|
||||
#include "unicode/uobject.h"
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
class IntDigitCountRange;
|
||||
|
||||
/**
|
||||
* The digit grouping policy.
|
||||
*/
|
||||
class U_I18N_API DigitGrouping : public UMemory {
|
||||
public:
|
||||
/**
|
||||
* Default is no digit grouping.
|
||||
*/
|
||||
DigitGrouping() : fGrouping(0), fGrouping2(0), fMinGrouping(0) { }
|
||||
|
||||
/**
|
||||
* Returns TRUE if this object is equal to rhs.
|
||||
*/
|
||||
UBool equals(const DigitGrouping &rhs) const {
|
||||
return ((fGrouping == rhs.fGrouping) &&
|
||||
(fGrouping2 == rhs.fGrouping2) &&
|
||||
(fMinGrouping == rhs.fMinGrouping));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a separator is needed after a particular digit.
|
||||
* @param digitsLeftOfDecimal the total count of digits left of the
|
||||
* decimal.
|
||||
* @param digitPos 0 is the one's place; 1 is the 10's place; -1 is the
|
||||
* 1/10's place etc.
|
||||
*/
|
||||
UBool isSeparatorAt(int32_t digitsLeftOfDecimal, int32_t digitPos) const;
|
||||
|
||||
/**
|
||||
* Returns the total number of separators to be used to format a particular
|
||||
* number.
|
||||
* @param digitsLeftOfDecimal the total number of digits to the left of
|
||||
* the decimal.
|
||||
*/
|
||||
int32_t getSeparatorCount(int32_t digitsLeftOfDecimal) const;
|
||||
|
||||
/**
|
||||
* Returns true if grouping is used FALSE otherwise. When
|
||||
* isGroupingUsed() returns FALSE; isSeparatorAt always returns FALSE
|
||||
* and getSeparatorCount always returns 0.
|
||||
*/
|
||||
UBool isGroupingUsed() const { return fGrouping > 0; }
|
||||
|
||||
/**
|
||||
* Returns TRUE if this instance would not add grouping separators
|
||||
* when formatting value using the given constraint on digit count.
|
||||
*
|
||||
* @param value the value to format.
|
||||
* @param range the minimum and maximum digits for formatting value.
|
||||
*/
|
||||
UBool isNoGrouping(
|
||||
int32_t positiveValue, const IntDigitCountRange &range) const;
|
||||
|
||||
/**
|
||||
* Clears this instance so that digit grouping is not in effect.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Primary grouping size. A value of 0, the default, or a negative
|
||||
* number causes isGroupingUsed() to return FALSE.
|
||||
*/
|
||||
int32_t fGrouping;
|
||||
|
||||
/**
|
||||
* Secondary grouping size. If > 0, this size is used instead of
|
||||
* 'fGrouping' for all but the group just to the left of the decimal
|
||||
* point. The default value of 0, or a negative value indicates that
|
||||
* there is no secondary grouping size.
|
||||
*/
|
||||
int32_t fGrouping2;
|
||||
|
||||
/**
|
||||
* If set (that is > 0), uses no grouping separators if fewer than
|
||||
* (fGrouping + fMinGrouping) digits appear left of the decimal place.
|
||||
* The default value for this field is 0.
|
||||
*/
|
||||
int32_t fMinGrouping;
|
||||
private:
|
||||
UBool isGroupingEnabled(int32_t digitsLeftOfDecimal) const;
|
||||
int32_t getGrouping2() const;
|
||||
int32_t getMinGrouping() const;
|
||||
};
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif // __DIGITGROUPING_H__
|
54
icu4c/source/i18n/digitinterval.cpp
Normal file
54
icu4c/source/i18n/digitinterval.cpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*
|
||||
* file name: digitinterval.cpp
|
||||
*/
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#include "digitinterval.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
void DigitInterval::expandToContain(const DigitInterval &rhs) {
|
||||
if (fSmallestInclusive > rhs.fSmallestInclusive) {
|
||||
fSmallestInclusive = rhs.fSmallestInclusive;
|
||||
}
|
||||
if (fLargestExclusive < rhs.fLargestExclusive) {
|
||||
fLargestExclusive = rhs.fLargestExclusive;
|
||||
}
|
||||
}
|
||||
|
||||
void DigitInterval::shrinkToFitWithin(const DigitInterval &rhs) {
|
||||
if (fSmallestInclusive < rhs.fSmallestInclusive) {
|
||||
fSmallestInclusive = rhs.fSmallestInclusive;
|
||||
}
|
||||
if (fLargestExclusive > rhs.fLargestExclusive) {
|
||||
fLargestExclusive = rhs.fLargestExclusive;
|
||||
}
|
||||
}
|
||||
|
||||
void DigitInterval::setIntDigitCount(int32_t count) {
|
||||
fLargestExclusive = count < 0 ? INT32_MAX : count;
|
||||
}
|
||||
|
||||
void DigitInterval::setFracDigitCount(int32_t count) {
|
||||
fSmallestInclusive = count < 0 ? INT32_MIN : -count;
|
||||
}
|
||||
|
||||
void DigitInterval::expandToContainDigit(int32_t digitExponent) {
|
||||
if (fLargestExclusive <= digitExponent) {
|
||||
fLargestExclusive = digitExponent + 1;
|
||||
} else if (fSmallestInclusive > digitExponent) {
|
||||
fSmallestInclusive = digitExponent;
|
||||
}
|
||||
}
|
||||
|
||||
UBool DigitInterval::contains(int32_t x) const {
|
||||
return (x < fLargestExclusive && x >= fSmallestInclusive);
|
||||
}
|
||||
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
157
icu4c/source/i18n/digitinterval.h
Normal file
157
icu4c/source/i18n/digitinterval.h
Normal file
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
* digitinterval.h
|
||||
*
|
||||
* created on: 2015jan6
|
||||
* created by: Travis Keep
|
||||
*/
|
||||
|
||||
#ifndef __DIGITINTERVAL_H__
|
||||
#define __DIGITINTERVAL_H__
|
||||
|
||||
#include "unicode/uobject.h"
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
/**
|
||||
* An interval of digits.
|
||||
* DigitIntervals are for fixed point formatting. A DigitInterval specifies
|
||||
* zero or more integer digits and zero or more fractional digits. This class
|
||||
* specifies particular digits in a number by their power of 10. For example,
|
||||
* the digit position just to the left of the decimal is 0, and the digit
|
||||
* position just left of that is 1. The digit position just to the right of
|
||||
* the decimal is -1. The digit position just to the right of that is -2.
|
||||
*/
|
||||
class U_I18N_API DigitInterval : public UMemory {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Spans all integer and fraction digits
|
||||
*/
|
||||
DigitInterval()
|
||||
: fLargestExclusive(INT32_MAX), fSmallestInclusive(INT32_MIN) { }
|
||||
|
||||
|
||||
/**
|
||||
* Makes this instance span all digits.
|
||||
*/
|
||||
void clear() {
|
||||
fLargestExclusive = INT32_MAX;
|
||||
fSmallestInclusive = INT32_MIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if this interval contains this digit position.
|
||||
*/
|
||||
UBool contains(int32_t digitPosition) const;
|
||||
|
||||
/**
|
||||
* Returns true if this object is the same as rhs.
|
||||
*/
|
||||
UBool equals(const DigitInterval &rhs) const {
|
||||
return ((fLargestExclusive == rhs.fLargestExclusive) &&
|
||||
(fSmallestInclusive == rhs.fSmallestInclusive));
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand this interval so that it contains all of rhs.
|
||||
*/
|
||||
void expandToContain(const DigitInterval &rhs);
|
||||
|
||||
/**
|
||||
* Shrink this interval so that it contains no more than rhs.
|
||||
*/
|
||||
void shrinkToFitWithin(const DigitInterval &rhs);
|
||||
|
||||
/**
|
||||
* Expand this interval as necessary to contain digit with given exponent
|
||||
* After this method returns, this interval is guaranteed to contain
|
||||
* digitExponent.
|
||||
*/
|
||||
void expandToContainDigit(int32_t digitExponent);
|
||||
|
||||
/**
|
||||
* Changes the number of digits to the left of the decimal point that
|
||||
* this interval spans. If count is negative, it means span all digits
|
||||
* to the left of the decimal point.
|
||||
*/
|
||||
void setIntDigitCount(int32_t count);
|
||||
|
||||
/**
|
||||
* Changes the number of digits to the right of the decimal point that
|
||||
* this interval spans. If count is negative, it means span all digits
|
||||
* to the right of the decimal point.
|
||||
*/
|
||||
void setFracDigitCount(int32_t count);
|
||||
|
||||
/**
|
||||
* Sets the least significant inclusive value to smallest. If smallest >= 0
|
||||
* then least significant inclusive value becomes 0.
|
||||
*/
|
||||
void setLeastSignificantInclusive(int32_t smallest) {
|
||||
fSmallestInclusive = smallest < 0 ? smallest : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the most significant exclusive value to largest.
|
||||
* If largest <= 0 then most significant exclusive value becomes 0.
|
||||
*/
|
||||
void setMostSignificantExclusive(int32_t largest) {
|
||||
fLargestExclusive = largest > 0 ? largest : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* If returns 8, the most significant digit in interval is the 10^7 digit.
|
||||
* Returns INT32_MAX if this interval spans all digits to left of
|
||||
* decimal point.
|
||||
*/
|
||||
int32_t getMostSignificantExclusive() const {
|
||||
return fLargestExclusive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of digits to the left of the decimal that this
|
||||
* interval includes. This is a synonym for getMostSignificantExclusive().
|
||||
*/
|
||||
int32_t getIntDigitCount() const {
|
||||
return fLargestExclusive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of digits to the right of the decimal that this
|
||||
* interval includes.
|
||||
*/
|
||||
int32_t getFracDigitCount() const {
|
||||
return fSmallestInclusive == INT32_MIN ? INT32_MAX : -fSmallestInclusive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of digits that this interval spans.
|
||||
* Caution: If this interval spans all digits to the left or right of
|
||||
* decimal point instead of some fixed number, then what length()
|
||||
* returns is undefined.
|
||||
*/
|
||||
int32_t length() const {
|
||||
return fLargestExclusive - fSmallestInclusive;
|
||||
}
|
||||
|
||||
/**
|
||||
* If returns -3, the least significant digit in interval is the 10^-3
|
||||
* digit. Returns INT32_MIN if this interval spans all digits to right of
|
||||
* decimal point.
|
||||
*/
|
||||
int32_t getLeastSignificantInclusive() const {
|
||||
return fSmallestInclusive;
|
||||
}
|
||||
private:
|
||||
int32_t fLargestExclusive;
|
||||
int32_t fSmallestInclusive;
|
||||
};
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif // __DIGITINTERVAL_H__
|
|
@ -34,6 +34,7 @@
|
|||
#include "mutex.h"
|
||||
#include "putilimp.h"
|
||||
#include "uassert.h"
|
||||
#include "digitinterval.h"
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
@ -396,6 +397,27 @@ DigitList::append(char digit)
|
|||
internalClear();
|
||||
}
|
||||
|
||||
char DigitList::getStrtodDecimalSeparator() {
|
||||
// TODO: maybe use andy's pthread once.
|
||||
static char gDecimal = 0;
|
||||
char result;
|
||||
{
|
||||
Mutex mutex;
|
||||
result = gDecimal;;
|
||||
if (result == 0) {
|
||||
// We need to know the decimal separator character that will be used with strtod().
|
||||
// Depends on the C runtime global locale.
|
||||
// Most commonly is '.'
|
||||
// TODO: caching could fail if the global locale is changed on the fly.
|
||||
char rep[MAX_DIGITS];
|
||||
sprintf(rep, "%+1.1f", 1.0);
|
||||
result = rep[2];
|
||||
gDecimal = result;;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
|
||||
/**
|
||||
|
@ -492,7 +514,7 @@ DigitList::getDouble() const
|
|||
int32_t DigitList::getLong() /*const*/
|
||||
{
|
||||
int32_t result = 0;
|
||||
if (fDecNumber->digits + fDecNumber->exponent > 10) {
|
||||
if (getUpperExponent() > 10) {
|
||||
// Overflow, absolute value too big.
|
||||
return result;
|
||||
}
|
||||
|
@ -522,7 +544,7 @@ int64_t DigitList::getInt64() /*const*/ {
|
|||
// Return 0 if out of range.
|
||||
// Range of in64_t is -9223372036854775808 to 9223372036854775807 (19 digits)
|
||||
//
|
||||
if (fDecNumber->digits + fDecNumber->exponent > 19) {
|
||||
if (getUpperExponent() > 19) {
|
||||
// Overflow, absolute value too big.
|
||||
return 0;
|
||||
}
|
||||
|
@ -536,7 +558,7 @@ int64_t DigitList::getInt64() /*const*/ {
|
|||
// TODO: It would be faster to store a table of powers of ten to multiply by
|
||||
// instead of looping over zero digits, multiplying each time.
|
||||
|
||||
int32_t numIntDigits = fDecNumber->digits + fDecNumber->exponent;
|
||||
int32_t numIntDigits = getUpperExponent();
|
||||
uint64_t value = 0;
|
||||
for (int32_t i = 0; i < numIntDigits; i++) {
|
||||
// Loop is iterating over digits starting with the most significant.
|
||||
|
@ -611,7 +633,7 @@ DigitList::fitsIntoLong(UBool ignoreNegativeZero) /*const*/
|
|||
// Negative Zero, not ingored. Cannot represent as a long.
|
||||
return FALSE;
|
||||
}
|
||||
if (fDecNumber->digits + fDecNumber->exponent < 10) {
|
||||
if (getUpperExponent() < 10) {
|
||||
// The number is 9 or fewer digits.
|
||||
// The max and min int32 are 10 digts, so this number fits.
|
||||
// This is the common case.
|
||||
|
@ -658,7 +680,7 @@ DigitList::fitsIntoInt64(UBool ignoreNegativeZero) /*const*/
|
|||
// Negative Zero, not ingored. Cannot represent as a long.
|
||||
return FALSE;
|
||||
}
|
||||
if (fDecNumber->digits + fDecNumber->exponent < 19) {
|
||||
if (getUpperExponent() < 19) {
|
||||
// The number is 18 or fewer digits.
|
||||
// The max and min int64 are 19 digts, so this number fits.
|
||||
// This is the common case.
|
||||
|
@ -832,6 +854,9 @@ DigitList::set(double source)
|
|||
*/
|
||||
void
|
||||
DigitList::mult(const DigitList &other, UErrorCode &status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
fContext.status = 0;
|
||||
int32_t requiredDigits = this->digits() + other.digits();
|
||||
if (requiredDigits > fContext.digits) {
|
||||
|
@ -902,18 +927,23 @@ DigitList::ensureCapacity(int32_t requestedCapacity, UErrorCode &status) {
|
|||
void
|
||||
DigitList::round(int32_t maximumDigits)
|
||||
{
|
||||
reduce();
|
||||
if (maximumDigits >= fDecNumber->digits) {
|
||||
return;
|
||||
}
|
||||
int32_t savedDigits = fContext.digits;
|
||||
fContext.digits = maximumDigits;
|
||||
uprv_decNumberPlus(fDecNumber, fDecNumber, &fContext);
|
||||
fContext.digits = savedDigits;
|
||||
uprv_decNumberTrim(fDecNumber);
|
||||
reduce();
|
||||
internalClear();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DigitList::roundFixedPoint(int32_t maximumFractionDigits) {
|
||||
trim(); // Remove trailing zeros.
|
||||
reduce(); // Remove trailing zeros.
|
||||
if (fDecNumber->exponent >= -maximumFractionDigits) {
|
||||
return;
|
||||
}
|
||||
|
@ -923,7 +953,7 @@ DigitList::roundFixedPoint(int32_t maximumFractionDigits) {
|
|||
scale.lsu[0] = 1;
|
||||
|
||||
uprv_decNumberQuantize(fDecNumber, fDecNumber, &scale, &fContext);
|
||||
trim();
|
||||
reduce();
|
||||
internalClear();
|
||||
}
|
||||
|
||||
|
@ -942,6 +972,98 @@ DigitList::isZero() const
|
|||
return decNumberIsZero(fDecNumber);
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
int32_t
|
||||
DigitList::getUpperExponent() const {
|
||||
return fDecNumber->digits + fDecNumber->exponent;
|
||||
}
|
||||
|
||||
DigitInterval &
|
||||
DigitList::getSmallestInterval(DigitInterval &result) const {
|
||||
result.setLeastSignificantInclusive(fDecNumber->exponent);
|
||||
result.setMostSignificantExclusive(getUpperExponent());
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
DigitList::getDigitByExponent(int32_t exponent) const {
|
||||
int32_t idx = exponent - fDecNumber->exponent;
|
||||
if (idx < 0 || idx >= fDecNumber->digits) {
|
||||
return 0;
|
||||
}
|
||||
return fDecNumber->lsu[idx];
|
||||
}
|
||||
|
||||
void
|
||||
DigitList::appendDigitsTo(CharString &str, UErrorCode &status) const {
|
||||
str.append((const char *) fDecNumber->lsu, fDecNumber->digits, status);
|
||||
}
|
||||
|
||||
void
|
||||
DigitList::roundAtExponent(int32_t exponent, int32_t maxSigDigits) {
|
||||
reduce();
|
||||
if (maxSigDigits < fDecNumber->digits) {
|
||||
int32_t minExponent = getUpperExponent() - maxSigDigits;
|
||||
if (exponent < minExponent) {
|
||||
exponent = minExponent;
|
||||
}
|
||||
}
|
||||
if (exponent <= fDecNumber->exponent) {
|
||||
return;
|
||||
}
|
||||
int32_t digits = getUpperExponent() - exponent;
|
||||
if (digits > 0) {
|
||||
round(digits);
|
||||
} else {
|
||||
roundFixedPoint(-exponent);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DigitList::quantize(const DigitList &quantity, UErrorCode &status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
div(quantity, status);
|
||||
roundAtExponent(0);
|
||||
mult(quantity, status);
|
||||
reduce();
|
||||
}
|
||||
|
||||
int32_t
|
||||
DigitList::getScientificExponent(
|
||||
int32_t minIntDigitCount, int32_t exponentMultiplier) const {
|
||||
// The exponent for zero is always zero.
|
||||
if (isZero()) {
|
||||
return 0;
|
||||
}
|
||||
int32_t intDigitCount = getUpperExponent();
|
||||
int32_t exponent;
|
||||
if (intDigitCount >= minIntDigitCount) {
|
||||
int32_t maxAdjustment = intDigitCount - minIntDigitCount;
|
||||
exponent = (maxAdjustment / exponentMultiplier) * exponentMultiplier;
|
||||
} else {
|
||||
int32_t minAdjustment = minIntDigitCount - intDigitCount;
|
||||
exponent = ((minAdjustment + exponentMultiplier - 1) / exponentMultiplier) * -exponentMultiplier;
|
||||
}
|
||||
return exponent;
|
||||
}
|
||||
|
||||
int32_t
|
||||
DigitList::toScientific(
|
||||
int32_t minIntDigitCount, int32_t exponentMultiplier) {
|
||||
int32_t exponent = getScientificExponent(
|
||||
minIntDigitCount, exponentMultiplier);
|
||||
shiftDecimalRight(-exponent);
|
||||
return exponent;
|
||||
}
|
||||
|
||||
void
|
||||
DigitList::shiftDecimalRight(int32_t n) {
|
||||
fDecNumber->exponent += n;
|
||||
internalClear();
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
||||
#endif // #if !UCONFIG_NO_FORMATTING
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
******************************************************************************
|
||||
*
|
||||
* Copyright (C) 1997-2013, International Business Machines
|
||||
* Copyright (C) 1997-2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*
|
||||
******************************************************************************
|
||||
|
@ -51,6 +51,7 @@ typedef enum EDigitListValues {
|
|||
U_NAMESPACE_BEGIN
|
||||
|
||||
class CharString;
|
||||
class DigitInterval;
|
||||
|
||||
// Export an explicit template instantiation of the MaybeStackHeaderAndArray that
|
||||
// is used as a data member of DigitList.
|
||||
|
@ -313,6 +314,7 @@ public:
|
|||
* Round the number to the given number of digits.
|
||||
* @param maximumDigits The maximum number of digits to be shown.
|
||||
* Upon return, count will be less than or equal to maximumDigits.
|
||||
* result is guaranteed to be trimmed.
|
||||
*/
|
||||
void round(int32_t maximumDigits);
|
||||
|
||||
|
@ -356,6 +358,78 @@ public:
|
|||
*/
|
||||
uint8_t getDigitValue(int32_t i);
|
||||
|
||||
/**
|
||||
* Gets the upper bound exponent for this value. For 987, returns 3
|
||||
* because 10^3 is the smallest power of 10 that is just greater than
|
||||
* 987.
|
||||
*/
|
||||
int32_t getUpperExponent() const;
|
||||
|
||||
/**
|
||||
* Gets the lower bound exponent for this value. For 98.7, returns -1
|
||||
* because the right most digit, is the 10^-1 place.
|
||||
*/
|
||||
int32_t getLowerExponent() const { return fDecNumber->exponent; }
|
||||
|
||||
/**
|
||||
* Sets result to the smallest DigitInterval needed to display this
|
||||
* DigitList in fixed point form and returns result.
|
||||
*/
|
||||
DigitInterval& getSmallestInterval(DigitInterval &result) const;
|
||||
|
||||
/**
|
||||
* Like getDigitValue, but the digit is identified by exponent.
|
||||
* For example, getDigitByExponent(7) returns the 10^7 place of this
|
||||
* DigitList. Unlike getDigitValue, there are no upper or lower bounds
|
||||
* for passed parameter. Instead, getDigitByExponent returns 0 if
|
||||
* the exponent falls outside the interval for this DigitList.
|
||||
*/
|
||||
uint8_t getDigitByExponent(int32_t exponent) const;
|
||||
|
||||
/**
|
||||
* Appends the digits in this object to a CharString.
|
||||
* 3 is appended as (char) 3, not '3'
|
||||
*/
|
||||
void appendDigitsTo(CharString &str, UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Equivalent to roundFixedPoint(-digitExponent) except unlike
|
||||
* roundFixedPoint, this works for any digitExponent value.
|
||||
* If maxSigDigits is set then this instance is rounded to have no more
|
||||
* than maxSigDigits. The end result is guaranteed to be trimmed.
|
||||
*/
|
||||
void roundAtExponent(int32_t digitExponent, int32_t maxSigDigits=INT32_MAX);
|
||||
|
||||
/**
|
||||
* Quantizes according to some amount and rounds according to the
|
||||
* context of this instance. Quantizing 3.233 with 0.05 gives 3.25.
|
||||
*/
|
||||
void quantize(const DigitList &amount, UErrorCode &status);
|
||||
|
||||
/**
|
||||
* Like toScientific but only returns the exponent
|
||||
* leaving this instance unchanged.
|
||||
*/
|
||||
int32_t getScientificExponent(
|
||||
int32_t minIntDigitCount, int32_t exponentMultiplier) const;
|
||||
|
||||
/**
|
||||
* Converts this instance to scientific notation. This instance
|
||||
* becomes the mantissa and the exponent is returned.
|
||||
* @param minIntDigitCount minimum integer digits in mantissa
|
||||
* Exponent is set so that the actual number of integer digits
|
||||
* in mantissa is as close to the minimum as possible.
|
||||
* @param exponentMultiplier The exponent is always a multiple of
|
||||
* This number. Usually 1, but set to 3 for engineering notation.
|
||||
* @return exponent
|
||||
*/
|
||||
int32_t toScientific(
|
||||
int32_t minIntDigitCount, int32_t exponentMultiplier);
|
||||
|
||||
/**
|
||||
* Shifts decimal to the right.
|
||||
*/
|
||||
void shiftDecimalRight(int32_t numPlaces);
|
||||
|
||||
private:
|
||||
/*
|
||||
|
@ -420,6 +494,7 @@ private:
|
|||
static inline void * U_EXPORT2 operator new(size_t size) U_NO_THROW { return ::operator new(size); };
|
||||
static inline void U_EXPORT2 operator delete(void *ptr ) U_NO_THROW { ::operator delete(ptr); };
|
||||
#endif
|
||||
static char U_EXPORT2 getStrtodDecimalSeparator();
|
||||
|
||||
/**
|
||||
* Placement new for stack usage
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2009-2010, International Business Machines Corporation and
|
||||
* Copyright (C) 2009-2015, International Business Machines Corporation and
|
||||
* others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
@ -29,7 +29,7 @@ FieldPositionHandler::shiftLast(int32_t) {
|
|||
}
|
||||
|
||||
UBool
|
||||
FieldPositionHandler::isRecording(void) {
|
||||
FieldPositionHandler::isRecording(void) const {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ FieldPositionOnlyHandler::shiftLast(int32_t delta) {
|
|||
}
|
||||
|
||||
UBool
|
||||
FieldPositionOnlyHandler::isRecording(void) {
|
||||
FieldPositionOnlyHandler::isRecording(void) const {
|
||||
return pos.getField() != FieldPosition::DONT_CARE;
|
||||
}
|
||||
|
||||
|
@ -111,7 +111,7 @@ FieldPositionIteratorHandler::shiftLast(int32_t delta) {
|
|||
}
|
||||
|
||||
UBool
|
||||
FieldPositionIteratorHandler::isRecording(void) {
|
||||
FieldPositionIteratorHandler::isRecording(void) const {
|
||||
return U_SUCCESS(status);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2009-2010, International Business Machines Corporation and *
|
||||
* Copyright (C) 2009-2015, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
@ -19,12 +19,12 @@ U_NAMESPACE_BEGIN
|
|||
// utility FieldPositionHandler
|
||||
// base class, null implementation
|
||||
|
||||
class FieldPositionHandler: public UMemory {
|
||||
class U_I18N_API FieldPositionHandler: public UMemory {
|
||||
public:
|
||||
virtual ~FieldPositionHandler();
|
||||
virtual void addAttribute(int32_t id, int32_t start, int32_t limit);
|
||||
virtual void shiftLast(int32_t delta);
|
||||
virtual UBool isRecording(void);
|
||||
virtual UBool isRecording(void) const;
|
||||
};
|
||||
|
||||
|
||||
|
@ -39,7 +39,7 @@ class FieldPositionOnlyHandler : public FieldPositionHandler {
|
|||
|
||||
virtual void addAttribute(int32_t id, int32_t start, int32_t limit);
|
||||
virtual void shiftLast(int32_t delta);
|
||||
virtual UBool isRecording(void);
|
||||
virtual UBool isRecording(void) const;
|
||||
};
|
||||
|
||||
|
||||
|
@ -63,7 +63,7 @@ class FieldPositionIteratorHandler : public FieldPositionHandler {
|
|||
|
||||
virtual void addAttribute(int32_t id, int32_t start, int32_t limit);
|
||||
virtual void shiftLast(int32_t delta);
|
||||
virtual UBool isRecording(void);
|
||||
virtual UBool isRecording(void) const;
|
||||
};
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
|
|
@ -281,6 +281,18 @@
|
|||
<ClCompile Include="ucol_res.cpp" />
|
||||
<ClCompile Include="ucol_sit.cpp" />
|
||||
<ClCompile Include="ucoleitr.cpp" />
|
||||
<ClCompile Include="affixpatternparser.cpp" />
|
||||
<ClCompile Include="decimfmtimpl.cpp" />
|
||||
<ClCompile Include="digitaffix.cpp" />
|
||||
<ClCompile Include="digitaffixesandpadding.cpp" />
|
||||
<ClCompile Include="digitformatter.cpp" />
|
||||
<ClCompile Include="digitgrouping.cpp" />
|
||||
<ClCompile Include="digitinterval.cpp" />
|
||||
<ClCompile Include="pluralaffix.cpp" />
|
||||
<ClCompile Include="precision.cpp" />
|
||||
<ClCompile Include="smallintformatter.cpp" />
|
||||
<ClCompile Include="valueformatter.cpp" />
|
||||
<ClCompile Include="visibledigits.cpp" />
|
||||
<ClCompile Include="uitercollationiterator.cpp" />
|
||||
<ClCompile Include="usearch.cpp" />
|
||||
<ClCompile Include="astro.cpp" />
|
||||
|
@ -564,6 +576,20 @@
|
|||
</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="affixpatternparser.h" />
|
||||
<ClInclude Include="decimalformatpatternimpl.h" />
|
||||
<ClInclude Include="decimfmtimpl.h" />
|
||||
<ClInclude Include="digitaffix.h" />
|
||||
<ClInclude Include="digitaffixesandpadding.h" />
|
||||
<ClInclude Include="digitformatter.h" />
|
||||
<ClInclude Include="digitgrouping.h" />
|
||||
<ClInclude Include="digitinterval.h" />
|
||||
<ClInclude Include="pluralaffix.h" />
|
||||
<ClInclude Include="precision.h" />
|
||||
<ClInclude Include="significantdigitinterval.h" />
|
||||
<ClInclude Include="smallintformatter.h" />
|
||||
<ClInclude Include="valueformatter.h" />
|
||||
<ClInclude Include="visibledigits.h" />
|
||||
<ClInclude Include="collation.h" />
|
||||
<ClInclude Include="collationbuilder.h" />
|
||||
<ClInclude Include="collationcompare.h" />
|
||||
|
@ -1715,4 +1741,4 @@
|
|||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -54,6 +54,42 @@
|
|||
<ClCompile Include="usearch.cpp">
|
||||
<Filter>collation</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="affixpatternparser.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="decimfmtimpl.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="digitaffix.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="digitaffixesandpadding.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="digitformatter.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="digitgrouping.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="digitinterval.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pluralaffix.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="precision.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="smallintformatter.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="valueformatter.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="visibledigits.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="astro.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
|
@ -593,6 +629,50 @@
|
|||
<ClInclude Include="usrchimp.h">
|
||||
<Filter>collation</Filter>
|
||||
</ClInclude>
|
||||
|
||||
<ClInclude Include="affixpatternparser.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="decimalformatpatternimpl.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="decimfmtimpl.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="digitaffix.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="digitaffixesandpadding.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="digitformatter.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="digitgrouping.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="digitinterval.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="pluralaffix.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="precision.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="significantdigitinterval.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="smallintformatter.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="valueformatter.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="visibledigits.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
|
||||
<ClInclude Include="astro.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
|
@ -1210,4 +1290,4 @@
|
|||
<Filter>formatting</Filter>
|
||||
</CustomBuild>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/********************************************************************
|
||||
* COPYRIGHT:
|
||||
* Copyright (c) 1997-2014, International Business Machines Corporation and
|
||||
* Copyright (c) 1997-2015, International Business Machines Corporation and
|
||||
* others. All Rights Reserved.
|
||||
********************************************************************
|
||||
*
|
||||
|
@ -46,6 +46,7 @@
|
|||
#include "ustrfmt.h"
|
||||
#include "util.h"
|
||||
#include "uvector.h"
|
||||
#include "visibledigits.h"
|
||||
|
||||
// *****************************************************************************
|
||||
// class MessageFormat
|
||||
|
@ -1955,8 +1956,12 @@ UnicodeString MessageFormat::PluralSelectorProvider::select(void *ctx, double nu
|
|||
context.formatter->format(context.number, context.numberString, ec);
|
||||
const DecimalFormat *decFmt = dynamic_cast<const DecimalFormat *>(context.formatter);
|
||||
if(decFmt != NULL) {
|
||||
FixedDecimal dec = decFmt->getFixedDecimal(context.number, ec);
|
||||
return rules->select(dec);
|
||||
VisibleDigitsWithExponent digits;
|
||||
decFmt->initVisibleDigitsWithExponent(context.number, digits, ec);
|
||||
if (U_FAILURE(ec)) {
|
||||
return UnicodeString(FALSE, OTHER_STRING, 5);
|
||||
}
|
||||
return rules->select(digits);
|
||||
} else {
|
||||
return rules->select(number);
|
||||
}
|
||||
|
|
99
icu4c/source/i18n/pluralaffix.cpp
Normal file
99
icu4c/source/i18n/pluralaffix.cpp
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*
|
||||
* file name: pluralaffix.cpp
|
||||
*/
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#include "cstring.h"
|
||||
#include "digitaffix.h"
|
||||
#include "pluralaffix.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
UBool
|
||||
PluralAffix::setVariant(
|
||||
const char *variant, const UnicodeString &value, UErrorCode &status) {
|
||||
DigitAffix *current = affixes.getMutable(variant, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return FALSE;
|
||||
}
|
||||
current->remove();
|
||||
current->append(value);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
PluralAffix::remove() {
|
||||
affixes.clear();
|
||||
}
|
||||
|
||||
void
|
||||
PluralAffix::appendUChar(
|
||||
const UChar value, int32_t fieldId) {
|
||||
PluralMapBase::Category index = PluralMapBase::NONE;
|
||||
for (DigitAffix *current = affixes.nextMutable(index);
|
||||
current != NULL; current = affixes.nextMutable(index)) {
|
||||
current->appendUChar(value, fieldId);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PluralAffix::append(
|
||||
const UnicodeString &value, int32_t fieldId) {
|
||||
PluralMapBase::Category index = PluralMapBase::NONE;
|
||||
for (DigitAffix *current = affixes.nextMutable(index);
|
||||
current != NULL; current = affixes.nextMutable(index)) {
|
||||
current->append(value, fieldId);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PluralAffix::append(
|
||||
const UChar *value, int32_t charCount, int32_t fieldId) {
|
||||
PluralMapBase::Category index = PluralMapBase::NONE;
|
||||
for (DigitAffix *current = affixes.nextMutable(index);
|
||||
current != NULL; current = affixes.nextMutable(index)) {
|
||||
current->append(value, charCount, fieldId);
|
||||
}
|
||||
}
|
||||
|
||||
UBool
|
||||
PluralAffix::append(
|
||||
const PluralAffix &rhs, int32_t fieldId, UErrorCode &status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return FALSE;
|
||||
}
|
||||
PluralMapBase::Category index = PluralMapBase::NONE;
|
||||
while(rhs.affixes.next(index) != NULL) {
|
||||
affixes.getMutableWithDefault(index, affixes.getOther(), status);
|
||||
}
|
||||
index = PluralMapBase::NONE;
|
||||
for (DigitAffix *current = affixes.nextMutable(index);
|
||||
current != NULL; current = affixes.nextMutable(index)) {
|
||||
current->append(rhs.affixes.get(index).toString(), fieldId);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
const DigitAffix &
|
||||
PluralAffix::getByCategory(const char *category) const {
|
||||
return affixes.get(category);
|
||||
}
|
||||
|
||||
const DigitAffix &
|
||||
PluralAffix::getByCategory(const UnicodeString &category) const {
|
||||
return affixes.get(category);
|
||||
}
|
||||
|
||||
UBool
|
||||
PluralAffix::hasMultipleVariants() const {
|
||||
// This works because OTHER is guaranteed to be the first enum value
|
||||
PluralMapBase::Category index = PluralMapBase::OTHER;
|
||||
return (affixes.next(index) != NULL);
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
159
icu4c/source/i18n/pluralaffix.h
Normal file
159
icu4c/source/i18n/pluralaffix.h
Normal file
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
* pluralaffix.h
|
||||
*
|
||||
* created on: 2015jan06
|
||||
* created by: Travis Keep
|
||||
*/
|
||||
|
||||
#ifndef __PLURALAFFIX_H__
|
||||
#define __PLURALAFFIX_H__
|
||||
|
||||
#include "unicode/unum.h"
|
||||
#include "unicode/uobject.h"
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#include "digitaffix.h"
|
||||
#include "pluralmap.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
class FieldPositionHandler;
|
||||
|
||||
/**
|
||||
* A plural aware prefix or suffix of a formatted number.
|
||||
*
|
||||
* PluralAffix is essentially a map of DigitAffix objects keyed by plural
|
||||
* category. The 'other' category is the default and always has some
|
||||
* value. The rest of the categories are optional. Querying for a category that
|
||||
* is not set always returns the DigitAffix stored in the 'other' category.
|
||||
*
|
||||
* To use one of these objects, build it up first using append() and
|
||||
* setVariant() methods. Once built, leave unchanged and let multiple threads
|
||||
* safely access.
|
||||
*
|
||||
* The following code is sample code for building up:
|
||||
* one: US Dollar -
|
||||
* other: US Dollars -
|
||||
*
|
||||
* and storing it in "negativeCurrencyPrefix"
|
||||
*
|
||||
* UErrorCode status = U_ZERO_ERROR;
|
||||
*
|
||||
* PluralAffix negativeCurrencyPrefix;
|
||||
*
|
||||
* PluralAffix currencyName;
|
||||
* currencyName.setVariant("one", "US Dollar", status);
|
||||
* currencyName.setVariant("other", "US Dollars", status);
|
||||
*
|
||||
* negativeCurrencyPrefix.append(currencyName, UNUM_CURRENCY_FIELD, status);
|
||||
* negativeCurrencyPrefix.append(" ");
|
||||
* negativeCurrencyPrefix.append("-", UNUM_SIGN_FIELD, status);
|
||||
*/
|
||||
class U_I18N_API PluralAffix : public UMemory {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Create empty PluralAffix.
|
||||
*/
|
||||
PluralAffix() : affixes() { }
|
||||
|
||||
/**
|
||||
* Create a PluralAffix where the 'other' variant is otherVariant.
|
||||
*/
|
||||
PluralAffix(const DigitAffix &otherVariant) : affixes(otherVariant) { }
|
||||
|
||||
/**
|
||||
* Sets a particular variant for a plural category while overwriting
|
||||
* anything that may have been previously stored for that plural
|
||||
* category. The set value has no field annotations.
|
||||
* @param category "one", "two", "few", ...
|
||||
* @param variant the variant to store under the particular category
|
||||
* @param status Any error returned here.
|
||||
*/
|
||||
UBool setVariant(
|
||||
const char *category,
|
||||
const UnicodeString &variant,
|
||||
UErrorCode &status);
|
||||
/**
|
||||
* Make the 'other' variant be the empty string with no field annotations
|
||||
* and remove the variants for the rest of the plural categories.
|
||||
*/
|
||||
void remove();
|
||||
|
||||
/**
|
||||
* Append value to all set plural categories. If fieldId present, value
|
||||
* is that field type.
|
||||
*/
|
||||
void appendUChar(UChar value, int32_t fieldId=UNUM_FIELD_COUNT);
|
||||
|
||||
/**
|
||||
* Append value to all set plural categories. If fieldId present, value
|
||||
* is that field type.
|
||||
*/
|
||||
void append(const UnicodeString &value, int32_t fieldId=UNUM_FIELD_COUNT);
|
||||
|
||||
/**
|
||||
* Append value to all set plural categories. If fieldId present, value
|
||||
* is that field type.
|
||||
*/
|
||||
void append(const UChar *value, int32_t charCount, int32_t fieldId=UNUM_FIELD_COUNT);
|
||||
|
||||
/**
|
||||
* Append the value for each plural category in rhs to the corresponding
|
||||
* plural category in this instance. Each value appended from rhs is
|
||||
* of type fieldId.
|
||||
*/
|
||||
UBool append(
|
||||
const PluralAffix &rhs,
|
||||
int32_t fieldId,
|
||||
UErrorCode &status);
|
||||
/**
|
||||
* Get the DigitAffix for a paricular category such as "zero", "one", ...
|
||||
* If the particular category is not set, returns the 'other' category
|
||||
* which is always set.
|
||||
*/
|
||||
const DigitAffix &getByCategory(const char *category) const;
|
||||
|
||||
/**
|
||||
* Get the DigitAffix for a paricular category such as "zero", "one", ...
|
||||
* If the particular category is not set, returns the 'other' category
|
||||
* which is always set.
|
||||
*/
|
||||
const DigitAffix &getByCategory(const UnicodeString &category) const;
|
||||
|
||||
/**
|
||||
* Get the DigitAffix for the other category which is always set.
|
||||
*/
|
||||
const DigitAffix &getOtherVariant() const {
|
||||
return affixes.getOther();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if this instance has variants stored besides the "other"
|
||||
* variant.
|
||||
*/
|
||||
UBool hasMultipleVariants() const;
|
||||
|
||||
/**
|
||||
* Returns TRUE if this instance equals rhs.
|
||||
*/
|
||||
UBool equals(const PluralAffix &rhs) const {
|
||||
return affixes.equals(rhs.affixes, &eq);
|
||||
}
|
||||
|
||||
private:
|
||||
PluralMap<DigitAffix> affixes;
|
||||
|
||||
static UBool eq(const DigitAffix &x, const DigitAffix &y) {
|
||||
return x.equals(y);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif // __PLURALAFFIX_H__
|
|
@ -19,6 +19,8 @@
|
|||
#include "plurrule_impl.h"
|
||||
#include "uassert.h"
|
||||
#include "uhash.h"
|
||||
#include "precision.h"
|
||||
#include "visibledigits.h"
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
|
@ -259,18 +261,37 @@ PluralFormat::format(const Formattable& numberObject, double number,
|
|||
double numberMinusOffset = number - offset;
|
||||
UnicodeString numberString;
|
||||
FieldPosition ignorePos;
|
||||
FixedDecimal dec(numberMinusOffset);
|
||||
FixedPrecision fp;
|
||||
VisibleDigitsWithExponent dec;
|
||||
fp.initVisibleDigitsWithExponent(numberMinusOffset, dec, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
if (offset == 0) {
|
||||
numberFormat->format(numberObject, numberString, ignorePos, status); // could be BigDecimal etc.
|
||||
DecimalFormat *decFmt = dynamic_cast<DecimalFormat *>(numberFormat);
|
||||
if(decFmt != NULL) {
|
||||
dec = decFmt->getFixedDecimal(numberObject, status);
|
||||
decFmt->initVisibleDigitsWithExponent(
|
||||
numberObject, dec, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
decFmt->format(dec, numberString, ignorePos, status);
|
||||
} else {
|
||||
numberFormat->format(
|
||||
numberObject, numberString, ignorePos, status); // could be BigDecimal etc.
|
||||
}
|
||||
} else {
|
||||
numberFormat->format(numberMinusOffset, numberString, ignorePos, status);
|
||||
DecimalFormat *decFmt = dynamic_cast<DecimalFormat *>(numberFormat);
|
||||
if(decFmt != NULL) {
|
||||
dec = decFmt->getFixedDecimal(numberMinusOffset, status);
|
||||
decFmt->initVisibleDigitsWithExponent(
|
||||
numberMinusOffset, dec, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
decFmt->format(dec, numberString, ignorePos, status);
|
||||
} else {
|
||||
numberFormat->format(
|
||||
numberMinusOffset, numberString, ignorePos, status);
|
||||
}
|
||||
}
|
||||
int32_t partIndex = findSubMessage(msgPattern, 0, pluralRulesWrapper, &dec, number, status);
|
||||
|
@ -562,8 +583,7 @@ PluralFormat::PluralSelectorAdapter::~PluralSelectorAdapter() {
|
|||
UnicodeString PluralFormat::PluralSelectorAdapter::select(void *context, double number,
|
||||
UErrorCode& /*ec*/) const {
|
||||
(void)number; // unused except in the assertion
|
||||
FixedDecimal *dec=static_cast<FixedDecimal *>(context);
|
||||
U_ASSERT(dec->source==number);
|
||||
VisibleDigitsWithExponent *dec=static_cast<VisibleDigitsWithExponent *>(context);
|
||||
return pluralRules->select(*dec);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2007-2014, International Business Machines Corporation and
|
||||
* Copyright (C) 2007-2015, International Business Machines Corporation and
|
||||
* others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
*
|
||||
|
@ -31,6 +31,9 @@
|
|||
#include "uvectr32.h"
|
||||
#include "sharedpluralrules.h"
|
||||
#include "unifiedcache.h"
|
||||
#include "digitinterval.h"
|
||||
#include "visibledigits.h"
|
||||
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
|
@ -253,6 +256,16 @@ PluralRules::select(const FixedDecimal &number) const {
|
|||
}
|
||||
}
|
||||
|
||||
UnicodeString
|
||||
PluralRules::select(const VisibleDigitsWithExponent &number) const {
|
||||
if (number.getExponent() != NULL) {
|
||||
return UnicodeString(TRUE, PLURAL_DEFAULT_RULE, -1);
|
||||
}
|
||||
return select(FixedDecimal(number.getMantissa()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
StringEnumeration*
|
||||
PluralRules::getKeywords(UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) return NULL;
|
||||
|
@ -1370,7 +1383,14 @@ PluralKeywordEnumeration::count(UErrorCode& /*status*/) const {
|
|||
PluralKeywordEnumeration::~PluralKeywordEnumeration() {
|
||||
}
|
||||
|
||||
|
||||
FixedDecimal::FixedDecimal(const VisibleDigits &digits) {
|
||||
digits.getFixedDecimal(
|
||||
source, intValue, decimalDigits,
|
||||
decimalDigitsWithoutTrailingZeros,
|
||||
visibleDecimalDigitCount, hasIntegerValue);
|
||||
isNegative = digits.isNegative();
|
||||
isNanOrInfinity = digits.isNaNOrInfinity();
|
||||
}
|
||||
|
||||
FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f) {
|
||||
init(n, v, f);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2007-2013, International Business Machines Corporation and
|
||||
* Copyright (C) 2007-2015, International Business Machines Corporation and
|
||||
* others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
*
|
||||
|
@ -31,6 +31,8 @@ U_NAMESPACE_BEGIN
|
|||
|
||||
class AndConstraint;
|
||||
class RuleChain;
|
||||
class DigitInterval;
|
||||
class VisibleDigits;
|
||||
|
||||
static const UChar DOT = ((UChar)0x002E);
|
||||
static const UChar SINGLE_QUOTE = ((UChar)0x0027);
|
||||
|
@ -187,6 +189,7 @@ class U_I18N_API FixedDecimal: public UMemory {
|
|||
FixedDecimal(double n, int32_t v, int64_t f);
|
||||
FixedDecimal(double n, int32_t);
|
||||
explicit FixedDecimal(double n);
|
||||
explicit FixedDecimal(const VisibleDigits &n);
|
||||
FixedDecimal();
|
||||
FixedDecimal(const UnicodeString &s, UErrorCode &ec);
|
||||
FixedDecimal(const FixedDecimal &other);
|
||||
|
|
439
icu4c/source/i18n/precision.cpp
Normal file
439
icu4c/source/i18n/precision.cpp
Normal file
|
@ -0,0 +1,439 @@
|
|||
/*
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*
|
||||
* file name: precisison.cpp
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#include "digitlst.h"
|
||||
#include "fmtableimp.h"
|
||||
#include "precision.h"
|
||||
#include "visibledigits.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
static const int32_t gPower10[] = {1, 10, 100, 1000};
|
||||
|
||||
FixedPrecision::FixedPrecision()
|
||||
: fExactOnly(FALSE), fFailIfOverMax(FALSE), fRoundingMode(DecimalFormat::kRoundHalfEven) {
|
||||
fMin.setIntDigitCount(1);
|
||||
fMin.setFracDigitCount(0);
|
||||
}
|
||||
|
||||
UBool
|
||||
FixedPrecision::isRoundingRequired(
|
||||
int32_t upperExponent, int32_t lowerExponent) const {
|
||||
int32_t leastSigAllowed = fMax.getLeastSignificantInclusive();
|
||||
int32_t maxSignificantDigits = fSignificant.getMax();
|
||||
int32_t roundDigit;
|
||||
if (maxSignificantDigits == INT32_MAX) {
|
||||
roundDigit = leastSigAllowed;
|
||||
} else {
|
||||
int32_t limitDigit = upperExponent - maxSignificantDigits;
|
||||
roundDigit =
|
||||
limitDigit > leastSigAllowed ? limitDigit : leastSigAllowed;
|
||||
}
|
||||
return (roundDigit > lowerExponent);
|
||||
}
|
||||
|
||||
DigitList &
|
||||
FixedPrecision::round(
|
||||
DigitList &value, int32_t exponent, UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return value;
|
||||
}
|
||||
value .fContext.status &= ~DEC_Inexact;
|
||||
if (!fRoundingIncrement.isZero()) {
|
||||
if (exponent == 0) {
|
||||
value.quantize(fRoundingIncrement, status);
|
||||
} else {
|
||||
DigitList adjustedIncrement(fRoundingIncrement);
|
||||
adjustedIncrement.shiftDecimalRight(exponent);
|
||||
value.quantize(adjustedIncrement, status);
|
||||
}
|
||||
if (U_FAILURE(status)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
int32_t leastSig = fMax.getLeastSignificantInclusive();
|
||||
if (leastSig == INT32_MIN) {
|
||||
value.round(fSignificant.getMax());
|
||||
} else {
|
||||
value.roundAtExponent(
|
||||
exponent + leastSig,
|
||||
fSignificant.getMax());
|
||||
}
|
||||
if (fExactOnly && (value.fContext.status & DEC_Inexact)) {
|
||||
status = U_FORMAT_INEXACT_ERROR;
|
||||
} else if (fFailIfOverMax) {
|
||||
// Smallest interval for value stored in interval
|
||||
DigitInterval interval;
|
||||
value.getSmallestInterval(interval);
|
||||
if (fMax.getIntDigitCount() < interval.getIntDigitCount()) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
DigitInterval &
|
||||
FixedPrecision::getIntervalForZero(DigitInterval &interval) const {
|
||||
interval = fMin;
|
||||
if (fSignificant.getMin() > 0) {
|
||||
interval.expandToContainDigit(interval.getIntDigitCount() - fSignificant.getMin());
|
||||
}
|
||||
interval.shrinkToFitWithin(fMax);
|
||||
return interval;
|
||||
}
|
||||
|
||||
DigitInterval &
|
||||
FixedPrecision::getInterval(
|
||||
int32_t upperExponent, DigitInterval &interval) const {
|
||||
if (fSignificant.getMin() > 0) {
|
||||
interval.expandToContainDigit(
|
||||
upperExponent - fSignificant.getMin());
|
||||
}
|
||||
interval.expandToContain(fMin);
|
||||
interval.shrinkToFitWithin(fMax);
|
||||
return interval;
|
||||
}
|
||||
|
||||
DigitInterval &
|
||||
FixedPrecision::getInterval(
|
||||
const DigitList &value, DigitInterval &interval) const {
|
||||
if (value.isZero()) {
|
||||
interval = fMin;
|
||||
if (fSignificant.getMin() > 0) {
|
||||
interval.expandToContainDigit(interval.getIntDigitCount() - fSignificant.getMin());
|
||||
}
|
||||
} else {
|
||||
value.getSmallestInterval(interval);
|
||||
if (fSignificant.getMin() > 0) {
|
||||
interval.expandToContainDigit(
|
||||
value.getUpperExponent() - fSignificant.getMin());
|
||||
}
|
||||
interval.expandToContain(fMin);
|
||||
}
|
||||
interval.shrinkToFitWithin(fMax);
|
||||
return interval;
|
||||
}
|
||||
|
||||
UBool
|
||||
FixedPrecision::isFastFormattable() const {
|
||||
return (fMin.getFracDigitCount() == 0 && fSignificant.isNoConstraints() && fRoundingIncrement.isZero() && !fFailIfOverMax);
|
||||
}
|
||||
|
||||
UBool
|
||||
FixedPrecision::handleNonNumeric(DigitList &value, VisibleDigits &digits) {
|
||||
if (value.isNaN()) {
|
||||
digits.setNaN();
|
||||
return TRUE;
|
||||
}
|
||||
if (value.isInfinite()) {
|
||||
digits.setInfinite();
|
||||
if (!value.isPositive()) {
|
||||
digits.setNegative();
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
VisibleDigits &
|
||||
FixedPrecision::initVisibleDigits(
|
||||
DigitList &value,
|
||||
VisibleDigits &digits,
|
||||
UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return digits;
|
||||
}
|
||||
digits.clear();
|
||||
if (handleNonNumeric(value, digits)) {
|
||||
return digits;
|
||||
}
|
||||
if (!value.isPositive()) {
|
||||
digits.setNegative();
|
||||
}
|
||||
value.setRoundingMode(fRoundingMode);
|
||||
round(value, 0, status);
|
||||
getInterval(value, digits.fInterval);
|
||||
digits.fExponent = value.getLowerExponent();
|
||||
value.appendDigitsTo(digits.fDigits, status);
|
||||
return digits;
|
||||
}
|
||||
|
||||
VisibleDigits &
|
||||
FixedPrecision::initVisibleDigits(
|
||||
int64_t value,
|
||||
VisibleDigits &digits,
|
||||
UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return digits;
|
||||
}
|
||||
if (!fRoundingIncrement.isZero()) {
|
||||
// If we have round increment, use digit list.
|
||||
DigitList digitList;
|
||||
digitList.set(value);
|
||||
return initVisibleDigits(digitList, digits, status);
|
||||
}
|
||||
// Try fast path
|
||||
if (initVisibleDigits(value, 0, digits, status)) {
|
||||
digits.fAbsDoubleValue = fabs((double) value);
|
||||
digits.fAbsDoubleValueSet = U_SUCCESS(status) && !digits.isOverMaxDigits();
|
||||
return digits;
|
||||
}
|
||||
// Oops have to use digit list
|
||||
DigitList digitList;
|
||||
digitList.set(value);
|
||||
return initVisibleDigits(digitList, digits, status);
|
||||
}
|
||||
|
||||
VisibleDigits &
|
||||
FixedPrecision::initVisibleDigits(
|
||||
double value,
|
||||
VisibleDigits &digits,
|
||||
UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return digits;
|
||||
}
|
||||
digits.clear();
|
||||
if (uprv_isNaN(value)) {
|
||||
digits.setNaN();
|
||||
return digits;
|
||||
}
|
||||
if (uprv_isPositiveInfinity(value)) {
|
||||
digits.setInfinite();
|
||||
return digits;
|
||||
}
|
||||
if (uprv_isNegativeInfinity(value)) {
|
||||
digits.setInfinite();
|
||||
digits.setNegative();
|
||||
return digits;
|
||||
}
|
||||
if (!fRoundingIncrement.isZero()) {
|
||||
// If we have round increment, use digit list.
|
||||
DigitList digitList;
|
||||
digitList.set(value);
|
||||
return initVisibleDigits(digitList, digits, status);
|
||||
}
|
||||
// Try to find n such that value * 10^n is an integer
|
||||
int32_t n = -1;
|
||||
double scaled;
|
||||
for (int32_t i = 0; i < UPRV_LENGTHOF(gPower10); ++i) {
|
||||
scaled = value * gPower10[i];
|
||||
if (scaled > MAX_INT64_IN_DOUBLE || scaled < -MAX_INT64_IN_DOUBLE) {
|
||||
break;
|
||||
}
|
||||
if (scaled == floor(scaled)) {
|
||||
n = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Try fast path
|
||||
if (n >= 0 && initVisibleDigits(scaled, -n, digits, status)) {
|
||||
digits.fAbsDoubleValue = fabs(value);
|
||||
digits.fAbsDoubleValueSet = U_SUCCESS(status) && !digits.isOverMaxDigits();
|
||||
// Adjust for negative 0 becuase when we cast to an int64,
|
||||
// negative 0 becomes positive 0.
|
||||
if (scaled == 0.0 && signbit(scaled)) {
|
||||
digits.setNegative();
|
||||
}
|
||||
return digits;
|
||||
}
|
||||
|
||||
// Oops have to use digit list
|
||||
DigitList digitList;
|
||||
digitList.set(value);
|
||||
return initVisibleDigits(digitList, digits, status);
|
||||
}
|
||||
|
||||
UBool
|
||||
FixedPrecision::initVisibleDigits(
|
||||
int64_t mantissa,
|
||||
int32_t exponent,
|
||||
VisibleDigits &digits,
|
||||
UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return TRUE;
|
||||
}
|
||||
digits.clear();
|
||||
|
||||
// Precompute fAbsIntValue if it is small enough, but we don't know yet
|
||||
// if it will be valid.
|
||||
UBool absIntValueComputed = FALSE;
|
||||
if (mantissa > -1000000000000000000LL /* -1e18 */
|
||||
&& mantissa < 1000000000000000000LL /* 1e18 */) {
|
||||
digits.fAbsIntValue = mantissa;
|
||||
if (digits.fAbsIntValue < 0) {
|
||||
digits.fAbsIntValue = -digits.fAbsIntValue;
|
||||
}
|
||||
int32_t i = 0;
|
||||
int32_t maxPower10Exp = UPRV_LENGTHOF(gPower10) - 1;
|
||||
for (; i > exponent + maxPower10Exp; i -= maxPower10Exp) {
|
||||
digits.fAbsIntValue /= gPower10[maxPower10Exp];
|
||||
}
|
||||
digits.fAbsIntValue /= gPower10[i - exponent];
|
||||
absIntValueComputed = TRUE;
|
||||
}
|
||||
if (mantissa == 0) {
|
||||
getIntervalForZero(digits.fInterval);
|
||||
digits.fAbsIntValueSet = absIntValueComputed;
|
||||
return TRUE;
|
||||
}
|
||||
// be sure least significant digit is non zero
|
||||
while (mantissa % 10 == 0) {
|
||||
mantissa /= 10;
|
||||
++exponent;
|
||||
}
|
||||
if (mantissa < 0) {
|
||||
digits.fDigits.append((char) -(mantissa % -10), status);
|
||||
mantissa /= -10;
|
||||
digits.setNegative();
|
||||
}
|
||||
while (mantissa) {
|
||||
digits.fDigits.append((char) (mantissa % 10), status);
|
||||
mantissa /= 10;
|
||||
}
|
||||
if (U_FAILURE(status)) {
|
||||
return TRUE;
|
||||
}
|
||||
digits.fExponent = exponent;
|
||||
int32_t upperExponent = exponent + digits.fDigits.length();
|
||||
if (fFailIfOverMax && upperExponent > fMax.getIntDigitCount()) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return TRUE;
|
||||
}
|
||||
UBool roundingRequired =
|
||||
isRoundingRequired(upperExponent, exponent);
|
||||
if (roundingRequired) {
|
||||
if (fExactOnly) {
|
||||
status = U_FORMAT_INEXACT_ERROR;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
digits.fInterval.setLeastSignificantInclusive(exponent);
|
||||
digits.fInterval.setMostSignificantExclusive(upperExponent);
|
||||
getInterval(upperExponent, digits.fInterval);
|
||||
|
||||
// The intValue we computed above is only valid if our visible digits
|
||||
// doesn't exceed the maximum integer digits allowed.
|
||||
digits.fAbsIntValueSet = absIntValueComputed && !digits.isOverMaxDigits();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
VisibleDigitsWithExponent &
|
||||
FixedPrecision::initVisibleDigitsWithExponent(
|
||||
DigitList &value,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const {
|
||||
digits.clear();
|
||||
initVisibleDigits(value, digits.fMantissa, status);
|
||||
return digits;
|
||||
}
|
||||
|
||||
VisibleDigitsWithExponent &
|
||||
FixedPrecision::initVisibleDigitsWithExponent(
|
||||
double value,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const {
|
||||
digits.clear();
|
||||
initVisibleDigits(value, digits.fMantissa, status);
|
||||
return digits;
|
||||
}
|
||||
|
||||
VisibleDigitsWithExponent &
|
||||
FixedPrecision::initVisibleDigitsWithExponent(
|
||||
int64_t value,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const {
|
||||
digits.clear();
|
||||
initVisibleDigits(value, digits.fMantissa, status);
|
||||
return digits;
|
||||
}
|
||||
|
||||
ScientificPrecision::ScientificPrecision() : fMinExponentDigits(1) {
|
||||
}
|
||||
|
||||
DigitList &
|
||||
ScientificPrecision::round(DigitList &value, UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return value;
|
||||
}
|
||||
int32_t exponent = value.getScientificExponent(
|
||||
fMantissa.fMin.getIntDigitCount(), getMultiplier());
|
||||
return fMantissa.round(value, exponent, status);
|
||||
}
|
||||
|
||||
int32_t
|
||||
ScientificPrecision::toScientific(DigitList &value) const {
|
||||
return value.toScientific(
|
||||
fMantissa.fMin.getIntDigitCount(), getMultiplier());
|
||||
}
|
||||
|
||||
int32_t
|
||||
ScientificPrecision::getMultiplier() const {
|
||||
int32_t maxIntDigitCount = fMantissa.fMax.getIntDigitCount();
|
||||
if (maxIntDigitCount == INT32_MAX) {
|
||||
return 1;
|
||||
}
|
||||
int32_t multiplier =
|
||||
maxIntDigitCount - fMantissa.fMin.getIntDigitCount() + 1;
|
||||
return (multiplier < 1 ? 1 : multiplier);
|
||||
}
|
||||
|
||||
VisibleDigitsWithExponent &
|
||||
ScientificPrecision::initVisibleDigitsWithExponent(
|
||||
DigitList &value,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return digits;
|
||||
}
|
||||
digits.clear();
|
||||
if (FixedPrecision::handleNonNumeric(value, digits.fMantissa)) {
|
||||
return digits;
|
||||
}
|
||||
value.setRoundingMode(fMantissa.fRoundingMode);
|
||||
int64_t exponent = toScientific(round(value, status));
|
||||
fMantissa.initVisibleDigits(value, digits.fMantissa, status);
|
||||
FixedPrecision exponentPrecision;
|
||||
exponentPrecision.fMin.setIntDigitCount(fMinExponentDigits);
|
||||
exponentPrecision.initVisibleDigits(exponent, digits.fExponent, status);
|
||||
digits.fHasExponent = TRUE;
|
||||
return digits;
|
||||
}
|
||||
|
||||
VisibleDigitsWithExponent &
|
||||
ScientificPrecision::initVisibleDigitsWithExponent(
|
||||
double value,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return digits;
|
||||
}
|
||||
DigitList digitList;
|
||||
digitList.set(value);
|
||||
return initVisibleDigitsWithExponent(digitList, digits, status);
|
||||
}
|
||||
|
||||
VisibleDigitsWithExponent &
|
||||
ScientificPrecision::initVisibleDigitsWithExponent(
|
||||
int64_t value,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return digits;
|
||||
}
|
||||
DigitList digitList;
|
||||
digitList.set(value);
|
||||
return initVisibleDigitsWithExponent(digitList, digits, status);
|
||||
}
|
||||
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
319
icu4c/source/i18n/precision.h
Normal file
319
icu4c/source/i18n/precision.h
Normal file
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
* precision.h
|
||||
*
|
||||
* created on: 2015jan06
|
||||
* created by: Travis Keep
|
||||
*/
|
||||
|
||||
#ifndef __PRECISION_H__
|
||||
#define __PRECISION_H__
|
||||
|
||||
#include "unicode/uobject.h"
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#include "digitinterval.h"
|
||||
#include "digitlst.h"
|
||||
#include "significantdigitinterval.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
class VisibleDigits;
|
||||
class VisibleDigitsWithExponent;
|
||||
|
||||
|
||||
/**
|
||||
* A precision manager for values to be formatted as fixed point.
|
||||
* Handles rounding of number to prepare it for formatting.
|
||||
*/
|
||||
class U_I18N_API FixedPrecision : public UMemory {
|
||||
public:
|
||||
|
||||
/**
|
||||
* The smallest format interval allowed. Default is 1 integer digit and no
|
||||
* fraction digits.
|
||||
*/
|
||||
DigitInterval fMin;
|
||||
|
||||
/**
|
||||
* The largest format interval allowed. Must contain fMin.
|
||||
* Default is all digits.
|
||||
*/
|
||||
DigitInterval fMax;
|
||||
|
||||
/**
|
||||
* Min and max significant digits allowed. The default is no constraints.
|
||||
*/
|
||||
SignificantDigitInterval fSignificant;
|
||||
|
||||
/**
|
||||
* The rounding increment or zero if there is no rounding increment.
|
||||
* Default is zero.
|
||||
*/
|
||||
DigitList fRoundingIncrement;
|
||||
|
||||
/**
|
||||
* If set, causes round() to set status to U_FORMAT_INEXACT_ERROR if
|
||||
* any rounding is done. Default is FALSE.
|
||||
*/
|
||||
UBool fExactOnly;
|
||||
|
||||
/**
|
||||
* If set, causes round() to set status to U_ILLEGAL_ARGUMENT_ERROR if
|
||||
* rounded number has more than maximum integer digits. Default is FALSE.
|
||||
*/
|
||||
UBool fFailIfOverMax;
|
||||
|
||||
/**
|
||||
* Controls the rounding mode that initVisibleDigits uses.
|
||||
* Default is DecimalFormat::kRoundHalfEven
|
||||
*/
|
||||
DecimalFormat::ERoundingMode fRoundingMode;
|
||||
|
||||
FixedPrecision();
|
||||
|
||||
/**
|
||||
* Returns TRUE if this object equals rhs.
|
||||
*/
|
||||
UBool equals(const FixedPrecision &rhs) const {
|
||||
return (fMin.equals(rhs.fMin) &&
|
||||
fMax.equals(rhs.fMax) &&
|
||||
fSignificant.equals(rhs.fSignificant) &&
|
||||
(fRoundingIncrement == rhs.fRoundingIncrement) &&
|
||||
fExactOnly == rhs.fExactOnly &&
|
||||
fFailIfOverMax == rhs.fFailIfOverMax &&
|
||||
fRoundingMode == rhs.fRoundingMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds value in place to prepare it for formatting.
|
||||
* @param value The value to be rounded. It is rounded in place.
|
||||
* @param exponent Always pass 0 for fixed decimal formatting. scientific
|
||||
* precision passes the exponent value. Essentially, it divides value by
|
||||
* 10^exponent, rounds and then multiplies by 10^exponent.
|
||||
* @param status error returned here.
|
||||
* @return reference to value.
|
||||
*/
|
||||
DigitList &round(DigitList &value, int32_t exponent, UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Returns the interval to use to format the rounded value.
|
||||
* @param roundedValue the already rounded value to format.
|
||||
* @param interval modified in place to be the interval to use to format
|
||||
* the rounded value.
|
||||
* @return a reference to interval.
|
||||
*/
|
||||
DigitInterval &getInterval(
|
||||
const DigitList &roundedValue, DigitInterval &interval) const;
|
||||
|
||||
/**
|
||||
* Returns TRUE if this instance allows for fast formatting of integers.
|
||||
*/
|
||||
UBool isFastFormattable() const;
|
||||
|
||||
/**
|
||||
* Initializes a VisibleDigits.
|
||||
* @param value value for VisibleDigits
|
||||
* Caller must not assume that the value of this parameter will remain
|
||||
* unchanged.
|
||||
* @param digits This is the value that is initialized.
|
||||
* @param status any error returned here.
|
||||
* @return digits
|
||||
*/
|
||||
VisibleDigits &initVisibleDigits(
|
||||
DigitList &value,
|
||||
VisibleDigits &digits,
|
||||
UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Initializes a VisibleDigits.
|
||||
* @param value value for VisibleDigits
|
||||
* @param digits This is the value that is initialized.
|
||||
* @param status any error returned here.
|
||||
* @return digits
|
||||
*/
|
||||
VisibleDigits &initVisibleDigits(
|
||||
double value,
|
||||
VisibleDigits &digits,
|
||||
UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Initializes a VisibleDigits.
|
||||
* @param value value for VisibleDigits
|
||||
* @param digits This is the value that is initialized.
|
||||
* @param status any error returned here.
|
||||
* @return digits
|
||||
*/
|
||||
VisibleDigits &initVisibleDigits(
|
||||
int64_t value,
|
||||
VisibleDigits &digits,
|
||||
UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Initializes a VisibleDigitsWithExponent.
|
||||
* @param value value for VisibleDigits
|
||||
* Caller must not assume that the value of this parameter will remain
|
||||
* unchanged.
|
||||
* @param digits This is the value that is initialized.
|
||||
* @param status any error returned here.
|
||||
* @return digits
|
||||
*/
|
||||
VisibleDigitsWithExponent &initVisibleDigitsWithExponent(
|
||||
DigitList &value,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Initializes a VisibleDigitsWithExponent.
|
||||
* @param value value for VisibleDigits
|
||||
* @param digits This is the value that is initialized.
|
||||
* @param status any error returned here.
|
||||
* @return digits
|
||||
*/
|
||||
VisibleDigitsWithExponent &initVisibleDigitsWithExponent(
|
||||
double value,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Initializes a VisibleDigitsWithExponent.
|
||||
* @param value value for VisibleDigits
|
||||
* @param digits This is the value that is initialized.
|
||||
* @param status any error returned here.
|
||||
* @return digits
|
||||
*/
|
||||
VisibleDigitsWithExponent &initVisibleDigitsWithExponent(
|
||||
int64_t value,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Attempts to initialize 'digits' using simple mod 10 arithmetic.
|
||||
* Returns FALSE if this is not possible such as when rounding
|
||||
* would change the value. Otherwise returns TRUE.
|
||||
*
|
||||
* If the method returns FALSE, caller should create a DigitList
|
||||
* and use it to initialize 'digits'. If this method returns TRUE,
|
||||
* caller should accept the value stored in 'digits'. If this
|
||||
* method returns TRUE along with a non zero error, caller must accept
|
||||
* the error and not try again with a DigitList.
|
||||
*
|
||||
* Before calling this method, caller must verify that this object
|
||||
* has no rounding increment set.
|
||||
*
|
||||
* The value that 'digits' is initialized to is mantissa * 10^exponent.
|
||||
* For example mantissa = 54700 and exponent = -3 means 54.7. The
|
||||
* properties of this object (such as min and max fraction digits),
|
||||
* not the number of trailing zeros in the mantissa, determine whether or
|
||||
* not the result contains any trailing 0's after the decimal point.
|
||||
*
|
||||
* @param mantissa the digits. May be positive or negative. May contain
|
||||
* trailing zeros.
|
||||
* @param exponent must always be zero or negative. An exponent > 0
|
||||
* yields undefined results!
|
||||
* @param digits result stored here.
|
||||
* @param status any error returned here.
|
||||
*/
|
||||
UBool
|
||||
initVisibleDigits(
|
||||
int64_t mantissa,
|
||||
int32_t exponent,
|
||||
VisibleDigits &digits,
|
||||
UErrorCode &status) const;
|
||||
UBool isRoundingRequired(
|
||||
int32_t upperExponent, int32_t lowerExponent) const;
|
||||
DigitInterval &getIntervalForZero(DigitInterval &interval) const;
|
||||
DigitInterval &getInterval(
|
||||
int32_t upperExponent, DigitInterval &interval) const;
|
||||
static UBool handleNonNumeric(DigitList &value, VisibleDigits &digits);
|
||||
|
||||
friend class ScientificPrecision;
|
||||
};
|
||||
|
||||
/**
|
||||
* A precision manager for values to be expressed as scientific notation.
|
||||
*/
|
||||
class U_I18N_API ScientificPrecision : public UMemory {
|
||||
public:
|
||||
FixedPrecision fMantissa;
|
||||
int32_t fMinExponentDigits;
|
||||
|
||||
ScientificPrecision();
|
||||
|
||||
/**
|
||||
* rounds value in place to prepare it for formatting.
|
||||
* @param value The value to be rounded. It is rounded in place.
|
||||
* @param status error returned here.
|
||||
* @return reference to value.
|
||||
*/
|
||||
DigitList &round(DigitList &value, UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Converts value to a mantissa and exponent.
|
||||
*
|
||||
* @param value modified in place to be the mantissa. Depending on
|
||||
* the precision settings, the resulting mantissa may not fall
|
||||
* between 1.0 and 10.0.
|
||||
* @return the exponent of value.
|
||||
*/
|
||||
int32_t toScientific(DigitList &value) const;
|
||||
|
||||
/**
|
||||
* Returns TRUE if this object equals rhs.
|
||||
*/
|
||||
UBool equals(const ScientificPrecision &rhs) const {
|
||||
return fMantissa.equals(rhs.fMantissa) && fMinExponentDigits == rhs.fMinExponentDigits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a VisibleDigitsWithExponent.
|
||||
* @param value the value
|
||||
* Caller must not assume that the value of this parameter will remain
|
||||
* unchanged.
|
||||
* @param digits This is the value that is initialized.
|
||||
* @param status any error returned here.
|
||||
* @return digits
|
||||
*/
|
||||
VisibleDigitsWithExponent &initVisibleDigitsWithExponent(
|
||||
DigitList &value,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Initializes a VisibleDigitsWithExponent.
|
||||
* @param value the value
|
||||
* @param digits This is the value that is initialized.
|
||||
* @param status any error returned here.
|
||||
* @return digits
|
||||
*/
|
||||
VisibleDigitsWithExponent &initVisibleDigitsWithExponent(
|
||||
double value,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Initializes a VisibleDigitsWithExponent.
|
||||
* @param value the value
|
||||
* @param digits This is the value that is initialized.
|
||||
* @param status any error returned here.
|
||||
* @return digits
|
||||
*/
|
||||
VisibleDigitsWithExponent &initVisibleDigitsWithExponent(
|
||||
int64_t value,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const;
|
||||
|
||||
private:
|
||||
int32_t getMultiplier() const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif // __PRECISION_H__
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
******************************************************************************
|
||||
* Copyright (C) 2014, International Business Machines
|
||||
* Copyright (C) 2014-2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
******************************************************************************
|
||||
* quantityformatter.cpp
|
||||
|
@ -11,11 +11,11 @@
|
|||
#include "unicode/unistr.h"
|
||||
#include "unicode/decimfmt.h"
|
||||
#include "cstring.h"
|
||||
#include "plurrule_impl.h"
|
||||
#include "unicode/plurrule.h"
|
||||
#include "charstr.h"
|
||||
#include "unicode/fmtable.h"
|
||||
#include "unicode/fieldpos.h"
|
||||
#include "visibledigits.h"
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
|
@ -136,13 +136,14 @@ UnicodeString &QuantityFormatter::format(
|
|||
return appendTo;
|
||||
}
|
||||
UnicodeString count;
|
||||
VisibleDigitsWithExponent digits;
|
||||
const DecimalFormat *decFmt = dynamic_cast<const DecimalFormat *>(&fmt);
|
||||
if (decFmt != NULL) {
|
||||
FixedDecimal fd = decFmt->getFixedDecimal(quantity, status);
|
||||
decFmt->initVisibleDigitsWithExponent(quantity, digits, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
count = rules.select(fd);
|
||||
count = rules.select(digits);
|
||||
} else {
|
||||
if (quantity.getType() == Formattable::kDouble) {
|
||||
count = rules.select(quantity.getDouble());
|
||||
|
@ -167,7 +168,11 @@ UnicodeString &QuantityFormatter::format(
|
|||
}
|
||||
UnicodeString formattedNumber;
|
||||
FieldPosition fpos(pos.getField());
|
||||
fmt.format(quantity, formattedNumber, fpos, status);
|
||||
if (decFmt != NULL) {
|
||||
decFmt->format(digits, formattedNumber, fpos, status);
|
||||
} else {
|
||||
fmt.format(quantity, formattedNumber, fpos, status);
|
||||
}
|
||||
const UnicodeString *params[1] = {&formattedNumber};
|
||||
int32_t offsets[1];
|
||||
pattern->formatAndAppend(
|
||||
|
|
90
icu4c/source/i18n/significantdigitinterval.h
Normal file
90
icu4c/source/i18n/significantdigitinterval.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
* significantdigitinterval.h
|
||||
*
|
||||
* created on: 2015jan6
|
||||
* created by: Travis Keep
|
||||
*/
|
||||
|
||||
#ifndef __SIGNIFICANTDIGITINTERVAL_H__
|
||||
#define __SIGNIFICANTDIGITINTERVAL_H__
|
||||
|
||||
#include "unicode/uobject.h"
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
/**
|
||||
* An interval of allowed significant digit counts.
|
||||
*/
|
||||
class U_I18N_API SignificantDigitInterval : public UMemory {
|
||||
public:
|
||||
|
||||
/**
|
||||
* No limits on significant digits.
|
||||
*/
|
||||
SignificantDigitInterval()
|
||||
: fMax(INT32_MAX), fMin(0) { }
|
||||
|
||||
/**
|
||||
* Make this instance have no limit on significant digits.
|
||||
*/
|
||||
void clear() {
|
||||
fMin = 0;
|
||||
fMax = INT32_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if this object is equal to rhs.
|
||||
*/
|
||||
UBool equals(const SignificantDigitInterval &rhs) const {
|
||||
return ((fMax == rhs.fMax) && (fMin == rhs.fMin));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets maximum significant digits. 0 or negative means no maximum.
|
||||
*/
|
||||
void setMax(int32_t count) {
|
||||
fMax = count <= 0 ? INT32_MAX : count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get maximum significant digits. INT32_MAX means no maximum.
|
||||
*/
|
||||
int32_t getMax() const {
|
||||
return fMax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets minimum significant digits. 0 or negative means no minimum.
|
||||
*/
|
||||
void setMin(int32_t count) {
|
||||
fMin = count <= 0 ? 0 : count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get maximum significant digits. 0 means no minimum.
|
||||
*/
|
||||
int32_t getMin() const {
|
||||
return fMin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if this instance represents no constraints on significant
|
||||
* digits.
|
||||
*/
|
||||
UBool isNoConstraints() const {
|
||||
return fMin == 0 && fMax == INT32_MAX;
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t fMax;
|
||||
int32_t fMin;
|
||||
};
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif // __SIGNIFICANTDIGITINTERVAL_H__
|
2621
icu4c/source/i18n/smallintformatter.cpp
Normal file
2621
icu4c/source/i18n/smallintformatter.cpp
Normal file
File diff suppressed because it is too large
Load diff
88
icu4c/source/i18n/smallintformatter.h
Normal file
88
icu4c/source/i18n/smallintformatter.h
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
* smallintformatter.h
|
||||
*
|
||||
* created on: 2015jan06
|
||||
* created by: Travis Keep
|
||||
*/
|
||||
|
||||
#ifndef __SMALLINTFORMATTER_H__
|
||||
#define __SMALLINTFORMATTER_H__
|
||||
|
||||
#include "unicode/uobject.h"
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
class UnicodeString;
|
||||
|
||||
/**
|
||||
* A representation an acceptable range of digit counts for integers.
|
||||
*/
|
||||
class U_I18N_API IntDigitCountRange : public UMemory {
|
||||
public:
|
||||
/**
|
||||
* No constraints: 0 up to INT32_MAX
|
||||
*/
|
||||
IntDigitCountRange() : fMin(0), fMax(INT32_MAX) { }
|
||||
IntDigitCountRange(int32_t min, int32_t max);
|
||||
int32_t pin(int32_t digitCount) const;
|
||||
int32_t getMax() const { return fMax; }
|
||||
int32_t getMin() const { return fMin; }
|
||||
private:
|
||||
int32_t fMin;
|
||||
int32_t fMax;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A formatter for small, positive integers.
|
||||
*/
|
||||
class U_I18N_API SmallIntFormatter : public UMemory {
|
||||
public:
|
||||
/**
|
||||
* Estimates the actual digit count needed to format positiveValue
|
||||
* using the given range of digit counts.
|
||||
* Returns a value that is at least the actual digit count needed.
|
||||
*
|
||||
* @param positiveValue the value to format
|
||||
* @param range the acceptable range of digit counts.
|
||||
*/
|
||||
static int32_t estimateDigitCount(
|
||||
int32_t positiveValue, const IntDigitCountRange &range);
|
||||
|
||||
/**
|
||||
* Returns TRUE if this class can format positiveValue using
|
||||
* the given range of digit counts.
|
||||
*
|
||||
* @param positiveValue the value to format
|
||||
* @param range the acceptable range of digit counts.
|
||||
*/
|
||||
static UBool canFormat(
|
||||
int32_t positiveValue, const IntDigitCountRange &range);
|
||||
|
||||
/**
|
||||
* Formats positiveValue using the given range of digit counts.
|
||||
* Always uses standard digits '0' through '9'. Formatted value is
|
||||
* left padded with '0' as necessary to achieve minimum digit count.
|
||||
* Does not produce any grouping separators or trailing decimal point.
|
||||
* Calling format to format a value with a particular digit count range
|
||||
* when canFormat indicates that the same value and digit count range
|
||||
* cannot be formatted results in undefined behavior.
|
||||
*
|
||||
* @param positiveValue the value to format
|
||||
* @param range the acceptable range of digit counts.
|
||||
*/
|
||||
static UnicodeString &format(
|
||||
int32_t positiveValue,
|
||||
const IntDigitCountRange &range,
|
||||
UnicodeString &appendTo);
|
||||
|
||||
};
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif // __SMALLINTFORMATTER_H__
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2009-2011, International Business Machines Corporation and *
|
||||
* Copyright (C) 2009-2015, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
@ -191,6 +191,7 @@ public:
|
|||
|
||||
private:
|
||||
friend class DecimalFormat;
|
||||
friend class DecimalFormatImpl;
|
||||
|
||||
void initialize(const Locale& loc, UErrorCode& status);
|
||||
|
||||
|
|
|
@ -356,6 +356,21 @@ private:
|
|||
void setCurrencyForSymbols();
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @internal For ICU use only
|
||||
*/
|
||||
inline UBool isCustomCurrencySymbol() const {
|
||||
return fIsCustomCurrencySymbol;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal For ICU use only
|
||||
*/
|
||||
inline UBool isCustomIntlCurrencySymbol() const {
|
||||
return fIsCustomIntlCurrencySymbol;
|
||||
}
|
||||
|
||||
/**
|
||||
* _Internal_ function - more efficient version of getSymbol,
|
||||
* returning a const reference to one of the symbol strings.
|
||||
|
@ -410,6 +425,8 @@ private:
|
|||
|
||||
UnicodeString currencySpcBeforeSym[UNUM_CURRENCY_SPACING_COUNT];
|
||||
UnicodeString currencySpcAfterSym[UNUM_CURRENCY_SPACING_COUNT];
|
||||
UBool fIsCustomCurrencySymbol;
|
||||
UBool fIsCustomIntlCurrencySymbol;
|
||||
};
|
||||
|
||||
// -------------------------------------
|
||||
|
@ -445,6 +462,12 @@ DecimalFormatSymbols::getConstSymbol(ENumberFormatSymbol symbol) const {
|
|||
|
||||
inline void
|
||||
DecimalFormatSymbols::setSymbol(ENumberFormatSymbol symbol, const UnicodeString &value, const UBool propogateDigits = TRUE) {
|
||||
if (symbol == kCurrencySymbol) {
|
||||
fIsCustomCurrencySymbol = TRUE;
|
||||
}
|
||||
else if (symbol == kIntlCurrencySymbol) {
|
||||
fIsCustomIntlCurrencySymbol = TRUE;
|
||||
}
|
||||
if(symbol<kFormatSymbolCount) {
|
||||
fSymbols[symbol]=value;
|
||||
}
|
||||
|
|
|
@ -59,6 +59,9 @@ class UnicodeSet;
|
|||
class FieldPositionHandler;
|
||||
class DecimalFormatStaticSets;
|
||||
class FixedDecimal;
|
||||
class DecimalFormatImpl;
|
||||
class PluralRules;
|
||||
class VisibleDigitsWithExponent;
|
||||
|
||||
// explicit template instantiation. see digitlst.h
|
||||
#if defined (_MSC_VER)
|
||||
|
@ -1111,6 +1114,40 @@ public:
|
|||
FieldPositionIterator* posIter,
|
||||
UErrorCode& status) const;
|
||||
|
||||
/**
|
||||
* Format a decimal number.
|
||||
* @param number The number
|
||||
* @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 VisibleDigitsWithExponent &number,
|
||||
UnicodeString& appendTo,
|
||||
FieldPosition& pos,
|
||||
UErrorCode& status) const;
|
||||
|
||||
/**
|
||||
* Format a decimal number.
|
||||
* @param number The number
|
||||
* @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 VisibleDigitsWithExponent &number,
|
||||
UnicodeString& appendTo,
|
||||
FieldPositionIterator* posIter,
|
||||
UErrorCode& status) const;
|
||||
|
||||
/**
|
||||
* Format a decimal number.
|
||||
* The number is a DigitList wrapper onto a floating point decimal number.
|
||||
|
@ -1944,6 +1981,41 @@ public:
|
|||
* @internal
|
||||
*/
|
||||
FixedDecimal getFixedDecimal(DigitList &number, UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Get a VisibleDigitsWithExponent corresponding to a double
|
||||
* as it would be formatted by this DecimalFormat.
|
||||
* Internal, not intended for public use.
|
||||
* @internal
|
||||
*/
|
||||
VisibleDigitsWithExponent &initVisibleDigitsWithExponent(
|
||||
double number,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Get a VisibleDigitsWithExponent corresponding to a formattable
|
||||
* as it would be formatted by this DecimalFormat.
|
||||
* Internal, not intended for public use.
|
||||
* @internal
|
||||
*/
|
||||
VisibleDigitsWithExponent &initVisibleDigitsWithExponent(
|
||||
const Formattable &number,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Get a VisibleDigitsWithExponent corresponding to a DigitList
|
||||
* as it would be formatted by this DecimalFormat.
|
||||
* Note: the DigitList may be modified.
|
||||
* Internal, not intended for public use.
|
||||
* @internal
|
||||
*/
|
||||
VisibleDigitsWithExponent &initVisibleDigitsWithExponent(
|
||||
DigitList &number,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const;
|
||||
|
||||
#endif /* U_HIDE_INTERNAL_API */
|
||||
|
||||
public:
|
||||
|
@ -1978,8 +2050,6 @@ private:
|
|||
|
||||
DecimalFormat(); // default constructor not implemented
|
||||
|
||||
int32_t precision() const;
|
||||
|
||||
/**
|
||||
* Initialize all fields of a new DecimalFormatter to a safe default value.
|
||||
* Common code for use by constructors.
|
||||
|
@ -1995,72 +2065,6 @@ private:
|
|||
DecimalFormatSymbols* symbolsToAdopt = 0
|
||||
);
|
||||
|
||||
/**
|
||||
* Does the real work of generating a pattern.
|
||||
*
|
||||
* @param result Output param which will receive the pattern.
|
||||
* Previous contents are deleted.
|
||||
* @param localized TRUE return localized pattern.
|
||||
* @return A reference to 'result'.
|
||||
*/
|
||||
UnicodeString& toPattern(UnicodeString& result, UBool localized) const;
|
||||
|
||||
/**
|
||||
* Does the real work of applying a pattern.
|
||||
* @param pattern The pattern to be applied.
|
||||
* @param localized If true, the pattern is localized; else false.
|
||||
* @param parseError Struct to recieve information on position
|
||||
* of error if an error is encountered
|
||||
* @param status Output param set to success/failure code on
|
||||
* exit. If the pattern is invalid, this will be
|
||||
* set to a failure result.
|
||||
*/
|
||||
void applyPattern(const UnicodeString& pattern,
|
||||
UBool localized,
|
||||
UParseError& parseError,
|
||||
UErrorCode& status);
|
||||
|
||||
/*
|
||||
* similar to applyPattern, but without re-gen affix for currency
|
||||
*/
|
||||
void applyPatternInternally(const UnicodeString& pluralCount,
|
||||
const UnicodeString& pattern,
|
||||
UBool localized,
|
||||
UParseError& parseError,
|
||||
UErrorCode& status);
|
||||
|
||||
/*
|
||||
* only apply pattern without expand affixes
|
||||
*/
|
||||
void applyPatternWithoutExpandAffix(const UnicodeString& pattern,
|
||||
UBool localized,
|
||||
UParseError& parseError,
|
||||
UErrorCode& status);
|
||||
|
||||
|
||||
/*
|
||||
* expand affixes (after apply patter) and re-compute fFormatWidth
|
||||
*/
|
||||
void expandAffixAdjustWidth(const UnicodeString* pluralCount);
|
||||
|
||||
|
||||
/**
|
||||
* Do the work of formatting a number, either a double or a long.
|
||||
*
|
||||
* @param appendTo Output parameter to receive result.
|
||||
* Result is appended to existing contents.
|
||||
* @param handler Records information about field positions.
|
||||
* @param digits the digits to be formatted.
|
||||
* @param isInteger if TRUE format the digits as Integer.
|
||||
* @return Reference to 'appendTo' parameter.
|
||||
*/
|
||||
UnicodeString& subformat(UnicodeString& appendTo,
|
||||
FieldPositionHandler& handler,
|
||||
DigitList& digits,
|
||||
UBool isInteger,
|
||||
UErrorCode &status) const;
|
||||
|
||||
|
||||
void parse(const UnicodeString& text,
|
||||
Formattable& result,
|
||||
ParsePosition& pos,
|
||||
|
@ -2144,69 +2148,12 @@ private:
|
|||
UChar32 decimalChar, const UnicodeSet *decimalSet,
|
||||
UChar32 schar);
|
||||
|
||||
/**
|
||||
* Get a decimal format symbol.
|
||||
* Returns a const reference to the symbol string.
|
||||
* @internal
|
||||
*/
|
||||
inline const UnicodeString &getConstSymbol(DecimalFormatSymbols::ENumberFormatSymbol symbol) const;
|
||||
|
||||
int32_t appendAffix(UnicodeString& buf,
|
||||
double number,
|
||||
FieldPositionHandler& handler,
|
||||
UBool isNegative,
|
||||
UBool isPrefix) const;
|
||||
|
||||
/**
|
||||
* Append an affix to the given UnicodeString, using quotes if
|
||||
* there are special characters. Single quotes themselves must be
|
||||
* escaped in either case.
|
||||
*/
|
||||
void appendAffixPattern(UnicodeString& appendTo, const UnicodeString& affix,
|
||||
UBool localized) const;
|
||||
|
||||
void appendAffixPattern(UnicodeString& appendTo,
|
||||
const UnicodeString* affixPattern,
|
||||
const UnicodeString& expAffix, UBool localized) const;
|
||||
|
||||
void expandAffix(const UnicodeString& pattern,
|
||||
UnicodeString& affix,
|
||||
double number,
|
||||
FieldPositionHandler& handler,
|
||||
UBool doFormat,
|
||||
const UnicodeString* pluralCount) const;
|
||||
|
||||
void expandAffixes(const UnicodeString* pluralCount);
|
||||
|
||||
void addPadding(UnicodeString& appendTo,
|
||||
FieldPositionHandler& handler,
|
||||
int32_t prefixLen, int32_t suffixLen) const;
|
||||
|
||||
UBool isGroupingPosition(int32_t pos) const;
|
||||
|
||||
void setCurrencyForSymbols();
|
||||
|
||||
// similar to setCurrency without re-compute the affixes for currency.
|
||||
// If currency changes, the affix pattern for currency is not changed,
|
||||
// but the affix will be changed. So, affixes need to be
|
||||
// re-computed in setCurrency(), but not in setCurrencyInternally().
|
||||
virtual void setCurrencyInternally(const UChar* theCurrency, UErrorCode& ec);
|
||||
|
||||
// set up currency affix patterns for mix parsing.
|
||||
// The patterns saved here are the affix patterns of default currency
|
||||
// pattern and the unique affix patterns of the plural currency patterns.
|
||||
// Those patterns are used by parseForCurrency().
|
||||
void setupCurrencyAffixPatterns(UErrorCode& status);
|
||||
|
||||
// set up the currency affixes used in currency plural formatting.
|
||||
// It sets up both fAffixesForCurrency for currency pattern if the current
|
||||
// pattern contains 3 currency signs,
|
||||
// and it sets up fPluralAffixesForCurrency for currency plural patterns.
|
||||
void setupCurrencyAffixes(const UnicodeString& pattern,
|
||||
UBool setupForCurrentPattern,
|
||||
UBool setupForPluralPattern,
|
||||
UErrorCode& status);
|
||||
|
||||
// get the currency rounding with respect to currency usage
|
||||
double getCurrencyRounding(const UChar* currency,
|
||||
UErrorCode* ec) const;
|
||||
|
@ -2217,159 +2164,30 @@ private:
|
|||
|
||||
// hashtable operations
|
||||
Hashtable* initHashForAffixPattern(UErrorCode& status);
|
||||
Hashtable* initHashForAffix(UErrorCode& status);
|
||||
|
||||
void deleteHashForAffixPattern();
|
||||
void deleteHashForAffix(Hashtable*& table);
|
||||
|
||||
void copyHashForAffixPattern(const Hashtable* source,
|
||||
Hashtable* target, UErrorCode& status);
|
||||
void copyHashForAffix(const Hashtable* source,
|
||||
Hashtable* target, UErrorCode& status);
|
||||
|
||||
UnicodeString& _format(int64_t number,
|
||||
UnicodeString& appendTo,
|
||||
FieldPositionHandler& handler,
|
||||
UErrorCode &status) const;
|
||||
UnicodeString& _format(double number,
|
||||
UnicodeString& appendTo,
|
||||
FieldPositionHandler& handler,
|
||||
UErrorCode &status) const;
|
||||
UnicodeString& _format(const DigitList &number,
|
||||
UnicodeString& appendTo,
|
||||
FieldPositionHandler& handler,
|
||||
UErrorCode &status) const;
|
||||
DecimalFormatImpl *fImpl;
|
||||
|
||||
/**
|
||||
* Constants.
|
||||
*/
|
||||
|
||||
UnicodeString fPositivePrefix;
|
||||
UnicodeString fPositiveSuffix;
|
||||
UnicodeString fNegativePrefix;
|
||||
UnicodeString fNegativeSuffix;
|
||||
UnicodeString* fPosPrefixPattern;
|
||||
UnicodeString* fPosSuffixPattern;
|
||||
UnicodeString* fNegPrefixPattern;
|
||||
UnicodeString* fNegSuffixPattern;
|
||||
|
||||
/**
|
||||
* Formatter for ChoiceFormat-based currency names. If this field
|
||||
* is not null, then delegate to it to format currency symbols.
|
||||
* @since ICU 2.6
|
||||
*/
|
||||
ChoiceFormat* fCurrencyChoice;
|
||||
|
||||
DigitList * fMultiplier; // NULL for multiplier of one
|
||||
int32_t fScale;
|
||||
int32_t fGroupingSize;
|
||||
int32_t fGroupingSize2;
|
||||
UBool fDecimalSeparatorAlwaysShown;
|
||||
DecimalFormatSymbols* fSymbols;
|
||||
|
||||
UBool fUseSignificantDigits;
|
||||
int32_t fMinSignificantDigits;
|
||||
int32_t fMaxSignificantDigits;
|
||||
|
||||
UBool fUseExponentialNotation;
|
||||
int8_t fMinExponentDigits;
|
||||
UBool fExponentSignAlwaysShown;
|
||||
|
||||
EnumSet<UNumberFormatAttribute,
|
||||
UNUM_MAX_NONBOOLEAN_ATTRIBUTE+1,
|
||||
UNUM_LIMIT_BOOLEAN_ATTRIBUTE>
|
||||
fBoolFlags;
|
||||
|
||||
DigitList* fRoundingIncrement; // NULL if no rounding increment specified.
|
||||
ERoundingMode fRoundingMode;
|
||||
|
||||
UChar32 fPad;
|
||||
int32_t fFormatWidth;
|
||||
EPadPosition fPadPosition;
|
||||
|
||||
/*
|
||||
* Following are used for currency format
|
||||
*/
|
||||
// pattern used in this formatter
|
||||
UnicodeString fFormatPattern;
|
||||
// style is only valid when decimal formatter is constructed by
|
||||
// DecimalFormat(pattern, decimalFormatSymbol, style)
|
||||
int fStyle;
|
||||
/*
|
||||
* Represents whether this is a currency format, and which
|
||||
* currency format style.
|
||||
* 0: not currency format type;
|
||||
* 1: currency style -- symbol name, such as "$" for US dollar.
|
||||
* 2: currency style -- ISO name, such as USD for US dollar.
|
||||
* 3: currency style -- plural long name, such as "US Dollar" for
|
||||
* "1.00 US Dollar", or "US Dollars" for
|
||||
* "3.00 US Dollars".
|
||||
*/
|
||||
int fCurrencySignCount;
|
||||
|
||||
|
||||
/* For currency parsing purose,
|
||||
* Need to remember all prefix patterns and suffix patterns of
|
||||
* every currency format pattern,
|
||||
* including the pattern of default currecny style
|
||||
* and plural currency style. And the patterns are set through applyPattern.
|
||||
*/
|
||||
// TODO: innerclass?
|
||||
/* This is not needed in the class declaration, so it is moved into decimfmp.cpp
|
||||
struct AffixPatternsForCurrency : public UMemory {
|
||||
// negative prefix pattern
|
||||
UnicodeString negPrefixPatternForCurrency;
|
||||
// negative suffix pattern
|
||||
UnicodeString negSuffixPatternForCurrency;
|
||||
// positive prefix pattern
|
||||
UnicodeString posPrefixPatternForCurrency;
|
||||
// positive suffix pattern
|
||||
UnicodeString posSuffixPatternForCurrency;
|
||||
int8_t patternType;
|
||||
|
||||
AffixPatternsForCurrency(const UnicodeString& negPrefix,
|
||||
const UnicodeString& negSuffix,
|
||||
const UnicodeString& posPrefix,
|
||||
const UnicodeString& posSuffix,
|
||||
int8_t type) {
|
||||
negPrefixPatternForCurrency = negPrefix;
|
||||
negSuffixPatternForCurrency = negSuffix;
|
||||
posPrefixPatternForCurrency = posPrefix;
|
||||
posSuffixPatternForCurrency = posSuffix;
|
||||
patternType = type;
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
/* affix for currency formatting when the currency sign in the pattern
|
||||
* equals to 3, such as the pattern contains 3 currency sign or
|
||||
* the formatter style is currency plural format style.
|
||||
*/
|
||||
/* This is not needed in the class declaration, so it is moved into decimfmp.cpp
|
||||
struct AffixesForCurrency : public UMemory {
|
||||
// negative prefix
|
||||
UnicodeString negPrefixForCurrency;
|
||||
// negative suffix
|
||||
UnicodeString negSuffixForCurrency;
|
||||
// positive prefix
|
||||
UnicodeString posPrefixForCurrency;
|
||||
// positive suffix
|
||||
UnicodeString posSuffixForCurrency;
|
||||
|
||||
int32_t formatWidth;
|
||||
|
||||
AffixesForCurrency(const UnicodeString& negPrefix,
|
||||
const UnicodeString& negSuffix,
|
||||
const UnicodeString& posPrefix,
|
||||
const UnicodeString& posSuffix) {
|
||||
negPrefixForCurrency = negPrefix;
|
||||
negSuffixForCurrency = negSuffix;
|
||||
posPrefixForCurrency = posPrefix;
|
||||
posSuffixForCurrency = posSuffix;
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
// Affix pattern set for currency.
|
||||
// It is a set of AffixPatternsForCurrency,
|
||||
// each element of the set saves the negative prefix pattern,
|
||||
|
@ -2385,16 +2203,6 @@ private:
|
|||
// count match or not.
|
||||
Hashtable* fAffixPatternsForCurrency;
|
||||
|
||||
// Following 2 are affixes for currency.
|
||||
// It is a hash map from plural count to AffixesForCurrency.
|
||||
// AffixesForCurrency saves the negative prefix,
|
||||
// negative suffix, positive prefix, and positive suffix of a pattern.
|
||||
// It is used during currency formatting only when the currency sign count
|
||||
// is 3. In which case, the affixes are getting from here, not
|
||||
// from the fNegativePrefix etc.
|
||||
Hashtable* fAffixesForCurrency; // for current pattern
|
||||
Hashtable* fPluralAffixesForCurrency; // for plural pattern
|
||||
|
||||
// Information needed for DecimalFormat to format/parse currency plural.
|
||||
CurrencyPluralInfo* fCurrencyPluralInfo;
|
||||
|
||||
|
@ -2405,9 +2213,6 @@ private:
|
|||
// Decimal Format Static Sets singleton.
|
||||
const DecimalFormatStaticSets *fStaticSets;
|
||||
|
||||
// Currency Usage(STANDARD vs CASH)
|
||||
UCurrencyUsage fCurrencyUsage;
|
||||
|
||||
protected:
|
||||
|
||||
#ifndef U_HIDE_INTERNAL_API
|
||||
|
@ -2449,27 +2254,8 @@ protected:
|
|||
*/
|
||||
static const int32_t kMaxScientificIntegerDigits;
|
||||
|
||||
#if UCONFIG_FORMAT_FASTPATHS_49
|
||||
private:
|
||||
/**
|
||||
* Internal state.
|
||||
* @internal
|
||||
*/
|
||||
uint8_t fReserved[UNUM_DECIMALFORMAT_INTERNAL_SIZE];
|
||||
|
||||
|
||||
/**
|
||||
* Called whenever any state changes. Recomputes whether fastpath is OK to use.
|
||||
*/
|
||||
void handleChanged();
|
||||
#endif
|
||||
};
|
||||
|
||||
inline const UnicodeString &
|
||||
DecimalFormat::getConstSymbol(DecimalFormatSymbols::ENumberFormatSymbol symbol) const {
|
||||
return fSymbols->getConstSymbol(symbol);
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
|
|
@ -39,6 +39,7 @@ U_NAMESPACE_BEGIN
|
|||
|
||||
class Hashtable;
|
||||
class FixedDecimal;
|
||||
class VisibleDigitsWithExponent;
|
||||
class RuleChain;
|
||||
class PluralRuleParser;
|
||||
class PluralKeywordEnumeration;
|
||||
|
@ -346,6 +347,7 @@ public:
|
|||
* @internal
|
||||
*/
|
||||
UnicodeString select(const FixedDecimal &number) const;
|
||||
UnicodeString select(const VisibleDigitsWithExponent &number) const;
|
||||
#endif /* U_HIDE_INTERNAL_API */
|
||||
|
||||
/**
|
||||
|
|
219
icu4c/source/i18n/valueformatter.cpp
Normal file
219
icu4c/source/i18n/valueformatter.cpp
Normal file
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2015, International Business Machines Corporation and
|
||||
* others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
#include "unicode/plurrule.h"
|
||||
#include "unicode/unistr.h"
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#include "digitformatter.h"
|
||||
#include "digitgrouping.h"
|
||||
#include "digitinterval.h"
|
||||
#include "digitlst.h"
|
||||
#include "precision.h"
|
||||
#include "plurrule_impl.h"
|
||||
#include "smallintformatter.h"
|
||||
#include "uassert.h"
|
||||
#include "valueformatter.h"
|
||||
#include "visibledigits.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
VisibleDigitsWithExponent &
|
||||
ValueFormatter::toVisibleDigitsWithExponent(
|
||||
int64_t value,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const {
|
||||
switch (fType) {
|
||||
case kFixedDecimal:
|
||||
return fFixedPrecision->initVisibleDigitsWithExponent(
|
||||
value, digits, status);
|
||||
break;
|
||||
case kScientificNotation:
|
||||
return fScientificPrecision->initVisibleDigitsWithExponent(
|
||||
value, digits, status);
|
||||
break;
|
||||
default:
|
||||
U_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
return digits;
|
||||
}
|
||||
|
||||
VisibleDigitsWithExponent &
|
||||
ValueFormatter::toVisibleDigitsWithExponent(
|
||||
DigitList &value,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const {
|
||||
switch (fType) {
|
||||
case kFixedDecimal:
|
||||
return fFixedPrecision->initVisibleDigitsWithExponent(
|
||||
value, digits, status);
|
||||
break;
|
||||
case kScientificNotation:
|
||||
return fScientificPrecision->initVisibleDigitsWithExponent(
|
||||
value, digits, status);
|
||||
break;
|
||||
default:
|
||||
U_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
return digits;
|
||||
}
|
||||
|
||||
static UBool isNoGrouping(
|
||||
const DigitGrouping &grouping,
|
||||
int32_t value,
|
||||
const FixedPrecision &precision) {
|
||||
IntDigitCountRange range(
|
||||
precision.fMin.getIntDigitCount(),
|
||||
precision.fMax.getIntDigitCount());
|
||||
return grouping.isNoGrouping(value, range);
|
||||
}
|
||||
|
||||
UBool
|
||||
ValueFormatter::isFastFormattable(int32_t value) const {
|
||||
switch (fType) {
|
||||
case kFixedDecimal:
|
||||
{
|
||||
if (value == INT32_MIN) {
|
||||
return FALSE;
|
||||
}
|
||||
if (value < 0) {
|
||||
value = -value;
|
||||
}
|
||||
return fFixedPrecision->isFastFormattable() && fFixedOptions->isFastFormattable() && isNoGrouping(*fGrouping, value, *fFixedPrecision);
|
||||
}
|
||||
case kScientificNotation:
|
||||
return FALSE;
|
||||
default:
|
||||
U_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DigitList &
|
||||
ValueFormatter::round(DigitList &value, UErrorCode &status) const {
|
||||
if (value.isNaN() || value.isInfinite()) {
|
||||
return value;
|
||||
}
|
||||
switch (fType) {
|
||||
case kFixedDecimal:
|
||||
return fFixedPrecision->round(value, 0, status);
|
||||
case kScientificNotation:
|
||||
return fScientificPrecision->round(value, status);
|
||||
default:
|
||||
U_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
UnicodeString &
|
||||
ValueFormatter::formatInt32(
|
||||
int32_t value,
|
||||
FieldPositionHandler &handler,
|
||||
UnicodeString &appendTo) const {
|
||||
switch (fType) {
|
||||
case kFixedDecimal:
|
||||
{
|
||||
IntDigitCountRange range(
|
||||
fFixedPrecision->fMin.getIntDigitCount(),
|
||||
fFixedPrecision->fMax.getIntDigitCount());
|
||||
return fDigitFormatter->formatPositiveInt32(
|
||||
value,
|
||||
range,
|
||||
handler,
|
||||
appendTo);
|
||||
}
|
||||
break;
|
||||
case kScientificNotation:
|
||||
default:
|
||||
U_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
UnicodeString &
|
||||
ValueFormatter::format(
|
||||
const VisibleDigitsWithExponent &value,
|
||||
FieldPositionHandler &handler,
|
||||
UnicodeString &appendTo) const {
|
||||
switch (fType) {
|
||||
case kFixedDecimal:
|
||||
return fDigitFormatter->format(
|
||||
value.getMantissa(),
|
||||
*fGrouping,
|
||||
*fFixedOptions,
|
||||
handler,
|
||||
appendTo);
|
||||
break;
|
||||
case kScientificNotation:
|
||||
return fDigitFormatter->format(
|
||||
value,
|
||||
*fScientificOptions,
|
||||
handler,
|
||||
appendTo);
|
||||
break;
|
||||
default:
|
||||
U_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
int32_t
|
||||
ValueFormatter::countChar32(const VisibleDigitsWithExponent &value) const {
|
||||
switch (fType) {
|
||||
case kFixedDecimal:
|
||||
return fDigitFormatter->countChar32(
|
||||
value.getMantissa(),
|
||||
*fGrouping,
|
||||
*fFixedOptions);
|
||||
break;
|
||||
case kScientificNotation:
|
||||
return fDigitFormatter->countChar32(
|
||||
value,
|
||||
*fScientificOptions);
|
||||
break;
|
||||
default:
|
||||
U_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
ValueFormatter::prepareFixedDecimalFormatting(
|
||||
const DigitFormatter &formatter,
|
||||
const DigitGrouping &grouping,
|
||||
const FixedPrecision &precision,
|
||||
const DigitFormatterOptions &options) {
|
||||
fType = kFixedDecimal;
|
||||
fDigitFormatter = &formatter;
|
||||
fGrouping = &grouping;
|
||||
fFixedPrecision = &precision;
|
||||
fFixedOptions = &options;
|
||||
}
|
||||
|
||||
void
|
||||
ValueFormatter::prepareScientificFormatting(
|
||||
const DigitFormatter &formatter,
|
||||
const ScientificPrecision &precision,
|
||||
const SciFormatterOptions &options) {
|
||||
fType = kScientificNotation;
|
||||
fDigitFormatter = &formatter;
|
||||
fScientificPrecision = &precision;
|
||||
fScientificOptions = &options;
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif /* !UCONFIG_NO_FORMATTING */
|
157
icu4c/source/i18n/valueformatter.h
Normal file
157
icu4c/source/i18n/valueformatter.h
Normal file
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2015, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef VALUEFORMATTER_H
|
||||
#define VALUEFORMATTER_H
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#include "unicode/uobject.h"
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
class UnicodeString;
|
||||
class DigitList;
|
||||
class FieldPositionHandler;
|
||||
class DigitGrouping;
|
||||
class PluralRules;
|
||||
class FixedPrecision;
|
||||
class DigitFormatter;
|
||||
class DigitFormatterOptions;
|
||||
class ScientificPrecision;
|
||||
class SciFormatterOptions;
|
||||
class FixedDecimal;
|
||||
class VisibleDigitsWithExponent;
|
||||
|
||||
|
||||
/**
|
||||
* A closure around rounding and formatting a value. As these instances are
|
||||
* designed to be short lived (they only exist while formatting a value), they
|
||||
* do not own their own attributes. Rather the caller maintains ownership of
|
||||
* all attributes. A caller first calls a prepareXXX method on an instance
|
||||
* to share its data before using that instance. Using an
|
||||
* instance without first calling a prepareXXX method results in an
|
||||
* assertion error and a program crash.
|
||||
*/
|
||||
class U_I18N_API ValueFormatter : public UObject {
|
||||
public:
|
||||
ValueFormatter() : fType(kFormatTypeCount) {
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is here only to support the protected round() method
|
||||
* in DecimalFormat. It serves no ther purpose than that.
|
||||
*
|
||||
* @param value this value is rounded in place.
|
||||
* @param status any error returned here.
|
||||
*/
|
||||
DigitList &round(DigitList &value, UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Returns TRUE if the absolute value of value can be fast formatted
|
||||
* using ValueFormatter::formatInt32.
|
||||
*/
|
||||
UBool isFastFormattable(int32_t value) const;
|
||||
|
||||
/**
|
||||
* Converts value to a VisibleDigitsWithExponent.
|
||||
* Result may be fixed point or scientific.
|
||||
*/
|
||||
VisibleDigitsWithExponent &toVisibleDigitsWithExponent(
|
||||
int64_t value,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Converts value to a VisibleDigitsWithExponent.
|
||||
* Result may be fixed point or scientific.
|
||||
*/
|
||||
VisibleDigitsWithExponent &toVisibleDigitsWithExponent(
|
||||
DigitList &value,
|
||||
VisibleDigitsWithExponent &digits,
|
||||
UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* formats positiveValue and appends to appendTo. Returns appendTo.
|
||||
* @param positiveValue If negative, no negative sign is formatted.
|
||||
* @param handler stores the field positions
|
||||
* @param appendTo formatted value appended here.
|
||||
*/
|
||||
UnicodeString &format(
|
||||
const VisibleDigitsWithExponent &positiveValue,
|
||||
FieldPositionHandler &handler,
|
||||
UnicodeString &appendTo) const;
|
||||
|
||||
|
||||
/**
|
||||
* formats positiveValue and appends to appendTo. Returns appendTo.
|
||||
* value must be positive. Calling formatInt32 to format a value when
|
||||
* isFastFormattable indicates that the value cannot be fast formatted
|
||||
* results in undefined behavior.
|
||||
*/
|
||||
UnicodeString &formatInt32(
|
||||
int32_t positiveValue,
|
||||
FieldPositionHandler &handler,
|
||||
UnicodeString &appendTo) const;
|
||||
|
||||
/**
|
||||
* Returns the number of code points needed to format.
|
||||
* @param positiveValue if negative, the negative sign is not included
|
||||
* in count.
|
||||
*/
|
||||
int32_t countChar32(
|
||||
const VisibleDigitsWithExponent &positiveValue) const;
|
||||
|
||||
/**
|
||||
* Prepares this instance for fixed decimal formatting.
|
||||
*/
|
||||
void prepareFixedDecimalFormatting(
|
||||
const DigitFormatter &formatter,
|
||||
const DigitGrouping &grouping,
|
||||
const FixedPrecision &precision,
|
||||
const DigitFormatterOptions &options);
|
||||
|
||||
/**
|
||||
* Prepares this instance for scientific formatting.
|
||||
*/
|
||||
void prepareScientificFormatting(
|
||||
const DigitFormatter &formatter,
|
||||
const ScientificPrecision &precision,
|
||||
const SciFormatterOptions &options);
|
||||
|
||||
private:
|
||||
ValueFormatter(const ValueFormatter &);
|
||||
ValueFormatter &operator=(const ValueFormatter &);
|
||||
enum FormatType {
|
||||
kFixedDecimal,
|
||||
kScientificNotation,
|
||||
kFormatTypeCount
|
||||
};
|
||||
|
||||
FormatType fType;
|
||||
|
||||
// for fixed decimal and scientific formatting
|
||||
const DigitFormatter *fDigitFormatter;
|
||||
|
||||
// for fixed decimal formatting
|
||||
const FixedPrecision *fFixedPrecision;
|
||||
const DigitFormatterOptions *fFixedOptions;
|
||||
const DigitGrouping *fGrouping;
|
||||
|
||||
// for scientific formatting
|
||||
const ScientificPrecision *fScientificPrecision;
|
||||
const SciFormatterOptions *fScientificOptions;
|
||||
};
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif /* !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif /* VALUEFORMATTER_H */
|
185
icu4c/source/i18n/visibledigits.cpp
Normal file
185
icu4c/source/i18n/visibledigits.cpp
Normal file
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*
|
||||
* file name: visibledigits.cpp
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#include "cstring.h"
|
||||
#include "decNumber.h"
|
||||
#include "digitlst.h"
|
||||
#include "uassert.h"
|
||||
#include "visibledigits.h"
|
||||
|
||||
static const int32_t kNegative = 1;
|
||||
static const int32_t kInfinite = 2;
|
||||
static const int32_t kNaN = 4;
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
void VisibleDigits::setNegative() {
|
||||
fFlags |= kNegative;
|
||||
}
|
||||
|
||||
void VisibleDigits::setNaN() {
|
||||
fFlags |= kNaN;
|
||||
}
|
||||
|
||||
void VisibleDigits::setInfinite() {
|
||||
fFlags |= kInfinite;
|
||||
}
|
||||
|
||||
void VisibleDigits::clear() {
|
||||
fInterval.clear();
|
||||
fDigits.clear();
|
||||
fExponent = 0;
|
||||
fFlags = 0;
|
||||
fAbsIntValue = 0LL;
|
||||
fAbsIntValueSet = FALSE;
|
||||
fAbsDoubleValue = 0.0;
|
||||
fAbsDoubleValueSet = FALSE;
|
||||
}
|
||||
|
||||
UBool VisibleDigits::isNegative() const {
|
||||
return (fFlags & kNegative);
|
||||
}
|
||||
|
||||
UBool VisibleDigits::isNaN() const {
|
||||
return (fFlags & kNaN);
|
||||
}
|
||||
|
||||
UBool VisibleDigits::isInfinite() const {
|
||||
return (fFlags & kInfinite);
|
||||
}
|
||||
|
||||
int32_t VisibleDigits::getDigitByExponent(int32_t digitPos) const {
|
||||
if (digitPos < fExponent || digitPos >= fExponent + fDigits.length()) {
|
||||
return 0;
|
||||
}
|
||||
const char *ptr = fDigits.data();
|
||||
return ptr[digitPos - fExponent];
|
||||
}
|
||||
|
||||
UBool VisibleDigits::isOverMaxDigits() const {
|
||||
return (fExponent + fDigits.length() > fInterval.getMostSignificantExclusive());
|
||||
}
|
||||
|
||||
UBool VisibleDigits::isNaNOrInfinity() const {
|
||||
return (fFlags & (kInfinite | kNaN)) != 0;
|
||||
}
|
||||
|
||||
double VisibleDigits::computeAbsDoubleValue() const {
|
||||
// Take care of NaN and infinity
|
||||
if (isNaN()) {
|
||||
return uprv_getNaN();
|
||||
}
|
||||
if (isInfinite()) {
|
||||
return uprv_getInfinity();
|
||||
}
|
||||
|
||||
// stack allocate a decNumber to hold MAX_DBL_DIGITS+3 significant digits
|
||||
char rawNumber[sizeof(decNumber) + MAX_DBL_DIGITS+3];
|
||||
decNumber *numberPtr = (decNumber *) rawNumber;
|
||||
|
||||
int32_t mostSig = fInterval.getMostSignificantExclusive();
|
||||
int32_t mostSigNonZero = fExponent + fDigits.length();
|
||||
int32_t end = mostSig > mostSigNonZero ? mostSigNonZero : mostSig;
|
||||
int32_t leastSig = fInterval.getLeastSignificantInclusive();
|
||||
int32_t start = leastSig > fExponent ? leastSig : fExponent;
|
||||
if (end <= start) {
|
||||
return 0.0;
|
||||
}
|
||||
if (start < end - (MAX_DBL_DIGITS+3)) {
|
||||
start = end - (MAX_DBL_DIGITS+3);
|
||||
}
|
||||
uint8_t *pos = numberPtr->lsu;
|
||||
const char *src = &(fDigits.data()[start - fExponent]);
|
||||
for (int32_t i = start; i < end; ++i) {
|
||||
*pos++ = (uint8_t) (*src++);
|
||||
}
|
||||
numberPtr->exponent = start;
|
||||
numberPtr->bits = 0;
|
||||
numberPtr->digits = end - start;
|
||||
char str[MAX_DBL_DIGITS+18];
|
||||
uprv_decNumberToString(numberPtr, str);
|
||||
U_ASSERT(uprv_strlen(str) < MAX_DBL_DIGITS+18);
|
||||
char decimalSeparator = DigitList::getStrtodDecimalSeparator();
|
||||
if (decimalSeparator != '.') {
|
||||
char *decimalPt = strchr(str, '.');
|
||||
if (decimalPt != NULL) {
|
||||
*decimalPt = decimalSeparator;
|
||||
}
|
||||
}
|
||||
char *unused = NULL;
|
||||
return uprv_strtod(str, &unused);
|
||||
}
|
||||
|
||||
void VisibleDigits::getFixedDecimal(
|
||||
double &source, int64_t &intValue, int64_t &f, int64_t &t, int32_t &v, UBool &hasIntValue) const {
|
||||
source = 0.0;
|
||||
intValue = 0;
|
||||
f = 0;
|
||||
t = 0;
|
||||
v = 0;
|
||||
hasIntValue = FALSE;
|
||||
if (isNaNOrInfinity()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// source
|
||||
if (fAbsDoubleValueSet) {
|
||||
source = fAbsDoubleValue;
|
||||
} else {
|
||||
source = computeAbsDoubleValue();
|
||||
}
|
||||
|
||||
// visible decimal digits
|
||||
v = fInterval.getFracDigitCount();
|
||||
|
||||
// intValue
|
||||
|
||||
// If we initialized from an int64 just use that instead of
|
||||
// calculating
|
||||
if (fAbsIntValueSet) {
|
||||
intValue = fAbsIntValue;
|
||||
} else {
|
||||
int32_t startPos = fInterval.getMostSignificantExclusive();
|
||||
if (startPos > 18) {
|
||||
startPos = 18;
|
||||
}
|
||||
// process the integer digits
|
||||
for (int32_t i = startPos - 1; i >= 0; --i) {
|
||||
intValue = intValue * 10LL + getDigitByExponent(i);
|
||||
}
|
||||
if (intValue == 0LL && startPos > 0) {
|
||||
intValue = 100000000000000000LL;
|
||||
}
|
||||
}
|
||||
|
||||
// f (decimal digits)
|
||||
// skip over any leading 0's in fraction digits.
|
||||
int32_t idx = -1;
|
||||
for (; idx >= -v && getDigitByExponent(idx) == 0; --idx);
|
||||
|
||||
// Only process up to first 18 non zero fraction digits for decimalDigits
|
||||
// since that is all we can fit into an int64.
|
||||
for (int32_t i = idx; i >= -v && i > idx - 18; --i) {
|
||||
f = f * 10LL + getDigitByExponent(i);
|
||||
}
|
||||
|
||||
// If we have no decimal digits, we don't have an integer value
|
||||
hasIntValue = (f == 0LL);
|
||||
|
||||
// t (decimal digits without trailing zeros)
|
||||
t = f;
|
||||
while (t > 0 && t % 10LL == 0) {
|
||||
t /= 10;
|
||||
}
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
154
icu4c/source/i18n/visibledigits.h
Normal file
154
icu4c/source/i18n/visibledigits.h
Normal file
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
******************************************************************************* * Copyright (C) 2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
* visibledigits.h
|
||||
*
|
||||
* created on: 2015jun20
|
||||
* created by: Travis Keep
|
||||
*/
|
||||
|
||||
#ifndef __VISIBLEDIGITS_H__
|
||||
#define __VISIBLEDIGITS_H__
|
||||
|
||||
#include "unicode/uobject.h"
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#include "charstr.h"
|
||||
#include "digitinterval.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
class DigitList;
|
||||
|
||||
/**
|
||||
* VisibleDigits represents the digits visible for formatting.
|
||||
* Once initialized using a FixedPrecision instance, VisibleDigits instances
|
||||
* remain unchanged until they are initialized again. A VisibleDigits with
|
||||
* a numeric value equal to 3.0 could be "3", "3.0", "3.00" or even "003.0"
|
||||
* depending on settings of the FixedPrecision instance used to initialize it.
|
||||
*/
|
||||
class U_I18N_API VisibleDigits : public UMemory {
|
||||
public:
|
||||
VisibleDigits() : fExponent(0), fFlags(0), fAbsIntValue(0), fAbsIntValueSet(FALSE), fAbsDoubleValue(0.0), fAbsDoubleValueSet(FALSE) { }
|
||||
|
||||
UBool isNegative() const;
|
||||
UBool isNaN() const;
|
||||
UBool isInfinite() const;
|
||||
UBool isNaNOrInfinity() const;
|
||||
|
||||
/**
|
||||
* Gets the digit at particular exponent, if number is 987.6, then
|
||||
* getDigit(2) == 9 and gitDigit(0) == 7 and gitDigit(-1) == 6.
|
||||
* If isNaN() or isInfinity() return TRUE, then the result of this
|
||||
* function is undefined.
|
||||
*/
|
||||
int32_t getDigitByExponent(int32_t digitPos) const;
|
||||
|
||||
/**
|
||||
* Returns the digit interval which indicates the leftmost and rightmost
|
||||
* position of this instance.
|
||||
* If isNaN() or isInfinity() return TRUE, then the result of this
|
||||
* function is undefined.
|
||||
*/
|
||||
const DigitInterval &getInterval() const { return fInterval; }
|
||||
|
||||
/**
|
||||
* Gets the parameters needed to create a FixedDecimal.
|
||||
*/
|
||||
void getFixedDecimal(double &source, int64_t &intValue, int64_t &f, int64_t &t, int32_t &v, UBool &hasIntValue) const;
|
||||
|
||||
|
||||
private:
|
||||
/**
|
||||
* The digits, least significant first. Both the least and most
|
||||
* significant digit in this list are non-zero; however, digits in the
|
||||
* middle may be zero. This field contains values between (char) 0, and
|
||||
* (char) 9 inclusive.
|
||||
*/
|
||||
CharString fDigits;
|
||||
|
||||
/**
|
||||
* The range of displayable digits. This field is needed to account for
|
||||
* any leading and trailing zeros which are not stored in fDigits.
|
||||
*/
|
||||
DigitInterval fInterval;
|
||||
|
||||
/**
|
||||
* The exponent value of the least significant digit in fDigits. For
|
||||
* example, fExponent = 2 and fDigits = {7, 8, 5} represents 58700.
|
||||
*/
|
||||
int32_t fExponent;
|
||||
|
||||
/**
|
||||
* Contains flags such as NaN, Inf, and negative.
|
||||
*/
|
||||
int32_t fFlags;
|
||||
|
||||
/**
|
||||
* Contains the absolute value of the digits left of the decimal place
|
||||
* if fAbsIntValueSet is TRUE
|
||||
*/
|
||||
int64_t fAbsIntValue;
|
||||
|
||||
/**
|
||||
* Indicates whether or not fAbsIntValue is set.
|
||||
*/
|
||||
UBool fAbsIntValueSet;
|
||||
|
||||
/**
|
||||
* Contains the absolute value of the value this instance represents
|
||||
* if fAbsDoubleValueSet is TRUE
|
||||
*/
|
||||
double fAbsDoubleValue;
|
||||
|
||||
/**
|
||||
* Indicates whether or not fAbsDoubleValue is set.
|
||||
*/
|
||||
UBool fAbsDoubleValueSet;
|
||||
|
||||
void setNegative();
|
||||
void setNaN();
|
||||
void setInfinite();
|
||||
void clear();
|
||||
double computeAbsDoubleValue() const;
|
||||
UBool isOverMaxDigits() const;
|
||||
|
||||
VisibleDigits(const VisibleDigits &);
|
||||
VisibleDigits &operator=(const VisibleDigits &);
|
||||
|
||||
friend class FixedPrecision;
|
||||
friend class VisibleDigitsWithExponent;
|
||||
};
|
||||
|
||||
/**
|
||||
* A VisibleDigits with a possible exponent.
|
||||
*/
|
||||
class U_I18N_API VisibleDigitsWithExponent : public UMemory {
|
||||
public:
|
||||
VisibleDigitsWithExponent() : fHasExponent(FALSE) { }
|
||||
const VisibleDigits &getMantissa() const { return fMantissa; }
|
||||
const VisibleDigits *getExponent() const {
|
||||
return fHasExponent ? &fExponent : NULL;
|
||||
}
|
||||
void clear() {
|
||||
fMantissa.clear();
|
||||
fExponent.clear();
|
||||
fHasExponent = FALSE;
|
||||
}
|
||||
UBool isNegative() const { return fMantissa.isNegative(); }
|
||||
UBool isNaN() const { return fMantissa.isNaN(); }
|
||||
UBool isInfinite() const { return fMantissa.isInfinite(); }
|
||||
private:
|
||||
VisibleDigits fMantissa;
|
||||
VisibleDigits fExponent;
|
||||
UBool fHasExponent;
|
||||
|
||||
friend class ScientificPrecision;
|
||||
friend class FixedPrecision;
|
||||
};
|
||||
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif // __VISIBLEDIGITS_H__
|
|
@ -59,7 +59,7 @@ windttst.o winnmtst.o winutil.o csdetest.o tzrulets.o tzoffloc.o tzfmttst.o ssea
|
|||
tufmtts.o itspoof.o simplethread.o bidiconf.o locnmtst.o dcfmtest.o alphaindextst.o listformattertest.o genderinfotest.o compactdecimalformattest.o regiontst.o \
|
||||
reldatefmttest.o simplepatternformattertest.o measfmttest.o numfmtspectest.o unifiedcachetest.o quantityformattertest.o \
|
||||
scientificnumberformattertest.o datadrivennumberformattestsuite.o \
|
||||
numberformattesttuple.o
|
||||
numberformattesttuple.o numberformat2test.o pluralmaptest.o
|
||||
|
||||
DEPS = $(OBJECTS:.o=.d)
|
||||
|
||||
|
|
|
@ -611,18 +611,105 @@ void IntlTestDecimalFormatAPI::TestFixedDecimal() {
|
|||
TEST_ASSERT_STATUS(status);
|
||||
ASSERT_EQUAL(44, fd.source);
|
||||
ASSERT_EQUAL(0, fd.visibleDecimalDigitCount);
|
||||
ASSERT_EQUAL(FALSE, fd.isNegative);
|
||||
|
||||
fd = df->getFixedDecimal(-44, status);
|
||||
TEST_ASSERT_STATUS(status);
|
||||
ASSERT_EQUAL(44, fd.source);
|
||||
ASSERT_EQUAL(0, fd.visibleDecimalDigitCount);
|
||||
ASSERT_EQUAL(TRUE, fd.isNegative);
|
||||
|
||||
df.adoptInsteadAndCheckErrorCode(new DecimalFormat("###.00##", status), status);
|
||||
TEST_ASSERT_STATUS(status);
|
||||
fd = df->getFixedDecimal(123.456, status);
|
||||
TEST_ASSERT_STATUS(status);
|
||||
ASSERT_EQUAL(3, fd.visibleDecimalDigitCount);
|
||||
ASSERT_EQUAL(456, fd.decimalDigits);
|
||||
ASSERT_EQUAL(456, fd.decimalDigitsWithoutTrailingZeros);
|
||||
ASSERT_EQUAL(123, fd.intValue);
|
||||
ASSERT_EQUAL(3, fd.visibleDecimalDigitCount); // v
|
||||
ASSERT_EQUAL(456, fd.decimalDigits); // f
|
||||
ASSERT_EQUAL(456, fd.decimalDigitsWithoutTrailingZeros); // t
|
||||
ASSERT_EQUAL(123, fd.intValue); // i
|
||||
ASSERT_EQUAL(123.456, fd.source); // n
|
||||
ASSERT_EQUAL(FALSE, fd.hasIntegerValue);
|
||||
ASSERT_EQUAL(FALSE, fd.isNegative);
|
||||
|
||||
fd = df->getFixedDecimal(-123.456, status);
|
||||
TEST_ASSERT_STATUS(status);
|
||||
ASSERT_EQUAL(3, fd.visibleDecimalDigitCount); // v
|
||||
ASSERT_EQUAL(456, fd.decimalDigits); // f
|
||||
ASSERT_EQUAL(456, fd.decimalDigitsWithoutTrailingZeros); // t
|
||||
ASSERT_EQUAL(123, fd.intValue); // i
|
||||
ASSERT_EQUAL(123.456, fd.source); // n
|
||||
ASSERT_EQUAL(FALSE, fd.hasIntegerValue);
|
||||
ASSERT_EQUAL(TRUE, fd.isNegative);
|
||||
|
||||
// test max int digits
|
||||
df->setMaximumIntegerDigits(2);
|
||||
fd = df->getFixedDecimal(123.456, status);
|
||||
TEST_ASSERT_STATUS(status);
|
||||
ASSERT_EQUAL(3, fd.visibleDecimalDigitCount); // v
|
||||
ASSERT_EQUAL(456, fd.decimalDigits); // f
|
||||
ASSERT_EQUAL(456, fd.decimalDigitsWithoutTrailingZeros); // t
|
||||
ASSERT_EQUAL(23, fd.intValue); // i
|
||||
ASSERT_EQUAL(23.456, fd.source); // n
|
||||
ASSERT_EQUAL(FALSE, fd.hasIntegerValue);
|
||||
ASSERT_EQUAL(FALSE, fd.isNegative);
|
||||
|
||||
fd = df->getFixedDecimal(-123.456, status);
|
||||
TEST_ASSERT_STATUS(status);
|
||||
ASSERT_EQUAL(3, fd.visibleDecimalDigitCount); // v
|
||||
ASSERT_EQUAL(456, fd.decimalDigits); // f
|
||||
ASSERT_EQUAL(456, fd.decimalDigitsWithoutTrailingZeros); // t
|
||||
ASSERT_EQUAL(23, fd.intValue); // i
|
||||
ASSERT_EQUAL(23.456, fd.source); // n
|
||||
ASSERT_EQUAL(FALSE, fd.hasIntegerValue);
|
||||
ASSERT_EQUAL(TRUE, fd.isNegative);
|
||||
|
||||
// test max fraction digits
|
||||
df->setMaximumIntegerDigits(2000000000);
|
||||
df->setMaximumFractionDigits(2);
|
||||
fd = df->getFixedDecimal(123.456, status);
|
||||
TEST_ASSERT_STATUS(status);
|
||||
ASSERT_EQUAL(2, fd.visibleDecimalDigitCount); // v
|
||||
ASSERT_EQUAL(46, fd.decimalDigits); // f
|
||||
ASSERT_EQUAL(46, fd.decimalDigitsWithoutTrailingZeros); // t
|
||||
ASSERT_EQUAL(123, fd.intValue); // i
|
||||
ASSERT_EQUAL(123.46, fd.source); // n
|
||||
ASSERT_EQUAL(FALSE, fd.hasIntegerValue);
|
||||
ASSERT_EQUAL(FALSE, fd.isNegative);
|
||||
|
||||
fd = df->getFixedDecimal(-123.456, status);
|
||||
TEST_ASSERT_STATUS(status);
|
||||
ASSERT_EQUAL(2, fd.visibleDecimalDigitCount); // v
|
||||
ASSERT_EQUAL(46, fd.decimalDigits); // f
|
||||
ASSERT_EQUAL(46, fd.decimalDigitsWithoutTrailingZeros); // t
|
||||
ASSERT_EQUAL(123, fd.intValue); // i
|
||||
ASSERT_EQUAL(123.46, fd.source); // n
|
||||
ASSERT_EQUAL(FALSE, fd.hasIntegerValue);
|
||||
ASSERT_EQUAL(TRUE, fd.isNegative);
|
||||
|
||||
// test esoteric rounding
|
||||
df->setMaximumFractionDigits(6);
|
||||
df->setRoundingIncrement(7.3);
|
||||
|
||||
fd = df->getFixedDecimal(30.0, status);
|
||||
TEST_ASSERT_STATUS(status);
|
||||
ASSERT_EQUAL(2, fd.visibleDecimalDigitCount); // v
|
||||
ASSERT_EQUAL(20, fd.decimalDigits); // f
|
||||
ASSERT_EQUAL(2, fd.decimalDigitsWithoutTrailingZeros); // t
|
||||
ASSERT_EQUAL(29, fd.intValue); // i
|
||||
ASSERT_EQUAL(29.2, fd.source); // n
|
||||
ASSERT_EQUAL(FALSE, fd.hasIntegerValue);
|
||||
ASSERT_EQUAL(FALSE, fd.isNegative);
|
||||
|
||||
fd = df->getFixedDecimal(-30.0, status);
|
||||
TEST_ASSERT_STATUS(status);
|
||||
ASSERT_EQUAL(2, fd.visibleDecimalDigitCount); // v
|
||||
ASSERT_EQUAL(20, fd.decimalDigits); // f
|
||||
ASSERT_EQUAL(2, fd.decimalDigitsWithoutTrailingZeros); // t
|
||||
ASSERT_EQUAL(29, fd.intValue); // i
|
||||
ASSERT_EQUAL(29.2, fd.source); // n
|
||||
ASSERT_EQUAL(FALSE, fd.hasIntegerValue);
|
||||
ASSERT_EQUAL(TRUE, fd.isNegative);
|
||||
|
||||
df.adoptInsteadAndCheckErrorCode(new DecimalFormat("###", status), status);
|
||||
TEST_ASSERT_STATUS(status);
|
||||
fd = df->getFixedDecimal(123.456, status);
|
||||
|
@ -775,6 +862,17 @@ void IntlTestDecimalFormatAPI::TestFixedDecimal() {
|
|||
ASSERT_EQUAL(FALSE, fd.hasIntegerValue);
|
||||
ASSERT_EQUAL(FALSE, fd.isNegative);
|
||||
|
||||
fable.setDecimalNumber("1000000000000000050000.3", status);
|
||||
TEST_ASSERT_STATUS(status);
|
||||
fd = df->getFixedDecimal(fable, status);
|
||||
TEST_ASSERT_STATUS(status);
|
||||
ASSERT_EQUAL(2, fd.visibleDecimalDigitCount);
|
||||
ASSERT_EQUAL(30, fd.decimalDigits);
|
||||
ASSERT_EQUAL(3, fd.decimalDigitsWithoutTrailingZeros);
|
||||
ASSERT_EQUAL(50000LL, fd.intValue);
|
||||
ASSERT_EQUAL(FALSE, fd.hasIntegerValue);
|
||||
ASSERT_EQUAL(FALSE, fd.isNegative);
|
||||
|
||||
// Test some int64_t values that are out of the range of a double
|
||||
fable.setInt64(4503599627370496LL);
|
||||
TEST_ASSERT_STATUS(status);
|
||||
|
|
|
@ -316,8 +316,10 @@
|
|||
<ClCompile Include="nmfmapts.cpp" />
|
||||
<ClCompile Include="nmfmtrt.cpp" />
|
||||
<ClCompile Include="numberformattesttuple.cpp" />
|
||||
<ClCompile Include="numberformat2test.cpp" />
|
||||
<ClCompile Include="numfmtst.cpp" />
|
||||
<ClCompile Include="numrgts.cpp" />
|
||||
<ClCompile Include="pluralmaptest.cpp" />
|
||||
<ClCompile Include="plurfmts.cpp" />
|
||||
<ClCompile Include="plurults.cpp" />
|
||||
<ClCompile Include="pptest.cpp" />
|
||||
|
|
|
@ -256,6 +256,9 @@
|
|||
<ClCompile Include="numberformattesttuple.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="numberformat2test.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="numfmtst.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
|
@ -274,6 +277,9 @@
|
|||
<ClCompile Include="quantityformattertest.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pluralmaptest.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="reldatefmttest.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
|
@ -865,4 +871,4 @@
|
|||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/********************************************************************
|
||||
* COPYRIGHT:
|
||||
* Copyright (c) 1997-2014, International Business Machines
|
||||
* Copyright (c) 1997-2015, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
********************************************************************/
|
||||
|
||||
|
@ -67,6 +67,8 @@ extern IntlTest *createRelativeDateTimeFormatterTest();
|
|||
extern IntlTest *createMeasureFormatTest();
|
||||
extern IntlTest *createNumberFormatSpecificationTest();
|
||||
extern IntlTest *createScientificNumberFormatterTest();
|
||||
extern IntlTest *createNumberFormat2Test();
|
||||
|
||||
|
||||
#define TESTCLASS(id, TestClass) \
|
||||
case id: \
|
||||
|
@ -201,6 +203,15 @@ void IntlTestFormat::runIndexedTest( int32_t index, UBool exec, const char* &nam
|
|||
callTest(*test, par);
|
||||
}
|
||||
break;
|
||||
case 50:
|
||||
name = "NumberFormat2Test";
|
||||
if (exec) {
|
||||
logln("NumberFormat2Test test---");
|
||||
logln((UnicodeString)"");
|
||||
LocalPointer<IntlTest> test(createNumberFormat2Test());
|
||||
callTest(*test, par);
|
||||
}
|
||||
break;
|
||||
default: name = ""; break; //needed to end loop
|
||||
}
|
||||
if (exec) {
|
||||
|
|
|
@ -37,6 +37,8 @@ static IntlTest *createEnumSetTest();
|
|||
extern IntlTest *createSimplePatternFormatterTest();
|
||||
extern IntlTest *createUnifiedCacheTest();
|
||||
extern IntlTest *createQuantityFormatterTest();
|
||||
extern IntlTest *createPluralMapTest();
|
||||
|
||||
|
||||
#define CASE(id, test) case id: \
|
||||
name = #test; \
|
||||
|
@ -122,6 +124,13 @@ void IntlTestUtilities::runIndexedTest( int32_t index, UBool exec, const char* &
|
|||
LocalPointer<IntlTest> test(createQuantityFormatterTest());
|
||||
callTest(*test, par);
|
||||
}
|
||||
case 23:
|
||||
name = "PluralMapTest";
|
||||
if (exec) {
|
||||
logln("TestSuite PluralMapTest---"); logln();
|
||||
LocalPointer<IntlTest> test(createPluralMapTest());
|
||||
callTest(*test, par);
|
||||
}
|
||||
break;
|
||||
default: name = ""; break; //needed to end loop
|
||||
}
|
||||
|
|
3080
icu4c/source/test/intltest/numberformat2test.cpp
Normal file
3080
icu4c/source/test/intltest/numberformat2test.cpp
Normal file
File diff suppressed because it is too large
Load diff
|
@ -34,6 +34,7 @@
|
|||
#include "fmtableimp.h"
|
||||
#include "numberformattesttuple.h"
|
||||
#include "datadrivennumberformattestsuite.h"
|
||||
#include "unicode/msgfmt.h"
|
||||
|
||||
class NumberFormatTestDataDriven : public DataDrivenNumberFormatTestSuite {
|
||||
protected:
|
||||
|
@ -92,6 +93,19 @@ static UnicodeString &format(
|
|||
return fmt.format(digitList, appendTo, fpos, status);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static UnicodeString &format(
|
||||
const DecimalFormat &fmt,
|
||||
T value,
|
||||
UnicodeString &appendTo,
|
||||
UErrorCode &status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
FieldPosition fpos(FieldPosition::DONT_CARE);
|
||||
return fmt.format(value, appendTo, fpos, status);
|
||||
}
|
||||
|
||||
static void adjustDecimalFormat(
|
||||
const NumberFormatTestTuple &tuple,
|
||||
DecimalFormat &fmt,
|
||||
|
@ -270,16 +284,48 @@ UBool NumberFormatTestDataDriven::isFormatPass(
|
|||
}
|
||||
DigitList digitList;
|
||||
strToDigitList(tuple.format, digitList, status);
|
||||
UnicodeString appendTo;
|
||||
format(*fmtPtr, digitList, appendTo, status);
|
||||
if (U_FAILURE(status)) {
|
||||
appendErrorMessage.append("Error formatting.");
|
||||
return FALSE;
|
||||
{
|
||||
UnicodeString appendTo;
|
||||
format(*fmtPtr, digitList, appendTo, status);
|
||||
if (U_FAILURE(status)) {
|
||||
appendErrorMessage.append("Error formatting.");
|
||||
return FALSE;
|
||||
}
|
||||
if (appendTo != tuple.output) {
|
||||
appendErrorMessage.append(
|
||||
UnicodeString("Expected: ") + tuple.output + ", got: " + appendTo);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
if (appendTo != tuple.output) {
|
||||
appendErrorMessage.append(
|
||||
UnicodeString("Expected: ") + tuple.output + ", got: " + appendTo);
|
||||
return FALSE;
|
||||
double doubleVal = digitList.getDouble();
|
||||
{
|
||||
UnicodeString appendTo;
|
||||
format(*fmtPtr, doubleVal, appendTo, status);
|
||||
if (U_FAILURE(status)) {
|
||||
appendErrorMessage.append("Error formatting.");
|
||||
return FALSE;
|
||||
}
|
||||
if (appendTo != tuple.output) {
|
||||
appendErrorMessage.append(
|
||||
UnicodeString("double Expected: ") + tuple.output + ", got: " + appendTo);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
if (!uprv_isNaN(doubleVal) && !uprv_isInfinite(doubleVal) && doubleVal == uprv_floor(doubleVal)) {
|
||||
int64_t intVal = digitList.getInt64();
|
||||
{
|
||||
UnicodeString appendTo;
|
||||
format(*fmtPtr, intVal, appendTo, status);
|
||||
if (U_FAILURE(status)) {
|
||||
appendErrorMessage.append("Error formatting.");
|
||||
return FALSE;
|
||||
}
|
||||
if (appendTo != tuple.output) {
|
||||
appendErrorMessage.append(
|
||||
UnicodeString("int64 Expected: ") + tuple.output + ", got: " + appendTo);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -520,6 +566,12 @@ void NumberFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &n
|
|||
TESTCASE_AUTO(TestDataDriven);
|
||||
TESTCASE_AUTO(TestDoubleLimit11439);
|
||||
TESTCASE_AUTO(TestFastPathConsistent11524);
|
||||
TESTCASE_AUTO(TestGetAffixes);
|
||||
TESTCASE_AUTO(TestToPatternScientific11648);
|
||||
TESTCASE_AUTO(TestBenchmark);
|
||||
TESTCASE_AUTO(TestCtorApplyPatternDifference);
|
||||
TESTCASE_AUTO(TestFractionalDigitsForCurrency);
|
||||
TESTCASE_AUTO(TestFormatCurrencyPlural);
|
||||
TESTCASE_AUTO_END;
|
||||
}
|
||||
|
||||
|
@ -8238,5 +8290,165 @@ void NumberFormatTest::TestFastPathConsistent11524() {
|
|||
delete fmt;
|
||||
}
|
||||
|
||||
void NumberFormatTest::TestGetAffixes() {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
DecimalFormatSymbols sym("en_US", status);
|
||||
UnicodeString pattern("\\u00a4\\u00a4\\u00a4 0.00 %\\u00a4\\u00a4");
|
||||
pattern = pattern.unescape();
|
||||
DecimalFormat fmt(pattern, sym, status);
|
||||
assertSuccess("", status);
|
||||
UnicodeString affixStr;
|
||||
assertEquals("", "US dollars ", fmt.getPositivePrefix(affixStr));
|
||||
assertEquals("", " %USD", fmt.getPositiveSuffix(affixStr));
|
||||
assertEquals("", "-US dollars ", fmt.getNegativePrefix(affixStr));
|
||||
assertEquals("", " %USD", fmt.getNegativeSuffix(affixStr));
|
||||
|
||||
// Test equality with affixes. set affix methods can't capture special
|
||||
// characters which is why equality should fail.
|
||||
{
|
||||
DecimalFormat fmtCopy(fmt);
|
||||
assertTrue("", fmt == fmtCopy);
|
||||
UnicodeString someAffix;
|
||||
fmtCopy.setPositivePrefix(fmtCopy.getPositivePrefix(someAffix));
|
||||
assertTrue("", fmt != fmtCopy);
|
||||
}
|
||||
{
|
||||
DecimalFormat fmtCopy(fmt);
|
||||
assertTrue("", fmt == fmtCopy);
|
||||
UnicodeString someAffix;
|
||||
fmtCopy.setPositiveSuffix(fmtCopy.getPositiveSuffix(someAffix));
|
||||
assertTrue("", fmt != fmtCopy);
|
||||
}
|
||||
{
|
||||
DecimalFormat fmtCopy(fmt);
|
||||
assertTrue("", fmt == fmtCopy);
|
||||
UnicodeString someAffix;
|
||||
fmtCopy.setNegativePrefix(fmtCopy.getNegativePrefix(someAffix));
|
||||
assertTrue("", fmt != fmtCopy);
|
||||
}
|
||||
{
|
||||
DecimalFormat fmtCopy(fmt);
|
||||
assertTrue("", fmt == fmtCopy);
|
||||
UnicodeString someAffix;
|
||||
fmtCopy.setNegativeSuffix(fmtCopy.getNegativeSuffix(someAffix));
|
||||
assertTrue("", fmt != fmtCopy);
|
||||
}
|
||||
fmt.setPositivePrefix("Don't");
|
||||
fmt.setPositiveSuffix("do");
|
||||
UnicodeString someAffix("be''eet\\u00a4\\u00a4\\u00a4 it.");
|
||||
someAffix = someAffix.unescape();
|
||||
fmt.setNegativePrefix(someAffix);
|
||||
fmt.setNegativeSuffix("%");
|
||||
assertEquals("", "Don't", fmt.getPositivePrefix(affixStr));
|
||||
assertEquals("", "do", fmt.getPositiveSuffix(affixStr));
|
||||
assertEquals("", someAffix, fmt.getNegativePrefix(affixStr));
|
||||
assertEquals("", "%", fmt.getNegativeSuffix(affixStr));
|
||||
}
|
||||
|
||||
void NumberFormatTest::TestToPatternScientific11648() {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
Locale en("en");
|
||||
DecimalFormatSymbols sym(en, status);
|
||||
DecimalFormat fmt("0.00", sym, status);
|
||||
fmt.setScientificNotation(TRUE);
|
||||
UnicodeString pattern;
|
||||
assertEquals("", "0.00E0", fmt.toPattern(pattern));
|
||||
DecimalFormat fmt2(pattern, sym, status);
|
||||
assertSuccess("", status);
|
||||
}
|
||||
|
||||
void NumberFormatTest::TestBenchmark() {
|
||||
/*
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
Locale en("en");
|
||||
DecimalFormatSymbols sym(en, status);
|
||||
DecimalFormat fmt("0.0000000", new DecimalFormatSymbols(sym), status);
|
||||
// DecimalFormat fmt("0.00000E0", new DecimalFormatSymbols(sym), status);
|
||||
// DecimalFormat fmt("0", new DecimalFormatSymbols(sym), status);
|
||||
FieldPosition fpos(0);
|
||||
clock_t start = clock();
|
||||
for (int32_t i = 0; i < 1000000; ++i) {
|
||||
UnicodeString append;
|
||||
fmt.format(3.0, append, fpos, status);
|
||||
// fmt.format(4.6692016, append, fpos, status);
|
||||
// fmt.format(1234567.8901, append, fpos, status);
|
||||
// fmt.format(2.99792458E8, append, fpos, status);
|
||||
// fmt.format(31, append);
|
||||
}
|
||||
errln("Took %f", (double) (clock() - start) / CLOCKS_PER_SEC);
|
||||
assertSuccess("", status);
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
MessageFormat fmt("{0, plural, one {I have # friend.} other {I have # friends.}}", status);
|
||||
FieldPosition fpos(0);
|
||||
Formattable one(1.0);
|
||||
Formattable three(3.0);
|
||||
clock_t start = clock();
|
||||
for (int32_t i = 0; i < 500000; ++i) {
|
||||
UnicodeString append;
|
||||
fmt.format(&one, 1, append, fpos, status);
|
||||
UnicodeString append2;
|
||||
fmt.format(&three, 1, append2, fpos, status);
|
||||
}
|
||||
errln("Took %f", (double) (clock() - start) / CLOCKS_PER_SEC);
|
||||
assertSuccess("", status);
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
Locale en("en");
|
||||
Measure measureC(23, MeasureUnit::createCelsius(status), status);
|
||||
MeasureFormat fmt(en, UMEASFMT_WIDTH_WIDE, status);
|
||||
FieldPosition fpos(0);
|
||||
clock_t start = clock();
|
||||
for (int32_t i = 0; i < 1000000; ++i) {
|
||||
UnicodeString appendTo;
|
||||
fmt.formatMeasures(
|
||||
&measureC, 1, appendTo, fpos, status);
|
||||
}
|
||||
errln("Took %f", (double) (clock() - start) / CLOCKS_PER_SEC);
|
||||
assertSuccess("", status);
|
||||
*/
|
||||
}
|
||||
|
||||
void NumberFormatTest::TestFractionalDigitsForCurrency() {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
LocalPointer<NumberFormat> fmt(NumberFormat::createCurrencyInstance("en", status));
|
||||
UChar JPY[] = {0x4A, 0x50, 0x59, 0x0};
|
||||
fmt->setCurrency(JPY, status);
|
||||
if (!assertSuccess("", status)) {
|
||||
return;
|
||||
}
|
||||
assertEquals("", 0, fmt->getMaximumFractionDigits());
|
||||
}
|
||||
|
||||
|
||||
void NumberFormatTest::TestFormatCurrencyPlural() {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
Locale locale = Locale::createCanonical("en_US");
|
||||
NumberFormat *fmt = NumberFormat::createInstance(locale, UNUM_CURRENCY_PLURAL, status);
|
||||
UnicodeString formattedNum;
|
||||
fmt->format(11234.567, formattedNum, NULL, status);
|
||||
assertEquals("", "11,234.57 US dollars", formattedNum);
|
||||
delete fmt;
|
||||
}
|
||||
|
||||
void NumberFormatTest::TestCtorApplyPatternDifference() {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
DecimalFormatSymbols sym("en_US", status);
|
||||
UnicodeString pattern("\\u00a40");
|
||||
DecimalFormat fmt(pattern.unescape(), sym, status);
|
||||
UnicodeString result;
|
||||
assertEquals(
|
||||
"ctor favors precision of currency",
|
||||
"$5.00",
|
||||
fmt.format(5, result));
|
||||
result.remove();
|
||||
fmt.applyPattern(pattern.unescape(), status);
|
||||
assertEquals(
|
||||
"applyPattern favors precision of pattern",
|
||||
"$5",
|
||||
fmt.format(5, result));
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
|
|
@ -188,6 +188,12 @@ class NumberFormatTest: public CalendarTimeZoneTest {
|
|||
|
||||
void TestDoubleLimit11439();
|
||||
void TestFastPathConsistent11524();
|
||||
void TestGetAffixes();
|
||||
void TestToPatternScientific11648();
|
||||
void TestBenchmark();
|
||||
void TestCtorApplyPatternDifference();
|
||||
void TestFractionalDigitsForCurrency();
|
||||
void TestFormatCurrencyPlural();
|
||||
|
||||
private:
|
||||
UBool testFormattableAsUFormattable(const char *file, int line, Formattable &f);
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "unicode/datefmt.h"
|
||||
#include "unicode/ucurr.h"
|
||||
#include "putilimp.h"
|
||||
#include "uassert.h"
|
||||
|
||||
class MyNumberFormatTest : public NumberFormat
|
||||
{
|
||||
|
@ -2438,18 +2439,20 @@ void NumberFormatRegressionTest::Test4212072(void) {
|
|||
}
|
||||
|
||||
// Test toLocalizedPattern/applyLocalizedPattern round trip
|
||||
df->toLocalizedPattern(pat);
|
||||
f2.applyLocalizedPattern(pat, status);
|
||||
// TODO(refactor): don't worry about localized patterns for now.
|
||||
// df->toLocalizedPattern(pat);
|
||||
// f2.applyLocalizedPattern(pat, status);
|
||||
failure(status,
|
||||
UnicodeString("applyLocalizedPattern(") + pat + ")", avail[i]);
|
||||
if (U_FAILURE(status)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO(refactor): don't worry about localized patterns for now.
|
||||
// Make sure we set the currency attributes appropriately
|
||||
if (j == 1) { // Currency format
|
||||
f2.setCurrency(f2.getCurrency(), status);
|
||||
}
|
||||
// if (j == 1) { // Currency format
|
||||
// f2.setCurrency(f2.getCurrency(), status);
|
||||
// }
|
||||
failure(status,
|
||||
UnicodeString("setCurrency() for (") + pat + ")", avail[i]);
|
||||
if (U_FAILURE(status)) {
|
||||
|
@ -2712,9 +2715,10 @@ void NumberFormatRegressionTest::TestJ691(void) {
|
|||
}
|
||||
#define TEST_ASSERT_EQUALS(x,y) \
|
||||
{ \
|
||||
char _msg[100]; \
|
||||
sprintf (_msg,"File %s, line %d: Assertion Failed: " #x "==" #y "\n", __FILE__, __LINE__); \
|
||||
assertEquals((const char*)_msg, x,y); \
|
||||
char _msg[1000]; \
|
||||
int32_t len = sprintf (_msg,"File %s, line %d: Assertion Failed: " #x "==" #y "\n", __FILE__, __LINE__); \
|
||||
U_ASSERT(len < (int32_t) sizeof(_msg)); \
|
||||
assertEquals((const char*) _msg, x,y); \
|
||||
}
|
||||
|
||||
|
||||
|
|
218
icu4c/source/test/intltest/pluralmaptest.cpp
Normal file
218
icu4c/source/test/intltest/pluralmaptest.cpp
Normal file
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2014-2015, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*
|
||||
* File PLURALMAPTEST.CPP
|
||||
*
|
||||
********************************************************************************
|
||||
*/
|
||||
#include "unicode/unistr.h"
|
||||
|
||||
#include "intltest.h"
|
||||
#include "pluralmap.h"
|
||||
|
||||
class PluralMapForPluralMapTest : public PluralMap<UnicodeString> {
|
||||
public:
|
||||
UBool operator==(const PluralMapForPluralMapTest &other) {
|
||||
return equals(other, strEqual);
|
||||
}
|
||||
private:
|
||||
static UBool strEqual(const UnicodeString &lhs, const UnicodeString &rhs) {
|
||||
return lhs == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
class PluralMapTest : public IntlTest {
|
||||
public:
|
||||
PluralMapTest() {
|
||||
}
|
||||
void TestToCategory();
|
||||
void TestGetCategoryName();
|
||||
void TestGet();
|
||||
void TestIterate();
|
||||
void TestEqual();
|
||||
void TestCopyAndAssign();
|
||||
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=0);
|
||||
void addVariant(
|
||||
PluralMapBase::Category v,
|
||||
const UnicodeString &value,
|
||||
PluralMapForPluralMapTest &map);
|
||||
private:
|
||||
};
|
||||
|
||||
void PluralMapTest::runIndexedTest(int32_t index, UBool exec, const char* &name, char* /*par*/) {
|
||||
TESTCASE_AUTO_BEGIN;
|
||||
TESTCASE_AUTO(TestToCategory);
|
||||
TESTCASE_AUTO(TestGetCategoryName);
|
||||
TESTCASE_AUTO(TestGet);
|
||||
TESTCASE_AUTO(TestIterate);
|
||||
TESTCASE_AUTO(TestEqual);
|
||||
TESTCASE_AUTO(TestCopyAndAssign);
|
||||
TESTCASE_AUTO_END;
|
||||
}
|
||||
|
||||
void PluralMapTest::TestToCategory() {
|
||||
assertEquals("", PluralMapBase::OTHER, PluralMapBase::toCategory("other"));
|
||||
assertEquals("", PluralMapBase::ZERO, PluralMapBase::toCategory("zero"));
|
||||
assertEquals("", PluralMapBase::ONE, PluralMapBase::toCategory("one"));
|
||||
assertEquals("", PluralMapBase::TWO, PluralMapBase::toCategory("two"));
|
||||
assertEquals("", PluralMapBase::FEW, PluralMapBase::toCategory("few"));
|
||||
assertEquals("", PluralMapBase::MANY, PluralMapBase::toCategory("many"));
|
||||
assertEquals("", PluralMapBase::NONE, PluralMapBase::toCategory("Many"));
|
||||
assertEquals(
|
||||
"",
|
||||
PluralMapBase::FEW,
|
||||
PluralMapBase::toCategory(UnicodeString("few")));
|
||||
assertEquals(
|
||||
"",
|
||||
PluralMapBase::MANY,
|
||||
PluralMapBase::toCategory(UnicodeString("many")));
|
||||
assertEquals(
|
||||
"",
|
||||
PluralMapBase::NONE,
|
||||
PluralMapBase::toCategory(UnicodeString("Many")));
|
||||
}
|
||||
|
||||
void PluralMapTest::TestGetCategoryName() {
|
||||
assertTrue("", PluralMapBase::getCategoryName(PluralMapBase::NONE) == NULL);
|
||||
assertTrue("", PluralMapBase::getCategoryName(PluralMapBase::CATEGORY_COUNT) == NULL);
|
||||
assertEquals("", "other", PluralMapBase::getCategoryName(PluralMapBase::OTHER));
|
||||
assertEquals("", "zero", PluralMapBase::getCategoryName(PluralMapBase::ZERO));
|
||||
assertEquals("", "one", PluralMapBase::getCategoryName(PluralMapBase::ONE));
|
||||
assertEquals("", "two", PluralMapBase::getCategoryName(PluralMapBase::TWO));
|
||||
assertEquals("", "few", PluralMapBase::getCategoryName(PluralMapBase::FEW));
|
||||
assertEquals("", "many", PluralMapBase::getCategoryName(PluralMapBase::MANY));
|
||||
}
|
||||
|
||||
void PluralMapTest::TestGet() {
|
||||
PluralMapForPluralMapTest map;
|
||||
addVariant(PluralMapBase::OTHER, "pickles", map);
|
||||
addVariant(PluralMapBase::ONE, "pickle", map);
|
||||
addVariant(PluralMapBase::FEW, "picklefew", map);
|
||||
assertEquals("", "pickles", map.get(PluralMapBase::OTHER));
|
||||
assertEquals("", "pickle", map.get(PluralMapBase::ONE));
|
||||
assertEquals("", "picklefew", map.get(PluralMapBase::FEW));
|
||||
assertEquals("", "pickles", map.get(PluralMapBase::MANY));
|
||||
assertEquals("", "pickles", map.get(PluralMapBase::NONE));
|
||||
assertEquals("", "pickles", map.get(PluralMapBase::CATEGORY_COUNT));
|
||||
assertEquals("", "picklefew", map.get("few"));
|
||||
assertEquals("", "pickles", map.get("many"));
|
||||
assertEquals("", "pickles", map.get("somebadform"));
|
||||
assertEquals("", "pickle", map.get(UnicodeString("one")));
|
||||
assertEquals("", "pickles", map.get(UnicodeString("many")));
|
||||
assertEquals("", "pickles", map.get(UnicodeString("somebadform")));
|
||||
assertEquals("", "pickles", map.getOther());
|
||||
}
|
||||
|
||||
void PluralMapTest::TestIterate() {
|
||||
PluralMapForPluralMapTest map;
|
||||
addVariant(PluralMapBase::OTHER, "pickles", map);
|
||||
addVariant(PluralMapBase::ONE, "pickle", map);
|
||||
addVariant(PluralMapBase::FEW, "pickleops", map);
|
||||
addVariant(PluralMapBase::FEW, "picklefew", map);
|
||||
PluralMapBase::Category index = PluralMapBase::NONE;
|
||||
const UnicodeString *current = map.next(index);
|
||||
assertEquals("", "pickles", *current);
|
||||
assertEquals("", PluralMapBase::OTHER, index);
|
||||
current = map.next(index);
|
||||
assertEquals("", "pickle", *current);
|
||||
assertEquals("", PluralMapBase::ONE, index);
|
||||
current = map.next(index);
|
||||
assertEquals("", "picklefew", *current);
|
||||
assertEquals("", PluralMapBase::FEW, index);
|
||||
current = map.next(index);
|
||||
assertEquals("", PluralMapBase::CATEGORY_COUNT, index);
|
||||
assertTrue("", current == NULL);
|
||||
|
||||
PluralMapForPluralMapTest map2;
|
||||
index = PluralMapBase::NONE;
|
||||
current = map2.next(index);
|
||||
assertEquals("", "", *current);
|
||||
assertEquals("", PluralMapBase::OTHER, index);
|
||||
current = map2.next(index);
|
||||
assertEquals("", PluralMapBase::CATEGORY_COUNT, index);
|
||||
assertTrue("", current == NULL);
|
||||
}
|
||||
|
||||
void PluralMapTest::TestEqual() {
|
||||
PluralMapForPluralMapTest control;
|
||||
addVariant(PluralMapBase::OTHER, "pickles", control);
|
||||
addVariant(PluralMapBase::ONE, "pickle", control);
|
||||
addVariant(PluralMapBase::FEW, "picklefew", control);
|
||||
|
||||
{
|
||||
PluralMapForPluralMapTest map;
|
||||
addVariant(PluralMapBase::FEW, "picklefew", map);
|
||||
addVariant(PluralMapBase::OTHER, "pickles", map);
|
||||
addVariant(PluralMapBase::ONE, "pickle", map);
|
||||
assertTrue("", control == map);
|
||||
addVariant(PluralMapBase::ONE, "pickl", map);
|
||||
assertFalse("", control == map);
|
||||
}
|
||||
{
|
||||
PluralMapForPluralMapTest map;
|
||||
addVariant(PluralMapBase::MANY, "picklemany", map);
|
||||
addVariant(PluralMapBase::OTHER, "pickles", map);
|
||||
addVariant(PluralMapBase::ONE, "pickle", map);
|
||||
assertFalse("", control == map);
|
||||
}
|
||||
}
|
||||
|
||||
void PluralMapTest::TestCopyAndAssign() {
|
||||
PluralMapForPluralMapTest control;
|
||||
addVariant(PluralMapBase::OTHER, "pickles", control);
|
||||
addVariant(PluralMapBase::ONE, "pickle", control);
|
||||
addVariant(PluralMapBase::FEW, "picklefew", control);
|
||||
{
|
||||
PluralMapForPluralMapTest *rhs = new PluralMapForPluralMapTest();
|
||||
if (rhs == NULL) {
|
||||
errln("Memory allocation error.");
|
||||
return;
|
||||
}
|
||||
addVariant(PluralMapBase::OTHER, "pickles", *rhs);
|
||||
addVariant(PluralMapBase::ONE, "pickle", *rhs);
|
||||
addVariant(PluralMapBase::FEW, "picklefew", *rhs);
|
||||
PluralMapForPluralMapTest lhs(*rhs);
|
||||
delete rhs;
|
||||
assertTrue("", lhs == control);
|
||||
}
|
||||
{
|
||||
PluralMapForPluralMapTest *rhs = new PluralMapForPluralMapTest();
|
||||
if (rhs == NULL) {
|
||||
errln("Memory allocation error.");
|
||||
return;
|
||||
}
|
||||
addVariant(PluralMapBase::OTHER, "pickles", *rhs);
|
||||
addVariant(PluralMapBase::ONE, "pickle", *rhs);
|
||||
addVariant(PluralMapBase::FEW, "picklefew", *rhs);
|
||||
PluralMapForPluralMapTest lhs;
|
||||
addVariant(PluralMapBase::OTHER, "pickles", lhs);
|
||||
addVariant(PluralMapBase::TWO, "pickletwo", lhs);
|
||||
addVariant(PluralMapBase::MANY, "picklemany", lhs);
|
||||
addVariant(PluralMapBase::FEW, "picklefew", lhs);
|
||||
lhs = *rhs;
|
||||
delete rhs;
|
||||
assertTrue("", lhs == control);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PluralMapTest::addVariant(
|
||||
PluralMapBase::Category v,
|
||||
const UnicodeString &value,
|
||||
PluralMapForPluralMapTest &map) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UnicodeString *current = map.getMutable(v, status);
|
||||
if (!assertSuccess("", status)) {
|
||||
return;
|
||||
}
|
||||
(*current) = value;
|
||||
}
|
||||
|
||||
extern IntlTest *createPluralMapTest() {
|
||||
return new PluralMapTest();
|
||||
}
|
|
@ -10,6 +10,14 @@
|
|||
// field names, please see
|
||||
// https://docs.google.com/document/d/1T2P0p953_Lh1pRwo-5CuPVrHlIBa_wcXElG-Hhg_WHM/edit?usp=sharing
|
||||
|
||||
test plus sign
|
||||
set locale ar
|
||||
set pattern +0;-#
|
||||
begin
|
||||
format output
|
||||
6 \u200F+\u0666
|
||||
-6 \u200F-\u0666
|
||||
|
||||
test basic patterns
|
||||
set locale fr_FR
|
||||
set format 1234.567
|
||||
|
@ -38,10 +46,19 @@ format output
|
|||
6 06.000
|
||||
6.000005 06.000
|
||||
6.000006 06.00001
|
||||
1002003004005 4005.000
|
||||
-1002003004005 -4005.000
|
||||
12 12.000
|
||||
12345 2345.000
|
||||
72.1234 72.1234
|
||||
|
||||
test int64
|
||||
set locale en
|
||||
begin
|
||||
format output
|
||||
1002003004005 1002003004005
|
||||
-1002003004005 -1002003004005
|
||||
|
||||
test rounding
|
||||
set locale fr
|
||||
begin
|
||||
|
|
Loading…
Add table
Reference in a new issue