ICU-13634 Merging new number formatting and parsing implementation into ICU4C, bringing number formatting code in ICU4C and ICU4J fully into sync.

X-SVN-Rev: 41269
This commit is contained in:
Shane Carr 2018-04-24 02:45:07 +00:00
commit 4c538a7d49
289 changed files with 19817 additions and 21154 deletions

View File

@ -464,12 +464,12 @@ AC_DEFUN([AC_CHECK_STRICT_COMPILE],
then
case "${host}" in
*-*-solaris*)
# Don't use -std=c99 on Solaris because of timezone check fails
# Don't use -std=c11 on Solaris because of timezone check fails
;;
*)
# Do not use -ansi. It limits us to C90, and it breaks some platforms.
# We use -std=c99 to disable the gnu99 defaults and its associated warnings
CFLAGS="$CFLAGS -std=c99"
# We use -std=c11 to disable the gnu99 defaults and its associated warnings
CFLAGS="$CFLAGS -std=c11"
;;
esac

View File

@ -111,7 +111,8 @@ util.o util_props.o parsepos.o locbased.o cwchar.o wintz.o dtintrv.o ucnvsel.o p
ulist.o uloc_tag.o icudataver.o icuplug.o listformatter.o ulistformatter.o \
sharedobject.o simpleformatter.o unifiedcache.o uloc_keytype.o \
ubiditransform.o \
pluralmap.o
pluralmap.o \
numparse_unisets.o
## Header files to install
HEADERS = $(srcdir)/unicode/*.h

View File

@ -23,6 +23,18 @@
U_NAMESPACE_BEGIN
CharString::CharString(CharString&& src) U_NOEXCEPT
: buffer(std::move(src.buffer)), len(src.len) {
src.len = 0; // not strictly necessary because we make no guarantees on the source string
}
CharString& CharString::operator=(CharString&& src) U_NOEXCEPT {
buffer = std::move(src.buffer);
len = src.len;
src.len = 0; // not strictly necessary because we make no guarantees on the source string
return *this;
}
CharString &CharString::copyFrom(const CharString &s, UErrorCode &errorCode) {
if(U_SUCCESS(errorCode) && this!=&s && ensureCapacity(s.len+1, 0, errorCode)) {
len=s.len;

View File

@ -55,6 +55,18 @@ public:
}
~CharString() {}
/**
* Move constructor; might leave src in an undefined state.
* This string will have the same contents and state that the source string had.
*/
CharString(CharString &&src) U_NOEXCEPT;
/**
* Move assignment operator; might leave src in an undefined state.
* This string will have the same contents and state that the source string had.
* The behavior is undefined if *this and src are the same object.
*/
CharString &operator=(CharString &&src) U_NOEXCEPT;
/**
* Replaces this string's contents with the other string's contents.
* CharString does not support the standard copy constructor nor

View File

@ -299,6 +299,14 @@ public:
* Destructor deletes the array (if owned).
*/
~MaybeStackArray() { releaseArray(); }
/**
* Move constructor: transfers ownership or copies the stack array.
*/
MaybeStackArray(MaybeStackArray<T, stackCapacity> &&src) U_NOEXCEPT;
/**
* Move assignment: transfers ownership or copies the stack array.
*/
MaybeStackArray<T, stackCapacity> &operator=(MaybeStackArray<T, stackCapacity> &&src) U_NOEXCEPT;
/**
* Returns the array capacity (number of T items).
* @return array capacity
@ -376,6 +384,11 @@ private:
uprv_free(ptr);
}
}
void resetToStackArray() {
ptr=stackArray;
capacity=stackCapacity;
needToRelease=FALSE;
}
/* No comparison operators with other MaybeStackArray's. */
bool operator==(const MaybeStackArray & /*other*/) {return FALSE;}
bool operator!=(const MaybeStackArray & /*other*/) {return TRUE;}
@ -398,6 +411,34 @@ private:
#endif
};
template<typename T, int32_t stackCapacity>
icu::MaybeStackArray<T, stackCapacity>::MaybeStackArray(
MaybeStackArray <T, stackCapacity>&& src) U_NOEXCEPT
: ptr(src.ptr), capacity(src.capacity), needToRelease(src.needToRelease) {
if (src.ptr == src.stackArray) {
ptr = stackArray;
uprv_memcpy(stackArray, src.stackArray, sizeof(T) * src.capacity);
} else {
src.resetToStackArray(); // take ownership away from src
}
}
template<typename T, int32_t stackCapacity>
inline MaybeStackArray <T, stackCapacity>&
MaybeStackArray<T, stackCapacity>::operator=(MaybeStackArray <T, stackCapacity>&& src) U_NOEXCEPT {
releaseArray(); // in case this instance had its own memory allocated
capacity = src.capacity;
needToRelease = src.needToRelease;
if (src.ptr == src.stackArray) {
ptr = stackArray;
uprv_memcpy(stackArray, src.stackArray, sizeof(T) * src.capacity);
} else {
ptr = src.ptr;
src.resetToStackArray(); // take ownership away from src
}
return *this;
}
template<typename T, int32_t stackCapacity>
inline T *MaybeStackArray<T, stackCapacity>::resize(int32_t newCapacity, int32_t length) {
if(newCapacity>0) {
@ -447,9 +488,7 @@ inline T *MaybeStackArray<T, stackCapacity>::orphanOrClone(int32_t length, int32
uprv_memcpy(p, ptr, (size_t)length*sizeof(T));
}
resultCapacity=length;
ptr=stackArray;
capacity=stackCapacity;
needToRelease=FALSE;
resetToStackArray();
return p;
}

View File

@ -334,6 +334,7 @@
<ClCompile Include="utf_impl.cpp" />
<ClCompile Include="listformatter.cpp" />
<ClCompile Include="ulistformatter.cpp" />
<ClCompile Include="numparse_unisets.cpp" />
<ClInclude Include="localsvc.h" />
<ClInclude Include="msvcres.h" />
<ClInclude Include="pluralmap.h" />
@ -439,6 +440,7 @@
<ClInclude Include="uinvchar.h" />
<ClInclude Include="ustr_cnv.h" />
<ClInclude Include="ustr_imp.h" />
<ClCompile Include="numparse_unisets.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="common.rc" />

View File

@ -607,6 +607,9 @@
<ClCompile Include="ubiditransform.cpp">
<Filter>bidi</Filter>
</ClCompile>
<ClCompile Include="numparse_unisets.cpp">
<Filter>formatting</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ubidi_props.h">
@ -936,6 +939,9 @@
<ClInclude Include="unicode\ubiditransform.h">
<Filter>bidi</Filter>
</ClInclude>
<ClInclude Include="numparse_unisets.h">
<Filter>formatting</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="common.rc">

View File

@ -459,6 +459,7 @@
<ClCompile Include="utf_impl.cpp" />
<ClCompile Include="listformatter.cpp" />
<ClCompile Include="ulistformatter.cpp" />
<ClCompile Include="numparse_unisets.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="localsvc.h" />
@ -565,6 +566,7 @@
<ClInclude Include="uinvchar.h" />
<ClInclude Include="ustr_cnv.h" />
<ClInclude Include="ustr_imp.h" />
<ClCompile Include="numparse_unisets.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="common.rc" />

View File

@ -0,0 +1,201 @@
// © 2018 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
// Allow implicit conversion from char16_t* to UnicodeString for this file:
// Helpful in toString methods and elsewhere.
#define UNISTR_FROM_STRING_EXPLICIT
#include "numparse_unisets.h"
#include "umutex.h"
#include "ucln_cmn.h"
#include "unicode/uniset.h"
#include "uresimp.h"
#include "cstring.h"
#include "uassert.h"
using namespace icu;
using namespace icu::numparse;
using namespace icu::numparse::impl;
using namespace icu::numparse::impl::unisets;
namespace {
static UnicodeSet* gUnicodeSets[COUNT] = {};
UnicodeSet* computeUnion(Key k1, Key k2) {
UnicodeSet* result = new UnicodeSet();
if (result == nullptr) {
return nullptr;
}
result->addAll(*gUnicodeSets[k1]);
result->addAll(*gUnicodeSets[k2]);
result->freeze();
return result;
}
UnicodeSet* computeUnion(Key k1, Key k2, Key k3) {
UnicodeSet* result = new UnicodeSet();
if (result == nullptr) {
return nullptr;
}
result->addAll(*gUnicodeSets[k1]);
result->addAll(*gUnicodeSets[k2]);
result->addAll(*gUnicodeSets[k3]);
result->freeze();
return result;
}
void saveSet(Key key, const UnicodeString& unicodeSetPattern, UErrorCode& status) {
// assert unicodeSets.get(key) == null;
gUnicodeSets[key] = new UnicodeSet(unicodeSetPattern, status);
}
class ParseDataSink : public ResourceSink {
public:
void put(const char* key, ResourceValue& value, UBool /*noFallback*/, UErrorCode& status) U_OVERRIDE {
ResourceTable contextsTable = value.getTable(status);
if (U_FAILURE(status)) { return; }
for (int i = 0; contextsTable.getKeyAndValue(i, key, value); i++) {
if (uprv_strcmp(key, "date") == 0) {
// ignore
} else {
ResourceTable strictnessTable = value.getTable(status);
if (U_FAILURE(status)) { return; }
for (int j = 0; strictnessTable.getKeyAndValue(j, key, value); j++) {
bool isLenient = (uprv_strcmp(key, "lenient") == 0);
ResourceArray array = value.getArray(status);
if (U_FAILURE(status)) { return; }
for (int k = 0; k < array.getSize(); k++) {
array.getValue(k, value);
UnicodeString str = value.getUnicodeString(status);
if (U_FAILURE(status)) { return; }
// There is both lenient and strict data for comma/period,
// but not for any of the other symbols.
if (str.indexOf(u'.') != -1) {
saveSet(isLenient ? PERIOD : STRICT_PERIOD, str, status);
} else if (str.indexOf(u',') != -1) {
saveSet(isLenient ? COMMA : STRICT_COMMA, str, status);
} else if (str.indexOf(u'+') != -1) {
saveSet(PLUS_SIGN, str, status);
} else if (str.indexOf(u'') != -1) {
saveSet(MINUS_SIGN, str, status);
} else if (str.indexOf(u'$') != -1) {
saveSet(DOLLAR_SIGN, str, status);
} else if (str.indexOf(u'£') != -1) {
saveSet(POUND_SIGN, str, status);
} else if (str.indexOf(u'') != -1) {
saveSet(RUPEE_SIGN, str, status);
}
if (U_FAILURE(status)) { return; }
}
}
}
}
}
};
icu::UInitOnce gNumberParseUniSetsInitOnce = U_INITONCE_INITIALIZER;
UBool U_CALLCONV cleanupNumberParseUniSets() {
for (int32_t i = 0; i < COUNT; i++) {
delete gUnicodeSets[i];
gUnicodeSets[i] = nullptr;
}
gNumberParseUniSetsInitOnce.reset();
return TRUE;
}
void U_CALLCONV initNumberParseUniSets(UErrorCode& status) {
ucln_common_registerCleanup(UCLN_COMMON_NUMPARSE_UNISETS, cleanupNumberParseUniSets);
gUnicodeSets[EMPTY] = new UnicodeSet();
// These sets were decided after discussion with icu-design@. See tickets #13084 and #13309.
// Zs+TAB is "horizontal whitespace" according to UTS #18 (blank property).
gUnicodeSets[DEFAULT_IGNORABLES] = new UnicodeSet(
u"[[:Zs:][\\u0009][:Bidi_Control:][:Variation_Selector:]]", status);
gUnicodeSets[STRICT_IGNORABLES] = new UnicodeSet(u"[[:Bidi_Control:]]", status);
LocalUResourceBundlePointer rb(ures_open(nullptr, "root", &status));
if (U_FAILURE(status)) { return; }
ParseDataSink sink;
ures_getAllItemsWithFallback(rb.getAlias(), "parse", sink, status);
if (U_FAILURE(status)) { return; }
// TODO: Should there be fallback behavior if for some reason these sets didn't get populated?
U_ASSERT(gUnicodeSets[COMMA] != nullptr);
U_ASSERT(gUnicodeSets[STRICT_COMMA] != nullptr);
U_ASSERT(gUnicodeSets[PERIOD] != nullptr);
U_ASSERT(gUnicodeSets[STRICT_PERIOD] != nullptr);
gUnicodeSets[OTHER_GROUPING_SEPARATORS] = new UnicodeSet(
u"['٬‘’'\\u0020\\u00A0\\u2000-\\u200A\\u202F\\u205F\\u3000]", status);
gUnicodeSets[ALL_SEPARATORS] = computeUnion(COMMA, PERIOD, OTHER_GROUPING_SEPARATORS);
gUnicodeSets[STRICT_ALL_SEPARATORS] = computeUnion(
STRICT_COMMA, STRICT_PERIOD, OTHER_GROUPING_SEPARATORS);
U_ASSERT(gUnicodeSets[MINUS_SIGN] != nullptr);
U_ASSERT(gUnicodeSets[PLUS_SIGN] != nullptr);
gUnicodeSets[PERCENT_SIGN] = new UnicodeSet(u"[%٪]", status);
gUnicodeSets[PERMILLE_SIGN] = new UnicodeSet(u"[‰؉]", status);
gUnicodeSets[INFINITY_KEY] = new UnicodeSet(u"[∞]", status);
U_ASSERT(gUnicodeSets[DOLLAR_SIGN] != nullptr);
U_ASSERT(gUnicodeSets[POUND_SIGN] != nullptr);
U_ASSERT(gUnicodeSets[RUPEE_SIGN] != nullptr);
gUnicodeSets[YEN_SIGN] = new UnicodeSet(u"\\uffe5]", status);
gUnicodeSets[DIGITS] = new UnicodeSet(u"[:digit:]", status);
gUnicodeSets[DIGITS_OR_ALL_SEPARATORS] = computeUnion(DIGITS, ALL_SEPARATORS);
gUnicodeSets[DIGITS_OR_STRICT_ALL_SEPARATORS] = computeUnion(DIGITS, STRICT_ALL_SEPARATORS);
for (int32_t i = 0; i < COUNT; i++) {
gUnicodeSets[i]->freeze();
}
}
}
const UnicodeSet* unisets::get(Key key) {
UErrorCode localStatus = U_ZERO_ERROR;
umtx_initOnce(gNumberParseUniSetsInitOnce, &initNumberParseUniSets, localStatus);
if (U_FAILURE(localStatus)) {
// TODO: This returns non-null in Java, and callers assume that.
return nullptr;
}
return gUnicodeSets[key];
}
Key unisets::chooseFrom(UnicodeString str, Key key1) {
return get(key1)->contains(str) ? key1 : COUNT;
}
Key unisets::chooseFrom(UnicodeString str, Key key1, Key key2) {
return get(key1)->contains(str) ? key1 : chooseFrom(str, key2);
}
//Key unisets::chooseCurrency(UnicodeString str) {
// if (get(DOLLAR_SIGN)->contains(str)) {
// return DOLLAR_SIGN;
// } else if (get(POUND_SIGN)->contains(str)) {
// return POUND_SIGN;
// } else if (get(RUPEE_SIGN)->contains(str)) {
// return RUPEE_SIGN;
// } else if (get(YEN_SIGN)->contains(str)) {
// return YEN_SIGN;
// } else {
// return COUNT;
// }
//}
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -0,0 +1,89 @@
// © 2018 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
// This file is in common instead of i18n because it is needed by ucurr.cpp.
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#ifndef __NUMPARSE_UNISETS_H__
#define __NUMPARSE_UNISETS_H__
#include "unicode/uniset.h"
#include "unicode/unistr.h"
U_NAMESPACE_BEGIN namespace numparse {
namespace impl {
namespace unisets {
enum Key {
EMPTY,
// Ignorables
DEFAULT_IGNORABLES,
STRICT_IGNORABLES,
// Separators
// Notes:
// - COMMA is a superset of STRICT_COMMA
// - PERIOD is a superset of SCRICT_PERIOD
// - ALL_SEPARATORS is the union of COMMA, PERIOD, and OTHER_GROUPING_SEPARATORS
// - STRICT_ALL_SEPARATORS is the union of STRICT_COMMA, STRICT_PERIOD, and OTHER_GRP_SEPARATORS
COMMA,
PERIOD,
STRICT_COMMA,
STRICT_PERIOD,
OTHER_GROUPING_SEPARATORS,
ALL_SEPARATORS,
STRICT_ALL_SEPARATORS,
// Symbols
MINUS_SIGN,
PLUS_SIGN,
PERCENT_SIGN,
PERMILLE_SIGN,
INFINITY_KEY, // INFINITY is defined in cmath
// Currency Symbols
DOLLAR_SIGN,
POUND_SIGN,
RUPEE_SIGN,
YEN_SIGN, // not in CLDR data, but Currency.java wants it
// Other
DIGITS,
// Combined Separators with Digits (for lead code points)
DIGITS_OR_ALL_SEPARATORS,
DIGITS_OR_STRICT_ALL_SEPARATORS,
// The number of elements in the enum. Also used to indicate null.
COUNT
};
const UnicodeSet* get(Key key);
Key chooseFrom(UnicodeString str, Key key1);
Key chooseFrom(UnicodeString str, Key key1, Key key2);
// Unused in C++:
// Key chooseCurrency(UnicodeString str);
// Used instead:
static const struct {
Key key;
UChar32 exemplar;
} kCurrencyEntries[] = {
{DOLLAR_SIGN, u'$'},
{POUND_SIGN, u'£'},
{RUPEE_SIGN, u''},
{YEN_SIGN, u'¥'},
};
} // namespace unisets
} // namespace impl
} // namespace numparse
U_NAMESPACE_END
#endif //__NUMPARSE_UNISETS_H__
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -533,6 +533,28 @@ uprv_fmin(double x, double y)
return (x > y ? y : x);
}
U_CAPI UBool U_EXPORT2
uprv_add32_overflow(int32_t a, int32_t b, int32_t* res) {
// NOTE: Some compilers (GCC, Clang) have primitives available, like __builtin_add_overflow.
// This function could be optimized by calling one of those primitives.
auto a64 = static_cast<int64_t>(a);
auto b64 = static_cast<int64_t>(b);
int64_t res64 = a64 + b64;
*res = static_cast<int32_t>(res64);
return res64 != *res;
}
U_CAPI UBool U_EXPORT2
uprv_mul32_overflow(int32_t a, int32_t b, int32_t* res) {
// NOTE: Some compilers (GCC, Clang) have primitives available, like __builtin_mul_overflow.
// This function could be optimized by calling one of those primitives.
auto a64 = static_cast<int64_t>(a);
auto b64 = static_cast<int64_t>(b);
int64_t res64 = a64 * b64;
*res = static_cast<int32_t>(res64);
return res64 != *res;
}
/**
* Truncates the given double.
* trunc(3.3) = 3.0, trunc (-3.3) = -3.0

View File

@ -391,6 +391,32 @@ U_INTERNAL double U_EXPORT2 uprv_log(double d);
*/
U_INTERNAL double U_EXPORT2 uprv_round(double x);
/**
* Adds the signed integers a and b, storing the result in res.
* Checks for signed integer overflow.
* Similar to the GCC/Clang extension __builtin_add_overflow
*
* @param a The first operand.
* @param b The second operand.
* @param res a + b
* @return true if overflow occurred; false if no overflow occurred.
* @internal
*/
U_INTERNAL UBool U_EXPORT2 uprv_add32_overflow(int32_t a, int32_t b, int32_t* res);
/**
* Multiplies the signed integers a and b, storing the result in res.
* Checks for signed integer overflow.
* Similar to the GCC/Clang extension __builtin_mul_overflow
*
* @param a The first multiplicand.
* @param b The second multiplicand.
* @param res a * b
* @return true if overflow occurred; false if no overflow occurred.
* @internal
*/
U_INTERNAL UBool U_EXPORT2 uprv_mul32_overflow(int32_t a, int32_t b, int32_t* res);
#if 0
/**
* Returns the number of digits after the decimal point in a double number x.

View File

@ -33,6 +33,7 @@ Please keep the order of enums declared in same order
as the cleanup functions are suppose to be called. */
typedef enum ECleanupCommonType {
UCLN_COMMON_START = -1,
UCLN_COMMON_NUMPARSE_UNISETS,
UCLN_COMMON_USPREP,
UCLN_COMMON_BREAKITERATOR,
UCLN_COMMON_RBBI,

View File

@ -16,10 +16,14 @@
#include "unicode/ures.h"
#include "unicode/ustring.h"
#include "unicode/parsepos.h"
#include "unicode/uniset.h"
#include "unicode/usetiter.h"
#include "unicode/utf16.h"
#include "ustr_imp.h"
#include "charstr.h"
#include "cmemory.h"
#include "cstring.h"
#include "numparse_unisets.h"
#include "uassert.h"
#include "umutex.h"
#include "ucln_cmn.h"
@ -65,14 +69,6 @@ static const int32_t POW10[] = { 1, 10, 100, 1000, 10000, 100000,
static const int32_t MAX_POW10 = UPRV_LENGTHOF(POW10) - 1;
// Defines equivalent currency symbols.
static const char *EQUIV_CURRENCY_SYMBOLS[][2] = {
{"\\u00a5", "\\uffe5"},
{"$", "\\ufe69"},
{"$", "\\uff04"},
{"\\u20a8", "\\u20b9"},
{"\\u00a3", "\\u20a4"}};
#define ISO_CURRENCY_CODE_LENGTH 3
//------------------------------------------------------------
@ -1287,17 +1283,28 @@ static void
linearSearch(const CurrencyNameStruct* currencyNames,
int32_t begin, int32_t end,
const UChar* text, int32_t textLen,
int32_t *partialMatchLen,
int32_t *maxMatchLen, int32_t* maxMatchIndex) {
int32_t initialPartialMatchLen = *partialMatchLen;
for (int32_t index = begin; index <= end; ++index) {
int32_t len = currencyNames[index].currencyNameLen;
if (len > *maxMatchLen && len <= textLen &&
uprv_memcmp(currencyNames[index].currencyName, text, len * sizeof(UChar)) == 0) {
*partialMatchLen = MAX(*partialMatchLen, len);
*maxMatchIndex = index;
*maxMatchLen = len;
#ifdef UCURR_DEBUG
printf("maxMatchIndex = %d, maxMatchLen = %d\n",
*maxMatchIndex, *maxMatchLen);
#endif
} else {
// Check for partial matches.
for (int32_t i=initialPartialMatchLen; i<MIN(len, textLen); i++) {
if (currencyNames[index].currencyName[i] != text[i]) {
break;
}
*partialMatchLen = MAX(*partialMatchLen, i + 1);
}
}
}
}
@ -1314,7 +1321,8 @@ linearSearch(const CurrencyNameStruct* currencyNames,
static void
searchCurrencyName(const CurrencyNameStruct* currencyNames,
int32_t total_currency_count,
const UChar* text, int32_t textLen,
const UChar* text, int32_t textLen,
int32_t *partialMatchLen,
int32_t* maxMatchLen, int32_t* maxMatchIndex) {
*maxMatchIndex = -1;
*maxMatchLen = 0;
@ -1344,6 +1352,7 @@ searchCurrencyName(const CurrencyNameStruct* currencyNames,
if (binarySearchBegin == -1) { // did not find the range
break;
}
*partialMatchLen = MAX(*partialMatchLen, index + 1);
if (matchIndex != -1) {
// find an exact match for text from text[0] to text[index]
// in currencyNames array.
@ -1354,6 +1363,7 @@ searchCurrencyName(const CurrencyNameStruct* currencyNames,
// linear search if within threshold.
linearSearch(currencyNames, binarySearchBegin, binarySearchEnd,
text, textLen,
partialMatchLen,
maxMatchLen, maxMatchIndex);
break;
}
@ -1422,19 +1432,13 @@ currency_cache_cleanup(void) {
}
U_CAPI void
uprv_parseCurrency(const char* locale,
const icu::UnicodeString& text,
icu::ParsePosition& pos,
int8_t type,
UChar* result,
UErrorCode& ec)
{
U_NAMESPACE_USE
if (U_FAILURE(ec)) {
return;
}
/**
* Loads the currency name data from the cache, or from resource bundles if necessary.
* The refCount is automatically incremented. It is the caller's responsibility
* to decrement it when done!
*/
static CurrencyNameCacheEntry*
getCacheEntry(const char* locale, UErrorCode& ec) {
int32_t total_currency_name_count = 0;
CurrencyNameStruct* currencyNames = NULL;
@ -1455,17 +1459,13 @@ uprv_parseCurrency(const char* locale,
}
if (found != -1) {
cacheEntry = currCache[found];
currencyNames = cacheEntry->currencyNames;
total_currency_name_count = cacheEntry->totalCurrencyNameCount;
currencySymbols = cacheEntry->currencySymbols;
total_currency_symbol_count = cacheEntry->totalCurrencySymbolCount;
++(cacheEntry->refCount);
}
umtx_unlock(&gCurrencyCacheMutex);
if (found == -1) {
collectCurrencyNames(locale, &currencyNames, &total_currency_name_count, &currencySymbols, &total_currency_symbol_count, ec);
if (U_FAILURE(ec)) {
return;
return NULL;
}
umtx_lock(&gCurrencyCacheMutex);
// check again.
@ -1505,15 +1505,45 @@ uprv_parseCurrency(const char* locale,
deleteCurrencyNames(currencyNames, total_currency_name_count);
deleteCurrencyNames(currencySymbols, total_currency_symbol_count);
cacheEntry = currCache[found];
currencyNames = cacheEntry->currencyNames;
total_currency_name_count = cacheEntry->totalCurrencyNameCount;
currencySymbols = cacheEntry->currencySymbols;
total_currency_symbol_count = cacheEntry->totalCurrencySymbolCount;
++(cacheEntry->refCount);
}
umtx_unlock(&gCurrencyCacheMutex);
}
return cacheEntry;
}
static void releaseCacheEntry(CurrencyNameCacheEntry* cacheEntry) {
umtx_lock(&gCurrencyCacheMutex);
--(cacheEntry->refCount);
if (cacheEntry->refCount == 0) { // remove
deleteCacheEntry(cacheEntry);
}
umtx_unlock(&gCurrencyCacheMutex);
}
U_CAPI void
uprv_parseCurrency(const char* locale,
const icu::UnicodeString& text,
icu::ParsePosition& pos,
int8_t type,
int32_t* partialMatchLen,
UChar* result,
UErrorCode& ec) {
U_NAMESPACE_USE
if (U_FAILURE(ec)) {
return;
}
CurrencyNameCacheEntry* cacheEntry = getCacheEntry(locale, ec);
if (U_FAILURE(ec)) {
return;
}
int32_t total_currency_name_count = cacheEntry->totalCurrencyNameCount;
CurrencyNameStruct* currencyNames = cacheEntry->currencyNames;
int32_t total_currency_symbol_count = cacheEntry->totalCurrencySymbolCount;
CurrencyNameStruct* currencySymbols = cacheEntry->currencySymbols;
int32_t start = pos.getIndex();
UChar inputText[MAX_CURRENCY_NAME_LEN];
@ -1523,11 +1553,14 @@ uprv_parseCurrency(const char* locale,
UErrorCode ec1 = U_ZERO_ERROR;
textLen = u_strToUpper(upperText, MAX_CURRENCY_NAME_LEN, inputText, textLen, locale, &ec1);
// Make sure partialMatchLen is initialized
*partialMatchLen = 0;
int32_t max = 0;
int32_t matchIndex = -1;
// case in-sensitive comparision against currency names
searchCurrencyName(currencyNames, total_currency_name_count,
upperText, textLen, &max, &matchIndex);
upperText, textLen, partialMatchLen, &max, &matchIndex);
#ifdef UCURR_DEBUG
printf("search in names, max = %d, matchIndex = %d\n", max, matchIndex);
@ -1538,7 +1571,8 @@ uprv_parseCurrency(const char* locale,
if (type != UCURR_LONG_NAME) { // not name only
// case sensitive comparison against currency symbols and ISO code.
searchCurrencyName(currencySymbols, total_currency_symbol_count,
inputText, textLen,
inputText, textLen,
partialMatchLen,
&maxInSymbol, &matchIndexInSymbol);
}
@ -1555,15 +1589,38 @@ uprv_parseCurrency(const char* locale,
} else if (maxInSymbol >= max && matchIndexInSymbol != -1) {
u_charsToUChars(currencySymbols[matchIndexInSymbol].IsoCode, result, 4);
pos.setIndex(start + maxInSymbol);
}
}
// decrease reference count
umtx_lock(&gCurrencyCacheMutex);
--(cacheEntry->refCount);
if (cacheEntry->refCount == 0) { // remove
deleteCacheEntry(cacheEntry);
releaseCacheEntry(cacheEntry);
}
void uprv_currencyLeads(const char* locale, icu::UnicodeSet& result, UErrorCode& ec) {
U_NAMESPACE_USE
if (U_FAILURE(ec)) {
return;
}
umtx_unlock(&gCurrencyCacheMutex);
CurrencyNameCacheEntry* cacheEntry = getCacheEntry(locale, ec);
if (U_FAILURE(ec)) {
return;
}
for (int32_t i=0; i<cacheEntry->totalCurrencySymbolCount; i++) {
const CurrencyNameStruct& info = cacheEntry->currencySymbols[i];
UChar32 cp;
U16_GET(info.currencyName, 0, 0, info.currencyNameLen, cp);
result.add(cp);
}
for (int32_t i=0; i<cacheEntry->totalCurrencyNameCount; i++) {
const CurrencyNameStruct& info = cacheEntry->currencyNames[i];
UChar32 cp;
U16_GET(info.currencyName, 0, 0, info.currencyNameLen, cp);
result.add(cp);
}
// decrease reference count
releaseCacheEntry(cacheEntry);
}
@ -2144,16 +2201,21 @@ static void U_CALLCONV initIsoCodes(UErrorCode &status) {
}
static void populateCurrSymbolsEquiv(icu::Hashtable *hash, UErrorCode &status) {
if (U_FAILURE(status)) {
return;
}
int32_t length = UPRV_LENGTHOF(EQUIV_CURRENCY_SYMBOLS);
for (int32_t i = 0; i < length; ++i) {
icu::UnicodeString lhs(EQUIV_CURRENCY_SYMBOLS[i][0], -1, US_INV);
icu::UnicodeString rhs(EQUIV_CURRENCY_SYMBOLS[i][1], -1, US_INV);
makeEquivalent(lhs.unescape(), rhs.unescape(), hash, status);
if (U_FAILURE(status)) {
return;
using namespace icu::numparse::impl;
if (U_FAILURE(status)) { return; }
for (auto& entry : unisets::kCurrencyEntries) {
UnicodeString exemplar(entry.exemplar);
const UnicodeSet* set = unisets::get(entry.key);
if (set == nullptr) { return; }
UnicodeSetIterator it(*set);
while (it.next()) {
UnicodeString value = it.getString();
if (value == exemplar) {
// No need to mark the exemplar character as an equivalent
continue;
}
makeEquivalent(exemplar, value, hash, status);
if (U_FAILURE(status)) { return; }
}
}
}

View File

@ -13,6 +13,7 @@
#include "unicode/utypes.h"
#include "unicode/unistr.h"
#include "unicode/parsepos.h"
#include "unicode/uniset.h"
/**
* Internal method. Given a currency ISO code and a locale, return
@ -36,6 +37,8 @@ uprv_getStaticCurrencyName(const UChar* iso, const char* loc,
* match, then the display name is preferred, unless it's length
* is less than 3.
*
* The parameters must not be NULL.
*
* @param locale the locale of the display names to match
* @param text the text to parse
* @param pos input-output position; on input, the position within
@ -43,6 +46,8 @@ uprv_getStaticCurrencyName(const UChar* iso, const char* loc,
* on output, the position after the last matched character. If
* the parse fails, the position in unchanged upon output.
* @param type currency type to parse against, LONG_NAME only or not
* @param partialMatchLen The length of the longest matching prefix;
* this may be nonzero even if no full currency was matched.
* @return the ISO 4217 code, as a string, of the best match, or
* null if there is no match
*
@ -53,9 +58,21 @@ uprv_parseCurrency(const char* locale,
const icu::UnicodeString& text,
icu::ParsePosition& pos,
int8_t type,
int32_t* partialMatchLen,
UChar* result,
UErrorCode& ec);
/**
* Puts all possible first-characters of a currency into the
* specified UnicodeSet.
*
* @param locale the locale of the display names of interest
* @param result the UnicodeSet to which to add the starting characters
*/
void uprv_currencyLeads(const char* locale, icu::UnicodeSet& result, UErrorCode& ec);
#endif /* #ifndef _UCURR_IMP_H_ */
//eof

View File

@ -196,20 +196,6 @@
# define U_PLATFORM U_PF_UNKNOWN
#endif
/**
* \def UPRV_INCOMPLETE_CPP11_SUPPORT
* This switch turns off ICU 60 NumberFormatter code.
* By default, this switch is enabled on AIX and z/OS,
* which have poor C++11 support.
*
* NOTE: This switch is intended to be temporary; see #13393.
*
* @internal
*/
#ifndef UPRV_INCOMPLETE_CPP11_SUPPORT
# define UPRV_INCOMPLETE_CPP11_SUPPORT (U_PLATFORM == U_PF_AIX || U_PLATFORM == U_PF_OS390 || U_PLATFORM == U_PF_SOLARIS )
#endif
/**
* \def CYGWINMSVC
* Defined if this is Windows with Cygwin, but using MSVC rather than gcc.

View File

@ -1892,7 +1892,7 @@ public:
UnicodeString &fastCopyFrom(const UnicodeString &src);
/**
* Move assignment operator, might leave src in bogus state.
* Move assignment operator; might leave src in bogus state.
* This string will have the same contents and state that the source string had.
* The behavior is undefined if *this and src are the same object.
* @param src source string
@ -1905,7 +1905,7 @@ public:
// do not use #ifndef U_HIDE_DRAFT_API for moveFrom, needed by non-draft API
/**
* Move assignment, might leave src in bogus state.
* Move assignment; might leave src in bogus state.
* This string will have the same contents and state that the source string had.
* The behavior is undefined if *this and src are the same object.
*
@ -3314,7 +3314,7 @@ public:
UnicodeString(const UnicodeString& that);
/**
* Move constructor, might leave src in bogus state.
* Move constructor; might leave src in bogus state.
* This string will have the same contents and state that the source string had.
* @param src source string
* @stable ICU 56

View File

@ -541,13 +541,14 @@ typedef enum UErrorCode {
U_FORMAT_INEXACT_ERROR, /**< Cannot format a number exactly and rounding mode is ROUND_UNNECESSARY @stable ICU 4.8 */
#ifndef U_HIDE_DRAFT_API
U_NUMBER_ARG_OUTOFBOUNDS_ERROR, /**< The argument to a NumberFormatter helper method was out of bounds; the bounds are usually 0 to 999. @draft ICU 61 */
U_NUMBER_SKELETON_SYNTAX_ERROR, /**< The number skeleton passed to C++ NumberFormatter or C UNumberFormatter was invalid or contained a syntax error. @draft ICU 62 */
#endif // U_HIDE_DRAFT_API
#ifndef U_HIDE_DEPRECATED_API
/**
* One more than the highest normal formatting API error code.
* @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420.
*/
U_FMT_PARSE_ERROR_LIMIT = 0x10113,
U_FMT_PARSE_ERROR_LIMIT = 0x10114,
#endif // U_HIDE_DEPRECATED_API
/*

View File

@ -126,7 +126,8 @@ _uFmtErrorName[U_FMT_PARSE_ERROR_LIMIT - U_FMT_PARSE_ERROR_START] = {
"U_DEFAULT_KEYWORD_MISSING",
"U_DECIMAL_NUMBER_SYNTAX_ERROR",
"U_FORMAT_INEXACT_ERROR",
"U_NUMBER_ARG_OUTOFBOUNDS_ERROR"
"U_NUMBER_ARG_OUTOFBOUNDS_ERROR",
"U_NUMBER_SKELETON_SYNTAX_ERROR",
};
static const char * const

View File

@ -7,7 +7,7 @@
## Flags for ICU 59+
CXXFLAGS += -std=c++11
CFLAGS += -std=c99
CFLAGS += -std=c11
## Flags for position independent code
SHAREDLIBCFLAGS = -KPIC

View File

@ -4354,12 +4354,12 @@ $as_echo "$ac_use_strict_options" >&6; }
then
case "${host}" in
*-*-solaris*)
# Don't use -std=c99 on Solaris because of timezone check fails
# Don't use -std=c11 on Solaris because of timezone check fails
;;
*)
# Do not use -ansi. It limits us to C90, and it breaks some platforms.
# We use -std=c99 to disable the gnu99 defaults and its associated warnings
CFLAGS="$CFLAGS -std=c99"
# We use -std=c11 to disable the gnu99 defaults and its associated warnings
CFLAGS="$CFLAGS -std=c11"
;;
esac
@ -7473,15 +7473,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
;;
esac
# GCC >= 4.4 supports UTF16 string literals. The CFLAGS and CXXFLAGS may change in the future.
# GCC >= 4.4 supports UTF16 string literals. As of ICU 62, both C and C++ files require them.
if test "$CHECK_UTF16_STRING_RESULT" = "unknown"; then
if test "$GCC" = yes; then
OLD_CFLAGS="${CFLAGS}"
CFLAGS="${CFLAGS} -std=gnu99"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
static const char16_t test[] = u"This is a UTF16 literal string.";
static const unsigned short test[] = u"This is a UTF16 literal string.";
int
main ()
@ -7498,10 +7496,7 @@ else
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
if test "$CC_UTF16_STRING" = 1; then
UCONFIG_CFLAGS="${UCONFIG_CFLAGS} -std=gnu99"
CHECK_UTF16_STRING_RESULT="C only";
else
CFLAGS="${OLD_CFLAGS}"
fi
fi
if test "$GXX" = yes; then

View File

@ -1038,19 +1038,14 @@ case "${host}" in
;;
esac
# GCC >= 4.4 supports UTF16 string literals. The CFLAGS and CXXFLAGS may change in the future.
# GCC >= 4.4 supports UTF16 string literals. As of spring 2018, all tested compilers support them.
if test "$CHECK_UTF16_STRING_RESULT" = "unknown"; then
if test "$GCC" = yes; then
OLD_CFLAGS="${CFLAGS}"
CFLAGS="${CFLAGS} -std=gnu99"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
static const char16_t test[] = u"This is a UTF16 literal string.";
static const unsigned short test[] = u"This is a UTF16 literal string.";
]], [[]])],[CC_UTF16_STRING=1],[CC_UTF16_STRING=0])
if test "$CC_UTF16_STRING" = 1; then
UCONFIG_CFLAGS="${UCONFIG_CFLAGS} -std=gnu99"
CHECK_UTF16_STRING_RESULT="C only";
else
CFLAGS="${OLD_CFLAGS}"
fi
fi
if test "$GXX" = yes; then

View File

@ -65,8 +65,8 @@ LDFLAGS += $(LDFLAGSICUI18N)
LIBS = $(LIBICUUC) $(DEFAULT_LIBS)
OBJECTS = ucln_in.o \
fmtable.o format.o msgfmt.o umsg.o numfmt.o unum.o decimfmt.o decimalformatpattern.o dcfmtsym.o \
digitlst.o fmtable_cnv.o \
fmtable.o format.o msgfmt.o umsg.o numfmt.o unum.o decimfmt.o dcfmtsym.o \
fmtable_cnv.o \
choicfmt.o datefmt.o smpdtfmt.o reldtfmt.o dtfmtsym.o udat.o dtptngen.o udatpg.o \
nfrs.o nfrule.o nfsubs.o rbnf.o numsys.o unumsys.o ucsdet.o \
ucal.o calendar.o gregocal.o timezone.o simpletz.o olsontz.o \
@ -92,25 +92,24 @@ csdetect.o csmatch.o csr2022.o csrecog.o csrmbcs.o csrsbcs.o csrucode.o csrutf8.
wintzimpl.o windtfmt.o winnmfmt.o basictz.o dtrule.o rbtz.o tzrule.o tztrans.o vtzone.o zonemeta.o \
standardplural.o upluralrules.o plurrule.o plurfmt.o selfmt.o dtitvfmt.o dtitvinf.o udateintervalformat.o \
tmunit.o tmutamt.o tmutfmt.o currpinf.o \
uspoof.o uspoof_impl.o uspoof_build.o uspoof_conf.o decfmtst.o smpdtfst.o \
uspoof.o uspoof_impl.o uspoof_build.o uspoof_conf.o smpdtfst.o \
ztrans.o zrule.o vzone.o fphdlimp.o fpositer.o ufieldpositer.o \
decNumber.o decContext.o alphaindex.o tznames.o tznames_impl.o tzgnames.o \
tzfmt.o compactdecimalformat.o gender.o region.o scriptset.o \
uregion.o reldatefmt.o quantityformatter.o measunit.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 dayperiodrules.o \
nounit.o \
sharedbreakiterator.o scientificnumberformatter.o dayperiodrules.o nounit.o \
number_affixutils.o number_compact.o number_decimalquantity.o \
number_decimfmtprops.o number_fluent.o number_formatimpl.o number_grouping.o \
number_integerwidth.o number_longnames.o number_modifiers.o number_notation.o \
number_padding.o number_patternmodifier.o number_patternstring.o \
number_rounding.o number_scientific.o number_stringbuilder.o \
number_rounding.o number_scientific.o number_stringbuilder.o number_utils.o \
number_mapper.o number_multiplier.o number_currencysymbols.o number_skeletons.o number_capi.o \
double-conversion.o double-conversion-bignum-dtoa.o double-conversion-bignum.o \
double-conversion-cached-powers.o double-conversion-diy-fp.o \
double-conversion-fast-dtoa.o double-conversion-strtod.o
double-conversion-fast-dtoa.o double-conversion-strtod.o \
numparse_stringsegment.o numparse_parsednumber.o numparse_impl.o \
numparse_symbols.o numparse_decimal.o numparse_scientific.o numparse_currency.o \
numparse_affixes.o numparse_compositions.o numparse_validators.o \
## Header files to install

View File

@ -1,698 +0,0 @@
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
* Copyright (C) 2015, International Business Machines
* Corporation and others. All Rights Reserved.
*
* file name: affixpatternparser.cpp
*/
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/dcfmtsym.h"
#include "unicode/plurrule.h"
#include "unicode/strenum.h"
#include "unicode/ucurr.h"
#include "unicode/ustring.h"
#include "affixpatternparser.h"
#include "charstr.h"
#include "precision.h"
#include "uassert.h"
#include "unistrappender.h"
static const UChar gDefaultSymbols[] = {0xa4, 0xa4, 0xa4};
static const UChar gPercent = 0x25;
static const UChar gPerMill = 0x2030;
static const UChar gNegative = 0x2D;
static const 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(static_cast<uint8_t>(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, static_cast<uint8_t>(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 &currencyAffixInfo,
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
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -1,402 +0,0 @@
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* Copyright (C) 2015, International Business Machines
* Corporation and others. All Rights Reserved.
*******************************************************************************
* affixpatternparser.h
*
* created on: 2015jan06
* created by: Travis Keep
*/
#ifndef __AFFIX_PATTERN_PARSER_H__
#define __AFFIX_PATTERN_PARSER_H__
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/unistr.h"
#include "unicode/uobject.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 &currencyAffixInfo,
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 /* #if !UCONFIG_NO_FORMATTING */
#endif // __AFFIX_PATTERN_PARSER_H__

File diff suppressed because it is too large Load Diff

View File

@ -17,21 +17,32 @@
#include "unicode/currunit.h"
#include "unicode/ustring.h"
#include "cstring.h"
#include "uinvchar.h"
static constexpr char16_t kDefaultCurrency[] = u"XXX";
U_NAMESPACE_BEGIN
CurrencyUnit::CurrencyUnit(ConstChar16Ptr _isoCode, UErrorCode& ec) {
*isoCode = 0;
if (U_SUCCESS(ec)) {
if (_isoCode != nullptr && u_strlen(_isoCode)==3) {
u_strcpy(isoCode, _isoCode);
char simpleIsoCode[4];
u_UCharsToChars(isoCode, simpleIsoCode, 4);
initCurrency(simpleIsoCode);
} else {
ec = U_ILLEGAL_ARGUMENT_ERROR;
}
// The constructor always leaves the CurrencyUnit in a valid state (with a 3-character currency code).
// Note: in ICU4J Currency.getInstance(), we check string length for 3, but in ICU4C we allow a
// non-NUL-terminated string to be passed as an argument, so it is not possible to check length.
const char16_t* isoCodeToUse;
if (U_FAILURE(ec) || _isoCode == nullptr) {
isoCodeToUse = kDefaultCurrency;
} else if (!uprv_isInvariantUString(_isoCode, 3)) {
// TODO: Perform a more strict ASCII check like in ICU4J isAlpha3Code?
isoCodeToUse = kDefaultCurrency;
ec = U_INVARIANT_CONVERSION_ERROR;
} else {
isoCodeToUse = _isoCode;
}
// TODO: Perform uppercasing here like in ICU4J Currency.getInstance()?
uprv_memcpy(isoCode, isoCodeToUse, sizeof(UChar) * 3);
isoCode[3] = 0;
char simpleIsoCode[4];
u_UCharsToChars(isoCode, simpleIsoCode, 4);
initCurrency(simpleIsoCode);
}
CurrencyUnit::CurrencyUnit(const CurrencyUnit& other) : MeasureUnit(other) {
@ -52,7 +63,7 @@ CurrencyUnit::CurrencyUnit(const MeasureUnit& other, UErrorCode& ec) : MeasureUn
}
CurrencyUnit::CurrencyUnit() : MeasureUnit() {
u_strcpy(isoCode, u"XXX");
u_strcpy(isoCode, kDefaultCurrency);
char simpleIsoCode[4];
u_UCharsToChars(isoCode, simpleIsoCode, 4);
initCurrency(simpleIsoCode);

View File

@ -6,6 +6,8 @@
* Corporation and others. All Rights Reserved.
********************************************************************************/
// TODO: This file, and the corresponding UCONFIG settings, may be obsolete.
#ifndef DCFMTIMP_H
#define DCFMTIMP_H

View File

@ -1,251 +0,0 @@
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* Copyright (C) 2009-2016, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*
* This file contains the class DecimalFormatStaticSets
*
* DecimalFormatStaticSets holds the UnicodeSets that are needed for lenient
* parsing of decimal and group separators.
********************************************************************************
*/
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/unistr.h"
#include "unicode/uniset.h"
#include "unicode/uchar.h"
#include "cmemory.h"
#include "cstring.h"
#include "uassert.h"
#include "ucln_in.h"
#include "umutex.h"
#include "decfmtst.h"
U_NAMESPACE_BEGIN
//------------------------------------------------------------------------------
//
// Unicode Set pattern strings for all of the required constant sets.
// Initialized with hex values for portability to EBCDIC based machines.
// Really ugly, but there's no good way to avoid it.
//
//------------------------------------------------------------------------------
static const UChar gDotEquivalentsPattern[] = {
// [ . \u2024 \u3002 \uFE12 \uFE52 \uFF0E \uFF61 ]
0x005B, 0x002E, 0x2024, 0x3002, 0xFE12, 0xFE52, 0xFF0E, 0xFF61, 0x005D, 0x0000};
static const UChar gCommaEquivalentsPattern[] = {
// [ , \u060C \u066B \u3001 \uFE10 \uFE11 \uFE50 \uFE51 \uFF0C \uFF64 ]
0x005B, 0x002C, 0x060C, 0x066B, 0x3001, 0xFE10, 0xFE11, 0xFE50, 0xFE51, 0xFF0C, 0xFF64, 0x005D, 0x0000};
static const UChar gOtherGroupingSeparatorsPattern[] = {
// [ \ SPACE ' NBSP \u066C \u2000 - \u200A \u2018 \u2019 \u202F \u205F \u3000 \uFF07 ]
0x005B, 0x005C, 0x0020, 0x0027, 0x00A0, 0x066C, 0x2000, 0x002D, 0x200A, 0x2018, 0x2019, 0x202F, 0x205F, 0x3000, 0xFF07, 0x005D, 0x0000};
static const UChar gDashEquivalentsPattern[] = {
// [ \ - HYPHEN F_DASH N_DASH MINUS ]
0x005B, 0x005C, 0x002D, 0x2010, 0x2012, 0x2013, 0x2212, 0x005D, 0x0000};
static const UChar gStrictDotEquivalentsPattern[] = {
// [ . \u2024 \uFE52 \uFF0E \uFF61 ]
0x005B, 0x002E, 0x2024, 0xFE52, 0xFF0E, 0xFF61, 0x005D, 0x0000};
static const UChar gStrictCommaEquivalentsPattern[] = {
// [ , \u066B \uFE10 \uFE50 \uFF0C ]
0x005B, 0x002C, 0x066B, 0xFE10, 0xFE50, 0xFF0C, 0x005D, 0x0000};
static const UChar gStrictOtherGroupingSeparatorsPattern[] = {
// [ \ SPACE ' NBSP \u066C \u2000 - \u200A \u2018 \u2019 \u202F \u205F \u3000 \uFF07 ]
0x005B, 0x005C, 0x0020, 0x0027, 0x00A0, 0x066C, 0x2000, 0x002D, 0x200A, 0x2018, 0x2019, 0x202F, 0x205F, 0x3000, 0xFF07, 0x005D, 0x0000};
static const UChar gStrictDashEquivalentsPattern[] = {
// [ \ - MINUS ]
0x005B, 0x005C, 0x002D, 0x2212, 0x005D, 0x0000};
static const UChar32 gMinusSigns[] = {
0x002D,
0x207B,
0x208B,
0x2212,
0x2796,
0xFE63,
0xFF0D};
static const UChar32 gPlusSigns[] = {
0x002B,
0x207A,
0x208A,
0x2795,
0xfB29,
0xFE62,
0xFF0B};
static void initUnicodeSet(const UChar32 *raw, int32_t len, UnicodeSet *s) {
for (int32_t i = 0; i < len; ++i) {
s->add(raw[i]);
}
}
DecimalFormatStaticSets::DecimalFormatStaticSets(UErrorCode &status)
: fDotEquivalents(NULL),
fCommaEquivalents(NULL),
fOtherGroupingSeparators(NULL),
fDashEquivalents(NULL),
fStrictDotEquivalents(NULL),
fStrictCommaEquivalents(NULL),
fStrictOtherGroupingSeparators(NULL),
fStrictDashEquivalents(NULL),
fDefaultGroupingSeparators(NULL),
fStrictDefaultGroupingSeparators(NULL),
fMinusSigns(NULL),
fPlusSigns(NULL)
{
fDotEquivalents = new UnicodeSet(UnicodeString(TRUE, gDotEquivalentsPattern, -1), status);
fCommaEquivalents = new UnicodeSet(UnicodeString(TRUE, gCommaEquivalentsPattern, -1), status);
fOtherGroupingSeparators = new UnicodeSet(UnicodeString(TRUE, gOtherGroupingSeparatorsPattern, -1), status);
fDashEquivalents = new UnicodeSet(UnicodeString(TRUE, gDashEquivalentsPattern, -1), status);
fStrictDotEquivalents = new UnicodeSet(UnicodeString(TRUE, gStrictDotEquivalentsPattern, -1), status);
fStrictCommaEquivalents = new UnicodeSet(UnicodeString(TRUE, gStrictCommaEquivalentsPattern, -1), status);
fStrictOtherGroupingSeparators = new UnicodeSet(UnicodeString(TRUE, gStrictOtherGroupingSeparatorsPattern, -1), status);
fStrictDashEquivalents = new UnicodeSet(UnicodeString(TRUE, gStrictDashEquivalentsPattern, -1), status);
fDefaultGroupingSeparators = new UnicodeSet(*fDotEquivalents);
fDefaultGroupingSeparators->addAll(*fCommaEquivalents);
fDefaultGroupingSeparators->addAll(*fOtherGroupingSeparators);
fStrictDefaultGroupingSeparators = new UnicodeSet(*fStrictDotEquivalents);
fStrictDefaultGroupingSeparators->addAll(*fStrictCommaEquivalents);
fStrictDefaultGroupingSeparators->addAll(*fStrictOtherGroupingSeparators);
fMinusSigns = new UnicodeSet();
fPlusSigns = new UnicodeSet();
// Check for null pointers
if (fDotEquivalents == NULL || fCommaEquivalents == NULL || fOtherGroupingSeparators == NULL || fDashEquivalents == NULL ||
fStrictDotEquivalents == NULL || fStrictCommaEquivalents == NULL || fStrictOtherGroupingSeparators == NULL || fStrictDashEquivalents == NULL ||
fDefaultGroupingSeparators == NULL || fStrictOtherGroupingSeparators == NULL ||
fMinusSigns == NULL || fPlusSigns == NULL) {
cleanup();
status = U_MEMORY_ALLOCATION_ERROR;
return;
}
initUnicodeSet(
gMinusSigns,
UPRV_LENGTHOF(gMinusSigns),
fMinusSigns);
initUnicodeSet(
gPlusSigns,
UPRV_LENGTHOF(gPlusSigns),
fPlusSigns);
// Freeze all the sets
fDotEquivalents->freeze();
fCommaEquivalents->freeze();
fOtherGroupingSeparators->freeze();
fDashEquivalents->freeze();
fStrictDotEquivalents->freeze();
fStrictCommaEquivalents->freeze();
fStrictOtherGroupingSeparators->freeze();
fStrictDashEquivalents->freeze();
fDefaultGroupingSeparators->freeze();
fStrictDefaultGroupingSeparators->freeze();
fMinusSigns->freeze();
fPlusSigns->freeze();
}
DecimalFormatStaticSets::~DecimalFormatStaticSets() {
cleanup();
}
void DecimalFormatStaticSets::cleanup() { // Be sure to clean up newly added fields!
delete fDotEquivalents; fDotEquivalents = NULL;
delete fCommaEquivalents; fCommaEquivalents = NULL;
delete fOtherGroupingSeparators; fOtherGroupingSeparators = NULL;
delete fDashEquivalents; fDashEquivalents = NULL;
delete fStrictDotEquivalents; fStrictDotEquivalents = NULL;
delete fStrictCommaEquivalents; fStrictCommaEquivalents = NULL;
delete fStrictOtherGroupingSeparators; fStrictOtherGroupingSeparators = NULL;
delete fStrictDashEquivalents; fStrictDashEquivalents = NULL;
delete fDefaultGroupingSeparators; fDefaultGroupingSeparators = NULL;
delete fStrictDefaultGroupingSeparators; fStrictDefaultGroupingSeparators = NULL;
delete fStrictOtherGroupingSeparators; fStrictOtherGroupingSeparators = NULL;
delete fMinusSigns; fMinusSigns = NULL;
delete fPlusSigns; fPlusSigns = NULL;
}
static DecimalFormatStaticSets *gStaticSets;
static icu::UInitOnce gStaticSetsInitOnce = U_INITONCE_INITIALIZER;
//------------------------------------------------------------------------------
//
// decfmt_cleanup Memory cleanup function, free/delete all
// cached memory. Called by ICU's u_cleanup() function.
//
//------------------------------------------------------------------------------
U_CDECL_BEGIN
static UBool U_CALLCONV
decimfmt_cleanup(void)
{
delete gStaticSets;
gStaticSets = NULL;
gStaticSetsInitOnce.reset();
return TRUE;
}
static void U_CALLCONV initSets(UErrorCode &status) {
U_ASSERT(gStaticSets == NULL);
ucln_i18n_registerCleanup(UCLN_I18N_DECFMT, decimfmt_cleanup);
gStaticSets = new DecimalFormatStaticSets(status);
if (U_FAILURE(status)) {
delete gStaticSets;
gStaticSets = NULL;
return;
}
if (gStaticSets == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
}
}
U_CDECL_END
const DecimalFormatStaticSets *DecimalFormatStaticSets::getStaticSets(UErrorCode &status) {
umtx_initOnce(gStaticSetsInitOnce, initSets, status);
return gStaticSets;
}
const UnicodeSet *DecimalFormatStaticSets::getSimilarDecimals(UChar32 decimal, UBool strictParse)
{
UErrorCode status = U_ZERO_ERROR;
umtx_initOnce(gStaticSetsInitOnce, initSets, status);
if (U_FAILURE(status)) {
return NULL;
}
if (gStaticSets->fDotEquivalents->contains(decimal)) {
return strictParse ? gStaticSets->fStrictDotEquivalents : gStaticSets->fDotEquivalents;
}
if (gStaticSets->fCommaEquivalents->contains(decimal)) {
return strictParse ? gStaticSets->fStrictCommaEquivalents : gStaticSets->fCommaEquivalents;
}
// if there is no match, return NULL
return NULL;
}
U_NAMESPACE_END
#endif // !UCONFIG_NO_FORMATTING

View File

@ -1,69 +0,0 @@
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* Copyright (C) 2009-2016, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*
* This file contains declarations for the class DecimalFormatStaticSets
*
* DecimalFormatStaticSets holds the UnicodeSets that are needed for lenient
* parsing of decimal and group separators.
********************************************************************************
*/
#ifndef DECFMTST_H
#define DECFMTST_H
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/uobject.h"
U_NAMESPACE_BEGIN
class UnicodeSet;
class DecimalFormatStaticSets : public UMemory
{
public:
// Constructor and Destructor not for general use.
// Public to permit access from plain C implementation functions.
DecimalFormatStaticSets(UErrorCode &status);
~DecimalFormatStaticSets();
/**
* Return a pointer to a lazy-initialized singleton instance of this class.
*/
static const DecimalFormatStaticSets *getStaticSets(UErrorCode &status);
static const UnicodeSet *getSimilarDecimals(UChar32 decimal, UBool strictParse);
UnicodeSet *fDotEquivalents;
UnicodeSet *fCommaEquivalents;
UnicodeSet *fOtherGroupingSeparators;
UnicodeSet *fDashEquivalents;
UnicodeSet *fStrictDotEquivalents;
UnicodeSet *fStrictCommaEquivalents;
UnicodeSet *fStrictOtherGroupingSeparators;
UnicodeSet *fStrictDashEquivalents;
UnicodeSet *fDefaultGroupingSeparators;
UnicodeSet *fStrictDefaultGroupingSeparators;
UnicodeSet *fMinusSigns;
UnicodeSet *fPlusSigns;
private:
void cleanup();
};
U_NAMESPACE_END
#endif // !UCONFIG_NO_FORMATTING
#endif // DECFMTST_H

View File

@ -1,656 +0,0 @@
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* Copyright (C) 1997-2015, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
#include "uassert.h"
#include "decimalformatpattern.h"
#if !UCONFIG_NO_FORMATTING
#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);
#else
#define debug(x)
#endif
U_NAMESPACE_BEGIN
// TODO: Travis Keep: Copied from numfmt.cpp
static int32_t kDoubleIntegerDigits = 309;
static int32_t kDoubleFractionDigits = 340;
// TODO: Travis Keep: Copied from numfmt.cpp
static int32_t gDefaultMaxIntegerDigits = 2000000000;
// TODO: Travis Keep: This function was copied from format.cpp
static void syntaxError(const UnicodeString& pattern,
int32_t pos,
UParseError& parseError) {
parseError.offset = pos;
parseError.line=0; // we are not using line number
// for pre-context
int32_t start = (pos < U_PARSE_CONTEXT_LEN)? 0 : (pos - (U_PARSE_CONTEXT_LEN-1
/* subtract 1 so that we have room for null*/));
int32_t stop = pos;
pattern.extract(start,stop-start,parseError.preContext,0);
//null terminate the buffer
parseError.preContext[stop-start] = 0;
//for post-context
start = pattern.moveIndex32(pos, 1);
stop = pos + U_PARSE_CONTEXT_LEN - 1;
if (stop > pattern.length()) {
stop = pattern.length();
}
pattern.extract(start, stop - start, parseError.postContext, 0);
//null terminate the buffer
parseError.postContext[stop-start]= 0;
}
DecimalFormatPattern::DecimalFormatPattern()
: fMinimumIntegerDigits(1),
fMaximumIntegerDigits(gDefaultMaxIntegerDigits),
fMinimumFractionDigits(0),
fMaximumFractionDigits(3),
fUseSignificantDigits(FALSE),
fMinimumSignificantDigits(1),
fMaximumSignificantDigits(6),
fUseExponentialNotation(FALSE),
fMinExponentDigits(0),
fExponentSignAlwaysShown(FALSE),
fCurrencySignCount(fgCurrencySignCountZero),
fGroupingUsed(TRUE),
fGroupingSize(0),
fGroupingSize2(0),
fMultiplier(1),
fDecimalSeparatorAlwaysShown(FALSE),
fFormatWidth(0),
fRoundingIncrementUsed(FALSE),
fRoundingIncrement(),
fPad(kDefaultPad),
fNegPatternsBogus(TRUE),
fPosPatternsBogus(TRUE),
fNegPrefixPattern(),
fNegSuffixPattern(),
fPosPrefixPattern(),
fPosSuffixPattern(),
fPadPosition(DecimalFormatPattern::kPadBeforePrefix) {
}
DecimalFormatPatternParser::DecimalFormatPatternParser() :
fZeroDigit(kPatternZeroDigit),
fSigDigit(kPatternSignificantDigit),
fGroupingSeparator((UChar)kPatternGroupingSeparator),
fDecimalSeparator((UChar)kPatternDecimalSeparator),
fPercent((UChar)kPatternPercent),
fPerMill((UChar)kPatternPerMill),
fDigit((UChar)kPatternDigit),
fSeparator((UChar)kPatternSeparator),
fExponent((UChar)kPatternExponent),
fPlus((UChar)kPatternPlus),
fMinus((UChar)kPatternMinus),
fPadEscape((UChar)kPatternPadEscape) {
}
void DecimalFormatPatternParser::useSymbols(
const DecimalFormatSymbols& symbols) {
fZeroDigit = symbols.getConstSymbol(
DecimalFormatSymbols::kZeroDigitSymbol).char32At(0);
fSigDigit = symbols.getConstSymbol(
DecimalFormatSymbols::kSignificantDigitSymbol).char32At(0);
fGroupingSeparator = symbols.getConstSymbol(
DecimalFormatSymbols::kGroupingSeparatorSymbol);
fDecimalSeparator = symbols.getConstSymbol(
DecimalFormatSymbols::kDecimalSeparatorSymbol);
fPercent = symbols.getConstSymbol(
DecimalFormatSymbols::kPercentSymbol);
fPerMill = symbols.getConstSymbol(
DecimalFormatSymbols::kPerMillSymbol);
fDigit = symbols.getConstSymbol(
DecimalFormatSymbols::kDigitSymbol);
fSeparator = symbols.getConstSymbol(
DecimalFormatSymbols::kPatternSeparatorSymbol);
fExponent = symbols.getConstSymbol(
DecimalFormatSymbols::kExponentialSymbol);
fPlus = symbols.getConstSymbol(
DecimalFormatSymbols::kPlusSignSymbol);
fMinus = symbols.getConstSymbol(
DecimalFormatSymbols::kMinusSignSymbol);
fPadEscape = symbols.getConstSymbol(
DecimalFormatSymbols::kPadEscapeSymbol);
}
void
DecimalFormatPatternParser::applyPatternWithoutExpandAffix(
const UnicodeString& pattern,
DecimalFormatPattern& out,
UParseError& parseError,
UErrorCode& status) {
if (U_FAILURE(status))
{
return;
}
out = DecimalFormatPattern();
// Clear error struct
parseError.offset = -1;
parseError.preContext[0] = parseError.postContext[0] = (UChar)0;
// TODO: Travis Keep: This won't always work.
UChar nineDigit = (UChar)(fZeroDigit + 9);
int32_t digitLen = fDigit.length();
int32_t groupSepLen = fGroupingSeparator.length();
int32_t decimalSepLen = fDecimalSeparator.length();
int32_t pos = 0;
int32_t patLen = pattern.length();
// Part 0 is the positive pattern. Part 1, if present, is the negative
// pattern.
for (int32_t part=0; part<2 && pos<patLen; ++part) {
// The subpart ranges from 0 to 4: 0=pattern proper, 1=prefix,
// 2=suffix, 3=prefix in quote, 4=suffix in quote. Subpart 0 is
// between the prefix and suffix, and consists of pattern
// characters. In the prefix and suffix, percent, perMill, and
// currency symbols are recognized and translated.
int32_t subpart = 1, sub0Start = 0, sub0Limit = 0, sub2Limit = 0;
// It's important that we don't change any fields of this object
// prematurely. We set the following variables for the multiplier,
// grouping, etc., and then only change the actual object fields if
// everything parses correctly. This also lets us register
// the data from part 0 and ignore the part 1, except for the
// prefix and suffix.
UnicodeString prefix;
UnicodeString suffix;
int32_t decimalPos = -1;
int32_t multiplier = 1;
int32_t digitLeftCount = 0, zeroDigitCount = 0, digitRightCount = 0, sigDigitCount = 0;
int8_t groupingCount = -1;
int8_t groupingCount2 = -1;
int32_t padPos = -1;
UChar32 padChar = 0;
int32_t roundingPos = -1;
DigitList roundingInc;
int8_t expDigits = -1;
UBool expSignAlways = FALSE;
// The affix is either the prefix or the suffix.
UnicodeString* affix = &prefix;
int32_t start = pos;
UBool isPartDone = FALSE;
UChar32 ch;
for (; !isPartDone && pos < patLen; ) {
// Todo: account for surrogate pairs
ch = pattern.char32At(pos);
switch (subpart) {
case 0: // Pattern proper subpart (between prefix & suffix)
// Process the digits, decimal, and grouping characters. We
// record five pieces of information. We expect the digits
// to occur in the pattern ####00.00####, and we record the
// number of left digits, zero (central) digits, and right
// digits. The position of the last grouping character is
// recorded (should be somewhere within the first two blocks
// of characters), as is the position of the decimal point,
// if any (should be in the zero digits). If there is no
// decimal point, then there should be no right digits.
if (pattern.compare(pos, digitLen, fDigit) == 0) {
if (zeroDigitCount > 0 || sigDigitCount > 0) {
++digitRightCount;
} else {
++digitLeftCount;
}
if (groupingCount >= 0 && decimalPos < 0) {
++groupingCount;
}
pos += digitLen;
} else if ((ch >= fZeroDigit && ch <= nineDigit) ||
ch == fSigDigit) {
if (digitRightCount > 0) {
// Unexpected '0'
debug("Unexpected '0'")
status = U_UNEXPECTED_TOKEN;
syntaxError(pattern,pos,parseError);
return;
}
if (ch == fSigDigit) {
++sigDigitCount;
} else {
if (ch != fZeroDigit && roundingPos < 0) {
roundingPos = digitLeftCount + zeroDigitCount;
}
if (roundingPos >= 0) {
roundingInc.append((char)(ch - fZeroDigit + '0'));
}
++zeroDigitCount;
}
if (groupingCount >= 0 && decimalPos < 0) {
++groupingCount;
}
pos += U16_LENGTH(ch);
} else if (pattern.compare(pos, groupSepLen, fGroupingSeparator) == 0) {
if (decimalPos >= 0) {
// Grouping separator after decimal
debug("Grouping separator after decimal")
status = U_UNEXPECTED_TOKEN;
syntaxError(pattern,pos,parseError);
return;
}
groupingCount2 = groupingCount;
groupingCount = 0;
pos += groupSepLen;
} else if (pattern.compare(pos, decimalSepLen, fDecimalSeparator) == 0) {
if (decimalPos >= 0) {
// Multiple decimal separators
debug("Multiple decimal separators")
status = U_MULTIPLE_DECIMAL_SEPARATORS;
syntaxError(pattern,pos,parseError);
return;
}
// Intentionally incorporate the digitRightCount,
// even though it is illegal for this to be > 0
// at this point. We check pattern syntax below.
decimalPos = digitLeftCount + zeroDigitCount + digitRightCount;
pos += decimalSepLen;
} else {
if (pattern.compare(pos, fExponent.length(), fExponent) == 0) {
if (expDigits >= 0) {
// Multiple exponential symbols
debug("Multiple exponential symbols")
status = U_MULTIPLE_EXPONENTIAL_SYMBOLS;
syntaxError(pattern,pos,parseError);
return;
}
if (groupingCount >= 0) {
// Grouping separator in exponential pattern
debug("Grouping separator in exponential pattern")
status = U_MALFORMED_EXPONENTIAL_PATTERN;
syntaxError(pattern,pos,parseError);
return;
}
pos += fExponent.length();
// Check for positive prefix
if (pos < patLen
&& pattern.compare(pos, fPlus.length(), fPlus) == 0) {
expSignAlways = TRUE;
pos += fPlus.length();
}
// Use lookahead to parse out the exponential part of the
// pattern, then jump into suffix subpart.
expDigits = 0;
while (pos < patLen &&
pattern.char32At(pos) == fZeroDigit) {
++expDigits;
pos += U16_LENGTH(fZeroDigit);
}
// 1. Require at least one mantissa pattern digit
// 2. Disallow "#+ @" in mantissa
// 3. Require at least one exponent pattern digit
if (((digitLeftCount + zeroDigitCount) < 1 &&
(sigDigitCount + digitRightCount) < 1) ||
(sigDigitCount > 0 && digitLeftCount > 0) ||
expDigits < 1) {
// Malformed exponential pattern
debug("Malformed exponential pattern")
status = U_MALFORMED_EXPONENTIAL_PATTERN;
syntaxError(pattern,pos,parseError);
return;
}
}
// Transition to suffix subpart
subpart = 2; // suffix subpart
affix = &suffix;
sub0Limit = pos;
continue;
}
break;
case 1: // Prefix subpart
case 2: // Suffix subpart
// Process the prefix / suffix characters
// Process unquoted characters seen in prefix or suffix
// subpart.
// Several syntax characters implicitly begins the
// next subpart if we are in the prefix; otherwise
// they are illegal if unquoted.
if (!pattern.compare(pos, digitLen, fDigit) ||
!pattern.compare(pos, groupSepLen, fGroupingSeparator) ||
!pattern.compare(pos, decimalSepLen, fDecimalSeparator) ||
(ch >= fZeroDigit && ch <= nineDigit) ||
ch == fSigDigit) {
if (subpart == 1) { // prefix subpart
subpart = 0; // pattern proper subpart
sub0Start = pos; // Reprocess this character
continue;
} else {
status = U_UNQUOTED_SPECIAL;
syntaxError(pattern,pos,parseError);
return;
}
} else if (ch == kCurrencySign) {
affix->append(kQuote); // Encode currency
// Use lookahead to determine if the currency sign is
// doubled or not.
U_ASSERT(U16_LENGTH(kCurrencySign) == 1);
if ((pos+1) < pattern.length() && pattern[pos+1] == kCurrencySign) {
affix->append(kCurrencySign);
++pos; // Skip over the doubled character
if ((pos+1) < pattern.length() &&
pattern[pos+1] == kCurrencySign) {
affix->append(kCurrencySign);
++pos; // Skip over the doubled character
out.fCurrencySignCount = fgCurrencySignCountInPluralFormat;
} else {
out.fCurrencySignCount = fgCurrencySignCountInISOFormat;
}
} else {
out.fCurrencySignCount = fgCurrencySignCountInSymbolFormat;
}
// Fall through to append(ch)
} else if (ch == kQuote) {
// A quote outside quotes indicates either the opening
// quote or two quotes, which is a quote literal. That is,
// we have the first quote in 'do' or o''clock.
U_ASSERT(U16_LENGTH(kQuote) == 1);
++pos;
if (pos < pattern.length() && pattern[pos] == kQuote) {
affix->append(kQuote); // Encode quote
// Fall through to append(ch)
} else {
subpart += 2; // open quote
continue;
}
} else if (pattern.compare(pos, fSeparator.length(), fSeparator) == 0) {
// Don't allow separators in the prefix, and don't allow
// separators in the second pattern (part == 1).
if (subpart == 1 || part == 1) {
// Unexpected separator
debug("Unexpected separator")
status = U_UNEXPECTED_TOKEN;
syntaxError(pattern,pos,parseError);
return;
}
sub2Limit = pos;
isPartDone = TRUE; // Go to next part
pos += fSeparator.length();
break;
} else if (pattern.compare(pos, fPercent.length(), fPercent) == 0) {
// Next handle characters which are appended directly.
if (multiplier != 1) {
// Too many percent/perMill characters
debug("Too many percent characters")
status = U_MULTIPLE_PERCENT_SYMBOLS;
syntaxError(pattern,pos,parseError);
return;
}
affix->append(kQuote); // Encode percent/perMill
affix->append(kPatternPercent); // Use unlocalized pattern char
multiplier = 100;
pos += fPercent.length();
break;
} else if (pattern.compare(pos, fPerMill.length(), fPerMill) == 0) {
// Next handle characters which are appended directly.
if (multiplier != 1) {
// Too many percent/perMill characters
debug("Too many perMill characters")
status = U_MULTIPLE_PERMILL_SYMBOLS;
syntaxError(pattern,pos,parseError);
return;
}
affix->append(kQuote); // Encode percent/perMill
affix->append(kPatternPerMill); // Use unlocalized pattern char
multiplier = 1000;
pos += fPerMill.length();
break;
} else if (pattern.compare(pos, fPadEscape.length(), fPadEscape) == 0) {
if (padPos >= 0 || // Multiple pad specifiers
(pos+1) == pattern.length()) { // Nothing after padEscape
debug("Multiple pad specifiers")
status = U_MULTIPLE_PAD_SPECIFIERS;
syntaxError(pattern,pos,parseError);
return;
}
padPos = pos;
pos += fPadEscape.length();
padChar = pattern.char32At(pos);
pos += U16_LENGTH(padChar);
break;
} else if (pattern.compare(pos, fMinus.length(), fMinus) == 0) {
affix->append(kQuote); // Encode minus
affix->append(kPatternMinus);
pos += fMinus.length();
break;
} else if (pattern.compare(pos, fPlus.length(), fPlus) == 0) {
affix->append(kQuote); // Encode plus
affix->append(kPatternPlus);
pos += fPlus.length();
break;
}
// Unquoted, non-special characters fall through to here, as
// well as other code which needs to append something to the
// affix.
affix->append(ch);
pos += U16_LENGTH(ch);
break;
case 3: // Prefix subpart, in quote
case 4: // Suffix subpart, in quote
// A quote within quotes indicates either the closing
// quote or two quotes, which is a quote literal. That is,
// we have the second quote in 'do' or 'don''t'.
if (ch == kQuote) {
++pos;
if (pos < pattern.length() && pattern[pos] == kQuote) {
affix->append(kQuote); // Encode quote
// Fall through to append(ch)
} else {
subpart -= 2; // close quote
continue;
}
}
affix->append(ch);
pos += U16_LENGTH(ch);
break;
}
}
if (sub0Limit == 0) {
sub0Limit = pattern.length();
}
if (sub2Limit == 0) {
sub2Limit = pattern.length();
}
/* Handle patterns with no '0' pattern character. These patterns
* are legal, but must be recodified to make sense. "##.###" ->
* "#0.###". ".###" -> ".0##".
*
* We allow patterns of the form "####" to produce a zeroDigitCount
* of zero (got that?); although this seems like it might make it
* possible for format() to produce empty strings, format() checks
* for this condition and outputs a zero digit in this situation.
* Having a zeroDigitCount of zero yields a minimum integer digits
* of zero, which allows proper round-trip patterns. We don't want
* "#" to become "#0" when toPattern() is called (even though that's
* what it really is, semantically).
*/
if (zeroDigitCount == 0 && sigDigitCount == 0 &&
digitLeftCount > 0 && decimalPos >= 0) {
// Handle "###.###" and "###." and ".###"
int n = decimalPos;
if (n == 0)
++n; // Handle ".###"
digitRightCount = digitLeftCount - n;
digitLeftCount = n - 1;
zeroDigitCount = 1;
}
// Do syntax checking on the digits, decimal points, and quotes.
if ((decimalPos < 0 && digitRightCount > 0 && sigDigitCount == 0) ||
(decimalPos >= 0 &&
(sigDigitCount > 0 ||
decimalPos < digitLeftCount ||
decimalPos > (digitLeftCount + zeroDigitCount))) ||
groupingCount == 0 || groupingCount2 == 0 ||
(sigDigitCount > 0 && zeroDigitCount > 0) ||
subpart > 2)
{ // subpart > 2 == unmatched quote
debug("Syntax error")
status = U_PATTERN_SYNTAX_ERROR;
syntaxError(pattern,pos,parseError);
return;
}
// Make sure pad is at legal position before or after affix.
if (padPos >= 0) {
if (padPos == start) {
padPos = DecimalFormatPattern::kPadBeforePrefix;
} else if (padPos+2 == sub0Start) {
padPos = DecimalFormatPattern::kPadAfterPrefix;
} else if (padPos == sub0Limit) {
padPos = DecimalFormatPattern::kPadBeforeSuffix;
} else if (padPos+2 == sub2Limit) {
padPos = DecimalFormatPattern::kPadAfterSuffix;
} else {
// Illegal pad position
debug("Illegal pad position")
status = U_ILLEGAL_PAD_POSITION;
syntaxError(pattern,pos,parseError);
return;
}
}
if (part == 0) {
out.fPosPatternsBogus = FALSE;
out.fPosPrefixPattern = prefix;
out.fPosSuffixPattern = suffix;
out.fNegPatternsBogus = TRUE;
out.fNegPrefixPattern.remove();
out.fNegSuffixPattern.remove();
out.fUseExponentialNotation = (expDigits >= 0);
if (out.fUseExponentialNotation) {
out.fMinExponentDigits = expDigits;
}
out.fExponentSignAlwaysShown = expSignAlways;
int32_t digitTotalCount = digitLeftCount + zeroDigitCount + digitRightCount;
// The effectiveDecimalPos is the position the decimal is at or
// would be at if there is no decimal. Note that if
// decimalPos<0, then digitTotalCount == digitLeftCount +
// zeroDigitCount.
int32_t effectiveDecimalPos = decimalPos >= 0 ? decimalPos : digitTotalCount;
UBool isSigDig = (sigDigitCount > 0);
out.fUseSignificantDigits = isSigDig;
if (isSigDig) {
out.fMinimumSignificantDigits = sigDigitCount;
out.fMaximumSignificantDigits = sigDigitCount + digitRightCount;
} else {
int32_t minInt = effectiveDecimalPos - digitLeftCount;
out.fMinimumIntegerDigits = minInt;
out.fMaximumIntegerDigits = out.fUseExponentialNotation
? digitLeftCount + out.fMinimumIntegerDigits
: gDefaultMaxIntegerDigits;
out.fMaximumFractionDigits = decimalPos >= 0
? (digitTotalCount - decimalPos) : 0;
out.fMinimumFractionDigits = decimalPos >= 0
? (digitLeftCount + zeroDigitCount - decimalPos) : 0;
}
out.fGroupingUsed = groupingCount > 0;
out.fGroupingSize = (groupingCount > 0) ? groupingCount : 0;
out.fGroupingSize2 = (groupingCount2 > 0 && groupingCount2 != groupingCount)
? groupingCount2 : 0;
out.fMultiplier = multiplier;
out.fDecimalSeparatorAlwaysShown = decimalPos == 0
|| decimalPos == digitTotalCount;
if (padPos >= 0) {
out.fPadPosition = (DecimalFormatPattern::EPadPosition) padPos;
// To compute the format width, first set up sub0Limit -
// sub0Start. Add in prefix/suffix length later.
// fFormatWidth = prefix.length() + suffix.length() +
// sub0Limit - sub0Start;
out.fFormatWidth = sub0Limit - sub0Start;
out.fPad = padChar;
} else {
out.fFormatWidth = 0;
}
if (roundingPos >= 0) {
out.fRoundingIncrementUsed = TRUE;
roundingInc.setDecimalAt(effectiveDecimalPos - roundingPos);
out.fRoundingIncrement = roundingInc;
} else {
out.fRoundingIncrementUsed = FALSE;
}
} else {
out.fNegPatternsBogus = FALSE;
out.fNegPrefixPattern = prefix;
out.fNegSuffixPattern = suffix;
}
}
if (pattern.length() == 0) {
out.fNegPatternsBogus = TRUE;
out.fNegPrefixPattern.remove();
out.fNegSuffixPattern.remove();
out.fPosPatternsBogus = FALSE;
out.fPosPrefixPattern.remove();
out.fPosSuffixPattern.remove();
out.fMinimumIntegerDigits = 0;
out.fMaximumIntegerDigits = kDoubleIntegerDigits;
out.fMinimumFractionDigits = 0;
out.fMaximumFractionDigits = kDoubleFractionDigits;
out.fUseExponentialNotation = FALSE;
out.fCurrencySignCount = fgCurrencySignCountZero;
out.fGroupingUsed = FALSE;
out.fGroupingSize = 0;
out.fGroupingSize2 = 0;
out.fMultiplier = 1;
out.fDecimalSeparatorAlwaysShown = FALSE;
out.fFormatWidth = 0;
out.fRoundingIncrementUsed = FALSE;
}
// If there was no negative pattern, or if the negative pattern is
// identical to the positive pattern, then prepend the minus sign to the
// positive pattern to form the negative pattern.
if (out.fNegPatternsBogus ||
(out.fNegPrefixPattern == out.fPosPrefixPattern
&& out.fNegSuffixPattern == out.fPosSuffixPattern)) {
out.fNegPatternsBogus = FALSE;
out.fNegSuffixPattern = out.fPosSuffixPattern;
out.fNegPrefixPattern.remove();
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
#endif /* !UCONFIG_NO_FORMATTING */

View File

@ -1,106 +0,0 @@
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* Copyright (C) 1997-2015, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
#ifndef _DECIMAL_FORMAT_PATTERN
#define _DECIMAL_FORMAT_PATTERN
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/uobject.h"
#include "unicode/unistr.h"
#include "digitlst.h"
#include "affixpatternparser.h"
U_NAMESPACE_BEGIN
// currency sign count
enum CurrencySignCount {
fgCurrencySignCountZero,
fgCurrencySignCountInSymbolFormat,
fgCurrencySignCountInISOFormat,
fgCurrencySignCountInPluralFormat
};
class DecimalFormatSymbols;
struct DecimalFormatPattern : public UMemory {
enum EPadPosition {
kPadBeforePrefix,
kPadAfterPrefix,
kPadBeforeSuffix,
kPadAfterSuffix
};
DecimalFormatPattern();
int32_t fMinimumIntegerDigits;
int32_t fMaximumIntegerDigits;
int32_t fMinimumFractionDigits;
int32_t fMaximumFractionDigits;
UBool fUseSignificantDigits;
int32_t fMinimumSignificantDigits;
int32_t fMaximumSignificantDigits;
UBool fUseExponentialNotation;
int32_t fMinExponentDigits;
UBool fExponentSignAlwaysShown;
int32_t fCurrencySignCount;
UBool fGroupingUsed;
int32_t fGroupingSize;
int32_t fGroupingSize2;
int32_t fMultiplier;
UBool fDecimalSeparatorAlwaysShown;
int32_t fFormatWidth;
UBool fRoundingIncrementUsed;
DigitList fRoundingIncrement;
UChar32 fPad;
UBool fNegPatternsBogus;
UBool fPosPatternsBogus;
UnicodeString fNegPrefixPattern;
UnicodeString fNegSuffixPattern;
UnicodeString fPosPrefixPattern;
UnicodeString fPosSuffixPattern;
AffixPattern fNegPrefixAffix;
AffixPattern fNegSuffixAffix;
AffixPattern fPosPrefixAffix;
AffixPattern fPosSuffixAffix;
EPadPosition fPadPosition;
};
class DecimalFormatPatternParser : public UMemory {
public:
DecimalFormatPatternParser();
void useSymbols(const DecimalFormatSymbols& symbols);
void applyPatternWithoutExpandAffix(
const UnicodeString& pattern,
DecimalFormatPattern& out,
UParseError& parseError,
UErrorCode& status);
private:
DecimalFormatPatternParser(const DecimalFormatPatternParser&);
DecimalFormatPatternParser& operator=(DecimalFormatPatternParser& rhs);
UChar32 fZeroDigit;
UChar32 fSigDigit;
UnicodeString fGroupingSeparator;
UnicodeString fDecimalSeparator;
UnicodeString fPercent;
UnicodeString fPerMill;
UnicodeString fDigit;
UnicodeString fSeparator;
UnicodeString fExponent;
UnicodeString fPlus;
UnicodeString fMinus;
UnicodeString fPadEscape;
};
U_NAMESPACE_END
#endif /* !UCONFIG_NO_FORMATTING */
#endif

View File

@ -1,35 +0,0 @@
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
********************************************************************************
* Copyright (C) 2015, International Business Machines
* Corporation and others. All Rights Reserved.
********************************************************************************
*
* File 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

File diff suppressed because it is too large Load Diff

View File

@ -1,549 +0,0 @@
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
********************************************************************************
* Copyright (C) 2015, International Business Machines
* Corporation and others. All Rights Reserved.
********************************************************************************
*
* File decimfmtimpl.h
********************************************************************************
*/
#ifndef DECIMFMTIMPL_H
#define DECIMFMTIMPL_H
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/decimfmt.h"
#include "unicode/uobject.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;
/**
* DecimalFormatImpl is the glue code between the legacy DecimalFormat class
* and the new decimal formatting classes. DecimalFormat still handles
* parsing directly. However, DecimalFormat uses attributes of this class
* for parsing when possible.
*
* The public API of this class closely mirrors the legacy API of the
* legacy DecimalFormat deviating only when the legacy API does not make
* sense. For example, although DecimalFormat has a
* getPadCharacterString() method, DecimalFormatImpl has a getPadCharacter()
* method because formatting uses only a single pad character for padding.
*
* Each legacy DecimalFormat instance heap allocates its own instance of
* this class. Most DecimalFormat methods that deal with formatting simply
* delegate to the DecimalFormat's DecimalFormatImpl method.
*
* Because DecimalFormat extends NumberFormat, Each instance of this class
* "borrows" a pointer to the NumberFormat part of its enclosing DecimalFormat
* instance. This way each DecimalFormatImpl instance can read or even modify
* the NumberFormat portion of its enclosing DecimalFormat instance.
*
* Directed acyclic graph (DAG):
*
* This class can be represented as a directed acyclic graph (DAG) where each
* vertex is an attribute, and each directed edge indicates that the value
* of the destination attribute is calculated from the value of the source
* attribute. Attributes with setter methods reside at the bottom of the
* DAG. That is, no edges point to them. We call these independent attributes
* because their values can be set independently of one another. The rest of
* the attributes are derived attributes because their values depend on the
* independent attributes. DecimalFormatImpl often uses the derived
* attributes, not the independent attributes, when formatting numbers.
*
* The independent attributes at the bottom of the DAG correspond to the legacy
* attributes of DecimalFormat while the attributes at the top of the DAG
* correspond to the attributes of the new code. The edges of the DAG
* correspond to the code that handles the complex interaction among all the
* legacy attributes of the DecimalFormat API.
*
* We use a DAG for three reasons.
*
* First, the DAG preserves backward compatibility. Clients of the legacy
* DecimalFormat expect existing getters and setters of each attribute to be
* consistent. That means if a client sets a particular attribute to a new
* value, the attribute should retain that value until the client sets it to
* a new value. The DAG allows these attributes to remain consistent even
* though the new code may not use them when formatting.
*
* Second, the DAG obviates the need to recalculate derived attributes with
* each format. Instead, the DAG "remembers" the values of all derived
* attributes. Only setting an independent attribute requires a recalculation.
* Moreover, setting an independent attribute recalculates only the affected
* dependent attributes rather than all dependent attributes.
*
* Third, the DAG abstracts away the complex interaction among the legacy
* attributes of the DecimalFormat API.
*
* Only the independent attributes of the DAG have setters and getters.
* Derived attributes have no setters (and often no getters either).
*
* Copy and assign:
*
* For copy and assign, DecimalFormatImpl copies and assigns every attribute
* regardless of whether or not it is independent. We do this for simplicity.
*
* Implementation of the DAG:
*
* The DAG consists of three smaller DAGs:
* 1. Grouping attributes
* 2. Precision attributes
* 3. Formatting attributes.
*
* The first two DAGs are simple in that setting any independent attribute
* in the DAG recalculates all the dependent attributes in that DAG.
* The updateGrouping() and updatePrecision() perform the respective
* recalculations.
*
* Because some of the derived formatting attributes are expensive to
* calculate, the formatting attributes DAG is more complex. The
* updateFormatting() method is composed of many updateFormattingXXX()
* methods, each of which recalculates a single derived attribute. The
* updateFormatting() method accepts a bitfield of recently changed
* attributes and passes this bitfield by reference to each of the
* updateFormattingXXX() methods. Each updateFormattingXXX() method checks
* the bitfield to see if any of the attributes it uses to compute the XXX
* attribute changed. If none of them changed, it exists immediately. However,
* if at least one of them changed, it recalculates the XXX attribute and
* sets the corresponding bit in the bitfield. In this way, each
* updateFormattingXXX() method encodes the directed edges in the formatting
* DAG that point to the attribute its calculating.
*
* Maintenance of the updateFormatting() method.
*
* Use care when changing the updateFormatting() method.
* The updateFormatting() method must call each updateFormattingXXX() in the
* same partial order that the formatting DAG prescribes. That is, the
* attributes near the bottom of the DAG must be calculated before attributes
* further up. As we mentioned in the prvious paragraph, the directed edges of
* the formatting DAG are encoded within each updateFormattingXXX() method.
* Finally, adding new attributes may involve adding to the bitmap that the
* updateFormatting() method uses. The top most attributes in the DAG,
* those that do not point to any attributes but only have attributes
* pointing to it, need not have a slot in the bitmap.
*
* Keep in mind that most of the code that makes the legacy DecimalFormat API
* work the way it always has before can be found in these various updateXXX()
* methods. For example the updatePrecisionForScientific() method
* handles the complex interactions amoung the various precision attributes
* when formatting in scientific notation. Changing the way attributes
* interract, often means changing one of these updateXXX() methods.
*
* Conclusion:
*
* The DecimFmtImpl class is the glue code between the legacy and new
* number formatting code. It uses a direct acyclic graph (DAG) to
* maintain backward compatibility, to make the code efficient, and to
* abstract away the complex interraction among legacy attributs.
*/
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(
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 /* #if !UCONFIG_NO_FORMATTING */
#endif // DECIMFMTIMPL_H
//eof

View File

@ -1,109 +0,0 @@
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
* Copyright (C) 2015, International Business Machines
* Corporation and others. All Rights Reserved.
*
* file name: digitaffix.cpp
*/
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#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
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -1,104 +0,0 @@
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* Copyright (C) 2015, International Business Machines
* Corporation and others. All Rights Reserved.
*******************************************************************************
* digitaffix.h
*
* created on: 2015jan06
* created by: Travis Keep
*/
#ifndef __DIGITAFFIX_H__
#define __DIGITAFFIX_H__
#include "unicode/uobject.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/unistr.h"
#include "unicode/unum.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 // #if !UCONFIG_NO_FORMATTING
#endif // __DIGITAFFIX_H__

View File

@ -1,175 +0,0 @@
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
* Copyright (C) 2015, International Business Machines
* Corporation and others. All Rights Reserved.
*
* file name: digitaffixesandpadding.cpp
*/
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#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
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -1,179 +0,0 @@
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* Copyright (C) 2015, International Business Machines
* Corporation and others. All Rights Reserved.
*******************************************************************************
* digitaffixesandpadding.h
*
* created on: 2015jan06
* created by: Travis Keep
*/
#ifndef __DIGITAFFIXESANDPADDING_H__
#define __DIGITAFFIXESANDPADDING_H__
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/uobject.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 /* #if !UCONFIG_NO_FORMATTING */
#endif // __DIGITAFFIXANDPADDING_H__

View File

@ -1,417 +0,0 @@
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
* Copyright (C) 2015, International Business Machines
* Corporation and others. All Rights Reserved.
*
* file name: digitformatter.cpp
*/
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/dcfmtsym.h"
#include "unicode/unum.h"
#include "digitformatter.h"
#include "digitgrouping.h"
#include "digitinterval.h"
#include "digitlst.h"
#include "fphdlimp.h"
#include "smallintformatter.h"
#include "unistrappender.h"
#include "visibledigits.h"
U_NAMESPACE_BEGIN
DigitFormatter::DigitFormatter()
: fGroupingSeparator(",", -1, US_INV), fDecimal(".", -1, US_INV),
fNegativeSign("-", -1, US_INV), fPositiveSign("+", -1, US_INV),
fIsStandardDigits(TRUE), fExponent("E", -1, US_INV) {
for (int32_t i = 0; i < 10; ++i) {
fLocalizedDigits[i] = (UChar32) (0x30 + i);
}
fInfinity.setTo(UnicodeString("Inf", -1, US_INV), UNUM_INTEGER_FIELD);
fNan.setTo(UnicodeString("Nan", -1, US_INV), UNUM_INTEGER_FIELD);
}
DigitFormatter::DigitFormatter(const DecimalFormatSymbols &symbols) {
setDecimalFormatSymbols(symbols);
}
void
DigitFormatter::setOtherDecimalFormatSymbols(
const DecimalFormatSymbols &symbols) {
fLocalizedDigits[0] = symbols.getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0);
fLocalizedDigits[1] = symbols.getConstSymbol(DecimalFormatSymbols::kOneDigitSymbol).char32At(0);
fLocalizedDigits[2] = symbols.getConstSymbol(DecimalFormatSymbols::kTwoDigitSymbol).char32At(0);
fLocalizedDigits[3] = symbols.getConstSymbol(DecimalFormatSymbols::kThreeDigitSymbol).char32At(0);
fLocalizedDigits[4] = symbols.getConstSymbol(DecimalFormatSymbols::kFourDigitSymbol).char32At(0);
fLocalizedDigits[5] = symbols.getConstSymbol(DecimalFormatSymbols::kFiveDigitSymbol).char32At(0);
fLocalizedDigits[6] = symbols.getConstSymbol(DecimalFormatSymbols::kSixDigitSymbol).char32At(0);
fLocalizedDigits[7] = symbols.getConstSymbol(DecimalFormatSymbols::kSevenDigitSymbol).char32At(0);
fLocalizedDigits[8] = symbols.getConstSymbol(DecimalFormatSymbols::kEightDigitSymbol).char32At(0);
fLocalizedDigits[9] = symbols.getConstSymbol(DecimalFormatSymbols::kNineDigitSymbol).char32At(0);
fIsStandardDigits = isStandardDigits();
fNegativeSign = symbols.getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol);
fPositiveSign = symbols.getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol);
fInfinity.setTo(symbols.getConstSymbol(DecimalFormatSymbols::kInfinitySymbol), UNUM_INTEGER_FIELD);
fNan.setTo(symbols.getConstSymbol(DecimalFormatSymbols::kNaNSymbol), UNUM_INTEGER_FIELD);
fExponent = symbols.getConstSymbol(DecimalFormatSymbols::kExponentialSymbol);
}
void
DigitFormatter::setDecimalFormatSymbolsForMonetary(
const DecimalFormatSymbols &symbols) {
setOtherDecimalFormatSymbols(symbols);
fGroupingSeparator = symbols.getConstSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol);
fDecimal = symbols.getConstSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol);
}
void
DigitFormatter::setDecimalFormatSymbols(
const DecimalFormatSymbols &symbols) {
setOtherDecimalFormatSymbols(symbols);
fGroupingSeparator = symbols.getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol);
fDecimal = symbols.getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol);
}
static void appendField(
int32_t fieldId,
const UnicodeString &value,
FieldPositionHandler &handler,
UnicodeString &appendTo) {
int32_t currentLength = appendTo.length();
appendTo.append(value);
handler.addAttribute(
fieldId,
currentLength,
appendTo.length());
}
int32_t DigitFormatter::countChar32(
const DigitGrouping &grouping,
const DigitInterval &interval,
const DigitFormatterOptions &options) const {
int32_t result = interval.length();
// We always emit '0' in lieu of no digits.
if (result == 0) {
result = 1;
}
if (options.fAlwaysShowDecimal || interval.getLeastSignificantInclusive() < 0) {
result += fDecimal.countChar32();
}
result += grouping.getSeparatorCount(interval.getIntDigitCount()) * fGroupingSeparator.countChar32();
return result;
}
int32_t
DigitFormatter::countChar32(
const VisibleDigits &digits,
const DigitGrouping &grouping,
const DigitFormatterOptions &options) const {
if (digits.isNaN()) {
return countChar32ForNaN();
}
if (digits.isInfinite()) {
return countChar32ForInfinity();
}
return countChar32(
grouping,
digits.getInterval(),
options);
}
int32_t
DigitFormatter::countChar32(
const VisibleDigitsWithExponent &digits,
const SciFormatterOptions &options) const {
if (digits.isNaN()) {
return countChar32ForNaN();
}
if (digits.isInfinite()) {
return countChar32ForInfinity();
}
const VisibleDigits *exponent = digits.getExponent();
if (exponent == NULL) {
DigitGrouping grouping;
return countChar32(
grouping,
digits.getMantissa().getInterval(),
options.fMantissa);
}
return countChar32(
*exponent, digits.getMantissa().getInterval(), options);
}
int32_t
DigitFormatter::countChar32(
const VisibleDigits &exponent,
const DigitInterval &mantissaInterval,
const SciFormatterOptions &options) const {
DigitGrouping grouping;
int32_t count = countChar32(
grouping, mantissaInterval, options.fMantissa);
count += fExponent.countChar32();
count += countChar32ForExponent(
exponent, options.fExponent);
return count;
}
UnicodeString &DigitFormatter::format(
const VisibleDigits &digits,
const DigitGrouping &grouping,
const DigitFormatterOptions &options,
FieldPositionHandler &handler,
UnicodeString &appendTo) const {
if (digits.isNaN()) {
return formatNaN(handler, appendTo);
}
if (digits.isInfinite()) {
return formatInfinity(handler, appendTo);
}
const DigitInterval &interval = digits.getInterval();
int32_t digitsLeftOfDecimal = interval.getMostSignificantExclusive();
int32_t lastDigitPos = interval.getLeastSignificantInclusive();
int32_t intBegin = appendTo.length();
int32_t fracBegin = 0; /* initialize to avoid compiler warning */
// Emit "0" instead of empty string.
if (digitsLeftOfDecimal == 0 && lastDigitPos == 0) {
appendTo.append(fLocalizedDigits[0]);
handler.addAttribute(UNUM_INTEGER_FIELD, intBegin, appendTo.length());
if (options.fAlwaysShowDecimal) {
appendField(
UNUM_DECIMAL_SEPARATOR_FIELD,
fDecimal,
handler,
appendTo);
}
return appendTo;
}
{
UnicodeStringAppender appender(appendTo);
for (int32_t i = interval.getMostSignificantExclusive() - 1;
i >= interval.getLeastSignificantInclusive(); --i) {
if (i == -1) {
appender.flush();
appendField(
UNUM_DECIMAL_SEPARATOR_FIELD,
fDecimal,
handler,
appendTo);
fracBegin = appendTo.length();
}
appender.append(fLocalizedDigits[digits.getDigitByExponent(i)]);
if (grouping.isSeparatorAt(digitsLeftOfDecimal, i)) {
appender.flush();
appendField(
UNUM_GROUPING_SEPARATOR_FIELD,
fGroupingSeparator,
handler,
appendTo);
}
if (i == 0) {
appender.flush();
if (digitsLeftOfDecimal > 0) {
handler.addAttribute(UNUM_INTEGER_FIELD, intBegin, appendTo.length());
}
}
}
if (options.fAlwaysShowDecimal && lastDigitPos == 0) {
appender.flush();
appendField(
UNUM_DECIMAL_SEPARATOR_FIELD,
fDecimal,
handler,
appendTo);
}
}
// lastDigitPos is never > 0 so we are guaranteed that kIntegerField
// is already added.
if (lastDigitPos < 0) {
handler.addAttribute(UNUM_FRACTION_FIELD, fracBegin, appendTo.length());
}
return appendTo;
}
UnicodeString &
DigitFormatter::format(
const VisibleDigitsWithExponent &digits,
const SciFormatterOptions &options,
FieldPositionHandler &handler,
UnicodeString &appendTo) const {
DigitGrouping grouping;
format(
digits.getMantissa(),
grouping,
options.fMantissa,
handler,
appendTo);
const VisibleDigits *exponent = digits.getExponent();
if (exponent == NULL) {
return appendTo;
}
int32_t expBegin = appendTo.length();
appendTo.append(fExponent);
handler.addAttribute(
UNUM_EXPONENT_SYMBOL_FIELD, expBegin, appendTo.length());
return formatExponent(
*exponent,
options.fExponent,
UNUM_EXPONENT_SIGN_FIELD,
UNUM_EXPONENT_FIELD,
handler,
appendTo);
}
static int32_t formatInt(
int32_t value, uint8_t *digits) {
int32_t idx = 0;
while (value > 0) {
digits[idx++] = (uint8_t) (value % 10);
value /= 10;
}
return idx;
}
UnicodeString &
DigitFormatter::formatDigits(
const uint8_t *digits,
int32_t count,
const IntDigitCountRange &range,
int32_t intField,
FieldPositionHandler &handler,
UnicodeString &appendTo) const {
int32_t i = range.pin(count) - 1;
int32_t begin = appendTo.length();
// Always emit '0' as placeholder for empty string.
if (i == -1) {
appendTo.append(fLocalizedDigits[0]);
handler.addAttribute(intField, begin, appendTo.length());
return appendTo;
}
{
UnicodeStringAppender appender(appendTo);
for (; i >= count; --i) {
appender.append(fLocalizedDigits[0]);
}
for (; i >= 0; --i) {
appender.append(fLocalizedDigits[digits[i]]);
}
}
handler.addAttribute(intField, begin, appendTo.length());
return appendTo;
}
UnicodeString &
DigitFormatter::formatExponent(
const VisibleDigits &digits,
const DigitFormatterIntOptions &options,
int32_t signField,
int32_t intField,
FieldPositionHandler &handler,
UnicodeString &appendTo) const {
UBool neg = digits.isNegative();
if (neg || options.fAlwaysShowSign) {
appendField(
signField,
neg ? fNegativeSign : fPositiveSign,
handler,
appendTo);
}
int32_t begin = appendTo.length();
DigitGrouping grouping;
DigitFormatterOptions expOptions;
FieldPosition fpos(FieldPosition::DONT_CARE);
FieldPositionOnlyHandler noHandler(fpos);
format(
digits,
grouping,
expOptions,
noHandler,
appendTo);
handler.addAttribute(intField, begin, appendTo.length());
return appendTo;
}
int32_t
DigitFormatter::countChar32ForExponent(
const VisibleDigits &exponent,
const DigitFormatterIntOptions &options) const {
int32_t result = 0;
UBool neg = exponent.isNegative();
if (neg || options.fAlwaysShowSign) {
result += neg ? fNegativeSign.countChar32() : fPositiveSign.countChar32();
}
DigitGrouping grouping;
DigitFormatterOptions expOptions;
result += countChar32(grouping, exponent.getInterval(), expOptions);
return result;
}
UnicodeString &
DigitFormatter::formatPositiveInt32(
int32_t positiveValue,
const IntDigitCountRange &range,
FieldPositionHandler &handler,
UnicodeString &appendTo) const {
// super fast path
if (fIsStandardDigits && SmallIntFormatter::canFormat(positiveValue, range)) {
int32_t begin = appendTo.length();
SmallIntFormatter::format(positiveValue, range, appendTo);
handler.addAttribute(UNUM_INTEGER_FIELD, begin, appendTo.length());
return appendTo;
}
uint8_t digits[10];
int32_t count = formatInt(positiveValue, digits);
return formatDigits(
digits,
count,
range,
UNUM_INTEGER_FIELD,
handler,
appendTo);
}
UBool DigitFormatter::isStandardDigits() const {
UChar32 cdigit = 0x30;
for (int32_t i = 0; i < UPRV_LENGTHOF(fLocalizedDigits); ++i) {
if (fLocalizedDigits[i] != cdigit) {
return FALSE;
}
++cdigit;
}
return TRUE;
}
UBool
DigitFormatter::equals(const DigitFormatter &rhs) const {
UBool result = (fGroupingSeparator == rhs.fGroupingSeparator) &&
(fDecimal == rhs.fDecimal) &&
(fNegativeSign == rhs.fNegativeSign) &&
(fPositiveSign == rhs.fPositiveSign) &&
(fInfinity.equals(rhs.fInfinity)) &&
(fNan.equals(rhs.fNan)) &&
(fIsStandardDigits == rhs.fIsStandardDigits) &&
(fExponent == rhs.fExponent);
if (!result) {
return FALSE;
}
for (int32_t i = 0; i < UPRV_LENGTHOF(fLocalizedDigits); ++i) {
if (fLocalizedDigits[i] != rhs.fLocalizedDigits[i]) {
return FALSE;
}
}
return TRUE;
}
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -1,288 +0,0 @@
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* Copyright (C) 2015, International Business Machines
* Corporation and others. All Rights Reserved.
*******************************************************************************
* digitformatter.h
*
* created on: 2015jan06
* created by: Travis Keep
*/
#ifndef __DIGITFORMATTER_H__
#define __DIGITFORMATTER_H__
#include "unicode/uobject.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/utypes.h"
#include "unicode/unistr.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 /* #if !UCONFIG_NO_FORMATTING */
#endif // __DIGITFORMATTER_H__

View File

@ -1,59 +0,0 @@
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
* Copyright (C) 2015, International Business Machines
* Corporation and others. All Rights Reserved.
*
* file name: 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

View File

@ -1,112 +0,0 @@
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* Copyright (C) 2015, International Business Machines
* Corporation and others. All Rights Reserved.
*******************************************************************************
* 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__

View File

@ -1,56 +0,0 @@
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
* Copyright (C) 2015, International Business Machines
* Corporation and others. All Rights Reserved.
*
* file name: 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

View File

@ -1,159 +0,0 @@
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* Copyright (C) 2015, International Business Machines
* Corporation and others. All Rights Reserved.
*******************************************************************************
* 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__

File diff suppressed because it is too large Load Diff

View File

@ -1,529 +0,0 @@
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
******************************************************************************
*
* Copyright (C) 1997-2015, International Business Machines
* Corporation and others. All Rights Reserved.
*
******************************************************************************
*
* File DIGITLST.H
*
* Modification History:
*
* Date Name Description
* 02/25/97 aliu Converted from java.
* 03/21/97 clhuang Updated per C++ implementation.
* 04/15/97 aliu Changed MAX_COUNT to DBL_DIG. Changed Digit to char.
* 09/09/97 aliu Adapted for exponential notation support.
* 08/02/98 stephen Added nearest/even rounding
* 06/29/99 stephen Made LONG_DIGITS a macro to satisfy SUN compiler
* 07/09/99 stephen Removed kMaxCount (unused, for HP compiler)
******************************************************************************
*/
#ifndef DIGITLST_H
#define DIGITLST_H
#include "unicode/uobject.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/decimfmt.h"
#include <float.h>
#include "decContext.h"
#include "decNumber.h"
#include "cmemory.h"
// Decimal digits in a 64-bit int
#define INT64_DIGITS 19
typedef enum EDigitListValues {
MAX_DBL_DIGITS = DBL_DIG,
MAX_I64_DIGITS = INT64_DIGITS,
MAX_DIGITS = MAX_I64_DIGITS,
MAX_EXPONENT = DBL_DIG,
DIGIT_PADDING = 3,
DEFAULT_DIGITS = 40, // Initial storage size, will grow as needed.
// "+." + fDigits + "e" + fDecimalAt
MAX_DEC_DIGITS = MAX_DIGITS + DIGIT_PADDING + MAX_EXPONENT
} 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.
//
// MSVC requires this, even though it should not be necessary.
// No direct access to the MaybeStackHeaderAndArray leaks out of the i18n library.
//
// Macintosh produces duplicate definition linker errors with the explicit template
// instantiation.
//
#if !U_PLATFORM_IS_DARWIN_BASED
template class U_I18N_API MaybeStackHeaderAndArray<decNumber, char, DEFAULT_DIGITS>;
#endif
enum EStackMode { kOnStack };
enum EFastpathBits { kFastpathOk = 1, kNoDecimal = 2 };
/**
* Digit List is actually a Decimal Floating Point number.
* The original implementation has been replaced by a thin wrapper onto a
* decimal number from the decNumber library.
*
* The original DigitList API has been retained, to minimize the impact of
* the change on the rest of the ICU formatting code.
*
* The change to decNumber enables support for big decimal numbers, and
* allows rounding computations to be done directly in decimal, avoiding
* extra, and inaccurate, conversions to and from doubles.
*
* Original DigitList comments:
*
* Digit List utility class. Private to DecimalFormat. Handles the transcoding
* between numeric values and strings of characters. Only handles
* non-negative numbers. The division of labor between DigitList and
* DecimalFormat is that DigitList handles the radix 10 representation
* issues; DecimalFormat handles the locale-specific issues such as
* positive/negative, grouping, decimal point, currency, and so on.
* <P>
* A DigitList is really a representation of a floating point value.
* It may be an integer value; we assume that a double has sufficient
* precision to represent all digits of a long.
* <P>
* The DigitList representation consists of a string of characters,
* which are the digits radix 10, from '0' to '9'. It also has a radix
* 10 exponent associated with it. The value represented by a DigitList
* object can be computed by mulitplying the fraction f, where 0 <= f < 1,
* derived by placing all the digits of the list to the right of the
* decimal point, by 10^exponent.
*
* --------
*
* DigitList vs. decimalNumber:
*
* DigitList stores digits with the most significant first.
* decNumber stores digits with the least significant first.
*
* DigitList, decimal point is before the most significant.
* decNumber, decimal point is after the least signficant digit.
*
* digitList: 0.ddddd * 10 ^ exp
* decNumber: ddddd. * 10 ^ exp
*
* digitList exponent = decNumber exponent + digit count
*
* digitList, digits are platform invariant chars, '0' - '9'
* decNumber, digits are binary, one per byte, 0 - 9.
*
* (decNumber library is configurable in how digits are stored, ICU has configured
* it this way for convenience in replacing the old DigitList implementation.)
*/
class U_I18N_API DigitList : public UMemory { // Declare external to make compiler happy
public:
DigitList();
~DigitList();
/* copy constructor
* @param DigitList The object to be copied.
* @return the newly created object.
*/
DigitList(const DigitList&); // copy constructor
/* assignment operator
* @param DigitList The object to be copied.
* @return the newly created object.
*/
DigitList& operator=(const DigitList&); // assignment operator
/**
* Return true if another object is semantically equal to this one.
* @param other The DigitList to be compared for equality
* @return true if another object is semantically equal to this one.
* return false otherwise.
*/
UBool operator==(const DigitList& other) const;
int32_t compare(const DigitList& other);
inline UBool operator!=(const DigitList& other) const { return !operator==(other); }
/**
* Clears out the digits.
* Use before appending them.
* Typically, you set a series of digits with append, then at the point
* you hit the decimal point, you set myDigitList.fDecimalAt = myDigitList.fCount;
* then go on appending digits.
*/
void clear(void);
/**
* Remove, by rounding, any fractional part of the decimal number,
* leaving an integer value.
*/
void toIntegralValue();
/**
* Appends digits to the list.
* CAUTION: this function is not recommended for new code.
* In the original DigitList implementation, decimal numbers were
* parsed by appending them to a digit list as they were encountered.
* With the revamped DigitList based on decNumber, append is very
* inefficient, and the interaction with the exponent value is confusing.
* Best avoided.
* TODO: remove this function once all use has been replaced.
* TODO: describe alternative to append()
* @param digit The digit to be appended.
*/
void append(char digit);
/**
* Utility routine to get the value of the digit list
* Returns 0.0 if zero length.
* @return the value of the digit list.
*/
double getDouble(void) const;
/**
* Utility routine to get the value of the digit list
* Make sure that fitsIntoLong() is called before calling this function.
* Returns 0 if zero length.
* @return the value of the digit list, return 0 if it is zero length
*/
int32_t getLong(void) /*const*/;
/**
* Utility routine to get the value of the digit list
* Make sure that fitsIntoInt64() is called before calling this function.
* Returns 0 if zero length.
* @return the value of the digit list, return 0 if it is zero length
*/
int64_t getInt64(void) /*const*/;
/**
* Utility routine to get the value of the digit list as a decimal string.
*/
void getDecimal(CharString &str, UErrorCode &status);
/**
* Return true if the number represented by this object can fit into
* a long.
* @param ignoreNegativeZero True if negative zero is ignored.
* @return true if the number represented by this object can fit into
* a long, return false otherwise.
*/
UBool fitsIntoLong(UBool ignoreNegativeZero) /*const*/;
/**
* Return true if the number represented by this object can fit into
* an int64_t.
* @param ignoreNegativeZero True if negative zero is ignored.
* @return true if the number represented by this object can fit into
* a long, return false otherwise.
*/
UBool fitsIntoInt64(UBool ignoreNegativeZero) /*const*/;
/**
* Utility routine to set the value of the digit list from a double.
* @param source The value to be set
*/
void set(double source);
/**
* Utility routine to set the value of the digit list from a long.
* If a non-zero maximumDigits is specified, no more than that number of
* significant digits will be produced.
* @param source The value to be set
*/
void set(int32_t source);
/**
* Utility routine to set the value of the digit list from an int64.
* If a non-zero maximumDigits is specified, no more than that number of
* significant digits will be produced.
* @param source The value to be set
*/
void set(int64_t source);
/**
* Utility routine to set the value of the digit list from an int64.
* Does not set the decnumber unless requested later
* If a non-zero maximumDigits is specified, no more than that number of
* significant digits will be produced.
* @param source The value to be set
*/
void setInteger(int64_t source);
/**
* Utility routine to set the value of the digit list from a decimal number
* string.
* @param source The value to be set. The string must be nul-terminated.
* @param fastpathBits special flags for fast parsing
*/
void set(StringPiece source, UErrorCode &status, uint32_t fastpathBits = 0);
/**
* Multiply this = this * arg
* This digitlist will be expanded if necessary to accomodate the result.
* @param arg the number to multiply by.
*/
void mult(const DigitList &arg, UErrorCode &status);
/**
* Divide this = this / arg
*/
void div(const DigitList &arg, UErrorCode &status);
// The following functions replace direct access to the original DigitList implmentation
// data structures.
void setRoundingMode(DecimalFormat::ERoundingMode m);
/** Test a number for zero.
* @return TRUE if the number is zero
*/
UBool isZero(void) const;
/** Test for a Nan
* @return TRUE if the number is a NaN
*/
UBool isNaN(void) const {return decNumberIsNaN(fDecNumber);}
UBool isInfinite() const {return decNumberIsInfinite(fDecNumber);}
/** Reduce, or normalize. Removes trailing zeroes, adjusts exponent appropriately. */
void reduce();
/** Remove trailing fraction zeros, adjust exponent accordingly. */
void trim();
/** Set to zero */
void setToZero() {uprv_decNumberZero(fDecNumber);}
/** get the number of digits in the decimal number */
int32_t digits() const {return fDecNumber->digits;}
/**
* 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);
void roundFixedPoint(int32_t maximumFractionDigits);
/** Ensure capacity for digits. Grow the storage if it is currently less than
* the requested size. Capacity is not reduced if it is already greater
* than requested.
*/
void ensureCapacity(int32_t requestedSize, UErrorCode &status);
UBool isPositive(void) const { return decNumberIsNegative(fDecNumber) == 0;}
void setPositive(UBool s);
void setDecimalAt(int32_t d);
int32_t getDecimalAt();
void setCount(int32_t c);
int32_t getCount() const;
/**
* Set the digit in platform (invariant) format, from '0'..'9'
* @param i index of digit
* @param v digit value, from '0' to '9' in platform invariant format
*/
void setDigit(int32_t i, char v);
/**
* Get the digit in platform (invariant) format, from '0'..'9' inclusive
* @param i index of digit
* @return invariant format of the digit
*/
char getDigit(int32_t i);
/**
* Get the digit's value, as an integer from 0..9 inclusive.
* Note that internally this value is a decNumberUnit, but ICU configures it to be a uint8_t.
* @param i index of digit
* @return value of that digit
*/
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:
/*
* These data members are intentionally public and can be set directly.
*<P>
* The value represented is given by placing the decimal point before
* fDigits[fDecimalAt]. If fDecimalAt is < 0, then leading zeros between
* the decimal point and the first nonzero digit are implied. If fDecimalAt
* is > fCount, then trailing zeros between the fDigits[fCount-1] and the
* decimal point are implied.
* <P>
* Equivalently, the represented value is given by f * 10^fDecimalAt. Here
* f is a value 0.1 <= f < 1 arrived at by placing the digits in fDigits to
* the right of the decimal.
* <P>
* DigitList is normalized, so if it is non-zero, fDigits[0] is non-zero. We
* don't allow denormalized numbers because our exponent is effectively of
* unlimited magnitude. The fCount value contains the number of significant
* digits present in fDigits[].
* <P>
* Zero is represented by any DigitList with fCount == 0 or with each fDigits[i]
* for all i <= fCount == '0'.
*
* int32_t fDecimalAt;
* int32_t fCount;
* UBool fIsPositive;
* char *fDigits;
* DecimalFormat::ERoundingMode fRoundingMode;
*/
public:
decContext fContext; // public access to status flags.
private:
decNumber *fDecNumber;
MaybeStackHeaderAndArray<decNumber, char, DEFAULT_DIGITS> fStorage;
/* Cached double value corresponding to this decimal number.
* This is an optimization for the formatting implementation, which may
* ask for the double value multiple times.
*/
union DoubleOrInt64 {
double fDouble;
int64_t fInt64;
} fUnion;
enum EHave {
kNone=0,
kDouble
} fHave;
UBool shouldRoundUp(int32_t maximumDigits) const;
public:
#if U_OVERRIDE_CXX_ALLOCATION
using UMemory::operator new;
using UMemory::operator delete;
#else
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 double U_EXPORT2 decimalStrToDouble(char *decstr, char **end);
/**
* Placement new for stack usage
* @internal
*/
static inline void * U_EXPORT2 operator new(size_t /*size*/, void * onStack, EStackMode /*mode*/) U_NO_THROW { return onStack; }
/**
* Placement delete for stack usage
* @internal
*/
static inline void U_EXPORT2 operator delete(void * /*ptr*/, void * /*onStack*/, EStackMode /*mode*/) U_NO_THROW {}
private:
inline void internalSetDouble(double d) {
fHave = kDouble;
fUnion.fDouble=d;
}
inline void internalClear() {
fHave = kNone;
}
};
U_NAMESPACE_END
#endif // #if !UCONFIG_NO_FORMATTING
#endif // _DIGITLST
//eof

View File

@ -0,0 +1,50 @@
// © 2018 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#ifndef __SOURCE_FIELDPOSUTIL_H__
#define __SOURCE_FIELDPOSUTIL_H__
U_NAMESPACE_BEGIN
/**
* Wraps a UFieldPosition and makes it usable as a FieldPosition. Example:
*
* <pre>
* UFieldPositionWrapper wrapper(myUFPos);
* u_favorite_function_taking_ufpos(wrapper);
* // when destructed, the wrapper saves the data back into myUFPos
* </pre>
*/
class UFieldPositionWrapper : public UMemory {
public:
explicit UFieldPositionWrapper(UFieldPosition& ufpos)
: _ufpos(ufpos) {
_fpos.setField(_ufpos.field);
_fpos.setBeginIndex(_ufpos.beginIndex);
_fpos.setEndIndex(_ufpos.endIndex);
}
/** When destructed, copies the information from the fpos into the ufpos. */
~UFieldPositionWrapper() {
_ufpos.field = _fpos.getField();
_ufpos.beginIndex = _fpos.getBeginIndex();
_ufpos.endIndex = _fpos.getEndIndex();
}
/** Conversion operator to FieldPosition */
operator FieldPosition&() {
return _fpos;
}
private:
FieldPosition _fpos;
UFieldPosition& _ufpos;
};
U_NAMESPACE_END
#endif //__SOURCE_FIELDPOSUTIL_H__
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -28,9 +28,8 @@
#include "charstr.h"
#include "cmemory.h"
#include "cstring.h"
#include "decNumber.h"
#include "digitlst.h"
#include "fmtableimp.h"
#include "number_decimalquantity.h"
// *****************************************************************************
// class Formattable
@ -40,6 +39,8 @@ U_NAMESPACE_BEGIN
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Formattable)
using number::impl::DecimalQuantity;
//-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
@ -103,7 +104,7 @@ void Formattable::init() {
fValue.fInt64 = 0;
fType = kLong;
fDecimalStr = NULL;
fDecimalNum = NULL;
fDecimalQuantity = NULL;
fBogus.setToBogus();
}
@ -257,8 +258,8 @@ Formattable::operator=(const Formattable& source)
}
UErrorCode status = U_ZERO_ERROR;
if (source.fDecimalNum != NULL) {
fDecimalNum = new DigitList(*source.fDecimalNum); // TODO: use internal digit list
if (source.fDecimalQuantity != NULL) {
fDecimalQuantity = new DecimalQuantity(*source.fDecimalQuantity);
}
if (source.fDecimalStr != NULL) {
fDecimalStr = new CharString(*source.fDecimalStr, status);
@ -356,14 +357,9 @@ void Formattable::dispose()
delete fDecimalStr;
fDecimalStr = NULL;
FmtStackData *stackData = (FmtStackData*)fStackData;
if(fDecimalNum != &(stackData->stackDecimalNum)) {
delete fDecimalNum;
} else {
fDecimalNum->~DigitList(); // destruct, don't deallocate
}
fDecimalNum = NULL;
delete fDecimalQuantity;
fDecimalQuantity = NULL;
}
Formattable *
@ -465,13 +461,13 @@ Formattable::getInt64(UErrorCode& status) const
} else if (fValue.fDouble < (double)U_INT64_MIN) {
status = U_INVALID_FORMAT_ERROR;
return U_INT64_MIN;
} else if (fabs(fValue.fDouble) > U_DOUBLE_MAX_EXACT_INT && fDecimalNum != NULL) {
int64_t val = fDecimalNum->getInt64();
if (val != 0) {
return val;
} else if (fabs(fValue.fDouble) > U_DOUBLE_MAX_EXACT_INT && fDecimalQuantity != NULL) {
if (fDecimalQuantity->fitsInLong(true)) {
return fDecimalQuantity->toLong();
} else {
// Unexpected
status = U_INVALID_FORMAT_ERROR;
return fValue.fDouble > 0 ? U_INT64_MAX : U_INT64_MIN;
return fDecimalQuantity->isNegative() ? U_INT64_MIN : U_INT64_MAX;
}
} else {
return (int64_t)fValue.fDouble;
@ -714,84 +710,85 @@ StringPiece Formattable::getDecimalNumber(UErrorCode &status) {
CharString *Formattable::internalGetCharString(UErrorCode &status) {
if(fDecimalStr == NULL) {
if (fDecimalNum == NULL) {
if (fDecimalQuantity == NULL) {
// No decimal number for the formattable yet. Which means the value was
// set directly by the user as an int, int64 or double. If the value came
// from parsing, or from the user setting a decimal number, fDecimalNum
// would already be set.
//
fDecimalNum = new DigitList; // TODO: use internal digit list
if (fDecimalNum == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
switch (fType) {
case kDouble:
fDecimalNum->set(this->getDouble());
break;
case kLong:
fDecimalNum->set(this->getLong());
break;
case kInt64:
fDecimalNum->set(this->getInt64());
break;
default:
// The formattable's value is not a numeric type.
status = U_INVALID_STATE_ERROR;
return NULL;
}
LocalPointer<DecimalQuantity> dq(new DecimalQuantity(), status);
if (U_FAILURE(status)) { return nullptr; }
populateDecimalQuantity(*dq, status);
if (U_FAILURE(status)) { return nullptr; }
fDecimalQuantity = dq.orphan();
}
fDecimalStr = new CharString;
fDecimalStr = new CharString();
if (fDecimalStr == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
fDecimalNum->getDecimal(*fDecimalStr, status);
// Older ICUs called uprv_decNumberToString here, which is not exactly the same as
// DecimalQuantity::toScientificString(). The biggest difference is that uprv_decNumberToString does
// not print scientific notation for magnitudes greater than -5 and smaller than some amount (+5?).
if (fDecimalQuantity->isZero()) {
fDecimalStr->append("0", -1, status);
} else if (std::abs(fDecimalQuantity->getMagnitude()) < 5) {
fDecimalStr->appendInvariantChars(fDecimalQuantity->toPlainString(), status);
} else {
fDecimalStr->appendInvariantChars(fDecimalQuantity->toScientificString(), status);
}
}
return fDecimalStr;
}
void
Formattable::populateDecimalQuantity(number::impl::DecimalQuantity& output, UErrorCode& status) const {
if (fDecimalQuantity != nullptr) {
output = *fDecimalQuantity;
return;
}
DigitList *
Formattable::getInternalDigitList() {
FmtStackData *stackData = (FmtStackData*)fStackData;
if(fDecimalNum != &(stackData->stackDecimalNum)) {
delete fDecimalNum;
fDecimalNum = new (&(stackData->stackDecimalNum), kOnStack) DigitList();
} else {
fDecimalNum->clear();
}
return fDecimalNum;
switch (fType) {
case kDouble:
output.setToDouble(this->getDouble());
output.roundToInfinity();
break;
case kLong:
output.setToInt(this->getLong());
break;
case kInt64:
output.setToLong(this->getInt64());
break;
default:
// The formattable's value is not a numeric type.
status = U_INVALID_STATE_ERROR;
}
}
// ---------------------------------------
void
Formattable::adoptDigitList(DigitList *dl) {
if(fDecimalNum==dl) {
fDecimalNum = NULL; // don't delete
}
dispose();
fDecimalNum = dl;
if(dl==NULL) { // allow adoptDigitList(NULL) to clear
return;
}
Formattable::adoptDecimalQuantity(DecimalQuantity *dq) {
if (fDecimalQuantity != NULL) {
delete fDecimalQuantity;
}
fDecimalQuantity = dq;
if (dq == NULL) { // allow adoptDigitList(NULL) to clear
return;
}
// Set the value into the Union of simple type values.
// Cannot use the set() functions because they would delete the fDecimalNum value,
if (fDecimalNum->fitsIntoLong(FALSE)) {
fType = kLong;
fValue.fInt64 = fDecimalNum->getLong();
} else if (fDecimalNum->fitsIntoInt64(FALSE)) {
fType = kInt64;
fValue.fInt64 = fDecimalNum->getInt64();
// Cannot use the set() functions because they would delete the fDecimalNum value.
if (fDecimalQuantity->fitsInLong()) {
fValue.fInt64 = fDecimalQuantity->toLong();
if (fValue.fInt64 <= INT32_MAX && fValue.fInt64 >= INT32_MIN) {
fType = kLong;
} else {
fType = kInt64;
}
} else {
fType = kDouble;
fValue.fDouble = fDecimalNum->getDouble();
fValue.fDouble = fDecimalQuantity->toDouble();
}
}
@ -804,24 +801,12 @@ Formattable::setDecimalNumber(StringPiece numberString, UErrorCode &status) {
}
dispose();
// Copy the input string and nul-terminate it.
// The decNumber library requires nul-terminated input. StringPiece input
// is not guaranteed nul-terminated. Too bad.
// CharString automatically adds the nul.
DigitList *dnum = new DigitList(); // TODO: use getInternalDigitList
if (dnum == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
return;
}
dnum->set(CharString(numberString, status).toStringPiece(), status);
if (U_FAILURE(status)) {
delete dnum;
return; // String didn't contain a decimal number.
}
adoptDigitList(dnum);
auto* dq = new DecimalQuantity();
dq->setToDecNumber(numberString, status);
adoptDecimalQuantity(dq);
// Note that we do not hang on to the caller's input string.
// If we are asked for the string, we will regenerate one from fDecimalNum.
// If we are asked for the string, we will regenerate one from fDecimalQuantity.
}
#if 0

View File

@ -10,7 +10,7 @@
#ifndef FMTABLEIMP_H
#define FMTABLEIMP_H
#include "digitlst.h"
#include "number_decimalquantity.h"
#if !UCONFIG_NO_FORMATTING
@ -20,7 +20,7 @@ U_NAMESPACE_BEGIN
* @internal
*/
struct FmtStackData {
DigitList stackDecimalNum; // 128
icu::number::impl::DecimalQuantity stackDecimalNum; // 128
//CharString stackDecimalStr; // 64
// -----
// 192 total

View File

@ -197,18 +197,6 @@
<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" />
@ -229,11 +217,8 @@
<ClCompile Include="dayperiodrules.cpp" />
<ClCompile Include="dcfmtsym.cpp" />
<ClCompile Include="decContext.cpp" />
<ClCompile Include="decfmtst.cpp" />
<ClCompile Include="decimalformatpattern.cpp" />
<ClCompile Include="decimfmt.cpp" />
<ClCompile Include="decNumber.cpp" />
<ClCompile Include="digitlst.cpp" />
<ClCompile Include="double-conversion-bignum-dtoa.cpp" />
<ClCompile Include="double-conversion-bignum.cpp" />
<ClCompile Include="double-conversion-cached-powers.cpp" />
@ -284,6 +269,22 @@
<ClCompile Include="number_rounding.cpp" />
<ClCompile Include="number_scientific.cpp" />
<ClCompile Include="number_stringbuilder.cpp" />
<ClCompile Include="number_utils.cpp" />
<ClCompile Include="number_mapper.cpp" />
<ClCompile Include="number_multiplier.cpp" />
<ClCompile Include="number_currencysymbols.cpp" />
<ClCompile Include="number_skeletons.cpp" />
<ClCompile Include="number_capi.cpp" />
<ClCompile Include="numparse_stringsegment.cpp" />
<ClCompile Include="numparse_parsednumber.cpp" />
<ClCompile Include="numparse_impl.cpp" />
<ClCompile Include="numparse_symbols.cpp" />
<ClCompile Include="numparse_decimal.cpp" />
<ClCompile Include="numparse_scientific.cpp" />
<ClCompile Include="numparse_currency.cpp" />
<ClCompile Include="numparse_affixes.cpp" />
<ClCompile Include="numparse_compositions.cpp" />
<ClCompile Include="numparse_validators.cpp" />
<ClCompile Include="numfmt.cpp" />
<ClCompile Include="numsys.cpp" />
<ClCompile Include="olsontz.cpp" />
@ -386,20 +387,6 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="bocsu.h" />
<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" />
@ -437,11 +424,8 @@
<ClInclude Include="currfmt.h" />
<ClInclude Include="dangical.h" />
<ClInclude Include="decContext.h" />
<ClInclude Include="decfmtst.h" />
<ClInclude Include="decimalformatpattern.h" />
<ClInclude Include="decNumber.h" />
<ClInclude Include="decNumberLocal.h" />
<ClInclude Include="digitlst.h" />
<ClInclude Include="double-conversion-bignum-dtoa.h" />
<ClInclude Include="double-conversion-bignum.h" />
<ClInclude Include="double-conversion-cached-powers.h" />
@ -544,7 +528,24 @@
<ClInclude Include="number_scientific.h" />
<ClInclude Include="number_stringbuilder.h" />
<ClInclude Include="number_types.h" />
<ClInclude Include="number_utypes.h" />
<ClInclude Include="number_utils.h" />
<ClInclude Include="number_mapper.h" />
<ClInclude Include="number_multiplier.h" />
<ClInclude Include="number_currencysymbols.h" />
<ClInclude Include="number_skeletons.h" />
<ClInclude Include="fieldposutil.h" />
<ClInclude Include="numparse_stringsegment.h" />
<ClInclude Include="numparse_impl.h" />
<ClInclude Include="numparse_symbols.h" />
<ClInclude Include="numparse_decimal.h" />
<ClInclude Include="numparse_scientific.h" />
<ClInclude Include="numparse_currency.h" />
<ClInclude Include="numparse_affixes.h" />
<ClInclude Include="numparse_compositions.h" />
<ClInclude Include="numparse_validators.h" />
<ClInclude Include="numparse_types.h" />
<ClInclude Include="numparse_utils.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="i18n.rc" />

View File

@ -54,45 +54,6 @@
<ClCompile Include="usearch.cpp">
<Filter>collation</Filter>
</ClCompile>
<ClCompile Include="affixpatternparser.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="dayperiodrules.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>
@ -141,21 +102,12 @@
<ClCompile Include="decContext.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="decfmtst.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="decimalformatpattern.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="decimfmt.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="decNumber.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="digitlst.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="double-conversion-bignum-dtoa.cpp">
<Filter>formatting</Filter>
</ClCompile>
@ -561,8 +513,8 @@
<ClCompile Include="uregion.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="number_stringbuilder.cpp">
<Filter>formatting</Filter>
<ClCompile Include="nounit.cpp">
<Filter>misc</Filter>
</ClCompile>
<ClCompile Include="number_affixutils.cpp">
<Filter>formatting</Filter>
@ -612,8 +564,56 @@
<ClCompile Include="number_scientific.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="nounit.cpp">
<Filter>misc</Filter>
<ClCompile Include="number_stringbuilder.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="number_utils.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="number_mapper.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="number_multiplier.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="number_currencysymbols.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="number_skeletons.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="number_capi.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="numparse_stringsegment.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="numparse_parsednumber.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="numparse_impl.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="numparse_symbols.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="numparse_decimal.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="numparse_scientific.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="numparse_currency.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="numparse_affixes.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="numparse_compositions.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="numparse_validators.cpp">
<Filter>formatting</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
@ -698,48 +698,6 @@
<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>
@ -767,21 +725,12 @@
<ClInclude Include="decContext.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="decfmtst.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="decimalformatpattern.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="decNumber.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="decNumberLocal.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="digitlst.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="double-conversion-bignum-dtoa.h">
<Filter>formatting</Filter>
</ClInclude>
@ -890,9 +839,60 @@
<ClInclude Include="number_types.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="number_utypes.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="number_utils.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="number_mapper.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="number_multiplier.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="number_currencysymbols.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="number_skeletons.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="fieldposutil.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="numparse_stringsegment.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="numparse_impl.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="numparse_symbols.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="numparse_decimal.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="numparse_scientific.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="numparse_currency.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="numparse_affixes.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="numparse_compositions.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="numparse_validators.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="numparse_types.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="numparse_utils.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="olsontz.h">
<Filter>formatting</Filter>
</ClInclude>
@ -1411,10 +1411,13 @@
<Filter>formatting</Filter>
</CustomBuild>
<CustomBuild Include="unicode\nounit.h">
<Filter>misc</Filter>
<Filter>formatting</Filter>
</CustomBuild>
<CustomBuild Include="unicode\numberformatter.h">
<Filter>formatting</Filter>
</CustomBuild>
<CustomBuild Include="unicode\unumberformatter.h">
<Filter>formatting</Filter>
</CustomBuild>
</ItemGroup>
</Project>

View File

@ -304,18 +304,6 @@
<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" />
@ -336,11 +324,8 @@
<ClCompile Include="dayperiodrules.cpp" />
<ClCompile Include="dcfmtsym.cpp" />
<ClCompile Include="decContext.cpp" />
<ClCompile Include="decfmtst.cpp" />
<ClCompile Include="decimalformatpattern.cpp" />
<ClCompile Include="decimfmt.cpp" />
<ClCompile Include="decNumber.cpp" />
<ClCompile Include="digitlst.cpp" />
<ClCompile Include="double-conversion-bignum-dtoa.cpp" />
<ClCompile Include="double-conversion-bignum.cpp" />
<ClCompile Include="double-conversion-cached-powers.cpp" />
@ -391,6 +376,22 @@
<ClCompile Include="number_rounding.cpp" />
<ClCompile Include="number_scientific.cpp" />
<ClCompile Include="number_stringbuilder.cpp" />
<ClCompile Include="number_utils.cpp" />
<ClCompile Include="number_mapper.cpp" />
<ClCompile Include="number_multiplier.cpp" />
<ClCompile Include="number_currencysymbols.cpp" />
<ClCompile Include="number_skeletons.cpp" />
<ClCompile Include="number_capi.cpp" />
<ClCompile Include="numparse_stringsegment.cpp" />
<ClCompile Include="numparse_parsednumber.cpp" />
<ClCompile Include="numparse_impl.cpp" />
<ClCompile Include="numparse_symbols.cpp" />
<ClCompile Include="numparse_decimal.cpp" />
<ClCompile Include="numparse_scientific.cpp" />
<ClCompile Include="numparse_currency.cpp" />
<ClCompile Include="numparse_affixes.cpp" />
<ClCompile Include="numparse_compositions.cpp" />
<ClCompile Include="numparse_validators.cpp" />
<ClCompile Include="numfmt.cpp" />
<ClCompile Include="numsys.cpp" />
<ClCompile Include="olsontz.cpp" />
@ -491,20 +492,6 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="bocsu.h" />
<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" />
@ -542,11 +529,8 @@
<ClInclude Include="currfmt.h" />
<ClInclude Include="dangical.h" />
<ClInclude Include="decContext.h" />
<ClInclude Include="decfmtst.h" />
<ClInclude Include="decimalformatpattern.h" />
<ClInclude Include="decNumber.h" />
<ClInclude Include="decNumberLocal.h" />
<ClInclude Include="digitlst.h" />
<ClInclude Include="double-conversion-bignum-dtoa.h" />
<ClInclude Include="double-conversion-bignum.h" />
<ClInclude Include="double-conversion-cached-powers.h" />
@ -649,7 +633,24 @@
<ClInclude Include="number_scientific.h" />
<ClInclude Include="number_stringbuilder.h" />
<ClInclude Include="number_types.h" />
<ClInclude Include="number_utypes.h" />
<ClInclude Include="number_utils.h" />
<ClInclude Include="number_mapper.h" />
<ClInclude Include="number_multiplier.h" />
<ClInclude Include="number_currencysymbols.h" />
<ClInclude Include="number_skeletons.h" />
<ClInclude Include="fieldposutil.h" />
<ClInclude Include="numparse_stringsegment.h" />
<ClInclude Include="numparse_impl.h" />
<ClInclude Include="numparse_symbols.h" />
<ClInclude Include="numparse_decimal.h" />
<ClInclude Include="numparse_scientific.h" />
<ClInclude Include="numparse_currency.h" />
<ClInclude Include="numparse_affixes.h" />
<ClInclude Include="numparse_compositions.h" />
<ClInclude Include="numparse_validators.h" />
<ClInclude Include="numparse_types.h" />
<ClInclude Include="numparse_utils.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="i18n.rc" />

View File

@ -557,6 +557,10 @@ static int32_t unitPerUnitToSingleUnit[][4] = {
{427, 363, 4, 1}
};
// Shortcuts to the base unit in order to make the default constructor fast
static const int32_t kBaseTypeIdx = 14;
static const int32_t kBaseSubTypeIdx = 0;
MeasureUnit *MeasureUnit::createGForce(UErrorCode &status) {
return MeasureUnit::create(0, 0, status);
}
@ -1118,7 +1122,8 @@ static int32_t binarySearch(
MeasureUnit::MeasureUnit() {
fCurrency[0] = 0;
initNoUnit("base");
fTypeId = kBaseTypeIdx;
fSubTypeId = kBaseSubTypeIdx;
}
MeasureUnit::MeasureUnit(const MeasureUnit &other)

View File

@ -48,7 +48,7 @@
#include "ustrfmt.h"
#include "util.h"
#include "uvector.h"
#include "visibledigits.h"
#include "number_decimalquantity.h"
// *****************************************************************************
// class MessageFormat
@ -1959,14 +1959,14 @@ UnicodeString MessageFormat::PluralSelectorProvider::select(void *ctx, double nu
return UnicodeString(FALSE, OTHER_STRING, 5);
}
context.formatter->format(context.number, context.numberString, ec);
const DecimalFormat *decFmt = dynamic_cast<const DecimalFormat *>(context.formatter);
auto* decFmt = dynamic_cast<const DecimalFormat *>(context.formatter);
if(decFmt != NULL) {
VisibleDigitsWithExponent digits;
decFmt->initVisibleDigitsWithExponent(context.number, digits, ec);
number::impl::DecimalQuantity dq;
decFmt->formatToDecimalQuantity(context.number, dq, ec);
if (U_FAILURE(ec)) {
return UnicodeString(FALSE, OTHER_STRING, 5);
}
return rules->select(digits);
return rules->select(dq);
} else {
return rules->select(number);
}

View File

@ -19,8 +19,9 @@
#include "utypeinfo.h" // for 'typeid' to work
#include "nfsubs.h"
#include "digitlst.h"
#include "fmtableimp.h"
#include "putilimp.h"
#include "number_decimalquantity.h"
#if U_HAVE_RBNF
@ -47,6 +48,8 @@ static const UChar gGreaterGreaterThan[] =
U_NAMESPACE_BEGIN
using number::impl::DecimalQuantity;
class SameValueSubstitution : public NFSubstitution {
public:
SameValueSubstitution(int32_t pos,
@ -1069,13 +1072,12 @@ FractionalPartSubstitution::doSubstitution(double number, UnicodeString& toInser
// numberToFormat /= 10;
// }
DigitList dl;
dl.set(number);
dl.roundFixedPoint(20); // round to 20 fraction digits.
dl.reduce(); // Removes any trailing zeros.
DecimalQuantity dl;
dl.setToDouble(number);
dl.roundToMagnitude(-20, UNUM_ROUND_HALFEVEN, status); // round to 20 fraction digits.
UBool pad = FALSE;
for (int32_t didx = dl.getCount()-1; didx>=dl.getDecimalAt(); didx--) {
for (int32_t didx = dl.getLowerDisplayMagnitude(); didx<0; didx++) {
// Loop iterates over fraction digits, starting with the LSD.
// include both real digits from the number, and zeros
// to the left of the MSD but to the right of the decimal point.
@ -1084,7 +1086,7 @@ FractionalPartSubstitution::doSubstitution(double number, UnicodeString& toInser
} else {
pad = TRUE;
}
int64_t digit = didx>=0 ? dl.getDigit(didx) - '0' : 0;
int64_t digit = dl.getDigit(didx);
getRuleSet()->format(digit, toInsertInto, _pos + getPos(), recursionCount, status);
}
@ -1142,7 +1144,8 @@ FractionalPartSubstitution::doParse(const UnicodeString& text,
int32_t digit;
// double p10 = 0.1;
DigitList dl;
DecimalQuantity dl;
int32_t totalDigits = 0;
NumberFormat* fmt = NULL;
while (workText.length() > 0 && workPos.getIndex() != 0) {
workPos.setIndex(0);
@ -1170,7 +1173,8 @@ FractionalPartSubstitution::doParse(const UnicodeString& text,
}
if (workPos.getIndex() != 0) {
dl.append((char)('0' + digit));
dl.appendDigit(static_cast<int8_t>(digit), 0, true);
totalDigits++;
// result += digit * p10;
// p10 /= 10;
parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex());
@ -1183,7 +1187,8 @@ FractionalPartSubstitution::doParse(const UnicodeString& text,
}
delete fmt;
result = dl.getCount() == 0 ? 0 : dl.getDouble();
dl.adjustMagnitude(-totalDigits);
result = dl.toDouble();
result = composeRuleValue(result, baseValue);
resVal.setDouble(result);
return TRUE;

View File

@ -3,21 +3,22 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#include "number_affixutils.h"
#include "unicode/utf16.h"
#include "unicode/uniset.h"
using namespace icu;
using namespace icu::number;
using namespace icu::number::impl;
int32_t AffixUtils::estimateLength(const CharSequence &patternString, UErrorCode &status) {
int32_t AffixUtils::estimateLength(const UnicodeString &patternString, UErrorCode &status) {
AffixPatternState state = STATE_BASE;
int32_t offset = 0;
int32_t length = 0;
for (; offset < patternString.length();) {
UChar32 cp = patternString.codePointAt(offset);
UChar32 cp = patternString.char32At(offset);
switch (state) {
case STATE_BASE:
@ -78,12 +79,12 @@ int32_t AffixUtils::estimateLength(const CharSequence &patternString, UErrorCode
return length;
}
UnicodeString AffixUtils::escape(const CharSequence &input) {
UnicodeString AffixUtils::escape(const UnicodeString &input) {
AffixPatternState state = STATE_BASE;
int32_t offset = 0;
UnicodeString output;
for (; offset < input.length();) {
UChar32 cp = input.codePointAt(offset);
UChar32 cp = input.char32At(offset);
switch (cp) {
case u'\'':
@ -153,7 +154,7 @@ Field AffixUtils::getFieldForType(AffixPatternType type) {
}
int32_t
AffixUtils::unescape(const CharSequence &affixPattern, NumberStringBuilder &output, int32_t position,
AffixUtils::unescape(const UnicodeString &affixPattern, NumberStringBuilder &output, int32_t position,
const SymbolProvider &provider, UErrorCode &status) {
int32_t length = 0;
AffixTag tag;
@ -173,7 +174,7 @@ AffixUtils::unescape(const CharSequence &affixPattern, NumberStringBuilder &outp
return length;
}
int32_t AffixUtils::unescapedCodePointCount(const CharSequence &affixPattern,
int32_t AffixUtils::unescapedCodePointCount(const UnicodeString &affixPattern,
const SymbolProvider &provider, UErrorCode &status) {
int32_t length = 0;
AffixTag tag;
@ -192,7 +193,7 @@ int32_t AffixUtils::unescapedCodePointCount(const CharSequence &affixPattern,
}
bool
AffixUtils::containsType(const CharSequence &affixPattern, AffixPatternType type, UErrorCode &status) {
AffixUtils::containsType(const UnicodeString &affixPattern, AffixPatternType type, UErrorCode &status) {
if (affixPattern.length() == 0) {
return false;
}
@ -207,7 +208,7 @@ AffixUtils::containsType(const CharSequence &affixPattern, AffixPatternType type
return false;
}
bool AffixUtils::hasCurrencySymbols(const CharSequence &affixPattern, UErrorCode &status) {
bool AffixUtils::hasCurrencySymbols(const UnicodeString &affixPattern, UErrorCode &status) {
if (affixPattern.length() == 0) {
return false;
}
@ -222,9 +223,9 @@ bool AffixUtils::hasCurrencySymbols(const CharSequence &affixPattern, UErrorCode
return false;
}
UnicodeString AffixUtils::replaceType(const CharSequence &affixPattern, AffixPatternType type,
UnicodeString AffixUtils::replaceType(const UnicodeString &affixPattern, AffixPatternType type,
char16_t replacementChar, UErrorCode &status) {
UnicodeString output = affixPattern.toUnicodeString();
UnicodeString output(affixPattern); // copy
if (affixPattern.length() == 0) {
return output;
};
@ -239,11 +240,41 @@ UnicodeString AffixUtils::replaceType(const CharSequence &affixPattern, AffixPat
return output;
}
AffixTag AffixUtils::nextToken(AffixTag tag, const CharSequence &patternString, UErrorCode &status) {
bool AffixUtils::containsOnlySymbolsAndIgnorables(const UnicodeString& affixPattern,
const UnicodeSet& ignorables, UErrorCode& status) {
if (affixPattern.length() == 0) {
return true;
};
AffixTag tag;
while (hasNext(tag, affixPattern)) {
tag = nextToken(tag, affixPattern, status);
if (U_FAILURE(status)) { return false; }
if (tag.type == TYPE_CODEPOINT && !ignorables.contains(tag.codePoint)) {
return false;
}
}
return true;
}
void AffixUtils::iterateWithConsumer(const UnicodeString& affixPattern, TokenConsumer& consumer,
UErrorCode& status) {
if (affixPattern.length() == 0) {
return;
};
AffixTag tag;
while (hasNext(tag, affixPattern)) {
tag = nextToken(tag, affixPattern, status);
if (U_FAILURE(status)) { return; }
consumer.consumeToken(tag.type, tag.codePoint, status);
if (U_FAILURE(status)) { return; }
}
}
AffixTag AffixUtils::nextToken(AffixTag tag, const UnicodeString &patternString, UErrorCode &status) {
int32_t offset = tag.offset;
int32_t state = tag.state;
for (; offset < patternString.length();) {
UChar32 cp = patternString.codePointAt(offset);
UChar32 cp = patternString.char32At(offset);
int32_t count = U16_LENGTH(cp);
switch (state) {
@ -382,7 +413,7 @@ AffixTag AffixUtils::nextToken(AffixTag tag, const CharSequence &patternString,
}
}
bool AffixUtils::hasNext(const AffixTag &tag, const CharSequence &string) {
bool AffixUtils::hasNext(const AffixTag &tag, const UnicodeString &string) {
// First check for the {-1} and default initializer syntax.
if (tag.offset < 0) {
return false;

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#ifndef __NUMBER_AFFIXUTILS_H__
#define __NUMBER_AFFIXUTILS_H__
@ -12,6 +12,7 @@
#include "unicode/stringpiece.h"
#include "unicode/unistr.h"
#include "number_stringbuilder.h"
#include "unicode/uniset.h"
U_NAMESPACE_BEGIN namespace number {
namespace impl {
@ -37,13 +38,19 @@ struct AffixTag {
AffixPatternState state;
AffixPatternType type;
AffixTag() : offset(0), state(STATE_BASE) {}
AffixTag()
: offset(0), state(STATE_BASE) {}
AffixTag(int32_t offset) : offset(offset) {}
AffixTag(int32_t offset)
: offset(offset) {}
AffixTag(int32_t offset, UChar32 codePoint, AffixPatternState state, AffixPatternType type)
: offset(offset), codePoint(codePoint), state(state), type(type)
{}
: offset(offset), codePoint(codePoint), state(state), type(type) {}
};
class TokenConsumer {
public:
virtual void consumeToken(AffixPatternType type, UChar32 cp, UErrorCode& status) = 0;
};
// Exported as U_I18N_API because it is a base class for other exported types
@ -107,7 +114,7 @@ class U_I18N_API AffixUtils {
* @param patternString The original string whose width will be estimated.
* @return The length of the unescaped string.
*/
static int32_t estimateLength(const CharSequence &patternString, UErrorCode &status);
static int32_t estimateLength(const UnicodeString& patternString, UErrorCode& status);
/**
* Takes a string and escapes (quotes) characters that have special meaning in the affix pattern
@ -118,7 +125,7 @@ class U_I18N_API AffixUtils {
* @param input The string to be escaped.
* @return The resulting UnicodeString.
*/
static UnicodeString escape(const CharSequence &input);
static UnicodeString escape(const UnicodeString& input);
static Field getFieldForType(AffixPatternType type);
@ -134,9 +141,8 @@ class U_I18N_API AffixUtils {
* @param position The index into the NumberStringBuilder to insert the string.
* @param provider An object to generate locale symbols.
*/
static int32_t
unescape(const CharSequence &affixPattern, NumberStringBuilder &output, int32_t position,
const SymbolProvider &provider, UErrorCode &status);
static int32_t unescape(const UnicodeString& affixPattern, NumberStringBuilder& output,
int32_t position, const SymbolProvider& provider, UErrorCode& status);
/**
* Sames as {@link #unescape}, but only calculates the code point count. More efficient than {@link #unescape}
@ -146,8 +152,8 @@ class U_I18N_API AffixUtils {
* @param provider An object to generate locale symbols.
* @return The same return value as if you called {@link #unescape}.
*/
static int32_t unescapedCodePointCount(const CharSequence &affixPattern,
const SymbolProvider &provider, UErrorCode &status);
static int32_t unescapedCodePointCount(const UnicodeString& affixPattern,
const SymbolProvider& provider, UErrorCode& status);
/**
* Checks whether the given affix pattern contains at least one token of the given type, which is
@ -157,8 +163,7 @@ class U_I18N_API AffixUtils {
* @param type The token type.
* @return true if the affix pattern contains the given token type; false otherwise.
*/
static bool
containsType(const CharSequence &affixPattern, AffixPatternType type, UErrorCode &status);
static bool containsType(const UnicodeString& affixPattern, AffixPatternType type, UErrorCode& status);
/**
* Checks whether the specified affix pattern has any unquoted currency symbols ("¤").
@ -166,7 +171,7 @@ class U_I18N_API AffixUtils {
* @param affixPattern The string to check for currency symbols.
* @return true if the literal has at least one unquoted currency symbol; false otherwise.
*/
static bool hasCurrencySymbols(const CharSequence &affixPattern, UErrorCode &status);
static bool hasCurrencySymbols(const UnicodeString& affixPattern, UErrorCode& status);
/**
* Replaces all occurrences of tokens with the given type with the given replacement char.
@ -176,9 +181,21 @@ class U_I18N_API AffixUtils {
* @param replacementChar The char to substitute in place of chars of the given token type.
* @return A string containing the new affix pattern.
*/
static UnicodeString
replaceType(const CharSequence &affixPattern, AffixPatternType type, char16_t replacementChar,
UErrorCode &status);
static UnicodeString replaceType(const UnicodeString& affixPattern, AffixPatternType type,
char16_t replacementChar, UErrorCode& status);
/**
* Returns whether the given affix pattern contains only symbols and ignorables as defined by the
* given ignorables set.
*/
static bool containsOnlySymbolsAndIgnorables(const UnicodeString& affixPattern,
const UnicodeSet& ignorables, UErrorCode& status);
/**
* Iterates over the affix pattern, calling the TokenConsumer for each token.
*/
static void iterateWithConsumer(const UnicodeString& affixPattern, TokenConsumer& consumer,
UErrorCode& status);
/**
* Returns the next token from the affix pattern.
@ -190,7 +207,7 @@ class U_I18N_API AffixUtils {
* (never negative), or -1 if there were no more tokens in the affix pattern.
* @see #hasNext
*/
static AffixTag nextToken(AffixTag tag, const CharSequence &patternString, UErrorCode &status);
static AffixTag nextToken(AffixTag tag, const UnicodeString& patternString, UErrorCode& status);
/**
* Returns whether the affix pattern string has any more tokens to be retrieved from a call to
@ -200,7 +217,7 @@ class U_I18N_API AffixUtils {
* @param string The affix pattern.
* @return true if there are more tokens to consume; false otherwise.
*/
static bool hasNext(const AffixTag &tag, const CharSequence &string);
static bool hasNext(const AffixTag& tag, const UnicodeString& string);
private:
/**
@ -208,8 +225,8 @@ class U_I18N_API AffixUtils {
* The order of the arguments is consistent with Java, but the order of the stored
* fields is not necessarily the same.
*/
static inline AffixTag
makeTag(int32_t offset, AffixPatternType type, AffixPatternState state, UChar32 cp) {
static inline AffixTag makeTag(int32_t offset, AffixPatternType type, AffixPatternState state,
UChar32 cp) {
return {offset, cp, state, type};
}
};

View File

@ -0,0 +1,234 @@
// © 2018 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
// Allow implicit conversion from char16_t* to UnicodeString for this file:
// Helpful in toString methods and elsewhere.
#define UNISTR_FROM_STRING_EXPLICIT
#include "numparse_types.h"
#include "number_utypes.h"
#include "unicode/numberformatter.h"
#include "unicode/unumberformatter.h"
#include "fieldposutil.h"
using namespace icu;
using namespace icu::number;
using namespace icu::number::impl;
//////////////////////////////////
/// C API CONVERSION FUNCTIONS ///
//////////////////////////////////
UNumberFormatterData* UNumberFormatterData::validate(UNumberFormatter* input, UErrorCode& status) {
auto* constInput = static_cast<const UNumberFormatter*>(input);
auto* validated = validate(constInput, status);
return const_cast<UNumberFormatterData*>(validated);
}
const UNumberFormatterData*
UNumberFormatterData::validate(const UNumberFormatter* input, UErrorCode& status) {
if (U_FAILURE(status)) {
return nullptr;
}
if (input == nullptr) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return nullptr;
}
auto* impl = reinterpret_cast<const UNumberFormatterData*>(input);
if (impl->fMagic != UNumberFormatterData::kMagic) {
status = U_INVALID_FORMAT_ERROR;
return nullptr;
}
return impl;
}
UNumberFormatter* UNumberFormatterData::exportForC() {
return reinterpret_cast<UNumberFormatter*>(this);
}
UFormattedNumberData* UFormattedNumberData::validate(UFormattedNumber* input, UErrorCode& status) {
auto* constInput = static_cast<const UFormattedNumber*>(input);
auto* validated = validate(constInput, status);
return const_cast<UFormattedNumberData*>(validated);
}
const UFormattedNumberData*
UFormattedNumberData::validate(const UFormattedNumber* input, UErrorCode& status) {
if (U_FAILURE(status)) {
return nullptr;
}
if (input == nullptr) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return nullptr;
}
auto* impl = reinterpret_cast<const UFormattedNumberData*>(input);
if (impl->fMagic != UFormattedNumberData::kMagic) {
status = U_INVALID_FORMAT_ERROR;
return nullptr;
}
return impl;
}
UFormattedNumber* UFormattedNumberData::exportForC() {
return reinterpret_cast<UFormattedNumber*>(this);
}
/////////////////////////////////////
/// END CAPI CONVERSION FUNCTIONS ///
/////////////////////////////////////
U_CAPI UNumberFormatter* U_EXPORT2
unumf_openFromSkeletonAndLocale(const UChar* skeleton, int32_t skeletonLen, const char* locale,
UErrorCode* ec) {
auto* impl = new UNumberFormatterData();
if (impl == nullptr) {
*ec = U_MEMORY_ALLOCATION_ERROR;
return nullptr;
}
// Readonly-alias constructor (first argument is whether we are NUL-terminated)
UnicodeString skeletonString(skeletonLen == -1, skeleton, skeletonLen);
impl->fFormatter = NumberFormatter::fromSkeleton(skeletonString, *ec).locale(locale);
return impl->exportForC();
}
U_CAPI UFormattedNumber* U_EXPORT2
unumf_openResult(UErrorCode* ec) {
auto* impl = new UFormattedNumberData();
if (impl == nullptr) {
*ec = U_MEMORY_ALLOCATION_ERROR;
return nullptr;
}
return impl->exportForC();
}
U_CAPI void U_EXPORT2
unumf_formatInt(const UNumberFormatter* uformatter, int64_t value, UFormattedNumber* uresult,
UErrorCode* ec) {
const UNumberFormatterData* formatter = UNumberFormatterData::validate(uformatter, *ec);
UFormattedNumberData* result = UFormattedNumberData::validate(uresult, *ec);
if (U_FAILURE(*ec)) { return; }
result->string.clear();
result->quantity.setToLong(value);
formatter->fFormatter.formatImpl(result, *ec);
}
U_CAPI void U_EXPORT2
unumf_formatDouble(const UNumberFormatter* uformatter, double value, UFormattedNumber* uresult,
UErrorCode* ec) {
const UNumberFormatterData* formatter = UNumberFormatterData::validate(uformatter, *ec);
UFormattedNumberData* result = UFormattedNumberData::validate(uresult, *ec);
if (U_FAILURE(*ec)) { return; }
result->string.clear();
result->quantity.setToDouble(value);
formatter->fFormatter.formatImpl(result, *ec);
}
U_CAPI void U_EXPORT2
unumf_formatDecimal(const UNumberFormatter* uformatter, const char* value, int32_t valueLen,
UFormattedNumber* uresult, UErrorCode* ec) {
const UNumberFormatterData* formatter = UNumberFormatterData::validate(uformatter, *ec);
UFormattedNumberData* result = UFormattedNumberData::validate(uresult, *ec);
if (U_FAILURE(*ec)) { return; }
result->string.clear();
result->quantity.setToDecNumber({value, valueLen}, *ec);
if (U_FAILURE(*ec)) { return; }
formatter->fFormatter.formatImpl(result, *ec);
}
U_CAPI int32_t U_EXPORT2
unumf_resultToString(const UFormattedNumber* uresult, UChar* buffer, int32_t bufferCapacity,
UErrorCode* ec) {
const UFormattedNumberData* result = UFormattedNumberData::validate(uresult, *ec);
if (U_FAILURE(*ec)) { return 0; }
if (buffer == nullptr ? bufferCapacity != 0 : bufferCapacity < 0) {
*ec = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
return result->string.toTempUnicodeString().extract(buffer, bufferCapacity, *ec);
}
U_CAPI void U_EXPORT2
unumf_resultGetField(const UFormattedNumber* uresult, UFieldPosition* ufpos, UErrorCode* ec) {
if (ufpos == nullptr) {
*ec = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
const UFormattedNumberData* result = UFormattedNumberData::validate(uresult, *ec);
if (U_FAILURE(*ec)) { return; }
UFieldPositionWrapper helper(*ufpos);
result->string.populateFieldPosition(helper, 0, *ec);
}
U_CAPI void U_EXPORT2
unumf_resultGetAllFields(const UFormattedNumber* uresult, UFieldPositionIterator* ufpositer,
UErrorCode* ec) {
if (ufpositer == nullptr) {
*ec = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
const UFormattedNumberData* result = UFormattedNumberData::validate(uresult, *ec);
if (U_FAILURE(*ec)) { return; }
auto* helper = reinterpret_cast<FieldPositionIterator*>(ufpositer);
result->string.populateFieldPositionIterator(*helper, *ec);
}
U_CAPI void U_EXPORT2
unumf_closeResult(UFormattedNumber* uresult) {
UErrorCode localStatus = U_ZERO_ERROR;
const UFormattedNumberData* impl = UFormattedNumberData::validate(uresult, localStatus);
if (U_FAILURE(localStatus)) { return; }
delete impl;
}
U_CAPI void U_EXPORT2
unumf_close(UNumberFormatter* f) {
UErrorCode localStatus = U_ZERO_ERROR;
const UNumberFormatterData* impl = UNumberFormatterData::validate(f, localStatus);
if (U_FAILURE(localStatus)) { return; }
delete impl;
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#include "resource.h"
#include "number_compact.h"

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#ifndef __NUMBER_COMPACT_H__
#define __NUMBER_COMPACT_H__

View File

@ -0,0 +1,123 @@
// © 2018 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
// Allow implicit conversion from char16_t* to UnicodeString for this file:
// Helpful in toString methods and elsewhere.
#define UNISTR_FROM_STRING_EXPLICIT
#include "numparse_types.h"
#include "number_currencysymbols.h"
using namespace icu;
using namespace icu::number;
using namespace icu::number::impl;
CurrencySymbols::CurrencySymbols(CurrencyUnit currency, const Locale& locale, UErrorCode& status)
: fCurrency(currency), fLocaleName(locale.getName(), status) {
fCurrencySymbol.setToBogus();
fIntlCurrencySymbol.setToBogus();
}
CurrencySymbols::CurrencySymbols(CurrencyUnit currency, const Locale& locale,
const DecimalFormatSymbols& symbols, UErrorCode& status)
: CurrencySymbols(currency, locale, status) {
// If either of the overrides is present, save it in the local UnicodeString.
if (symbols.isCustomCurrencySymbol()) {
fCurrencySymbol = symbols.getConstSymbol(DecimalFormatSymbols::kCurrencySymbol);
}
if (symbols.isCustomIntlCurrencySymbol()) {
fIntlCurrencySymbol = symbols.getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol);
}
}
const char16_t* CurrencySymbols::getIsoCode() const {
return fCurrency.getISOCurrency();
}
UnicodeString CurrencySymbols::getNarrowCurrencySymbol(UErrorCode& status) const {
// Note: currently no override is available for narrow currency symbol
return loadSymbol(UCURR_NARROW_SYMBOL_NAME, status);
}
UnicodeString CurrencySymbols::getCurrencySymbol(UErrorCode& status) const {
if (!fCurrencySymbol.isBogus()) {
return fCurrencySymbol;
}
return loadSymbol(UCURR_SYMBOL_NAME, status);
}
UnicodeString CurrencySymbols::loadSymbol(UCurrNameStyle selector, UErrorCode& status) const {
const char16_t* isoCode = fCurrency.getISOCurrency();
UBool ignoredIsChoiceFormatFillIn = FALSE;
int32_t symbolLen = 0;
const char16_t* symbol = ucurr_getName(
isoCode,
fLocaleName.data(),
selector,
&ignoredIsChoiceFormatFillIn,
&symbolLen,
&status);
// If given an unknown currency, ucurr_getName returns the input string, which we can't alias safely!
// Otherwise, symbol points to a resource bundle, and we can use readonly-aliasing constructor.
if (symbol == isoCode) {
return UnicodeString(isoCode, 3);
} else {
return UnicodeString(TRUE, symbol, symbolLen);
}
}
UnicodeString CurrencySymbols::getIntlCurrencySymbol(UErrorCode&) const {
if (!fIntlCurrencySymbol.isBogus()) {
return fIntlCurrencySymbol;
}
// Note: Not safe to use readonly-aliasing constructor here because the buffer belongs to this object,
// which could be destructed or moved during the lifetime of the return value.
return UnicodeString(fCurrency.getISOCurrency(), 3);
}
UnicodeString CurrencySymbols::getPluralName(StandardPlural::Form plural, UErrorCode& status) const {
const char16_t* isoCode = fCurrency.getISOCurrency();
UBool isChoiceFormat = FALSE;
int32_t symbolLen = 0;
const char16_t* symbol = ucurr_getPluralName(
isoCode,
fLocaleName.data(),
&isChoiceFormat,
StandardPlural::getKeyword(plural),
&symbolLen,
&status);
// If given an unknown currency, ucurr_getName returns the input string, which we can't alias safely!
// Otherwise, symbol points to a resource bundle, and we can use readonly-aliasing constructor.
if (symbol == isoCode) {
return UnicodeString(isoCode, 3);
} else {
return UnicodeString(TRUE, symbol, symbolLen);
}
}
CurrencyUnit
icu::number::impl::resolveCurrency(const DecimalFormatProperties& properties, const Locale& locale,
UErrorCode& status) {
if (!properties.currency.isNull()) {
return properties.currency.getNoError();
} else {
UErrorCode localStatus = U_ZERO_ERROR;
char16_t buf[4] = {};
ucurr_forLocale(locale.getName(), buf, 4, &localStatus);
if (U_SUCCESS(localStatus)) {
return CurrencyUnit(buf, status);
} else {
// Default currency (XXX)
return CurrencyUnit();
}
}
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -0,0 +1,64 @@
// © 2018 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#ifndef __SOURCE_NUMBER_CURRENCYSYMBOLS_H__
#define __SOURCE_NUMBER_CURRENCYSYMBOLS_H__
#include "numparse_types.h"
#include "charstr.h"
#include "number_decimfmtprops.h"
U_NAMESPACE_BEGIN namespace number {
namespace impl {
class CurrencySymbols : public UMemory {
public:
CurrencySymbols() = default; // default constructor: leaves class in valid but undefined state
/** Creates an instance in which all symbols are loaded from data. */
CurrencySymbols(CurrencyUnit currency, const Locale& locale, UErrorCode& status);
/** Creates an instance in which some symbols might be pre-populated. */
CurrencySymbols(CurrencyUnit currency, const Locale& locale, const DecimalFormatSymbols& symbols,
UErrorCode& status);
const char16_t* getIsoCode() const;
UnicodeString getNarrowCurrencySymbol(UErrorCode& status) const;
UnicodeString getCurrencySymbol(UErrorCode& status) const;
UnicodeString getIntlCurrencySymbol(UErrorCode& status) const;
UnicodeString getPluralName(StandardPlural::Form plural, UErrorCode& status) const;
protected:
// Required fields:
CurrencyUnit fCurrency;
CharString fLocaleName;
// Optional fields:
UnicodeString fCurrencySymbol;
UnicodeString fIntlCurrencySymbol;
UnicodeString loadSymbol(UCurrNameStyle selector, UErrorCode& status) const;
};
/**
* Resolves the effective currency from the property bag.
*/
CurrencyUnit
resolveCurrency(const DecimalFormatProperties& properties, const Locale& locale, UErrorCode& status);
} // namespace impl
} // namespace numparse
U_NAMESPACE_END
#endif //__SOURCE_NUMBER_CURRENCYSYMBOLS_H__
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -3,19 +3,20 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#include "uassert.h"
#include <cmath>
#include "cmemory.h"
#include "decNumber.h"
#include <limits>
#include <stdlib.h>
#include "putilimp.h"
#include "number_decimalquantity.h"
#include "decContext.h"
#include "decNumber.h"
#include "number_roundingutils.h"
#include "double-conversion.h"
#include "unicode/plurrule.h"
#include "charstr.h"
#include "number_utils.h"
using namespace icu;
using namespace icu::number;
@ -30,25 +31,6 @@ int8_t NEGATIVE_FLAG = 1;
int8_t INFINITY_FLAG = 2;
int8_t NAN_FLAG = 4;
static constexpr int32_t DEFAULT_DIGITS = 34;
typedef MaybeStackHeaderAndArray<decNumber, char, DEFAULT_DIGITS> DecNumberWithStorage;
/** Helper function to convert a decNumber-compatible string into a decNumber. */
void stringToDecNumber(StringPiece n, DecNumberWithStorage &dn) {
decContext set;
uprv_decContextDefault(&set, DEC_INIT_BASE);
uprv_decContextSetRounding(&set, DEC_ROUND_HALF_EVEN);
set.traps = 0; // no traps, thank you
if (n.length() > DEFAULT_DIGITS) {
dn.resize(n.length(), 0);
set.digits = n.length();
} else {
set.digits = DEFAULT_DIGITS;
}
uprv_decNumberFromString(dn.getAlias(), n.data(), &set);
U_ASSERT(DECDPUN == 1);
}
/** Helper function for safe subtraction (no overflow). */
inline int32_t safeSubtract(int32_t a, int32_t b) {
// Note: In C++, signed integer subtraction is undefined behavior.
@ -102,11 +84,30 @@ DecimalQuantity::DecimalQuantity(const DecimalQuantity &other) {
*this = other;
}
DecimalQuantity::DecimalQuantity(DecimalQuantity&& src) U_NOEXCEPT {
*this = std::move(src);
}
DecimalQuantity &DecimalQuantity::operator=(const DecimalQuantity &other) {
if (this == &other) {
return *this;
}
copyBcdFrom(other);
copyFieldsFrom(other);
return *this;
}
DecimalQuantity& DecimalQuantity::operator=(DecimalQuantity&& src) U_NOEXCEPT {
if (this == &src) {
return *this;
}
moveBcdFrom(src);
copyFieldsFrom(src);
return *this;
}
void DecimalQuantity::copyFieldsFrom(const DecimalQuantity& other) {
bogus = other.bogus;
lOptPos = other.lOptPos;
lReqPos = other.lReqPos;
rReqPos = other.rReqPos;
@ -117,7 +118,6 @@ DecimalQuantity &DecimalQuantity::operator=(const DecimalQuantity &other) {
origDouble = other.origDouble;
origDelta = other.origDelta;
isApproximate = other.isApproximate;
return *this;
}
void DecimalQuantity::clear() {
@ -134,6 +134,12 @@ void DecimalQuantity::setIntegerLength(int32_t minInt, int32_t maxInt) {
U_ASSERT(minInt >= 0);
U_ASSERT(maxInt >= minInt);
// Special behavior: do not set minInt to be less than what is already set.
// This is so significant digits rounding can set the integer length.
if (minInt < lReqPos) {
minInt = lReqPos;
}
// Save values into internal state
// Negation is safe for minFrac/maxFrac because -Integer.MAX_VALUE > Integer.MIN_VALUE
lOptPos = maxInt;
@ -161,29 +167,53 @@ uint64_t DecimalQuantity::getPositionFingerprint() const {
}
void DecimalQuantity::roundToIncrement(double roundingIncrement, RoundingMode roundingMode,
int32_t minMaxFrac, UErrorCode& status) {
// TODO: This is innefficient. Improve?
// TODO: Should we convert to decNumber instead?
int32_t maxFrac, UErrorCode& status) {
// TODO(13701): This is innefficient. Improve?
// TODO(13701): Should we convert to decNumber instead?
roundToInfinity();
double temp = toDouble();
temp /= roundingIncrement;
setToDouble(temp);
roundToMagnitude(0, roundingMode, status);
temp = toDouble();
// Use another DecimalQuantity to perform the actual rounding...
DecimalQuantity dq;
dq.setToDouble(temp);
dq.roundToMagnitude(0, roundingMode, status);
temp = dq.toDouble();
temp *= roundingIncrement;
setToDouble(temp);
// Since we reset the value to a double, we need to specify the rounding boundary
// in order to get the DecimalQuantity out of approximation mode.
roundToMagnitude(-minMaxFrac, roundingMode, status);
// NOTE: In Java, we have minMaxFrac, but in C++, the two are differentiated.
roundToMagnitude(-maxFrac, roundingMode, status);
}
void DecimalQuantity::multiplyBy(int32_t multiplicand) {
void DecimalQuantity::multiplyBy(const DecNum& multiplicand, UErrorCode& status) {
if (isInfinite() || isZero() || isNaN()) {
return;
}
// TODO: Should we convert to decNumber instead?
double temp = toDouble();
temp *= multiplicand;
setToDouble(temp);
// Convert to DecNum, multiply, and convert back.
DecNum decnum;
toDecNum(decnum, status);
if (U_FAILURE(status)) { return; }
decnum.multiplyBy(multiplicand, status);
if (U_FAILURE(status)) { return; }
setToDecNum(decnum, status);
}
void DecimalQuantity::divideBy(const DecNum& divisor, UErrorCode& status) {
if (isInfinite() || isZero() || isNaN()) {
return;
}
// Convert to DecNum, multiply, and convert back.
DecNum decnum;
toDecNum(decnum, status);
if (U_FAILURE(status)) { return; }
decnum.divideBy(divisor, status);
if (U_FAILURE(status)) { return; }
setToDecNum(decnum, status);
}
void DecimalQuantity::negate() {
flags ^= NEGATIVE_FLAG;
}
int32_t DecimalQuantity::getMagnitude() const {
@ -191,11 +221,14 @@ int32_t DecimalQuantity::getMagnitude() const {
return scale + precision - 1;
}
void DecimalQuantity::adjustMagnitude(int32_t delta) {
bool DecimalQuantity::adjustMagnitude(int32_t delta) {
if (precision != 0) {
scale += delta;
origDelta += delta;
// i.e., scale += delta; origDelta += delta
bool overflow = uprv_add32_overflow(scale, delta, &scale);
overflow = uprv_add32_overflow(origDelta, delta, &origDelta) || overflow;
return overflow;
}
return false;
}
StandardPlural::Form DecimalQuantity::getStandardPlural(const PluralRules *rules) const {
@ -215,7 +248,8 @@ double DecimalQuantity::getPluralOperand(PluralOperand operand) const {
switch (operand) {
case PLURAL_OPERAND_I:
return static_cast<double>(toLong());
// Invert the negative sign if necessary
return static_cast<double>(isNegative() ? -toLong(true) : toLong(true));
case PLURAL_OPERAND_F:
return static_cast<double>(toFractionLong(true));
case PLURAL_OPERAND_T:
@ -229,6 +263,10 @@ double DecimalQuantity::getPluralOperand(PluralOperand operand) const {
}
}
bool DecimalQuantity::hasIntegerValue() const {
return scale >= 0;
}
int32_t DecimalQuantity::getUpperDisplayMagnitude() const {
// If this assertion fails, you need to call roundToInfinity() or some other rounding method.
// See the comment in the header file explaining the "isApproximate" field.
@ -310,7 +348,7 @@ void DecimalQuantity::_setToInt(int32_t n) {
DecimalQuantity &DecimalQuantity::setToLong(int64_t n) {
setBcdToZero();
flags = 0;
if (n < 0) {
if (n < 0 && n > INT64_MIN) {
flags |= NEGATIVE_FLAG;
n = -n;
}
@ -323,10 +361,12 @@ DecimalQuantity &DecimalQuantity::setToLong(int64_t n) {
void DecimalQuantity::_setToLong(int64_t n) {
if (n == INT64_MIN) {
static const char *int64minStr = "9.223372036854775808E+18";
DecNumberWithStorage dn;
stringToDecNumber(int64minStr, dn);
readDecNumberToBcd(dn.getAlias());
DecNum decnum;
UErrorCode localStatus = U_ZERO_ERROR;
decnum.setTo("9.223372036854775808E+18", localStatus);
if (U_FAILURE(localStatus)) { return; } // unexpected
flags |= NEGATIVE_FLAG;
readDecNumberToBcd(decnum);
} else if (n <= INT32_MAX) {
readIntToBcd(static_cast<int32_t>(n));
} else {
@ -425,51 +465,107 @@ void DecimalQuantity::convertToAccurateDouble() {
explicitExactDouble = true;
}
DecimalQuantity &DecimalQuantity::setToDecNumber(StringPiece n) {
DecimalQuantity &DecimalQuantity::setToDecNumber(StringPiece n, UErrorCode& status) {
setBcdToZero();
flags = 0;
DecNumberWithStorage dn;
stringToDecNumber(n, dn);
// Compute the decNumber representation
DecNum decnum;
decnum.setTo(n, status);
// The code path for decNumber is modeled after BigDecimal in Java.
if (decNumberIsNegative(dn.getAlias())) {
flags |= NEGATIVE_FLAG;
}
if (!decNumberIsZero(dn.getAlias())) {
_setToDecNumber(dn.getAlias());
}
_setToDecNum(decnum, status);
return *this;
}
void DecimalQuantity::_setToDecNumber(decNumber *n) {
// Java fastpaths for ints here. In C++, just always read directly from the decNumber.
readDecNumberToBcd(n);
compact();
DecimalQuantity& DecimalQuantity::setToDecNum(const DecNum& decnum, UErrorCode& status) {
setBcdToZero();
flags = 0;
_setToDecNum(decnum, status);
return *this;
}
int64_t DecimalQuantity::toLong() const {
int64_t result = 0L;
for (int32_t magnitude = scale + precision - 1; magnitude >= 0; magnitude--) {
void DecimalQuantity::_setToDecNum(const DecNum& decnum, UErrorCode& status) {
if (U_FAILURE(status)) { return; }
if (decnum.isNegative()) {
flags |= NEGATIVE_FLAG;
}
if (!decnum.isZero()) {
readDecNumberToBcd(decnum);
compact();
}
}
int64_t DecimalQuantity::toLong(bool truncateIfOverflow) const {
// NOTE: Call sites should be guarded by fitsInLong(), like this:
// if (dq.fitsInLong()) { /* use dq.toLong() */ } else { /* use some fallback */ }
// Fallback behavior upon truncateIfOverflow is to truncate at 17 digits.
uint64_t result = 0L;
int32_t upperMagnitude = std::min(scale + precision, lOptPos) - 1;
if (truncateIfOverflow) {
upperMagnitude = std::min(upperMagnitude, 17);
}
for (int32_t magnitude = upperMagnitude; magnitude >= 0; magnitude--) {
result = result * 10 + getDigitPos(magnitude - scale);
}
if (isNegative()) {
return -result;
}
return result;
}
int64_t DecimalQuantity::toFractionLong(bool includeTrailingZeros) const {
int64_t result = 0L;
uint64_t DecimalQuantity::toFractionLong(bool includeTrailingZeros) const {
uint64_t result = 0L;
int32_t magnitude = -1;
for (; (magnitude >= scale || (includeTrailingZeros && magnitude >= rReqPos)) &&
magnitude >= rOptPos; magnitude--) {
int32_t lowerMagnitude = std::max(scale, rOptPos);
if (includeTrailingZeros) {
lowerMagnitude = std::min(lowerMagnitude, rReqPos);
}
for (; magnitude >= lowerMagnitude && result <= 1e18L; magnitude--) {
result = result * 10 + getDigitPos(magnitude - scale);
}
// Remove trailing zeros; this can happen during integer overflow cases.
if (!includeTrailingZeros) {
while (result > 0 && (result % 10) == 0) {
result /= 10;
}
}
return result;
}
bool DecimalQuantity::fitsInLong(bool ignoreFraction) const {
if (isZero()) {
return true;
}
if (scale < 0 && !ignoreFraction) {
return false;
}
int magnitude = getMagnitude();
if (magnitude < 18) {
return true;
}
if (magnitude > 18) {
return false;
}
// Hard case: the magnitude is 10^18.
// The largest int64 is: 9,223,372,036,854,775,807
for (int p = 0; p < precision; p++) {
int8_t digit = getDigit(18 - p);
static int8_t INT64_BCD[] = { 9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 8 };
if (digit < INT64_BCD[p]) {
return true;
} else if (digit > INT64_BCD[p]) {
return false;
}
}
// Exactly equal to max long plus one.
return isNegative();
}
double DecimalQuantity::toDouble() const {
if (isApproximate) {
return toDoubleFromOriginal();
}
// If this assertion fails, you need to call roundToInfinity() or some other rounding method.
// See the comment in the header file explaining the "isApproximate" field.
U_ASSERT(!isApproximate);
if (isNaN()) {
return NAN;
@ -479,27 +575,27 @@ double DecimalQuantity::toDouble() const {
// We are processing well-formed input, so we don't need any special options to StringToDoubleConverter.
StringToDoubleConverter converter(0, 0, 0, "", "");
UnicodeString numberString = toNumberString();
UnicodeString numberString = this->toScientificString();
int32_t count;
double result = converter.StringToDouble(reinterpret_cast<const uint16_t*>(numberString.getBuffer()), numberString.length(), &count);
if (isNegative()) { result = -result; }
return result;
return converter.StringToDouble(
reinterpret_cast<const uint16_t*>(numberString.getBuffer()),
numberString.length(),
&count);
}
double DecimalQuantity::toDoubleFromOriginal() const {
double result = origDouble;
int32_t delta = origDelta;
if (delta >= 0) {
// 1e22 is the largest exact double.
for (; delta >= 22; delta -= 22) result *= 1e22;
result *= DOUBLE_MULTIPLIERS[delta];
} else {
// 1e22 is the largest exact double.
for (; delta <= -22; delta += 22) result /= 1e22;
result /= DOUBLE_MULTIPLIERS[-delta];
void DecimalQuantity::toDecNum(DecNum& output, UErrorCode& status) const {
// Special handling for zero
if (precision == 0) {
output.setTo("0", status);
}
if (isNegative()) { result *= -1; }
return result;
// Use the BCD constructor. We need to do a little bit of work to convert, though.
// The decNumber constructor expects most-significant first, but we store least-significant first.
MaybeStackArray<uint8_t, 20> ubcd(precision);
for (int32_t m = 0; m < precision; m++) {
ubcd[precision - m - 1] = static_cast<uint8_t>(getDigitPos(m));
}
output.setTo(ubcd.getAlias(), precision, scale, isNegative(), status);
}
void DecimalQuantity::roundToMagnitude(int32_t magnitude, RoundingMode roundingMode, UErrorCode& status) {
@ -677,17 +773,63 @@ void DecimalQuantity::appendDigit(int8_t value, int32_t leadingZeros, bool appen
}
UnicodeString DecimalQuantity::toPlainString() const {
U_ASSERT(!isApproximate);
UnicodeString sb;
if (isNegative()) {
sb.append(u'-');
}
if (precision == 0 || getMagnitude() < 0) {
sb.append(u'0');
}
for (int m = getUpperDisplayMagnitude(); m >= getLowerDisplayMagnitude(); m--) {
if (m == -1) { sb.append(u'.'); }
sb.append(getDigit(m) + u'0');
if (m == 0) { sb.append(u'.'); }
}
return sb;
}
UnicodeString DecimalQuantity::toScientificString() const {
U_ASSERT(!isApproximate);
UnicodeString result;
if (isNegative()) {
result.append(u'-');
}
if (precision == 0) {
result.append(u"0E+0", -1);
return result;
}
// NOTE: It is not safe to add to lOptPos (aka maxInt) or subtract from
// rOptPos (aka -maxFrac) due to overflow.
int32_t upperPos = std::min(precision + scale, lOptPos) - scale - 1;
int32_t lowerPos = std::max(scale, rOptPos) - scale;
int32_t p = upperPos;
result.append(u'0' + getDigitPos(p));
if ((--p) >= lowerPos) {
result.append(u'.');
for (; p >= lowerPos; p--) {
result.append(u'0' + getDigitPos(p));
}
}
result.append(u'E');
int32_t _scale = upperPos + scale;
if (_scale < 0) {
_scale *= -1;
result.append(u'-');
} else {
result.append(u'+');
}
if (_scale == 0) {
result.append(u'0');
}
int32_t insertIndex = result.length();
while (_scale > 0) {
std::div_t res = std::div(_scale, 10);
result.insert(insertIndex, u'0' + res.rem);
_scale = res.quot;
}
return result;
}
////////////////////////////////////////////////////
/// End of DecimalQuantity_AbstractBCD.java ///
/// Start of DecimalQuantity_DualStorageBCD.java ///
@ -695,7 +837,7 @@ UnicodeString DecimalQuantity::toPlainString() const {
int8_t DecimalQuantity::getDigitPos(int32_t position) const {
if (usingBytes) {
if (position < 0 || position > precision) { return 0; }
if (position < 0 || position >= precision) { return 0; }
return fBCD.bcdBytes.ptr[position];
} else {
if (position < 0 || position >= 16) { return 0; }
@ -807,7 +949,8 @@ void DecimalQuantity::readLongToBcd(int64_t n) {
}
}
void DecimalQuantity::readDecNumberToBcd(decNumber *dn) {
void DecimalQuantity::readDecNumberToBcd(const DecNum& decnum) {
const decNumber* dn = decnum.getRawDecNumber();
if (dn->digits > 16) {
ensureCapacity(dn->digits);
for (int32_t i = 0; i < dn->digits; i++) {
@ -950,6 +1093,20 @@ void DecimalQuantity::copyBcdFrom(const DecimalQuantity &other) {
}
}
void DecimalQuantity::moveBcdFrom(DecimalQuantity &other) {
setBcdToZero();
if (other.usingBytes) {
usingBytes = true;
fBCD.bcdBytes.ptr = other.fBCD.bcdBytes.ptr;
fBCD.bcdBytes.len = other.fBCD.bcdBytes.len;
// Take ownership away from the old instance:
other.fBCD.bcdBytes.ptr = nullptr;
other.usingBytes = false;
} else {
fBCD.bcdLong = other.fBCD.bcdLong;
}
}
const char16_t* DecimalQuantity::checkHealth() const {
if (usingBytes) {
if (precision == 0) { return u"Zero precision but we are in byte mode"; }
@ -988,6 +1145,11 @@ const char16_t* DecimalQuantity::checkHealth() const {
return nullptr;
}
bool DecimalQuantity::operator==(const DecimalQuantity& other) const {
// FIXME: Make a faster implementation.
return toString() == other.toString();
}
UnicodeString DecimalQuantity::toString() const {
MaybeStackArray<char, 30> digits(precision + 1);
for (int32_t i = 0; i < precision; i++) {
@ -998,38 +1160,17 @@ UnicodeString DecimalQuantity::toString() const {
snprintf(
buffer8,
sizeof(buffer8),
"<DecimalQuantity %d:%d:%d:%d %s %s%s%d>",
"<DecimalQuantity %d:%d:%d:%d %s %s%s%s%d>",
(lOptPos > 999 ? 999 : lOptPos),
lReqPos,
rReqPos,
(rOptPos < -999 ? -999 : rOptPos),
(usingBytes ? "bytes" : "long"),
(isNegative() ? "-" : ""),
(precision == 0 ? "0" : digits.getAlias()),
"E",
scale);
return UnicodeString(buffer8, -1, US_INV);
}
UnicodeString DecimalQuantity::toNumberString() const {
UnicodeString result;
for (int32_t i = 0; i < precision; i++) {
result.append(u'0' + getDigitPos(precision - i - 1));
}
result.append(u'E');
int32_t _scale = scale;
if (_scale < 0) {
_scale *= -1;
result.append(u'-');
}
if (_scale == 0) {
result.append(u'0');
}
int32_t insertIndex = result.length();
while (_scale > 0) {
result.insert(insertIndex, u'0' + (_scale % 10));
_scale /= 10;
}
return result;
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -3,13 +3,12 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#ifndef __NUMBER_DECIMALQUANTITY_H__
#define __NUMBER_DECIMALQUANTITY_H__
#include <cstdint>
#include "unicode/umachine.h"
#include "decNumber.h"
#include "standardplural.h"
#include "plurrule_impl.h"
#include "number_types.h"
@ -17,6 +16,9 @@
U_NAMESPACE_BEGIN namespace number {
namespace impl {
// Forward-declare (maybe don't want number_utils.h included here):
class DecNum;
/**
* An class for representing a number to be processed by the decimal formatting pipeline. Includes
* methods for rounding, plural rules, and decimal digit extraction.
@ -33,9 +35,12 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
/** Copy constructor. */
DecimalQuantity(const DecimalQuantity &other);
/** Move constructor. */
DecimalQuantity(DecimalQuantity &&src) U_NOEXCEPT;
DecimalQuantity();
~DecimalQuantity();
~DecimalQuantity() override;
/**
* Sets this instance to be equal to another instance.
@ -44,6 +49,9 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
*/
DecimalQuantity &operator=(const DecimalQuantity &other);
/** Move assignment */
DecimalQuantity &operator=(DecimalQuantity&& src) U_NOEXCEPT;
/**
* Sets the minimum and maximum integer digits that this {@link DecimalQuantity} should generate.
* This method does not perform rounding.
@ -71,7 +79,7 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
* @param mathContext The {@link RoundingMode} to use if rounding is necessary.
*/
void roundToIncrement(double roundingIncrement, RoundingMode roundingMode,
int32_t minMaxFrac, UErrorCode& status);
int32_t maxFrac, UErrorCode& status);
/**
* Rounds the number to a specified magnitude (power of ten).
@ -89,19 +97,30 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
void roundToInfinity();
/**
* Multiply the internal value.
* Multiply the internal value. Uses decNumber.
*
* @param multiplicand The value by which to multiply.
*/
void multiplyBy(int32_t multiplicand);
void multiplyBy(const DecNum& multiplicand, UErrorCode& status);
/**
* Divide the internal value. Uses decNumber.
*
* @param multiplicand The value by which to multiply.
*/
void divideBy(const DecNum& divisor, UErrorCode& status);
/** Flips the sign from positive to negative and back. */
void negate();
/**
* Scales the number by a power of ten. For example, if the value is currently "1234.56", calling
* this method with delta=-3 will change the value to "1.23456".
*
* @param delta The number of magnitudes of ten to change by.
* @return true if integer overflow occured; false otherwise.
*/
void adjustMagnitude(int32_t delta);
bool adjustMagnitude(int32_t delta);
/**
* @return The power of ten corresponding to the most significant nonzero digit.
@ -124,13 +143,23 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
/** @return Whether the value represented by this {@link DecimalQuantity} is not a number. */
bool isNaN() const U_OVERRIDE;
int64_t toLong() const;
/** @param truncateIfOverflow if false and the number does NOT fit, fails with an assertion error. */
int64_t toLong(bool truncateIfOverflow = false) const;
int64_t toFractionLong(bool includeTrailingZeros) const;
uint64_t toFractionLong(bool includeTrailingZeros) const;
/**
* Returns whether or not a Long can fully represent the value stored in this DecimalQuantity.
* @param ignoreFraction if true, silently ignore digits after the decimal place.
*/
bool fitsInLong(bool ignoreFraction = false) const;
/** @return The value contained in this {@link DecimalQuantity} approximated as a double. */
double toDouble() const;
/** Computes a DecNum representation of this DecimalQuantity, saving it to the output parameter. */
void toDecNum(DecNum& output, UErrorCode& status) const;
DecimalQuantity &setToInt(int32_t n);
DecimalQuantity &setToLong(int64_t n);
@ -138,8 +167,10 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
DecimalQuantity &setToDouble(double n);
/** decNumber is similar to BigDecimal in Java. */
DecimalQuantity &setToDecNumber(StringPiece n, UErrorCode& status);
DecimalQuantity &setToDecNumber(StringPiece n);
/** Internal method if the caller already has a DecNum. */
DecimalQuantity &setToDecNum(const DecNum& n, UErrorCode& status);
/**
* Appends a digit, optionally with one or more leading zeros, to the end of the value represented
@ -171,6 +202,8 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
double getPluralOperand(PluralOperand operand) const U_OVERRIDE;
bool hasIntegerValue() const U_OVERRIDE;
/**
* Gets the digit at the specified magnitude. For example, if the represented number is 12.3,
* getDigit(-1) returns 3, since 3 is the digit corresponding to 10^-1.
@ -223,10 +256,10 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
UnicodeString toString() const;
/* Returns the string in exponential notation. */
UnicodeString toNumberString() const;
/** Returns the string in standard exponential notation. */
UnicodeString toScientificString() const;
/* Returns the string without exponential notation. Slightly slower than toNumberString(). */
/** Returns the string without exponential notation. Slightly slower than toScientificString(). */
UnicodeString toPlainString() const;
/** Visible for testing */
@ -235,6 +268,17 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
/** Visible for testing */
inline bool isExplicitExactDouble() { return explicitExactDouble; };
bool operator==(const DecimalQuantity& other) const;
inline bool operator!=(const DecimalQuantity& other) const {
return !(*this == other);
}
/**
* Bogus flag for when a DecimalQuantity is stored on the stack.
*/
bool bogus = false;
private:
/**
* The power of ten corresponding to the least significant digit in the BCD. For example, if this
@ -396,12 +440,16 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
*/
void readLongToBcd(int64_t n);
void readDecNumberToBcd(decNumber *dn);
void readDecNumberToBcd(const DecNum& dn);
void readDoubleConversionToBcd(const char* buffer, int32_t length, int32_t point);
void copyFieldsFrom(const DecimalQuantity& other);
void copyBcdFrom(const DecimalQuantity &other);
void moveBcdFrom(DecimalQuantity& src);
/**
* Removes trailing zeros from the BCD (adjusting the scale as required) and then computes the
* precision. The precision is the number of digits in the number up through the greatest nonzero
@ -418,12 +466,10 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
void _setToDoubleFast(double n);
void _setToDecNumber(decNumber *n);
void _setToDecNum(const DecNum& dn, UErrorCode& status);
void convertToAccurateDouble();
double toDoubleFromOriginal() const;
/** Ensure that a byte array of at least 40 digits is allocated. */
void ensureCapacity();

View File

@ -3,14 +3,29 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#include "number_decimfmtprops.h"
#include "umutex.h"
using namespace icu;
using namespace icu::number;
using namespace icu::number::impl;
namespace {
char kRawDefaultProperties[sizeof(DecimalFormatProperties)];
icu::UInitOnce gDefaultPropertiesInitOnce = U_INITONCE_INITIALIZER;
void U_CALLCONV initDefaultProperties(UErrorCode&) {
*reinterpret_cast<DecimalFormatProperties*>(kRawDefaultProperties) = {}; // set to the default instance
}
}
DecimalFormatProperties::DecimalFormatProperties() {
clear();
}
@ -23,8 +38,10 @@ void DecimalFormatProperties::clear() {
decimalPatternMatchRequired = false;
decimalSeparatorAlwaysShown = false;
exponentSignAlwaysShown = false;
formatFailIfMoreThanMaxDigits = false;
formatWidth = -1;
groupingSize = -1;
groupingUsed = true;
magnitudeMultiplier = 0;
maximumFractionDigits = -1;
maximumIntegerDigits = -1;
@ -34,7 +51,8 @@ void DecimalFormatProperties::clear() {
minimumGroupingDigits = -1;
minimumIntegerDigits = -1;
minimumSignificantDigits = -1;
multiplier = 0;
multiplier = 1;
multiplierScale = 0;
negativePrefix.setToBogus();
negativePrefixPattern.setToBogus();
negativeSuffix.setToBogus();
@ -43,9 +61,10 @@ void DecimalFormatProperties::clear() {
padString.setToBogus();
parseCaseSensitive = false;
parseIntegerOnly = false;
parseLenient = false;
parseMode.nullify();
parseNoExponent = false;
parseToBigDecimal = false;
parseAllInput = UNUM_MAYBE;
positivePrefix.setToBogus();
positivePrefixPattern.setToBogus();
positiveSuffix.setToBogus();
@ -56,47 +75,70 @@ void DecimalFormatProperties::clear() {
signAlwaysShown = false;
}
bool DecimalFormatProperties::operator==(const DecimalFormatProperties &other) const {
bool
DecimalFormatProperties::_equals(const DecimalFormatProperties& other, bool ignoreForFastFormat) const {
bool eq = true;
// Properties that must be equal both normally and for fast-path formatting
eq = eq && compactStyle == other.compactStyle;
eq = eq && currency == other.currency;
eq = eq && currencyPluralInfo.fPtr.getAlias() == other.currencyPluralInfo.fPtr.getAlias();
eq = eq && currencyUsage == other.currencyUsage;
eq = eq && decimalPatternMatchRequired == other.decimalPatternMatchRequired;
eq = eq && decimalSeparatorAlwaysShown == other.decimalSeparatorAlwaysShown;
eq = eq && exponentSignAlwaysShown == other.exponentSignAlwaysShown;
eq = eq && formatFailIfMoreThanMaxDigits == other.formatFailIfMoreThanMaxDigits;
eq = eq && formatWidth == other.formatWidth;
eq = eq && groupingSize == other.groupingSize;
eq = eq && magnitudeMultiplier == other.magnitudeMultiplier;
eq = eq && maximumFractionDigits == other.maximumFractionDigits;
eq = eq && maximumIntegerDigits == other.maximumIntegerDigits;
eq = eq && maximumSignificantDigits == other.maximumSignificantDigits;
eq = eq && minimumExponentDigits == other.minimumExponentDigits;
eq = eq && minimumFractionDigits == other.minimumFractionDigits;
eq = eq && minimumGroupingDigits == other.minimumGroupingDigits;
eq = eq && minimumIntegerDigits == other.minimumIntegerDigits;
eq = eq && minimumSignificantDigits == other.minimumSignificantDigits;
eq = eq && multiplier == other.multiplier;
eq = eq && multiplierScale == other.multiplierScale;
eq = eq && negativePrefix == other.negativePrefix;
eq = eq && negativePrefixPattern == other.negativePrefixPattern;
eq = eq && negativeSuffix == other.negativeSuffix;
eq = eq && negativeSuffixPattern == other.negativeSuffixPattern;
eq = eq && padPosition == other.padPosition;
eq = eq && padString == other.padString;
eq = eq && parseCaseSensitive == other.parseCaseSensitive;
eq = eq && parseIntegerOnly == other.parseIntegerOnly;
eq = eq && parseLenient == other.parseLenient;
eq = eq && parseNoExponent == other.parseNoExponent;
eq = eq && parseToBigDecimal == other.parseToBigDecimal;
eq = eq && positivePrefix == other.positivePrefix;
eq = eq && positivePrefixPattern == other.positivePrefixPattern;
eq = eq && positiveSuffix == other.positiveSuffix;
eq = eq && positiveSuffixPattern == other.positiveSuffixPattern;
eq = eq && roundingIncrement == other.roundingIncrement;
eq = eq && roundingMode == other.roundingMode;
eq = eq && secondaryGroupingSize == other.secondaryGroupingSize;
eq = eq && signAlwaysShown == other.signAlwaysShown;
if (ignoreForFastFormat) {
return eq;
}
// Properties ignored by fast-path formatting
// Formatting (special handling required):
eq = eq && groupingSize == other.groupingSize;
eq = eq && groupingUsed == other.groupingUsed;
eq = eq && minimumFractionDigits == other.minimumFractionDigits;
eq = eq && maximumFractionDigits == other.maximumFractionDigits;
eq = eq && maximumIntegerDigits == other.maximumIntegerDigits;
eq = eq && minimumIntegerDigits == other.minimumIntegerDigits;
eq = eq && negativePrefixPattern == other.negativePrefixPattern;
eq = eq && negativeSuffixPattern == other.negativeSuffixPattern;
eq = eq && positivePrefixPattern == other.positivePrefixPattern;
eq = eq && positiveSuffixPattern == other.positiveSuffixPattern;
// Parsing (always safe to ignore):
eq = eq && decimalPatternMatchRequired == other.decimalPatternMatchRequired;
eq = eq && parseCaseSensitive == other.parseCaseSensitive;
eq = eq && parseIntegerOnly == other.parseIntegerOnly;
eq = eq && parseMode == other.parseMode;
eq = eq && parseNoExponent == other.parseNoExponent;
eq = eq && parseToBigDecimal == other.parseToBigDecimal;
eq = eq && parseAllInput == other.parseAllInput;
return eq;
}
bool DecimalFormatProperties::equalsDefaultExceptFastFormat() const {
UErrorCode localStatus = U_ZERO_ERROR;
umtx_initOnce(gDefaultPropertiesInitOnce, &initDefaultProperties, localStatus);
return _equals(*reinterpret_cast<DecimalFormatProperties*>(kRawDefaultProperties), true);
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#ifndef __NUMBER_DECIMFMTPROPS_H__
#define __NUMBER_DECIMFMTPROPS_H__
@ -30,17 +30,59 @@ template class U_I18N_API LocalPointer<CurrencyPluralInfo>;
namespace number {
namespace impl {
// TODO: Figure out a nicer way to deal with CurrencyPluralInfo.
// Exported as U_I18N_API because it is a public member field of exported DecimalFormatProperties
struct U_I18N_API CurrencyPluralInfoWrapper {
LocalPointer<CurrencyPluralInfo> fPtr;
template<typename T>
struct U_I18N_API CopyableLocalPointer {
LocalPointer<T> fPtr;
CurrencyPluralInfoWrapper() {}
CurrencyPluralInfoWrapper(const CurrencyPluralInfoWrapper& other) {
CopyableLocalPointer() = default;
CopyableLocalPointer(const CopyableLocalPointer& other) {
if (!other.fPtr.isNull()) {
fPtr.adoptInstead(new CurrencyPluralInfo(*other.fPtr));
fPtr.adoptInstead(new T(*other.fPtr));
}
}
CopyableLocalPointer& operator=(const CopyableLocalPointer& other) {
if (!other.fPtr.isNull()) {
fPtr.adoptInstead(new T(*other.fPtr));
}
return *this;
}
};
/** Controls the set of rules for parsing a string from the old DecimalFormat API. */
enum ParseMode {
/**
* Lenient mode should be used if you want to accept malformed user input. It will use heuristics
* to attempt to parse through typographical errors in the string.
*/
PARSE_MODE_LENIENT,
/**
* Strict mode should be used if you want to require that the input is well-formed. More
* specifically, it differs from lenient mode in the following ways:
*
* <ul>
* <li>Grouping widths must match the grouping settings. For example, "12,3,45" will fail if the
* grouping width is 3, as in the pattern "#,##0".
* <li>The string must contain a complete prefix and suffix. For example, if the pattern is
* "{#};(#)", then "{123}" or "(123)" would match, but "{123", "123}", and "123" would all fail.
* (The latter strings would be accepted in lenient mode.)
* <li>Whitespace may not appear at arbitrary places in the string. In lenient mode, whitespace
* is allowed to occur arbitrarily before and after prefixes and exponent separators.
* <li>Leading grouping separators are not allowed, as in ",123".
* <li>Minus and plus signs can only appear if specified in the pattern. In lenient mode, a plus
* or minus sign can always precede a number.
* <li>The set of characters that can be interpreted as a decimal or grouping separator is
* smaller.
* <li><strong>If currency parsing is enabled,</strong> currencies must only appear where
* specified in either the current pattern string or in a valid pattern string for the current
* locale. For example, if the pattern is "¤0.00", then "$1.23" would match, but "1.23$" would
* fail to match.
* </ul>
*/
PARSE_MODE_STRICT,
};
// Exported as U_I18N_API because it is needed for the unit test PatternStringTest
@ -49,14 +91,16 @@ struct U_I18N_API DecimalFormatProperties {
public:
NullableValue<UNumberCompactStyle> compactStyle;
NullableValue<CurrencyUnit> currency;
CurrencyPluralInfoWrapper currencyPluralInfo;
CopyableLocalPointer<CurrencyPluralInfo> currencyPluralInfo;
NullableValue<UCurrencyUsage> currencyUsage;
bool decimalPatternMatchRequired;
bool decimalSeparatorAlwaysShown;
bool exponentSignAlwaysShown;
bool formatFailIfMoreThanMaxDigits; // ICU4C-only
int32_t formatWidth;
int32_t groupingSize;
int32_t magnitudeMultiplier;
bool groupingUsed;
int32_t magnitudeMultiplier; // internal field like multiplierScale but separate to avoid conflict
int32_t maximumFractionDigits;
int32_t maximumIntegerDigits;
int32_t maximumSignificantDigits;
@ -66,6 +110,7 @@ struct U_I18N_API DecimalFormatProperties {
int32_t minimumIntegerDigits;
int32_t minimumSignificantDigits;
int32_t multiplier;
int32_t multiplierScale; // ICU4C-only
UnicodeString negativePrefix;
UnicodeString negativePrefixPattern;
UnicodeString negativeSuffix;
@ -74,9 +119,10 @@ struct U_I18N_API DecimalFormatProperties {
UnicodeString padString;
bool parseCaseSensitive;
bool parseIntegerOnly;
bool parseLenient;
NullableValue<ParseMode> parseMode;
bool parseNoExponent;
bool parseToBigDecimal;
bool parseToBigDecimal; // TODO: Not needed in ICU4C?
UNumberFormatAttributeValue parseAllInput; // ICU4C-only
//PluralRules pluralRules;
UnicodeString positivePrefix;
UnicodeString positivePrefixPattern;
@ -89,13 +135,20 @@ struct U_I18N_API DecimalFormatProperties {
DecimalFormatProperties();
//DecimalFormatProperties(const DecimalFormatProperties &other) = default;
DecimalFormatProperties &operator=(const DecimalFormatProperties &other) = default;
bool operator==(const DecimalFormatProperties &other) const;
inline bool operator==(const DecimalFormatProperties& other) const {
return _equals(other, false);
}
void clear();
/**
* Checks for equality to the default DecimalFormatProperties, but ignores the prescribed set of
* options for fast-path formatting.
*/
bool equalsDefaultExceptFastFormat() const;
private:
bool _equals(const DecimalFormatProperties& other, bool ignoreForFastFormat) const;
};
} // namespace impl

View File

@ -1,22 +1,25 @@
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include <unicode/numberformatter.h>
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#include "uassert.h"
#include "unicode/numberformatter.h"
#include "number_decimalquantity.h"
#include "number_formatimpl.h"
#include "umutex.h"
#include "number_skeletons.h"
#include "number_utypes.h"
using namespace icu;
using namespace icu::number;
using namespace icu::number::impl;
template<typename Derived>
Derived NumberFormatterSettings<Derived>::notation(const Notation &notation) const {
Derived NumberFormatterSettings<Derived>::notation(const Notation& notation) const& {
Derived copy(*this);
// NOTE: Slicing is OK.
copy.fMacros.notation = notation;
@ -24,7 +27,15 @@ Derived NumberFormatterSettings<Derived>::notation(const Notation &notation) con
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::unit(const icu::MeasureUnit &unit) const {
Derived NumberFormatterSettings<Derived>::notation(const Notation& notation)&& {
Derived move(std::move(*this));
// NOTE: Slicing is OK.
move.fMacros.notation = notation;
return move;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::unit(const icu::MeasureUnit& unit) const& {
Derived copy(*this);
// NOTE: Slicing occurs here. However, CurrencyUnit can be restored from MeasureUnit.
// TimeUnit may be affected, but TimeUnit is not as relevant to number formatting.
@ -33,21 +44,41 @@ Derived NumberFormatterSettings<Derived>::unit(const icu::MeasureUnit &unit) con
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::adoptUnit(icu::MeasureUnit *unit) const {
Derived NumberFormatterSettings<Derived>::unit(const icu::MeasureUnit& unit)&& {
Derived move(std::move(*this));
// See comments above about slicing.
move.fMacros.unit = unit;
return move;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::adoptUnit(icu::MeasureUnit* unit) const& {
Derived copy(*this);
// Just copy the unit into the MacroProps by value, and delete it since we have ownership.
// Just move the unit into the MacroProps by value, and delete it since we have ownership.
// NOTE: Slicing occurs here. However, CurrencyUnit can be restored from MeasureUnit.
// TimeUnit may be affected, but TimeUnit is not as relevant to number formatting.
if (unit != nullptr) {
// TODO: On nullptr, reset to default value?
copy.fMacros.unit = *unit;
// TODO: On nullptr, reset to default value?
copy.fMacros.unit = std::move(*unit);
delete unit;
}
return copy;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::perUnit(const icu::MeasureUnit &perUnit) const {
Derived NumberFormatterSettings<Derived>::adoptUnit(icu::MeasureUnit* unit)&& {
Derived move(std::move(*this));
// See comments above about slicing and ownership.
if (unit != nullptr) {
// TODO: On nullptr, reset to default value?
move.fMacros.unit = std::move(*unit);
delete unit;
}
return move;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::perUnit(const icu::MeasureUnit& perUnit) const& {
Derived copy(*this);
// See comments above about slicing.
copy.fMacros.perUnit = perUnit;
@ -55,19 +86,39 @@ Derived NumberFormatterSettings<Derived>::perUnit(const icu::MeasureUnit &perUni
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::adoptPerUnit(icu::MeasureUnit *perUnit) const {
Derived NumberFormatterSettings<Derived>::perUnit(const icu::MeasureUnit& perUnit)&& {
Derived copy(*this);
// See comments above about slicing.
copy.fMacros.perUnit = perUnit;
return copy;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::adoptPerUnit(icu::MeasureUnit* perUnit) const& {
Derived move(std::move(*this));
// See comments above about slicing and ownership.
if (perUnit != nullptr) {
// TODO: On nullptr, reset to default value?
move.fMacros.perUnit = std::move(*perUnit);
delete perUnit;
}
return move;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::adoptPerUnit(icu::MeasureUnit* perUnit)&& {
Derived copy(*this);
// See comments above about slicing and ownership.
if (perUnit != nullptr) {
// TODO: On nullptr, reset to default value?
copy.fMacros.perUnit = *perUnit;
// TODO: On nullptr, reset to default value?
copy.fMacros.perUnit = std::move(*perUnit);
delete perUnit;
}
return copy;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::rounding(const Rounder &rounder) const {
Derived NumberFormatterSettings<Derived>::rounding(const Rounder& rounder) const& {
Derived copy(*this);
// NOTE: Slicing is OK.
copy.fMacros.rounder = rounder;
@ -75,7 +126,15 @@ Derived NumberFormatterSettings<Derived>::rounding(const Rounder &rounder) const
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::grouping(const UGroupingStrategy &strategy) const {
Derived NumberFormatterSettings<Derived>::rounding(const Rounder& rounder)&& {
Derived move(std::move(*this));
// NOTE: Slicing is OK.
move.fMacros.rounder = rounder;
return move;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::grouping(const UGroupingStrategy& strategy) const& {
Derived copy(*this);
// NOTE: This is slightly different than how the setting is stored in Java
// because we want to put it on the stack.
@ -84,61 +143,174 @@ Derived NumberFormatterSettings<Derived>::grouping(const UGroupingStrategy &stra
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::integerWidth(const IntegerWidth &style) const {
Derived NumberFormatterSettings<Derived>::grouping(const UGroupingStrategy& strategy)&& {
Derived move(std::move(*this));
move.fMacros.grouper = Grouper::forStrategy(strategy);
return move;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::integerWidth(const IntegerWidth& style) const& {
Derived copy(*this);
copy.fMacros.integerWidth = style;
return copy;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::symbols(const DecimalFormatSymbols &symbols) const {
Derived NumberFormatterSettings<Derived>::integerWidth(const IntegerWidth& style)&& {
Derived move(std::move(*this));
move.fMacros.integerWidth = style;
return move;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::symbols(const DecimalFormatSymbols& symbols) const& {
Derived copy(*this);
copy.fMacros.symbols.setTo(symbols);
return copy;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::adoptSymbols(NumberingSystem *ns) const {
Derived NumberFormatterSettings<Derived>::symbols(const DecimalFormatSymbols& symbols)&& {
Derived move(std::move(*this));
move.fMacros.symbols.setTo(symbols);
return move;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::adoptSymbols(NumberingSystem* ns) const& {
Derived copy(*this);
copy.fMacros.symbols.setTo(ns);
return copy;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::unitWidth(const UNumberUnitWidth &width) const {
Derived NumberFormatterSettings<Derived>::adoptSymbols(NumberingSystem* ns)&& {
Derived move(std::move(*this));
move.fMacros.symbols.setTo(ns);
return move;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::unitWidth(const UNumberUnitWidth& width) const& {
Derived copy(*this);
copy.fMacros.unitWidth = width;
return copy;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::sign(const UNumberSignDisplay &style) const {
Derived NumberFormatterSettings<Derived>::unitWidth(const UNumberUnitWidth& width)&& {
Derived move(std::move(*this));
move.fMacros.unitWidth = width;
return move;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::sign(const UNumberSignDisplay& style) const& {
Derived copy(*this);
copy.fMacros.sign = style;
return copy;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::decimal(const UNumberDecimalSeparatorDisplay &style) const {
Derived NumberFormatterSettings<Derived>::sign(const UNumberSignDisplay& style)&& {
Derived move(std::move(*this));
move.fMacros.sign = style;
return move;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::decimal(const UNumberDecimalSeparatorDisplay& style) const& {
Derived copy(*this);
copy.fMacros.decimal = style;
return copy;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::padding(const Padder &padder) const {
Derived NumberFormatterSettings<Derived>::decimal(const UNumberDecimalSeparatorDisplay& style)&& {
Derived move(std::move(*this));
move.fMacros.decimal = style;
return move;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::scale(const Scale& scale) const& {
Derived copy(*this);
copy.fMacros.scale = scale;
return copy;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::scale(const Scale& scale)&& {
Derived move(std::move(*this));
move.fMacros.scale = scale;
return move;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::padding(const Padder& padder) const& {
Derived copy(*this);
copy.fMacros.padder = padder;
return copy;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::threshold(int32_t threshold) const {
Derived NumberFormatterSettings<Derived>::padding(const Padder& padder)&& {
Derived move(std::move(*this));
move.fMacros.padder = padder;
return move;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::threshold(int32_t threshold) const& {
Derived copy(*this);
copy.fMacros.threshold = threshold;
return copy;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::threshold(int32_t threshold)&& {
Derived move(std::move(*this));
move.fMacros.threshold = threshold;
return move;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::macros(const impl::MacroProps& macros) const& {
Derived copy(*this);
copy.fMacros = macros;
return copy;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::macros(const impl::MacroProps& macros)&& {
Derived move(std::move(*this));
move.fMacros = macros;
return move;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::macros(impl::MacroProps&& macros) const& {
Derived copy(*this);
copy.fMacros = std::move(macros);
return copy;
}
template<typename Derived>
Derived NumberFormatterSettings<Derived>::macros(impl::MacroProps&& macros)&& {
Derived move(std::move(*this));
move.fMacros = std::move(macros);
return move;
}
template<typename Derived>
UnicodeString NumberFormatterSettings<Derived>::toSkeleton(UErrorCode& status) const {
if (fMacros.copyErrorTo(status)) {
return {};
}
return skeleton::generate(fMacros, status);
}
// Declare all classes that implement NumberFormatterSettings
// See https://stackoverflow.com/a/495056/1407170
template
@ -152,38 +324,119 @@ UnlocalizedNumberFormatter NumberFormatter::with() {
return result;
}
LocalizedNumberFormatter NumberFormatter::withLocale(const Locale &locale) {
LocalizedNumberFormatter NumberFormatter::withLocale(const Locale& locale) {
return with().locale(locale);
}
// Make the child class constructor that takes the parent class call the parent class's copy constructor
UnlocalizedNumberFormatter::UnlocalizedNumberFormatter(
const NumberFormatterSettings <UnlocalizedNumberFormatter> &other)
: NumberFormatterSettings<UnlocalizedNumberFormatter>(other) {
UnlocalizedNumberFormatter
NumberFormatter::fromSkeleton(const UnicodeString& skeleton, UErrorCode& status) {
return skeleton::create(skeleton, status);
}
// Make the child class constructor that takes the parent class call the parent class's copy constructor
// For LocalizedNumberFormatter, also copy over the extra fields
LocalizedNumberFormatter::LocalizedNumberFormatter(
const NumberFormatterSettings <LocalizedNumberFormatter> &other)
: NumberFormatterSettings<LocalizedNumberFormatter>(other) {
// No additional copies required
template<typename T> using NFS = NumberFormatterSettings<T>;
using LNF = LocalizedNumberFormatter;
using UNF = UnlocalizedNumberFormatter;
UnlocalizedNumberFormatter::UnlocalizedNumberFormatter(const UNF& other)
: UNF(static_cast<const NFS<UNF>&>(other)) {}
UnlocalizedNumberFormatter::UnlocalizedNumberFormatter(const NFS<UNF>& other)
: NFS<UNF>(other) {
// No additional fields to assign
}
LocalizedNumberFormatter::LocalizedNumberFormatter(const MacroProps &macros, const Locale &locale) {
UnlocalizedNumberFormatter::UnlocalizedNumberFormatter(UNF&& src) U_NOEXCEPT
: UNF(static_cast<NFS<UNF>&&>(src)) {}
UnlocalizedNumberFormatter::UnlocalizedNumberFormatter(NFS<UNF>&& src) U_NOEXCEPT
: NFS<UNF>(std::move(src)) {
// No additional fields to assign
}
UnlocalizedNumberFormatter& UnlocalizedNumberFormatter::operator=(const UNF& other) {
NFS<UNF>::operator=(static_cast<const NFS<UNF>&>(other));
// No additional fields to assign
return *this;
}
UnlocalizedNumberFormatter& UnlocalizedNumberFormatter::operator=(UNF&& src) U_NOEXCEPT {
NFS<UNF>::operator=(static_cast<NFS<UNF>&&>(src));
// No additional fields to assign
return *this;
}
LocalizedNumberFormatter::LocalizedNumberFormatter(const LNF& other)
: LNF(static_cast<const NFS<LNF>&>(other)) {}
LocalizedNumberFormatter::LocalizedNumberFormatter(const NFS<LNF>& other)
: NFS<LNF>(other) {
// No additional fields to assign (let call count and compiled formatter reset to defaults)
}
LocalizedNumberFormatter::LocalizedNumberFormatter(LocalizedNumberFormatter&& src) U_NOEXCEPT
: LNF(static_cast<NFS<LNF>&&>(src)) {}
LocalizedNumberFormatter::LocalizedNumberFormatter(NFS<LNF>&& src) U_NOEXCEPT
: NFS<LNF>(std::move(src)) {
// For the move operators, copy over the call count and compiled formatter.
auto&& srcAsLNF = static_cast<LNF&&>(src);
fCompiled = srcAsLNF.fCompiled;
uprv_memcpy(fUnsafeCallCount, srcAsLNF.fUnsafeCallCount, sizeof(fUnsafeCallCount));
// Reset the source object to leave it in a safe state.
srcAsLNF.fCompiled = nullptr;
uprv_memset(srcAsLNF.fUnsafeCallCount, 0, sizeof(fUnsafeCallCount));
}
LocalizedNumberFormatter& LocalizedNumberFormatter::operator=(const LNF& other) {
NFS<LNF>::operator=(static_cast<const NFS<LNF>&>(other));
// No additional fields to assign (let call count and compiled formatter reset to defaults)
return *this;
}
LocalizedNumberFormatter& LocalizedNumberFormatter::operator=(LNF&& src) U_NOEXCEPT {
NFS<LNF>::operator=(static_cast<NFS<LNF>&&>(src));
// For the move operators, copy over the call count and compiled formatter.
fCompiled = src.fCompiled;
uprv_memcpy(fUnsafeCallCount, src.fUnsafeCallCount, sizeof(fUnsafeCallCount));
// Reset the source object to leave it in a safe state.
src.fCompiled = nullptr;
uprv_memset(src.fUnsafeCallCount, 0, sizeof(fUnsafeCallCount));
return *this;
}
LocalizedNumberFormatter::~LocalizedNumberFormatter() {
delete fCompiled;
}
LocalizedNumberFormatter::LocalizedNumberFormatter(const MacroProps& macros, const Locale& locale) {
fMacros = macros;
fMacros.locale = locale;
}
LocalizedNumberFormatter UnlocalizedNumberFormatter::locale(const Locale &locale) const {
LocalizedNumberFormatter::LocalizedNumberFormatter(MacroProps&& macros, const Locale& locale) {
fMacros = std::move(macros);
fMacros.locale = locale;
}
LocalizedNumberFormatter UnlocalizedNumberFormatter::locale(const Locale& locale) const& {
return LocalizedNumberFormatter(fMacros, locale);
}
SymbolsWrapper::SymbolsWrapper(const SymbolsWrapper &other) {
LocalizedNumberFormatter UnlocalizedNumberFormatter::locale(const Locale& locale)&& {
return LocalizedNumberFormatter(std::move(fMacros), locale);
}
SymbolsWrapper::SymbolsWrapper(const SymbolsWrapper& other) {
doCopyFrom(other);
}
SymbolsWrapper &SymbolsWrapper::operator=(const SymbolsWrapper &other) {
SymbolsWrapper::SymbolsWrapper(SymbolsWrapper&& src) U_NOEXCEPT {
doMoveFrom(std::move(src));
}
SymbolsWrapper& SymbolsWrapper::operator=(const SymbolsWrapper& other) {
if (this == &other) {
return *this;
}
@ -192,23 +445,32 @@ SymbolsWrapper &SymbolsWrapper::operator=(const SymbolsWrapper &other) {
return *this;
}
SymbolsWrapper& SymbolsWrapper::operator=(SymbolsWrapper&& src) U_NOEXCEPT {
if (this == &src) {
return *this;
}
doCleanup();
doMoveFrom(std::move(src));
return *this;
}
SymbolsWrapper::~SymbolsWrapper() {
doCleanup();
}
void SymbolsWrapper::setTo(const DecimalFormatSymbols &dfs) {
void SymbolsWrapper::setTo(const DecimalFormatSymbols& dfs) {
doCleanup();
fType = SYMPTR_DFS;
fPtr.dfs = new DecimalFormatSymbols(dfs);
}
void SymbolsWrapper::setTo(const NumberingSystem *ns) {
void SymbolsWrapper::setTo(const NumberingSystem* ns) {
doCleanup();
fType = SYMPTR_NS;
fPtr.ns = ns;
}
void SymbolsWrapper::doCopyFrom(const SymbolsWrapper &other) {
void SymbolsWrapper::doCopyFrom(const SymbolsWrapper& other) {
fType = other.fType;
switch (fType) {
case SYMPTR_NONE:
@ -233,6 +495,23 @@ void SymbolsWrapper::doCopyFrom(const SymbolsWrapper &other) {
}
}
void SymbolsWrapper::doMoveFrom(SymbolsWrapper&& src) {
fType = src.fType;
switch (fType) {
case SYMPTR_NONE:
// No action necessary
break;
case SYMPTR_DFS:
fPtr.dfs = src.fPtr.dfs;
src.fPtr.dfs = nullptr;
break;
case SYMPTR_NS:
fPtr.ns = src.fPtr.ns;
src.fPtr.ns = nullptr;
break;
}
}
void SymbolsWrapper::doCleanup() {
switch (fType) {
case SYMPTR_NONE:
@ -265,53 +544,139 @@ const NumberingSystem* SymbolsWrapper::getNumberingSystem() const {
return fPtr.ns;
}
LocalizedNumberFormatter::~LocalizedNumberFormatter() {
delete fCompiled;
FormattedNumber::FormattedNumber(FormattedNumber&& src) U_NOEXCEPT
: fResults(src.fResults), fErrorCode(src.fErrorCode) {
// Disown src.fResults to prevent double-deletion
src.fResults = nullptr;
src.fErrorCode = U_INVALID_STATE_ERROR;
}
FormattedNumber LocalizedNumberFormatter::formatInt(int64_t value, UErrorCode &status) const {
FormattedNumber& FormattedNumber::operator=(FormattedNumber&& src) U_NOEXCEPT {
delete fResults;
fResults = src.fResults;
fErrorCode = src.fErrorCode;
// Disown src.fResults to prevent double-deletion
src.fResults = nullptr;
src.fErrorCode = U_INVALID_STATE_ERROR;
return *this;
}
FormattedNumber LocalizedNumberFormatter::formatInt(int64_t value, UErrorCode& status) const {
if (U_FAILURE(status)) { return FormattedNumber(U_ILLEGAL_ARGUMENT_ERROR); }
auto results = new NumberFormatterResults();
auto results = new UFormattedNumberData();
if (results == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
return FormattedNumber(status);
}
results->quantity.setToLong(value);
return formatImpl(results, status);
formatImpl(results, status);
// Do not save the results object if we encountered a failure.
if (U_SUCCESS(status)) {
return FormattedNumber(results);
} else {
delete results;
return FormattedNumber(status);
}
}
FormattedNumber LocalizedNumberFormatter::formatDouble(double value, UErrorCode &status) const {
FormattedNumber LocalizedNumberFormatter::formatDouble(double value, UErrorCode& status) const {
if (U_FAILURE(status)) { return FormattedNumber(U_ILLEGAL_ARGUMENT_ERROR); }
auto results = new NumberFormatterResults();
auto results = new UFormattedNumberData();
if (results == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
return FormattedNumber(status);
}
results->quantity.setToDouble(value);
return formatImpl(results, status);
formatImpl(results, status);
// Do not save the results object if we encountered a failure.
if (U_SUCCESS(status)) {
return FormattedNumber(results);
} else {
delete results;
return FormattedNumber(status);
}
}
FormattedNumber LocalizedNumberFormatter::formatDecimal(StringPiece value, UErrorCode &status) const {
FormattedNumber LocalizedNumberFormatter::formatDecimal(StringPiece value, UErrorCode& status) const {
if (U_FAILURE(status)) { return FormattedNumber(U_ILLEGAL_ARGUMENT_ERROR); }
auto results = new NumberFormatterResults();
auto results = new UFormattedNumberData();
if (results == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
return FormattedNumber(status);
}
results->quantity.setToDecNumber(value);
return formatImpl(results, status);
results->quantity.setToDecNumber(value, status);
formatImpl(results, status);
// Do not save the results object if we encountered a failure.
if (U_SUCCESS(status)) {
return FormattedNumber(results);
} else {
delete results;
return FormattedNumber(status);
}
}
FormattedNumber
LocalizedNumberFormatter::formatImpl(impl::NumberFormatterResults *results, UErrorCode &status) const {
LocalizedNumberFormatter::formatDecimalQuantity(const DecimalQuantity& dq, UErrorCode& status) const {
if (U_FAILURE(status)) { return FormattedNumber(U_ILLEGAL_ARGUMENT_ERROR); }
auto results = new UFormattedNumberData();
if (results == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
return FormattedNumber(status);
}
results->quantity = dq;
formatImpl(results, status);
// Do not save the results object if we encountered a failure.
if (U_SUCCESS(status)) {
return FormattedNumber(results);
} else {
delete results;
return FormattedNumber(status);
}
}
void LocalizedNumberFormatter::formatImpl(impl::UFormattedNumberData* results, UErrorCode& status) const {
if (computeCompiled(status)) {
fCompiled->apply(results->quantity, results->string, status);
} else {
NumberFormatterImpl::applyStatic(fMacros, results->quantity, results->string, status);
}
}
void LocalizedNumberFormatter::getAffixImpl(bool isPrefix, bool isNegative, UnicodeString& result,
UErrorCode& status) const {
NumberStringBuilder string;
auto signum = static_cast<int8_t>(isNegative ? -1 : 1);
// Always return affixes for plural form OTHER.
static const StandardPlural::Form plural = StandardPlural::OTHER;
int32_t prefixLength;
if (computeCompiled(status)) {
prefixLength = fCompiled->getPrefixSuffix(signum, plural, string, status);
} else {
prefixLength = NumberFormatterImpl::getPrefixSuffixStatic(fMacros, signum, plural, string, status);
}
result.remove();
if (isPrefix) {
result.append(string.toTempUnicodeString().tempSubStringBetween(0, prefixLength));
} else {
result.append(string.toTempUnicodeString().tempSubStringBetween(prefixLength, string.length()));
}
}
bool LocalizedNumberFormatter::computeCompiled(UErrorCode& status) const {
// fUnsafeCallCount contains memory to be interpreted as an atomic int, most commonly
// std::atomic<int32_t>. Since the type of atomic int is platform-dependent, we cast the
// bytes in fUnsafeCallCount to u_atomic_int32_t, a typedef for the platform-dependent
// atomic int type defined in umutex.h.
static_assert(sizeof(u_atomic_int32_t) <= sizeof(fUnsafeCallCount),
"Atomic integer size on this platform exceeds the size allocated by fUnsafeCallCount");
u_atomic_int32_t* callCount = reinterpret_cast<u_atomic_int32_t*>(
const_cast<LocalizedNumberFormatter*>(this)->fUnsafeCallCount);
static_assert(
sizeof(u_atomic_int32_t) <= sizeof(fUnsafeCallCount),
"Atomic integer size on this platform exceeds the size allocated by fUnsafeCallCount");
auto* callCount = reinterpret_cast<u_atomic_int32_t*>(
const_cast<LocalizedNumberFormatter*>(this)->fUnsafeCallCount);
// A positive value in the atomic int indicates that the data structure is not yet ready;
// a negative value indicates that it is ready. If, after the increment, the atomic int
@ -325,28 +690,29 @@ LocalizedNumberFormatter::formatImpl(impl::NumberFormatterResults *results, UErr
if (currentCount == fMacros.threshold && fMacros.threshold > 0) {
// Build the data structure and then use it (slow to fast path).
const NumberFormatterImpl* compiled =
NumberFormatterImpl::fromMacros(fMacros, status);
const NumberFormatterImpl* compiled = NumberFormatterImpl::fromMacros(fMacros, status);
U_ASSERT(fCompiled == nullptr);
const_cast<LocalizedNumberFormatter *>(this)->fCompiled = compiled;
const_cast<LocalizedNumberFormatter*>(this)->fCompiled = compiled;
umtx_storeRelease(*callCount, INT32_MIN);
compiled->apply(results->quantity, results->string, status);
return true;
} else if (currentCount < 0) {
// The data structure is already built; use it (fast path).
U_ASSERT(fCompiled != nullptr);
fCompiled->apply(results->quantity, results->string, status);
return true;
} else {
// Format the number without building the data structure (slow path).
NumberFormatterImpl::applyStatic(fMacros, results->quantity, results->string, status);
return false;
}
}
// Do not save the results object if we encountered a failure.
if (U_SUCCESS(status)) {
return FormattedNumber(results);
} else {
delete results;
return FormattedNumber(status);
}
const impl::NumberFormatterImpl* LocalizedNumberFormatter::getCompiled() const {
return fCompiled;
}
int32_t LocalizedNumberFormatter::getCallCount() const {
auto* callCount = reinterpret_cast<u_atomic_int32_t*>(
const_cast<LocalizedNumberFormatter*>(this)->fUnsafeCallCount);
return umtx_loadAcquire(*callCount);
}
UnicodeString FormattedNumber::toString() const {
@ -357,7 +723,7 @@ UnicodeString FormattedNumber::toString() const {
return fResults->string.toUnicodeString();
}
Appendable &FormattedNumber::appendTo(Appendable &appendable) {
Appendable& FormattedNumber::appendTo(Appendable& appendable) {
if (fResults == nullptr) {
// TODO: http://bugs.icu-project.org/trac/ticket/13437
return appendable;
@ -366,7 +732,7 @@ Appendable &FormattedNumber::appendTo(Appendable &appendable) {
return appendable;
}
void FormattedNumber::populateFieldPosition(FieldPosition &fieldPosition, UErrorCode &status) {
void FormattedNumber::populateFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) {
if (U_FAILURE(status)) { return; }
if (fResults == nullptr) {
status = fErrorCode;
@ -375,8 +741,7 @@ void FormattedNumber::populateFieldPosition(FieldPosition &fieldPosition, UError
fResults->string.populateFieldPosition(fieldPosition, 0, status);
}
void
FormattedNumber::populateFieldPositionIterator(FieldPositionIterator &iterator, UErrorCode &status) {
void FormattedNumber::populateFieldPositionIterator(FieldPositionIterator& iterator, UErrorCode& status) {
if (U_FAILURE(status)) { return; }
if (fResults == nullptr) {
status = fErrorCode;
@ -385,6 +750,15 @@ FormattedNumber::populateFieldPositionIterator(FieldPositionIterator &iterator,
fResults->string.populateFieldPositionIterator(iterator, status);
}
void FormattedNumber::getDecimalQuantity(DecimalQuantity& output, UErrorCode& status) const {
if (U_FAILURE(status)) { return; }
if (fResults == nullptr) {
status = fErrorCode;
return;
}
output = fResults->quantity;
}
FormattedNumber::~FormattedNumber() {
delete fResults;
}

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#include "cstring.h"
#include "unicode/ures.h"
@ -26,88 +26,28 @@ using namespace icu::number::impl;
namespace {
// NOTE: In Java, the method to get a pattern from the resource bundle exists in NumberFormat.
// In C++, we have to implement that logic here.
// TODO: Make Java and C++ consistent?
enum CldrPatternStyle {
CLDR_PATTERN_STYLE_DECIMAL,
CLDR_PATTERN_STYLE_CURRENCY,
CLDR_PATTERN_STYLE_ACCOUNTING,
CLDR_PATTERN_STYLE_PERCENT
// TODO: Consider scientific format.
};
const char16_t *
doGetPattern(UResourceBundle *res, const char *nsName, const char *patternKey, UErrorCode &publicStatus,
UErrorCode &localStatus) {
// Construct the path into the resource bundle
CharString key;
key.append("NumberElements/", publicStatus);
key.append(nsName, publicStatus);
key.append("/patterns/", publicStatus);
key.append(patternKey, publicStatus);
if (U_FAILURE(publicStatus)) {
return u"";
}
return ures_getStringByKeyWithFallback(res, key.data(), nullptr, &localStatus);
}
const char16_t *getPatternForStyle(const Locale &locale, const char *nsName, CldrPatternStyle style,
UErrorCode &status) {
const char *patternKey;
switch (style) {
case CLDR_PATTERN_STYLE_DECIMAL:
patternKey = "decimalFormat";
break;
case CLDR_PATTERN_STYLE_CURRENCY:
patternKey = "currencyFormat";
break;
case CLDR_PATTERN_STYLE_ACCOUNTING:
patternKey = "accountingFormat";
break;
case CLDR_PATTERN_STYLE_PERCENT:
default:
patternKey = "percentFormat";
break;
}
LocalUResourceBundlePointer res(ures_open(nullptr, locale.getName(), &status));
if (U_FAILURE(status)) { return u""; }
// Attempt to get the pattern with the native numbering system.
UErrorCode localStatus = U_ZERO_ERROR;
const char16_t *pattern;
pattern = doGetPattern(res.getAlias(), nsName, patternKey, status, localStatus);
if (U_FAILURE(status)) { return u""; }
// Fall back to latn if native numbering system does not have the right pattern
if (U_FAILURE(localStatus) && uprv_strcmp("latn", nsName) != 0) {
localStatus = U_ZERO_ERROR;
pattern = doGetPattern(res.getAlias(), "latn", patternKey, status, localStatus);
if (U_FAILURE(status)) { return u""; }
}
return pattern;
}
struct CurrencyFormatInfoResult {
bool exists;
const char16_t* pattern;
const char16_t* decimalSeparator;
const char16_t* groupingSeparator;
};
CurrencyFormatInfoResult getCurrencyFormatInfo(const Locale& locale, const char* isoCode, UErrorCode& status) {
CurrencyFormatInfoResult
getCurrencyFormatInfo(const Locale& locale, const char* isoCode, UErrorCode& status) {
// TODO: Load this data in a centralized location like ICU4J?
// TODO: Move this into the CurrencySymbols class?
// TODO: Parts of this same data are loaded in dcfmtsym.cpp; should clean up.
CurrencyFormatInfoResult result = { false, nullptr, nullptr, nullptr };
if (U_FAILURE(status)) return result;
CurrencyFormatInfoResult result = {false, nullptr, nullptr, nullptr};
if (U_FAILURE(status)) { return result; }
CharString key;
key.append("Currencies/", status);
key.append(isoCode, status);
UErrorCode localStatus = status;
LocalUResourceBundlePointer bundle(ures_open(U_ICUDATA_CURR, locale.getName(), &localStatus));
ures_getByKeyWithFallback(bundle.getAlias(), key.data(), bundle.getAlias(), &localStatus);
if (U_SUCCESS(localStatus) && ures_getSize(bundle.getAlias())>2) { // the length is 3 if more data is present
if (U_SUCCESS(localStatus) &&
ures_getSize(bundle.getAlias()) > 2) { // the length is 3 if more data is present
ures_getByIndex(bundle.getAlias(), 2, bundle.getAlias(), &localStatus);
int32_t dummy;
result.exists = true;
@ -121,41 +61,32 @@ CurrencyFormatInfoResult getCurrencyFormatInfo(const Locale& locale, const char*
return result;
}
inline bool unitIsCurrency(const MeasureUnit &unit) {
return uprv_strcmp("currency", unit.getType()) == 0;
}
inline bool unitIsNoUnit(const MeasureUnit &unit) {
return uprv_strcmp("none", unit.getType()) == 0;
}
inline bool unitIsPercent(const MeasureUnit &unit) {
return uprv_strcmp("percent", unit.getSubtype()) == 0;
}
inline bool unitIsPermille(const MeasureUnit &unit) {
return uprv_strcmp("permille", unit.getSubtype()) == 0;
}
} // namespace
NumberFormatterImpl *NumberFormatterImpl::fromMacros(const MacroProps &macros, UErrorCode &status) {
NumberFormatterImpl* NumberFormatterImpl::fromMacros(const MacroProps& macros, UErrorCode& status) {
return new NumberFormatterImpl(macros, true, status);
}
void NumberFormatterImpl::applyStatic(const MacroProps &macros, DecimalQuantity &inValue,
NumberStringBuilder &outString, UErrorCode &status) {
void NumberFormatterImpl::applyStatic(const MacroProps& macros, DecimalQuantity& inValue,
NumberStringBuilder& outString, UErrorCode& status) {
NumberFormatterImpl impl(macros, false, status);
impl.applyUnsafe(inValue, outString, status);
}
int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, int8_t signum,
StandardPlural::Form plural,
NumberStringBuilder& outString, UErrorCode& status) {
NumberFormatterImpl impl(macros, false, status);
return impl.getPrefixSuffixUnsafe(signum, plural, outString, status);
}
// NOTE: C++ SPECIFIC DIFFERENCE FROM JAVA:
// The "safe" apply method uses a new MicroProps. In the MicroPropsGenerator, fMicros is copied into the new instance.
// The "unsafe" method simply re-uses fMicros, eliminating the extra copy operation.
// See MicroProps::processQuantity() for details.
void NumberFormatterImpl::apply(DecimalQuantity &inValue, NumberStringBuilder &outString,
UErrorCode &status) const {
void NumberFormatterImpl::apply(DecimalQuantity& inValue, NumberStringBuilder& outString,
UErrorCode& status) const {
if (U_FAILURE(status)) { return; }
MicroProps micros;
fMicroPropsGenerator->processQuantity(inValue, micros, status);
@ -163,23 +94,45 @@ void NumberFormatterImpl::apply(DecimalQuantity &inValue, NumberStringBuilder &o
microsToString(micros, inValue, outString, status);
}
void NumberFormatterImpl::applyUnsafe(DecimalQuantity &inValue, NumberStringBuilder &outString,
UErrorCode &status) {
void NumberFormatterImpl::applyUnsafe(DecimalQuantity& inValue, NumberStringBuilder& outString,
UErrorCode& status) {
if (U_FAILURE(status)) { return; }
fMicroPropsGenerator->processQuantity(inValue, fMicros, status);
if (U_FAILURE(status)) { return; }
microsToString(fMicros, inValue, outString, status);
}
NumberFormatterImpl::NumberFormatterImpl(const MacroProps &macros, bool safe, UErrorCode &status) {
int32_t NumberFormatterImpl::getPrefixSuffix(int8_t signum, StandardPlural::Form plural,
NumberStringBuilder& outString, UErrorCode& status) const {
if (U_FAILURE(status)) { return 0; }
// #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier).
// Safe path: use fImmutablePatternModifier.
const Modifier* modifier = fImmutablePatternModifier->getModifier(signum, plural);
modifier->apply(outString, 0, 0, status);
if (U_FAILURE(status)) { return 0; }
return modifier->getPrefixLength(status);
}
int32_t NumberFormatterImpl::getPrefixSuffixUnsafe(int8_t signum, StandardPlural::Form plural,
NumberStringBuilder& outString, UErrorCode& status) {
if (U_FAILURE(status)) { return 0; }
// #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier).
// Unsafe path: use fPatternModifier.
fPatternModifier->setNumberProperties(signum, plural);
fPatternModifier->apply(outString, 0, 0, status);
if (U_FAILURE(status)) { return 0; }
return fPatternModifier->getPrefixLength(status);
}
NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, bool safe, UErrorCode& status) {
fMicroPropsGenerator = macrosToMicroGenerator(macros, safe, status);
}
//////////
const MicroPropsGenerator *
NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe, UErrorCode &status) {
const MicroPropsGenerator *chain = &fMicros;
const MicroPropsGenerator*
NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe, UErrorCode& status) {
const MicroPropsGenerator* chain = &fMicros;
// Check that macros is error-free before continuing.
if (macros.copyErrorTo(status)) {
@ -189,18 +142,26 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe,
// TODO: Accept currency symbols from DecimalFormatSymbols?
// Pre-compute a few values for efficiency.
bool isCurrency = unitIsCurrency(macros.unit);
bool isNoUnit = unitIsNoUnit(macros.unit);
bool isPercent = isNoUnit && unitIsPercent(macros.unit);
bool isPermille = isNoUnit && unitIsPermille(macros.unit);
bool isCurrency = utils::unitIsCurrency(macros.unit);
bool isNoUnit = utils::unitIsNoUnit(macros.unit);
bool isPercent = isNoUnit && utils::unitIsPercent(macros.unit);
bool isPermille = isNoUnit && utils::unitIsPermille(macros.unit);
bool isCldrUnit = !isCurrency && !isNoUnit;
bool isAccounting = macros.sign == UNUM_SIGN_ACCOUNTING
|| macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS
|| macros.sign == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO;
CurrencyUnit currency(kDefaultCurrency, status);
bool isAccounting =
macros.sign == UNUM_SIGN_ACCOUNTING || macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS ||
macros.sign == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO;
CurrencyUnit currency(nullptr, status);
if (isCurrency) {
currency = CurrencyUnit(macros.unit, status); // Restore CurrencyUnit from MeasureUnit
}
CurrencySymbols* currencySymbols;
if (macros.currencySymbols != nullptr) {
// Used by the DecimalFormat code path
currencySymbols = macros.currencySymbols;
} else {
fWarehouse.fCurrencySymbols = {currency, macros.locale, status};
currencySymbols = &fWarehouse.fCurrencySymbols;
}
UNumberUnitWidth unitWidth = UNUM_UNIT_WIDTH_SHORT;
if (macros.unitWidth != UNUM_UNIT_WIDTH_COUNT) {
unitWidth = macros.unitWidth;
@ -208,7 +169,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe,
// Select the numbering system.
LocalPointer<const NumberingSystem> nsLocal;
const NumberingSystem *ns;
const NumberingSystem* ns;
if (macros.symbols.isNumberingSystem()) {
ns = macros.symbols.getNumberingSystem();
} else {
@ -217,7 +178,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe,
// Give ownership to the function scope.
nsLocal.adoptInstead(ns);
}
const char *nsName = U_SUCCESS(status) ? ns->getName() : "latn";
const char* nsName = U_SUCCESS(status) ? ns->getName() : "latn";
// Resolve the symbols. Do this here because currency may need to customize them.
if (macros.symbols.isDecimalFormatSymbols()) {
@ -232,21 +193,22 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe,
// If we are formatting currency, check for a currency-specific pattern.
const char16_t* pattern = nullptr;
if (isCurrency) {
CurrencyFormatInfoResult info = getCurrencyFormatInfo(macros.locale, currency.getSubtype(), status);
CurrencyFormatInfoResult info = getCurrencyFormatInfo(
macros.locale, currency.getSubtype(), status);
if (info.exists) {
pattern = info.pattern;
// It's clunky to clone an object here, but this code is not frequently executed.
DecimalFormatSymbols* symbols = new DecimalFormatSymbols(*fMicros.symbols);
auto* symbols = new DecimalFormatSymbols(*fMicros.symbols);
fMicros.symbols = symbols;
fSymbols.adoptInstead(symbols);
symbols->setSymbol(
DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol,
UnicodeString(info.decimalSeparator),
FALSE);
DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol,
UnicodeString(info.decimalSeparator),
FALSE);
symbols->setSymbol(
DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol,
UnicodeString(info.groupingSeparator),
FALSE);
DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol,
UnicodeString(info.groupingSeparator),
FALSE);
}
}
if (pattern == nullptr) {
@ -262,7 +224,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe,
} else {
patternStyle = CLDR_PATTERN_STYLE_CURRENCY;
}
pattern = getPatternForStyle(macros.locale, nsName, patternStyle, status);
pattern = utils::getPatternForStyle(macros.locale, nsName, patternStyle, status);
}
auto patternInfo = new ParsedPatternInfo();
fPatternInfo.adoptInstead(patternInfo);
@ -272,6 +234,12 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe,
/// START POPULATING THE DEFAULT MICROPROPS AND BUILDING THE MICROPROPS GENERATOR ///
/////////////////////////////////////////////////////////////////////////////////////
// Multiplier
if (macros.scale.isValid()) {
fMicros.helpers.multiplier.setAndChain(macros.scale, chain);
chain = &fMicros.helpers.multiplier;
}
// Rounding strategy
if (!macros.rounder.isBogus()) {
fMicros.rounding = macros.rounder;
@ -306,7 +274,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe,
if (!macros.integerWidth.isBogus()) {
fMicros.integerWidth = macros.integerWidth;
} else {
fMicros.integerWidth = IntegerWidth::zeroFillTo(1);
fMicros.integerWidth = IntegerWidth::standard();
}
// Sign display
@ -338,16 +306,18 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe,
// Middle modifier (patterns, positive/negative, currency symbols, percent)
auto patternModifier = new MutablePatternModifier(false);
fPatternModifier.adoptInstead(patternModifier);
patternModifier->setPatternInfo(fPatternInfo.getAlias());
patternModifier->setPatternInfo(
macros.affixProvider != nullptr ? macros.affixProvider
: static_cast<const AffixPatternProvider*>(fPatternInfo.getAlias()));
patternModifier->setPatternAttributes(fMicros.sign, isPermille);
if (patternModifier->needsPlurals()) {
patternModifier->setSymbols(
fMicros.symbols,
currency,
currencySymbols,
unitWidth,
resolvePluralRules(macros.rules, macros.locale, status));
} else {
patternModifier->setSymbols(fMicros.symbols, currency, unitWidth, nullptr);
patternModifier->setSymbols(fMicros.symbols, currencySymbols, unitWidth, nullptr);
}
if (safe) {
fImmutablePatternModifier.adoptInstead(patternModifier->createImmutableAndChain(chain, status));
@ -407,9 +377,9 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe,
return chain;
}
const PluralRules *
NumberFormatterImpl::resolvePluralRules(const PluralRules *rulesPtr, const Locale &locale,
UErrorCode &status) {
const PluralRules*
NumberFormatterImpl::resolvePluralRules(const PluralRules* rulesPtr, const Locale& locale,
UErrorCode& status) {
if (rulesPtr != nullptr) {
return rulesPtr;
}
@ -420,8 +390,8 @@ NumberFormatterImpl::resolvePluralRules(const PluralRules *rulesPtr, const Local
return fRules.getAlias();
}
int32_t NumberFormatterImpl::microsToString(const MicroProps &micros, DecimalQuantity &quantity,
NumberStringBuilder &string, UErrorCode &status) {
int32_t NumberFormatterImpl::microsToString(const MicroProps& micros, DecimalQuantity& quantity,
NumberStringBuilder& string, UErrorCode& status) {
micros.rounding.apply(quantity, status);
micros.integerWidth.apply(quantity, status);
int32_t length = writeNumber(micros, quantity, string, status);
@ -439,8 +409,8 @@ int32_t NumberFormatterImpl::microsToString(const MicroProps &micros, DecimalQua
return length;
}
int32_t NumberFormatterImpl::writeNumber(const MicroProps &micros, DecimalQuantity &quantity,
NumberStringBuilder &string, UErrorCode &status) {
int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuantity& quantity,
NumberStringBuilder& string, UErrorCode& status) {
int32_t length = 0;
if (quantity.isInfinite()) {
length += string.insert(
@ -480,8 +450,8 @@ int32_t NumberFormatterImpl::writeNumber(const MicroProps &micros, DecimalQuanti
return length;
}
int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps &micros, DecimalQuantity &quantity,
NumberStringBuilder &string, UErrorCode &status) {
int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, DecimalQuantity& quantity,
NumberStringBuilder& string, UErrorCode& status) {
int length = 0;
int integerCount = quantity.getUpperDisplayMagnitude() + 1;
for (int i = 0; i < integerCount; i++) {
@ -499,21 +469,21 @@ int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps &micros, Decima
// Get and append the next digit value
int8_t nextDigit = quantity.getDigit(i);
length += string.insert(
0, getDigitFromSymbols(nextDigit, *micros.symbols), UNUM_INTEGER_FIELD, status);
length += utils::insertDigitFromSymbols(
string, 0, nextDigit, *micros.symbols, UNUM_INTEGER_FIELD, status);
}
return length;
}
int32_t NumberFormatterImpl::writeFractionDigits(const MicroProps &micros, DecimalQuantity &quantity,
NumberStringBuilder &string, UErrorCode &status) {
int32_t NumberFormatterImpl::writeFractionDigits(const MicroProps& micros, DecimalQuantity& quantity,
NumberStringBuilder& string, UErrorCode& status) {
int length = 0;
int fractionCount = -quantity.getLowerDisplayMagnitude();
for (int i = 0; i < fractionCount; i++) {
// Get and append the next digit value
int8_t nextDigit = quantity.getDigit(-i - 1);
length += string.append(
getDigitFromSymbols(nextDigit, *micros.symbols), UNUM_FRACTION_FIELD, status);
length += utils::insertDigitFromSymbols(
string, string.length(), nextDigit, *micros.symbols, UNUM_FRACTION_FIELD, status);
}
return length;
}

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#ifndef __NUMBER_FORMATIMPL_H__
#define __NUMBER_FORMATIMPL_H__
@ -37,10 +37,26 @@ class NumberFormatterImpl : public UMemory {
applyStatic(const MacroProps &macros, DecimalQuantity &inValue, NumberStringBuilder &outString,
UErrorCode &status);
/**
* Prints only the prefix and suffix; used for DecimalFormat getters.
*
* @return The index into the output at which the prefix ends and the suffix starts; in other words,
* the prefix length.
*/
static int32_t getPrefixSuffixStatic(const MacroProps& macros, int8_t signum,
StandardPlural::Form plural, NumberStringBuilder& outString,
UErrorCode& status);
/**
* Evaluates the "safe" MicroPropsGenerator created by "fromMacros".
*/
void apply(DecimalQuantity &inValue, NumberStringBuilder &outString, UErrorCode &status) const;
void apply(DecimalQuantity& inValue, NumberStringBuilder& outString, UErrorCode& status) const;
/**
* Like getPrefixSuffixStatic() but uses the safe compiled object.
*/
int32_t getPrefixSuffix(int8_t signum, StandardPlural::Form plural, NumberStringBuilder& outString,
UErrorCode& status) const;
private:
// Head of the MicroPropsGenerator linked list:
@ -50,21 +66,29 @@ class NumberFormatterImpl : public UMemory {
MicroProps fMicros;
// Other fields possibly used by the number formatting pipeline:
// TODO: Convert some of these LocalPointers to value objects to reduce the number of news?
// TODO: Convert more of these LocalPointers to value objects to reduce the number of news?
LocalPointer<const DecimalFormatSymbols> fSymbols;
LocalPointer<const PluralRules> fRules;
LocalPointer<const ParsedPatternInfo> fPatternInfo;
LocalPointer<const ScientificHandler> fScientificHandler;
LocalPointer<const MutablePatternModifier> fPatternModifier;
LocalPointer<MutablePatternModifier> fPatternModifier;
LocalPointer<const ImmutablePatternModifier> fImmutablePatternModifier;
LocalPointer<const LongNameHandler> fLongNameHandler;
LocalPointer<const CompactHandler> fCompactHandler;
// Value objects possibly used by the number formatting pipeline:
struct Warehouse {
CurrencySymbols fCurrencySymbols;
} fWarehouse;
NumberFormatterImpl(const MacroProps &macros, bool safe, UErrorCode &status);
void applyUnsafe(DecimalQuantity &inValue, NumberStringBuilder &outString, UErrorCode &status);
int32_t getPrefixSuffixUnsafe(int8_t signum, StandardPlural::Form plural,
NumberStringBuilder& outString, UErrorCode& status);
/**
* If rulesPtr is non-null, return it. Otherwise, return a PluralRules owned by this object for the
* specified locale, creating it if necessary.

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#include "unicode/numberformatter.h"
#include "number_patternstring.h"
@ -37,20 +37,32 @@ int16_t getMinGroupingForLocale(const Locale& locale) {
Grouper Grouper::forStrategy(UGroupingStrategy grouping) {
switch (grouping) {
case UNUM_GROUPING_OFF:
return {-1, -1, -2};
return {-1, -1, -2, grouping};
case UNUM_GROUPING_AUTO:
return {-2, -2, -2};
return {-2, -2, -2, grouping};
case UNUM_GROUPING_MIN2:
return {-2, -2, -3};
return {-2, -2, -3, grouping};
case UNUM_GROUPING_ON_ALIGNED:
return {-4, -4, 1};
return {-4, -4, 1, grouping};
case UNUM_GROUPING_THOUSANDS:
return {3, 3, 1};
return {3, 3, 1, grouping};
default:
U_ASSERT(FALSE);
}
}
Grouper Grouper::forProperties(const DecimalFormatProperties& properties) {
if (!properties.groupingUsed) {
return forStrategy(UNUM_GROUPING_OFF);
}
auto grouping1 = static_cast<int16_t>(properties.groupingSize);
auto grouping2 = static_cast<int16_t>(properties.secondaryGroupingSize);
auto minGrouping = static_cast<int16_t>(properties.minimumGroupingDigits);
grouping1 = grouping1 > 0 ? grouping1 : grouping2 > 0 ? grouping2 : grouping1;
grouping2 = grouping2 > 0 ? grouping2 : grouping1;
return {grouping1, grouping2, minGrouping, UNUM_GROUPING_COUNT};
}
void Grouper::setLocaleData(const impl::ParsedPatternInfo &patternInfo, const Locale& locale) {
if (fGrouping1 != -2 && fGrouping2 != -4) {
return;
@ -86,4 +98,12 @@ bool Grouper::groupAtPosition(int32_t position, const impl::DecimalQuantity &val
&& value.getUpperDisplayMagnitude() - fGrouping1 + 1 >= fMinGrouping;
}
int16_t Grouper::getPrimary() const {
return fGrouping1;
}
int16_t Grouper::getSecondary() const {
return fGrouping2;
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -1,9 +1,10 @@
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include <unicode/numberformatter.h>
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#include "unicode/numberformatter.h"
#include "number_types.h"
@ -13,14 +14,15 @@ using namespace icu;
using namespace icu::number;
using namespace icu::number::impl;
IntegerWidth::IntegerWidth(digits_t minInt, digits_t maxInt) {
IntegerWidth::IntegerWidth(digits_t minInt, digits_t maxInt, bool formatFailIfMoreThanMaxDigits) {
fUnion.minMaxInt.fMinInt = minInt;
fUnion.minMaxInt.fMaxInt = maxInt;
fUnion.minMaxInt.fFormatFailIfMoreThanMaxDigits = formatFailIfMoreThanMaxDigits;
}
IntegerWidth IntegerWidth::zeroFillTo(int32_t minInt) {
if (minInt >= 0 && minInt <= kMaxIntFracSig) {
return {static_cast<digits_t>(minInt), -1};
return {static_cast<digits_t>(minInt), -1, false};
} else {
return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
}
@ -30,22 +32,37 @@ IntegerWidth IntegerWidth::truncateAt(int32_t maxInt) {
if (fHasError) { return *this; } // No-op on error
digits_t minInt = fUnion.minMaxInt.fMinInt;
if (maxInt >= 0 && maxInt <= kMaxIntFracSig && minInt <= maxInt) {
return {minInt, static_cast<digits_t>(maxInt)};
return {minInt, static_cast<digits_t>(maxInt), false};
} else if (maxInt == -1) {
return {minInt, -1};
return {minInt, -1, false};
} else {
return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
}
}
void IntegerWidth::apply(impl::DecimalQuantity &quantity, UErrorCode &status) const {
void IntegerWidth::apply(impl::DecimalQuantity& quantity, UErrorCode& status) const {
if (fHasError) {
status = U_ILLEGAL_ARGUMENT_ERROR;
} else if (fUnion.minMaxInt.fMaxInt == -1) {
quantity.setIntegerLength(fUnion.minMaxInt.fMinInt, INT32_MAX);
} else {
// Enforce the backwards-compatibility feature "FormatFailIfMoreThanMaxDigits"
if (fUnion.minMaxInt.fFormatFailIfMoreThanMaxDigits &&
fUnion.minMaxInt.fMaxInt < quantity.getMagnitude()) {
status = U_ILLEGAL_ARGUMENT_ERROR;
}
quantity.setIntegerLength(fUnion.minMaxInt.fMinInt, fUnion.minMaxInt.fMaxInt);
}
}
bool IntegerWidth::operator==(const IntegerWidth& other) const {
// Private operator==; do error and bogus checking first!
U_ASSERT(!fHasError);
U_ASSERT(!other.fHasError);
U_ASSERT(!isBogus());
U_ASSERT(!other.isBogus());
return fUnion.minMaxInt.fMinInt == other.fUnion.minMaxInt.fMinInt &&
fUnion.minMaxInt.fMaxInt == other.fUnion.minMaxInt.fMaxInt;
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#include "unicode/simpleformatter.h"
#include "unicode/ures.h"

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#ifndef __NUMBER_LONGNAMES_H__
#define __NUMBER_LONGNAMES_H__

View File

@ -0,0 +1,500 @@
// © 2018 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
// Allow implicit conversion from char16_t* to UnicodeString for this file:
// Helpful in toString methods and elsewhere.
#define UNISTR_FROM_STRING_EXPLICIT
#include "number_mapper.h"
#include "number_patternstring.h"
#include "unicode/errorcode.h"
#include "number_utils.h"
#include "number_currencysymbols.h"
using namespace icu;
using namespace icu::number;
using namespace icu::number::impl;
UnlocalizedNumberFormatter NumberPropertyMapper::create(const DecimalFormatProperties& properties,
const DecimalFormatSymbols& symbols,
DecimalFormatWarehouse& warehouse,
UErrorCode& status) {
return NumberFormatter::with().macros(oldToNew(properties, symbols, warehouse, nullptr, status));
}
UnlocalizedNumberFormatter NumberPropertyMapper::create(const DecimalFormatProperties& properties,
const DecimalFormatSymbols& symbols,
DecimalFormatWarehouse& warehouse,
DecimalFormatProperties& exportedProperties,
UErrorCode& status) {
return NumberFormatter::with().macros(
oldToNew(
properties, symbols, warehouse, &exportedProperties, status));
}
MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& properties,
const DecimalFormatSymbols& symbols,
DecimalFormatWarehouse& warehouse,
DecimalFormatProperties* exportedProperties,
UErrorCode& status) {
MacroProps macros;
Locale locale = symbols.getLocale();
/////////////
// SYMBOLS //
/////////////
macros.symbols.setTo(symbols);
//////////////////
// PLURAL RULES //
//////////////////
// TODO
/////////////
// AFFIXES //
/////////////
AffixPatternProvider* affixProvider;
if (properties.currencyPluralInfo.fPtr.isNull()) {
warehouse.currencyPluralInfoAPP.setToBogus();
warehouse.propertiesAPP.setTo(properties, status);
affixProvider = &warehouse.propertiesAPP;
} else {
warehouse.currencyPluralInfoAPP.setTo(*properties.currencyPluralInfo.fPtr, properties, status);
warehouse.propertiesAPP.setToBogus();
affixProvider = &warehouse.currencyPluralInfoAPP;
}
macros.affixProvider = affixProvider;
///////////
// UNITS //
///////////
bool useCurrency = (
!properties.currency.isNull() || !properties.currencyPluralInfo.fPtr.isNull() ||
!properties.currencyUsage.isNull() || affixProvider->hasCurrencySign());
CurrencyUnit currency = resolveCurrency(properties, locale, status);
UCurrencyUsage currencyUsage = properties.currencyUsage.getOrDefault(UCURR_USAGE_STANDARD);
if (useCurrency) {
// NOTE: Slicing is OK.
macros.unit = currency; // NOLINT
}
warehouse.currencySymbols = {currency, locale, symbols, status};
macros.currencySymbols = &warehouse.currencySymbols;
///////////////////////
// ROUNDING STRATEGY //
///////////////////////
int32_t maxInt = properties.maximumIntegerDigits;
int32_t minInt = properties.minimumIntegerDigits;
int32_t maxFrac = properties.maximumFractionDigits;
int32_t minFrac = properties.minimumFractionDigits;
int32_t minSig = properties.minimumSignificantDigits;
int32_t maxSig = properties.maximumSignificantDigits;
double roundingIncrement = properties.roundingIncrement;
RoundingMode roundingMode = properties.roundingMode.getOrDefault(UNUM_ROUND_HALFEVEN);
bool explicitMinMaxFrac = minFrac != -1 || maxFrac != -1;
bool explicitMinMaxSig = minSig != -1 || maxSig != -1;
// Resolve min/max frac for currencies, required for the validation logic and for when minFrac or
// maxFrac was
// set (but not both) on a currency instance.
// NOTE: Increments are handled in "Rounder.constructCurrency()".
if (useCurrency && (minFrac == -1 || maxFrac == -1)) {
int32_t digits = ucurr_getDefaultFractionDigitsForUsage(
currency.getISOCurrency(), currencyUsage, &status);
if (minFrac == -1 && maxFrac == -1) {
minFrac = digits;
maxFrac = digits;
} else if (minFrac == -1) {
minFrac = std::min(maxFrac, digits);
} else /* if (maxFrac == -1) */ {
maxFrac = std::max(minFrac, digits);
}
}
// Validate min/max int/frac.
// For backwards compatibility, minimum overrides maximum if the two conflict.
// The following logic ensures that there is always a minimum of at least one digit.
if (minInt == 0 && maxFrac != 0) {
// Force a digit after the decimal point.
minFrac = minFrac <= 0 ? 1 : minFrac;
maxFrac = maxFrac < 0 ? -1 : maxFrac < minFrac ? minFrac : maxFrac;
minInt = 0;
maxInt = maxInt < 0 ? -1 : maxInt > kMaxIntFracSig ? -1 : maxInt;
} else {
// Force a digit before the decimal point.
minFrac = minFrac < 0 ? 0 : minFrac;
maxFrac = maxFrac < 0 ? -1 : maxFrac < minFrac ? minFrac : maxFrac;
minInt = minInt <= 0 ? 1 : minInt > kMaxIntFracSig ? 1 : minInt;
maxInt = maxInt < 0 ? -1 : maxInt < minInt ? minInt : maxInt > kMaxIntFracSig ? -1 : maxInt;
}
Rounder rounding;
if (!properties.currencyUsage.isNull()) {
rounding = Rounder::constructCurrency(currencyUsage).withCurrency(currency);
} else if (roundingIncrement != 0.0) {
rounding = Rounder::constructIncrement(roundingIncrement, minFrac);
} else if (explicitMinMaxSig) {
minSig = minSig < 1 ? 1 : minSig > kMaxIntFracSig ? kMaxIntFracSig : minSig;
maxSig = maxSig < 0 ? kMaxIntFracSig : maxSig < minSig ? minSig : maxSig > kMaxIntFracSig
? kMaxIntFracSig : maxSig;
rounding = Rounder::constructSignificant(minSig, maxSig);
} else if (explicitMinMaxFrac) {
rounding = Rounder::constructFraction(minFrac, maxFrac);
} else if (useCurrency) {
rounding = Rounder::constructCurrency(currencyUsage);
}
if (!rounding.isBogus()) {
rounding = rounding.withMode(roundingMode);
macros.rounder = rounding;
}
///////////////////
// INTEGER WIDTH //
///////////////////
macros.integerWidth = IntegerWidth(
static_cast<digits_t>(minInt),
static_cast<digits_t>(maxInt),
properties.formatFailIfMoreThanMaxDigits);
///////////////////////
// GROUPING STRATEGY //
///////////////////////
macros.grouper = Grouper::forProperties(properties);
/////////////
// PADDING //
/////////////
if (properties.formatWidth != -1) {
macros.padder = Padder::forProperties(properties);
}
///////////////////////////////
// DECIMAL MARK ALWAYS SHOWN //
///////////////////////////////
macros.decimal = properties.decimalSeparatorAlwaysShown ? UNUM_DECIMAL_SEPARATOR_ALWAYS
: UNUM_DECIMAL_SEPARATOR_AUTO;
///////////////////////
// SIGN ALWAYS SHOWN //
///////////////////////
macros.sign = properties.signAlwaysShown ? UNUM_SIGN_ALWAYS : UNUM_SIGN_AUTO;
/////////////////////////
// SCIENTIFIC NOTATION //
/////////////////////////
if (properties.minimumExponentDigits != -1) {
// Scientific notation is required.
// This whole section feels like a hack, but it is needed for regression tests.
// The mapping from property bag to scientific notation is nontrivial due to LDML rules.
if (maxInt > 8) {
// But #13110: The maximum of 8 digits has unknown origins and is not in the spec.
// If maxInt is greater than 8, it is set to minInt, even if minInt is greater than 8.
maxInt = minInt;
macros.integerWidth = IntegerWidth::zeroFillTo(minInt).truncateAt(maxInt);
} else if (maxInt > minInt && minInt > 1) {
// Bug #13289: if maxInt > minInt > 1, then minInt should be 1.
minInt = 1;
macros.integerWidth = IntegerWidth::zeroFillTo(minInt).truncateAt(maxInt);
}
int engineering = maxInt < 0 ? -1 : maxInt;
macros.notation = ScientificNotation(
// Engineering interval:
static_cast<int8_t>(engineering),
// Enforce minimum integer digits (for patterns like "000.00E0"):
(engineering == minInt),
// Minimum exponent digits:
static_cast<digits_t>(properties.minimumExponentDigits),
// Exponent sign always shown:
properties.exponentSignAlwaysShown ? UNUM_SIGN_ALWAYS : UNUM_SIGN_AUTO);
// Scientific notation also involves overriding the rounding mode.
// TODO: Overriding here is a bit of a hack. Should this logic go earlier?
if (macros.rounder.fType == Rounder::RounderType::RND_FRACTION) {
// For the purposes of rounding, get the original min/max int/frac, since the local
// variables
// have been manipulated for display purposes.
int minInt_ = properties.minimumIntegerDigits;
int minFrac_ = properties.minimumFractionDigits;
int maxFrac_ = properties.maximumFractionDigits;
if (minInt_ == 0 && maxFrac_ == 0) {
// Patterns like "#E0" and "##E0", which mean no rounding!
macros.rounder = Rounder::unlimited().withMode(roundingMode);
} else if (minInt_ == 0 && minFrac_ == 0) {
// Patterns like "#.##E0" (no zeros in the mantissa), which mean round to maxFrac+1
macros.rounder = Rounder::constructSignificant(1, maxFrac_ + 1).withMode(roundingMode);
} else {
// All other scientific patterns, which mean round to minInt+maxFrac
macros.rounder = Rounder::constructSignificant(
minInt_ + minFrac_, minInt_ + maxFrac_).withMode(roundingMode);
}
}
}
//////////////////////
// COMPACT NOTATION //
//////////////////////
if (!properties.compactStyle.isNull()) {
if (properties.compactStyle.getNoError() == UNumberCompactStyle::UNUM_LONG) {
macros.notation = Notation::compactLong();
} else {
macros.notation = Notation::compactShort();
}
// Do not forward the affix provider.
macros.affixProvider = nullptr;
}
/////////////////
// MULTIPLIERS //
/////////////////
macros.scale = scaleFromProperties(properties);
//////////////////////
// PROPERTY EXPORTS //
//////////////////////
if (exportedProperties != nullptr) {
exportedProperties->currency = currency;
exportedProperties->roundingMode = roundingMode;
exportedProperties->minimumIntegerDigits = minInt;
exportedProperties->maximumIntegerDigits = maxInt == -1 ? INT32_MAX : maxInt;
Rounder rounding_;
if (rounding.fType == Rounder::RounderType::RND_CURRENCY) {
rounding_ = rounding.withCurrency(currency, status);
} else {
rounding_ = rounding;
}
int minFrac_ = minFrac;
int maxFrac_ = maxFrac;
int minSig_ = minSig;
int maxSig_ = maxSig;
double increment_ = 0.0;
if (rounding_.fType == Rounder::RounderType::RND_FRACTION) {
minFrac_ = rounding_.fUnion.fracSig.fMinFrac;
maxFrac_ = rounding_.fUnion.fracSig.fMaxFrac;
} else if (rounding_.fType == Rounder::RounderType::RND_INCREMENT) {
increment_ = rounding_.fUnion.increment.fIncrement;
minFrac_ = rounding_.fUnion.increment.fMinFrac;
maxFrac_ = rounding_.fUnion.increment.fMinFrac;
} else if (rounding_.fType == Rounder::RounderType::RND_SIGNIFICANT) {
minSig_ = rounding_.fUnion.fracSig.fMinSig;
maxSig_ = rounding_.fUnion.fracSig.fMaxSig;
}
exportedProperties->minimumFractionDigits = minFrac_;
exportedProperties->maximumFractionDigits = maxFrac_;
exportedProperties->minimumSignificantDigits = minSig_;
exportedProperties->maximumSignificantDigits = maxSig_;
exportedProperties->roundingIncrement = increment_;
}
return macros;
}
void PropertiesAffixPatternProvider::setTo(const DecimalFormatProperties& properties, UErrorCode&) {
fBogus = false;
// There are two ways to set affixes in DecimalFormat: via the pattern string (applyPattern), and via the
// explicit setters (setPositivePrefix and friends). The way to resolve the settings is as follows:
//
// 1) If the explicit setting is present for the field, use it.
// 2) Otherwise, follows UTS 35 rules based on the pattern string.
//
// Importantly, the explicit setters affect only the one field they override. If you set the positive
// prefix, that should not affect the negative prefix. Since it is impossible for the user of this class
// to know whether the origin for a string was the override or the pattern, we have to say that we always
// have a negative subpattern and perform all resolution logic here.
// Convenience: Extract the properties into local variables.
// Variables are named with three chars: [p/n][p/s][o/p]
// [p/n] => p for positive, n for negative
// [p/s] => p for prefix, s for suffix
// [o/p] => o for escaped custom override string, p for pattern string
UnicodeString ppo = AffixUtils::escape(properties.positivePrefix);
UnicodeString pso = AffixUtils::escape(properties.positiveSuffix);
UnicodeString npo = AffixUtils::escape(properties.negativePrefix);
UnicodeString nso = AffixUtils::escape(properties.negativeSuffix);
const UnicodeString& ppp = properties.positivePrefixPattern;
const UnicodeString& psp = properties.positiveSuffixPattern;
const UnicodeString& npp = properties.negativePrefixPattern;
const UnicodeString& nsp = properties.negativeSuffixPattern;
if (!properties.positivePrefix.isBogus()) {
posPrefix = ppo;
} else if (!ppp.isBogus()) {
posPrefix = ppp;
} else {
// UTS 35: Default positive prefix is empty string.
posPrefix = u"";
}
if (!properties.positiveSuffix.isBogus()) {
posSuffix = pso;
} else if (!psp.isBogus()) {
posSuffix = psp;
} else {
// UTS 35: Default positive suffix is empty string.
posSuffix = u"";
}
if (!properties.negativePrefix.isBogus()) {
negPrefix = npo;
} else if (!npp.isBogus()) {
negPrefix = npp;
} else {
// UTS 35: Default negative prefix is "-" with positive prefix.
// Important: We prepend the "-" to the pattern, not the override!
negPrefix = ppp.isBogus() ? u"-" : u"-" + ppp;
}
if (!properties.negativeSuffix.isBogus()) {
negSuffix = nso;
} else if (!nsp.isBogus()) {
negSuffix = nsp;
} else {
// UTS 35: Default negative prefix is the positive prefix.
negSuffix = psp.isBogus() ? u"" : psp;
}
}
char16_t PropertiesAffixPatternProvider::charAt(int flags, int i) const {
return getStringInternal(flags).charAt(i);
}
int PropertiesAffixPatternProvider::length(int flags) const {
return getStringInternal(flags).length();
}
UnicodeString PropertiesAffixPatternProvider::getString(int32_t flags) const {
return getStringInternal(flags);
}
const UnicodeString& PropertiesAffixPatternProvider::getStringInternal(int32_t flags) const {
bool prefix = (flags & AFFIX_PREFIX) != 0;
bool negative = (flags & AFFIX_NEGATIVE_SUBPATTERN) != 0;
if (prefix && negative) {
return negPrefix;
} else if (prefix) {
return posPrefix;
} else if (negative) {
return negSuffix;
} else {
return posSuffix;
}
}
bool PropertiesAffixPatternProvider::positiveHasPlusSign() const {
// TODO: Change the internal APIs to propagate out the error?
ErrorCode localStatus;
return AffixUtils::containsType(posPrefix, TYPE_PLUS_SIGN, localStatus) ||
AffixUtils::containsType(posSuffix, TYPE_PLUS_SIGN, localStatus);
}
bool PropertiesAffixPatternProvider::hasNegativeSubpattern() const {
// See comments in the constructor for more information on why this is always true.
return true;
}
bool PropertiesAffixPatternProvider::negativeHasMinusSign() const {
ErrorCode localStatus;
return AffixUtils::containsType(negPrefix, TYPE_MINUS_SIGN, localStatus) ||
AffixUtils::containsType(negSuffix, TYPE_MINUS_SIGN, localStatus);
}
bool PropertiesAffixPatternProvider::hasCurrencySign() const {
ErrorCode localStatus;
return AffixUtils::hasCurrencySymbols(posPrefix, localStatus) ||
AffixUtils::hasCurrencySymbols(posSuffix, localStatus) ||
AffixUtils::hasCurrencySymbols(negPrefix, localStatus) ||
AffixUtils::hasCurrencySymbols(negSuffix, localStatus);
}
bool PropertiesAffixPatternProvider::containsSymbolType(AffixPatternType type, UErrorCode& status) const {
return AffixUtils::containsType(posPrefix, type, status) ||
AffixUtils::containsType(posSuffix, type, status) ||
AffixUtils::containsType(negPrefix, type, status) ||
AffixUtils::containsType(negSuffix, type, status);
}
bool PropertiesAffixPatternProvider::hasBody() const {
return true;
}
void CurrencyPluralInfoAffixProvider::setTo(const CurrencyPluralInfo& cpi,
const DecimalFormatProperties& properties,
UErrorCode& status) {
// We need to use a PropertiesAffixPatternProvider, not the simpler version ParsedPatternInfo,
// because user-specified affix overrides still need to work.
fBogus = false;
DecimalFormatProperties pluralProperties(properties);
for (int32_t plural = 0; plural < StandardPlural::COUNT; plural++) {
const char* keyword = StandardPlural::getKeyword(static_cast<StandardPlural::Form>(plural));
UnicodeString patternString;
patternString = cpi.getCurrencyPluralPattern(keyword, patternString);
PatternParser::parseToExistingProperties(
patternString,
pluralProperties,
IGNORE_ROUNDING_NEVER,
status);
affixesByPlural[plural].setTo(pluralProperties, status);
}
}
char16_t CurrencyPluralInfoAffixProvider::charAt(int32_t flags, int32_t i) const {
int32_t pluralOrdinal = (flags & AFFIX_PLURAL_MASK);
return affixesByPlural[pluralOrdinal].charAt(flags, i);
}
int32_t CurrencyPluralInfoAffixProvider::length(int32_t flags) const {
int32_t pluralOrdinal = (flags & AFFIX_PLURAL_MASK);
return affixesByPlural[pluralOrdinal].length(flags);
}
UnicodeString CurrencyPluralInfoAffixProvider::getString(int32_t flags) const {
int32_t pluralOrdinal = (flags & AFFIX_PLURAL_MASK);
return affixesByPlural[pluralOrdinal].getString(flags);
}
bool CurrencyPluralInfoAffixProvider::positiveHasPlusSign() const {
return affixesByPlural[StandardPlural::OTHER].positiveHasPlusSign();
}
bool CurrencyPluralInfoAffixProvider::hasNegativeSubpattern() const {
return affixesByPlural[StandardPlural::OTHER].hasNegativeSubpattern();
}
bool CurrencyPluralInfoAffixProvider::negativeHasMinusSign() const {
return affixesByPlural[StandardPlural::OTHER].negativeHasMinusSign();
}
bool CurrencyPluralInfoAffixProvider::hasCurrencySign() const {
return affixesByPlural[StandardPlural::OTHER].hasCurrencySign();
}
bool CurrencyPluralInfoAffixProvider::containsSymbolType(AffixPatternType type, UErrorCode& status) const {
return affixesByPlural[StandardPlural::OTHER].containsSymbolType(type, status);
}
bool CurrencyPluralInfoAffixProvider::hasBody() const {
return affixesByPlural[StandardPlural::OTHER].hasBody();
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -0,0 +1,162 @@
// © 2018 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#ifndef __NUMBER_MAPPER_H__
#define __NUMBER_MAPPER_H__
#include "number_types.h"
#include "unicode/currpinf.h"
#include "standardplural.h"
#include "number_patternstring.h"
#include "number_currencysymbols.h"
U_NAMESPACE_BEGIN namespace number {
namespace impl {
class PropertiesAffixPatternProvider : public AffixPatternProvider, public UMemory {
public:
bool isBogus() const {
return fBogus;
}
void setToBogus() {
fBogus = true;
}
void setTo(const DecimalFormatProperties& properties, UErrorCode& status);
PropertiesAffixPatternProvider() = default; // puts instance in valid but undefined state
PropertiesAffixPatternProvider(const DecimalFormatProperties& properties, UErrorCode& status) {
setTo(properties, status);
}
// AffixPatternProvider Methods:
char16_t charAt(int32_t flags, int32_t i) const U_OVERRIDE;
int32_t length(int32_t flags) const U_OVERRIDE;
UnicodeString getString(int32_t flags) const U_OVERRIDE;
bool hasCurrencySign() const U_OVERRIDE;
bool positiveHasPlusSign() const U_OVERRIDE;
bool hasNegativeSubpattern() const U_OVERRIDE;
bool negativeHasMinusSign() const U_OVERRIDE;
bool containsSymbolType(AffixPatternType, UErrorCode&) const U_OVERRIDE;
bool hasBody() const U_OVERRIDE;
private:
UnicodeString posPrefix;
UnicodeString posSuffix;
UnicodeString negPrefix;
UnicodeString negSuffix;
const UnicodeString& getStringInternal(int32_t flags) const;
bool fBogus{true};
};
class CurrencyPluralInfoAffixProvider : public AffixPatternProvider, public UMemory {
public:
bool isBogus() const {
return fBogus;
}
void setToBogus() {
fBogus = true;
}
void setTo(const CurrencyPluralInfo& cpi, const DecimalFormatProperties& properties,
UErrorCode& status);
// AffixPatternProvider Methods:
char16_t charAt(int32_t flags, int32_t i) const U_OVERRIDE;
int32_t length(int32_t flags) const U_OVERRIDE;
UnicodeString getString(int32_t flags) const U_OVERRIDE;
bool hasCurrencySign() const U_OVERRIDE;
bool positiveHasPlusSign() const U_OVERRIDE;
bool hasNegativeSubpattern() const U_OVERRIDE;
bool negativeHasMinusSign() const U_OVERRIDE;
bool containsSymbolType(AffixPatternType, UErrorCode&) const U_OVERRIDE;
bool hasBody() const U_OVERRIDE;
private:
PropertiesAffixPatternProvider affixesByPlural[StandardPlural::COUNT];
bool fBogus{true};
};
/**
* A struct for ownership of a few objects needed for formatting.
*/
struct DecimalFormatWarehouse {
PropertiesAffixPatternProvider propertiesAPP;
CurrencyPluralInfoAffixProvider currencyPluralInfoAPP;
CurrencySymbols currencySymbols;
};
/**
* Utilities for converting between a DecimalFormatProperties and a MacroProps.
*/
class NumberPropertyMapper {
public:
/** Convenience method to create a NumberFormatter directly from Properties. */
static UnlocalizedNumberFormatter create(const DecimalFormatProperties& properties,
const DecimalFormatSymbols& symbols,
DecimalFormatWarehouse& warehouse, UErrorCode& status);
/** Convenience method to create a NumberFormatter directly from Properties. */
static UnlocalizedNumberFormatter create(const DecimalFormatProperties& properties,
const DecimalFormatSymbols& symbols,
DecimalFormatWarehouse& warehouse,
DecimalFormatProperties& exportedProperties,
UErrorCode& status);
/**
* Creates a new {@link MacroProps} object based on the content of a {@link DecimalFormatProperties}
* object. In other words, maps Properties to MacroProps. This function is used by the
* JDK-compatibility API to call into the ICU 60 fluent number formatting pipeline.
*
* @param properties
* The property bag to be mapped.
* @param symbols
* The symbols associated with the property bag.
* @param exportedProperties
* A property bag in which to store validated properties. Used by some DecimalFormat
* getters.
* @return A new MacroProps containing all of the information in the Properties.
*/
static MacroProps oldToNew(const DecimalFormatProperties& properties,
const DecimalFormatSymbols& symbols, DecimalFormatWarehouse& warehouse,
DecimalFormatProperties* exportedProperties, UErrorCode& status);
};
} // namespace impl
} // namespace numparse
U_NAMESPACE_END
#endif //__NUMBER_MAPPER_H__
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#include "umutex.h"
#include "ucln_cmn.h"
@ -32,6 +32,7 @@ UBool U_CALLCONV cleanupDefaultCurrencySpacing() {
UNISET_DIGIT = nullptr;
delete UNISET_NOTS;
UNISET_NOTS = nullptr;
gDefaultCurrencySpacingInitOnce.reset();
return TRUE;
}

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#ifndef __NUMBER_MODIFIERS_H__
#define __NUMBER_MODIFIERS_H__

View File

@ -0,0 +1,172 @@
// © 2018 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
// Allow implicit conversion from char16_t* to UnicodeString for this file:
// Helpful in toString methods and elsewhere.
#define UNISTR_FROM_STRING_EXPLICIT
#include "number_types.h"
#include "number_multiplier.h"
#include "numparse_validators.h"
#include "number_utils.h"
#include "decNumber.h"
using namespace icu;
using namespace icu::number;
using namespace icu::number::impl;
using namespace icu::numparse::impl;
Scale::Scale(int32_t magnitude, DecNum* arbitraryToAdopt)
: fMagnitude(magnitude), fArbitrary(arbitraryToAdopt), fError(U_ZERO_ERROR) {
if (fArbitrary != nullptr) {
// Attempt to convert the DecNum to a magnitude multiplier.
fArbitrary->normalize();
if (fArbitrary->getRawDecNumber()->digits == 1 && fArbitrary->getRawDecNumber()->lsu[0] == 1 &&
!fArbitrary->isNegative()) {
// Success!
fMagnitude += fArbitrary->getRawDecNumber()->exponent;
delete fArbitrary;
fArbitrary = nullptr;
}
}
}
Scale::Scale(const Scale& other)
: fMagnitude(other.fMagnitude), fArbitrary(nullptr), fError(other.fError) {
if (other.fArbitrary != nullptr) {
UErrorCode localStatus = U_ZERO_ERROR;
fArbitrary = new DecNum(*other.fArbitrary, localStatus);
}
}
Scale& Scale::operator=(const Scale& other) {
fMagnitude = other.fMagnitude;
if (other.fArbitrary != nullptr) {
UErrorCode localStatus = U_ZERO_ERROR;
fArbitrary = new DecNum(*other.fArbitrary, localStatus);
} else {
fArbitrary = nullptr;
}
fError = other.fError;
return *this;
}
Scale::Scale(Scale&& src) U_NOEXCEPT
: fMagnitude(src.fMagnitude), fArbitrary(src.fArbitrary), fError(src.fError) {
// Take ownership away from src if necessary
src.fArbitrary = nullptr;
}
Scale& Scale::operator=(Scale&& src) U_NOEXCEPT {
fMagnitude = src.fMagnitude;
fArbitrary = src.fArbitrary;
fError = src.fError;
// Take ownership away from src if necessary
src.fArbitrary = nullptr;
return *this;
}
Scale::~Scale() {
delete fArbitrary;
}
Scale Scale::none() {
return {0, nullptr};
}
Scale Scale::powerOfTen(int32_t power) {
return {power, nullptr};
}
Scale Scale::byDecimal(StringPiece multiplicand) {
UErrorCode localError = U_ZERO_ERROR;
LocalPointer<DecNum> decnum(new DecNum(), localError);
if (U_FAILURE(localError)) {
return {localError};
}
decnum->setTo(multiplicand, localError);
if (U_FAILURE(localError)) {
return {localError};
}
return {0, decnum.orphan()};
}
Scale Scale::byDouble(double multiplicand) {
UErrorCode localError = U_ZERO_ERROR;
LocalPointer<DecNum> decnum(new DecNum(), localError);
if (U_FAILURE(localError)) {
return {localError};
}
decnum->setTo(multiplicand, localError);
if (U_FAILURE(localError)) {
return {localError};
}
return {0, decnum.orphan()};
}
Scale Scale::byDoubleAndPowerOfTen(double multiplicand, int32_t power) {
UErrorCode localError = U_ZERO_ERROR;
LocalPointer<DecNum> decnum(new DecNum(), localError);
if (U_FAILURE(localError)) {
return {localError};
}
decnum->setTo(multiplicand, localError);
if (U_FAILURE(localError)) {
return {localError};
}
return {power, decnum.orphan()};
}
void Scale::applyTo(impl::DecimalQuantity& quantity) const {
quantity.adjustMagnitude(fMagnitude);
if (fArbitrary != nullptr) {
UErrorCode localStatus = U_ZERO_ERROR;
quantity.multiplyBy(*fArbitrary, localStatus);
}
}
void Scale::applyReciprocalTo(impl::DecimalQuantity& quantity) const {
quantity.adjustMagnitude(-fMagnitude);
if (fArbitrary != nullptr) {
UErrorCode localStatus = U_ZERO_ERROR;
quantity.divideBy(*fArbitrary, localStatus);
}
}
void
MultiplierFormatHandler::setAndChain(const Scale& multiplier, const MicroPropsGenerator* parent) {
this->multiplier = multiplier;
this->parent = parent;
}
void MultiplierFormatHandler::processQuantity(DecimalQuantity& quantity, MicroProps& micros,
UErrorCode& status) const {
parent->processQuantity(quantity, micros, status);
multiplier.applyTo(quantity);
}
// NOTE: MultiplierParseHandler is declared in the header numparse_validators.h
MultiplierParseHandler::MultiplierParseHandler(::icu::number::Scale multiplier)
: fMultiplier(std::move(multiplier)) {}
void MultiplierParseHandler::postProcess(ParsedNumber& result) const {
if (!result.quantity.bogus) {
fMultiplier.applyReciprocalTo(result.quantity);
// NOTE: It is okay if the multiplier was negative.
}
}
UnicodeString MultiplierParseHandler::toString() const {
return u"<Scale>";
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -0,0 +1,54 @@
// © 2018 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#ifndef __SOURCE_NUMBER_MULTIPLIER_H__
#define __SOURCE_NUMBER_MULTIPLIER_H__
#include "numparse_types.h"
#include "number_decimfmtprops.h"
U_NAMESPACE_BEGIN namespace number {
namespace impl {
/**
* Wraps a {@link Multiplier} for use in the number formatting pipeline.
*/
class MultiplierFormatHandler : public MicroPropsGenerator, public UMemory {
public:
void setAndChain(const Scale& multiplier, const MicroPropsGenerator* parent);
void processQuantity(DecimalQuantity& quantity, MicroProps& micros,
UErrorCode& status) const U_OVERRIDE;
private:
Scale multiplier;
const MicroPropsGenerator *parent;
};
/** Gets a Scale from a DecimalFormatProperties. In Java, defined in RoundingUtils.java */
static inline Scale scaleFromProperties(const DecimalFormatProperties& properties) {
int32_t magnitudeMultiplier = properties.magnitudeMultiplier + properties.multiplierScale;
int32_t arbitraryMultiplier = properties.multiplier;
if (magnitudeMultiplier != 0 && arbitraryMultiplier != 1) {
return Scale::byDoubleAndPowerOfTen(arbitraryMultiplier, magnitudeMultiplier);
} else if (magnitudeMultiplier != 0) {
return Scale::powerOfTen(magnitudeMultiplier);
} else if (arbitraryMultiplier != 1) {
return Scale::byDouble(arbitraryMultiplier);
} else {
return Scale::none();
}
}
} // namespace impl
} // namespace number
U_NAMESPACE_END
#endif //__SOURCE_NUMBER_MULTIPLIER_H__
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#include "unicode/numberformatter.h"
#include "number_types.h"
@ -36,6 +36,19 @@ ScientificNotation Notation::engineering() {
return {NTN_SCIENTIFIC, union_};
}
ScientificNotation::ScientificNotation(int8_t fEngineeringInterval, bool fRequireMinInt,
impl::digits_t fMinExponentDigits,
UNumberSignDisplay fExponentSignDisplay) {
ScientificSettings settings;
settings.fEngineeringInterval = fEngineeringInterval;
settings.fRequireMinInt = fRequireMinInt;
settings.fMinExponentDigits = fMinExponentDigits;
settings.fExponentSignDisplay = fExponentSignDisplay;
NotationUnion union_;
union_.scientific = settings;
*this = {NTN_SCIENTIFIC, union_};
}
Notation Notation::compactShort() {
NotationUnion union_;
union_.compactStyle = CompactStyle::UNUM_SHORT;

View File

@ -3,11 +3,12 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#include "unicode/numberformatter.h"
#include "number_types.h"
#include "number_stringbuilder.h"
#include "number_decimfmtprops.h"
using namespace icu;
using namespace icu::number;
@ -47,6 +48,16 @@ Padder Padder::codePoints(UChar32 cp, int32_t targetWidth, UNumberFormatPadPosit
}
}
Padder Padder::forProperties(const DecimalFormatProperties& properties) {
UChar32 padCp;
if (properties.padString.length() > 0) {
padCp = properties.padString.char32At(0);
} else {
padCp = kFallbackPaddingString[0];
}
return {padCp, properties.formatWidth, properties.padPosition.getOrDefault(UNUM_PAD_BEFORE_PREFIX)};
}
int32_t Padder::padAndApply(const Modifier &mod1, const Modifier &mod2,
NumberStringBuilder &string, int32_t leftIndex, int32_t rightIndex,
UErrorCode &status) const {

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#include "cstring.h"
#include "number_patternmodifier.h"
@ -15,9 +15,10 @@ using namespace icu;
using namespace icu::number;
using namespace icu::number::impl;
MutablePatternModifier::MutablePatternModifier(bool isStrong) : fStrong(isStrong) {}
MutablePatternModifier::MutablePatternModifier(bool isStrong)
: fStrong(isStrong) {}
void MutablePatternModifier::setPatternInfo(const AffixPatternProvider *patternInfo) {
void MutablePatternModifier::setPatternInfo(const AffixPatternProvider* patternInfo) {
this->patternInfo = patternInfo;
}
@ -26,14 +27,12 @@ void MutablePatternModifier::setPatternAttributes(UNumberSignDisplay signDisplay
this->perMilleReplacesPercent = perMille;
}
void
MutablePatternModifier::setSymbols(const DecimalFormatSymbols *symbols, const CurrencyUnit &currency,
const UNumberUnitWidth unitWidth, const PluralRules *rules) {
void MutablePatternModifier::setSymbols(const DecimalFormatSymbols* symbols,
const CurrencySymbols* currencySymbols,
const UNumberUnitWidth unitWidth, const PluralRules* rules) {
U_ASSERT((rules != nullptr) == needsPlurals());
this->symbols = symbols;
uprv_memcpy(static_cast<char16_t *>(this->currencyCode),
currency.getISOCurrency(),
sizeof(char16_t) * 4);
this->currencySymbols = currencySymbols;
this->unitWidth = unitWidth;
this->rules = rules;
}
@ -49,12 +48,12 @@ bool MutablePatternModifier::needsPlurals() const {
// Silently ignore any error codes.
}
ImmutablePatternModifier *MutablePatternModifier::createImmutable(UErrorCode &status) {
ImmutablePatternModifier* MutablePatternModifier::createImmutable(UErrorCode& status) {
return createImmutableAndChain(nullptr, status);
}
ImmutablePatternModifier *
MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator *parent, UErrorCode &status) {
ImmutablePatternModifier*
MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator* parent, UErrorCode& status) {
// TODO: Move StandardPlural VALUES to standardplural.h
static const StandardPlural::Form STANDARD_PLURAL_VALUES[] = {
@ -89,11 +88,11 @@ MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator *paren
} else {
// Faster path when plural keyword is not needed.
setNumberProperties(1, StandardPlural::Form::COUNT);
Modifier *positive = createConstantModifier(status);
Modifier* positive = createConstantModifier(status);
setNumberProperties(0, StandardPlural::Form::COUNT);
Modifier *zero = createConstantModifier(status);
Modifier* zero = createConstantModifier(status);
setNumberProperties(-1, StandardPlural::Form::COUNT);
Modifier *negative = createConstantModifier(status);
Modifier* negative = createConstantModifier(status);
pm->adoptPositiveNegativeModifiers(positive, zero, negative);
if (U_FAILURE(status)) {
delete pm;
@ -103,29 +102,30 @@ MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator *paren
}
}
ConstantMultiFieldModifier *MutablePatternModifier::createConstantModifier(UErrorCode &status) {
ConstantMultiFieldModifier* MutablePatternModifier::createConstantModifier(UErrorCode& status) {
NumberStringBuilder a;
NumberStringBuilder b;
insertPrefix(a, 0, status);
insertSuffix(b, 0, status);
if (patternInfo->hasCurrencySign()) {
return new CurrencySpacingEnabledModifier(a, b, !patternInfo->hasBody(), fStrong, *symbols, status);
return new CurrencySpacingEnabledModifier(
a, b, !patternInfo->hasBody(), fStrong, *symbols, status);
} else {
return new ConstantMultiFieldModifier(a, b, !patternInfo->hasBody(), fStrong);
}
}
ImmutablePatternModifier::ImmutablePatternModifier(ParameterizedModifier *pm, const PluralRules *rules,
const MicroPropsGenerator *parent)
ImmutablePatternModifier::ImmutablePatternModifier(ParameterizedModifier* pm, const PluralRules* rules,
const MicroPropsGenerator* parent)
: pm(pm), rules(rules), parent(parent) {}
void ImmutablePatternModifier::processQuantity(DecimalQuantity &quantity, MicroProps &micros,
UErrorCode &status) const {
void ImmutablePatternModifier::processQuantity(DecimalQuantity& quantity, MicroProps& micros,
UErrorCode& status) const {
parent->processQuantity(quantity, micros, status);
applyToMicros(micros, quantity);
}
void ImmutablePatternModifier::applyToMicros(MicroProps &micros, DecimalQuantity &quantity) const {
void ImmutablePatternModifier::applyToMicros(MicroProps& micros, DecimalQuantity& quantity) const {
if (rules == nullptr) {
micros.modMiddle = pm->getModifier(quantity.signum());
} else {
@ -137,18 +137,27 @@ void ImmutablePatternModifier::applyToMicros(MicroProps &micros, DecimalQuantity
}
}
const Modifier* ImmutablePatternModifier::getModifier(int8_t signum, StandardPlural::Form plural) const {
if (rules == nullptr) {
return pm->getModifier(signum);
} else {
return pm->getModifier(signum, plural);
}
}
/** Used by the unsafe code path. */
MicroPropsGenerator &MutablePatternModifier::addToChain(const MicroPropsGenerator *parent) {
MicroPropsGenerator& MutablePatternModifier::addToChain(const MicroPropsGenerator* parent) {
this->parent = parent;
return *this;
}
void MutablePatternModifier::processQuantity(DecimalQuantity &fq, MicroProps &micros,
UErrorCode &status) const {
void MutablePatternModifier::processQuantity(DecimalQuantity& fq, MicroProps& micros,
UErrorCode& status) const {
parent->processQuantity(fq, micros, status);
// The unsafe code path performs self-mutation, so we need a const_cast.
// This method needs to be const because it overrides a const method in the parent class.
auto nonConstThis = const_cast<MutablePatternModifier *>(this);
auto nonConstThis = const_cast<MutablePatternModifier*>(this);
if (needsPlurals()) {
// TODO: Fix this. Avoid the copy.
DecimalQuantity copy(fq);
@ -160,20 +169,24 @@ void MutablePatternModifier::processQuantity(DecimalQuantity &fq, MicroProps &mi
micros.modMiddle = this;
}
int32_t MutablePatternModifier::apply(NumberStringBuilder &output, int32_t leftIndex, int32_t rightIndex,
UErrorCode &status) const {
int32_t MutablePatternModifier::apply(NumberStringBuilder& output, int32_t leftIndex, int32_t rightIndex,
UErrorCode& status) const {
// The unsafe code path performs self-mutation, so we need a const_cast.
// This method needs to be const because it overrides a const method in the parent class.
auto nonConstThis = const_cast<MutablePatternModifier *>(this);
auto nonConstThis = const_cast<MutablePatternModifier*>(this);
int32_t prefixLen = nonConstThis->insertPrefix(output, leftIndex, status);
int32_t suffixLen = nonConstThis->insertSuffix(output, rightIndex + prefixLen, status);
// If the pattern had no decimal stem body (like #,##0.00), overwrite the value.
int32_t overwriteLen = 0;
if (!patternInfo->hasBody()) {
overwriteLen = output.splice(
leftIndex + prefixLen, rightIndex + prefixLen,
UnicodeString(), 0, 0, UNUM_FIELD_COUNT,
status);
leftIndex + prefixLen,
rightIndex + prefixLen,
UnicodeString(),
0,
0,
UNUM_FIELD_COUNT,
status);
}
CurrencySpacingEnabledModifier::applyCurrencySpacing(
output,
@ -186,30 +199,27 @@ int32_t MutablePatternModifier::apply(NumberStringBuilder &output, int32_t leftI
return prefixLen + overwriteLen + suffixLen;
}
int32_t MutablePatternModifier::getPrefixLength(UErrorCode &status) const {
int32_t MutablePatternModifier::getPrefixLength(UErrorCode& status) const {
// The unsafe code path performs self-mutation, so we need a const_cast.
// This method needs to be const because it overrides a const method in the parent class.
auto nonConstThis = const_cast<MutablePatternModifier *>(this);
auto nonConstThis = const_cast<MutablePatternModifier*>(this);
// Enter and exit CharSequence Mode to get the length.
nonConstThis->enterCharSequenceMode(true);
int result = AffixUtils::unescapedCodePointCount(*this, *this, status); // prefix length
nonConstThis->exitCharSequenceMode();
nonConstThis->prepareAffix(true);
int result = AffixUtils::unescapedCodePointCount(currentAffix, *this, status); // prefix length
return result;
}
int32_t MutablePatternModifier::getCodePointCount(UErrorCode &status) const {
int32_t MutablePatternModifier::getCodePointCount(UErrorCode& status) const {
// The unsafe code path performs self-mutation, so we need a const_cast.
// This method needs to be const because it overrides a const method in the parent class.
auto nonConstThis = const_cast<MutablePatternModifier *>(this);
auto nonConstThis = const_cast<MutablePatternModifier*>(this);
// Enter and exit CharSequence Mode to get the length.
nonConstThis->enterCharSequenceMode(true);
int result = AffixUtils::unescapedCodePointCount(*this, *this, status); // prefix length
nonConstThis->exitCharSequenceMode();
nonConstThis->enterCharSequenceMode(false);
result += AffixUtils::unescapedCodePointCount(*this, *this, status); // suffix length
nonConstThis->exitCharSequenceMode();
// Render the affixes to get the length
nonConstThis->prepareAffix(true);
int result = AffixUtils::unescapedCodePointCount(currentAffix, *this, status); // prefix length
nonConstThis->prepareAffix(false);
result += AffixUtils::unescapedCodePointCount(currentAffix, *this, status); // suffix length
return result;
}
@ -217,21 +227,26 @@ bool MutablePatternModifier::isStrong() const {
return fStrong;
}
int32_t MutablePatternModifier::insertPrefix(NumberStringBuilder &sb, int position, UErrorCode &status) {
enterCharSequenceMode(true);
int length = AffixUtils::unescape(*this, sb, position, *this, status);
exitCharSequenceMode();
int32_t MutablePatternModifier::insertPrefix(NumberStringBuilder& sb, int position, UErrorCode& status) {
prepareAffix(true);
int length = AffixUtils::unescape(currentAffix, sb, position, *this, status);
return length;
}
int32_t MutablePatternModifier::insertSuffix(NumberStringBuilder &sb, int position, UErrorCode &status) {
enterCharSequenceMode(false);
int length = AffixUtils::unescape(*this, sb, position, *this, status);
exitCharSequenceMode();
int32_t MutablePatternModifier::insertSuffix(NumberStringBuilder& sb, int position, UErrorCode& status) {
prepareAffix(false);
int length = AffixUtils::unescape(currentAffix, sb, position, *this, status);
return length;
}
/** This method contains the heart of the logic for rendering LDML affix strings. */
void MutablePatternModifier::prepareAffix(bool isPrefix) {
PatternStringUtils::patternInfoToStringBuilder(
*patternInfo, isPrefix, signum, signDisplay, plural, perMilleReplacesPercent, currentAffix);
}
UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const {
UErrorCode localStatus = U_ZERO_ERROR;
switch (type) {
case AffixPatternType::TYPE_MINUS_SIGN:
return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol);
@ -244,45 +259,23 @@ UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const {
case AffixPatternType::TYPE_CURRENCY_SINGLE: {
// UnitWidth ISO and HIDDEN overrides the singular currency symbol.
if (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE) {
return UnicodeString(currencyCode, 3);
return currencySymbols->getIntlCurrencySymbol(localStatus);
} else if (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_HIDDEN) {
return UnicodeString();
} else if (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW) {
return currencySymbols->getNarrowCurrencySymbol(localStatus);
} else {
UCurrNameStyle selector = (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW)
? UCurrNameStyle::UCURR_NARROW_SYMBOL_NAME
: UCurrNameStyle::UCURR_SYMBOL_NAME;
UErrorCode status = U_ZERO_ERROR;
UBool isChoiceFormat = FALSE;
int32_t symbolLen = 0;
const char16_t *symbol = ucurr_getName(
currencyCode,
symbols->getLocale().getName(),
selector,
&isChoiceFormat,
&symbolLen,
&status);
return UnicodeString(symbol, symbolLen);
return currencySymbols->getCurrencySymbol(localStatus);
}
}
case AffixPatternType::TYPE_CURRENCY_DOUBLE:
return UnicodeString(currencyCode, 3);
case AffixPatternType::TYPE_CURRENCY_TRIPLE: {
return currencySymbols->getIntlCurrencySymbol(localStatus);
case AffixPatternType::TYPE_CURRENCY_TRIPLE:
// NOTE: This is the code path only for patterns containing "¤¤¤".
// Plural currencies set via the API are formatted in LongNameHandler.
// This code path is used by DecimalFormat via CurrencyPluralInfo.
U_ASSERT(plural != StandardPlural::Form::COUNT);
UErrorCode status = U_ZERO_ERROR;
UBool isChoiceFormat = FALSE;
int32_t symbolLen = 0;
const char16_t *symbol = ucurr_getPluralName(
currencyCode,
symbols->getLocale().getName(),
&isChoiceFormat,
StandardPlural::getKeyword(plural),
&symbolLen,
&status);
return UnicodeString(symbol, symbolLen);
}
return currencySymbols->getPluralName(plural, localStatus);
case AffixPatternType::TYPE_CURRENCY_QUAD:
return UnicodeString(u"\uFFFD");
case AffixPatternType::TYPE_CURRENCY_QUINT:
@ -293,79 +286,6 @@ UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const {
}
}
/** This method contains the heart of the logic for rendering LDML affix strings. */
void MutablePatternModifier::enterCharSequenceMode(bool isPrefix) {
U_ASSERT(!inCharSequenceMode);
inCharSequenceMode = true;
// Should the output render '+' where '-' would normally appear in the pattern?
plusReplacesMinusSign = signum != -1
&& (signDisplay == UNUM_SIGN_ALWAYS
|| signDisplay == UNUM_SIGN_ACCOUNTING_ALWAYS
|| (signum == 1
&& (signDisplay == UNUM_SIGN_EXCEPT_ZERO
|| signDisplay == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO)))
&& patternInfo->positiveHasPlusSign() == false;
// Should we use the affix from the negative subpattern? (If not, we will use the positive subpattern.)
bool useNegativeAffixPattern = patternInfo->hasNegativeSubpattern() && (
signum == -1 || (patternInfo->negativeHasMinusSign() && plusReplacesMinusSign));
// Resolve the flags for the affix pattern.
fFlags = 0;
if (useNegativeAffixPattern) {
fFlags |= AffixPatternProvider::AFFIX_NEGATIVE_SUBPATTERN;
}
if (isPrefix) {
fFlags |= AffixPatternProvider::AFFIX_PREFIX;
}
if (plural != StandardPlural::Form::COUNT) {
U_ASSERT(plural == (AffixPatternProvider::AFFIX_PLURAL_MASK & plural));
fFlags |= plural;
}
// Should we prepend a sign to the pattern?
if (!isPrefix || useNegativeAffixPattern) {
prependSign = false;
} else if (signum == -1) {
prependSign = signDisplay != UNUM_SIGN_NEVER;
} else {
prependSign = plusReplacesMinusSign;
}
// Finally, compute the length of the affix pattern.
fLength = patternInfo->length(fFlags) + (prependSign ? 1 : 0);
}
void MutablePatternModifier::exitCharSequenceMode() {
U_ASSERT(inCharSequenceMode);
inCharSequenceMode = false;
}
int32_t MutablePatternModifier::length() const {
U_ASSERT(inCharSequenceMode);
return fLength;
}
char16_t MutablePatternModifier::charAt(int32_t index) const {
U_ASSERT(inCharSequenceMode);
char16_t candidate;
if (prependSign && index == 0) {
candidate = u'-';
} else if (prependSign) {
candidate = patternInfo->charAt(fFlags, index - 1);
} else {
candidate = patternInfo->charAt(fFlags, index);
}
if (plusReplacesMinusSign && candidate == u'-') {
return u'+';
}
if (perMilleReplacesPercent && candidate == u'%') {
return u'';
}
return candidate;
}
UnicodeString MutablePatternModifier::toUnicodeString() const {
// Never called by AffixUtils
U_ASSERT(false);

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#ifndef __NUMBER_PATTERNMODIFIER_H__
#define __NUMBER_PATTERNMODIFIER_H__
@ -13,6 +13,7 @@
#include "number_types.h"
#include "number_modifiers.h"
#include "number_utils.h"
#include "number_currencysymbols.h"
U_NAMESPACE_BEGIN
@ -35,20 +36,23 @@ class MutablePatternModifier;
// Exported as U_I18N_API because it is needed for the unit test PatternModifierTest
class U_I18N_API ImmutablePatternModifier : public MicroPropsGenerator, public UMemory {
public:
~ImmutablePatternModifier() U_OVERRIDE = default;
~ImmutablePatternModifier() U_OVERRIDE = default;
void processQuantity(DecimalQuantity &, MicroProps &micros, UErrorCode &status) const U_OVERRIDE;
void processQuantity(DecimalQuantity&, MicroProps& micros, UErrorCode& status) const U_OVERRIDE;
void applyToMicros(MicroProps &micros, DecimalQuantity &quantity) const;
void applyToMicros(MicroProps& micros, DecimalQuantity& quantity) const;
const Modifier* getModifier(int8_t signum, StandardPlural::Form plural) const;
private:
ImmutablePatternModifier(ParameterizedModifier *pm, const PluralRules *rules, const MicroPropsGenerator *parent);
ImmutablePatternModifier(ParameterizedModifier* pm, const PluralRules* rules,
const MicroPropsGenerator* parent);
const LocalPointer<ParameterizedModifier> pm;
const PluralRules *rules;
const MicroPropsGenerator *parent;
const PluralRules* rules;
const MicroPropsGenerator* parent;
friend class MutablePatternModifier;
friend class MutablePatternModifier;
};
/**
@ -74,7 +78,6 @@ class U_I18N_API MutablePatternModifier
: public MicroPropsGenerator,
public Modifier,
public SymbolProvider,
public CharSequence,
public UMemory {
public:
@ -110,17 +113,16 @@ class U_I18N_API MutablePatternModifier
*
* @param symbols
* The desired instance of DecimalFormatSymbols.
* @param currency
* The currency to be used when substituting currency values into the affixes.
* @param currencySymbols
* The currency symbols to be used when substituting currency values into the affixes.
* @param unitWidth
* The width used to render currencies.
* @param rules
* Required if the triple currency sign, "¤¤¤", appears in the pattern, which can be determined from the
* convenience method {@link #needsPlurals()}.
*/
void
setSymbols(const DecimalFormatSymbols *symbols, const CurrencyUnit &currency, UNumberUnitWidth unitWidth,
const PluralRules *rules);
void setSymbols(const DecimalFormatSymbols* symbols, const CurrencySymbols* currencySymbols,
UNumberUnitWidth unitWidth, const PluralRules* rules);
/**
* Sets attributes of the current number being processed.
@ -187,13 +189,7 @@ class U_I18N_API MutablePatternModifier
*/
UnicodeString getSymbol(AffixPatternType type) const U_OVERRIDE;
int32_t length() const U_OVERRIDE;
char16_t charAt(int32_t index) const U_OVERRIDE;
// Use default implementation of codePointAt
UnicodeString toUnicodeString() const U_OVERRIDE;
UnicodeString toUnicodeString() const;
private:
// Modifier details (initialized in constructor)
@ -207,7 +203,7 @@ class U_I18N_API MutablePatternModifier
// Symbol details (initialized in setSymbols)
const DecimalFormatSymbols *symbols;
UNumberUnitWidth unitWidth;
char16_t currencyCode[4];
const CurrencySymbols *currencySymbols;
const PluralRules *rules;
// Number details (initialized in setNumberProperties)
@ -217,12 +213,8 @@ class U_I18N_API MutablePatternModifier
// QuantityChain details (initialized in addToChain)
const MicroPropsGenerator *parent;
// Transient CharSequence fields (initialized in enterCharSequenceMode)
bool inCharSequenceMode = false;
int32_t fFlags;
int32_t fLength;
bool prependSign;
bool plusReplacesMinusSign;
// Transient fields for rendering
UnicodeString currentAffix;
/**
* Uses the current properties to create a single {@link ConstantMultiFieldModifier} with currency spacing support
@ -244,9 +236,7 @@ class U_I18N_API MutablePatternModifier
int32_t insertSuffix(NumberStringBuilder &sb, int position, UErrorCode &status);
void enterCharSequenceMode(bool isPrefix);
void exitCharSequenceMode();
void prepareAffix(bool isPrefix);
};

View File

@ -3,36 +3,51 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
// Allow implicit conversion from char16_t* to UnicodeString for this file:
// Helpful in toString methods and elsewhere.
#define UNISTR_FROM_STRING_EXPLICIT
#define UNISTR_FROM_CHAR_EXPLICIT
#include "uassert.h"
#include "number_patternstring.h"
#include "unicode/utf16.h"
#include "number_utils.h"
#include "number_roundingutils.h"
using namespace icu;
using namespace icu::number;
using namespace icu::number::impl;
void PatternParser::parseToPatternInfo(const UnicodeString& patternString, ParsedPatternInfo& patternInfo, UErrorCode &status) {
void PatternParser::parseToPatternInfo(const UnicodeString& patternString, ParsedPatternInfo& patternInfo,
UErrorCode& status) {
patternInfo.consumePattern(patternString, status);
}
DecimalFormatProperties
PatternParser::parseToProperties(const UnicodeString& pattern, IgnoreRounding ignoreRounding,
UErrorCode &status) {
UErrorCode& status) {
DecimalFormatProperties properties;
parseToExistingPropertiesImpl(pattern, properties, ignoreRounding, status);
return properties;
}
void PatternParser::parseToExistingProperties(const UnicodeString& pattern, DecimalFormatProperties& properties,
IgnoreRounding ignoreRounding, UErrorCode &status) {
DecimalFormatProperties PatternParser::parseToProperties(const UnicodeString& pattern,
UErrorCode& status) {
return parseToProperties(pattern, IGNORE_ROUNDING_NEVER, status);
}
void
PatternParser::parseToExistingProperties(const UnicodeString& pattern, DecimalFormatProperties& properties,
IgnoreRounding ignoreRounding, UErrorCode& status) {
parseToExistingPropertiesImpl(pattern, properties, ignoreRounding, status);
}
char16_t ParsedPatternInfo::charAt(int32_t flags, int32_t index) const {
const Endpoints &endpoints = getEndpoints(flags);
const Endpoints& endpoints = getEndpoints(flags);
if (index < 0 || index >= endpoints.end - endpoints.start) {
U_ASSERT(false);
}
@ -43,12 +58,12 @@ int32_t ParsedPatternInfo::length(int32_t flags) const {
return getLengthFromEndpoints(getEndpoints(flags));
}
int32_t ParsedPatternInfo::getLengthFromEndpoints(const Endpoints &endpoints) {
int32_t ParsedPatternInfo::getLengthFromEndpoints(const Endpoints& endpoints) {
return endpoints.end - endpoints.start;
}
UnicodeString ParsedPatternInfo::getString(int32_t flags) const {
const Endpoints &endpoints = getEndpoints(flags);
const Endpoints& endpoints = getEndpoints(flags);
if (endpoints.start == endpoints.end) {
return UnicodeString();
}
@ -56,7 +71,7 @@ UnicodeString ParsedPatternInfo::getString(int32_t flags) const {
return UnicodeString(pattern, endpoints.start, endpoints.end - endpoints.start);
}
const Endpoints &ParsedPatternInfo::getEndpoints(int32_t flags) const {
const Endpoints& ParsedPatternInfo::getEndpoints(int32_t flags) const {
bool prefix = (flags & AFFIX_PREFIX) != 0;
bool isNegative = (flags & AFFIX_NEGATIVE_SUBPATTERN) != 0;
bool padding = (flags & AFFIX_PADDING) != 0;
@ -91,8 +106,8 @@ bool ParsedPatternInfo::hasCurrencySign() const {
return positive.hasCurrencySign || (fHasNegativeSubpattern && negative.hasCurrencySign);
}
bool ParsedPatternInfo::containsSymbolType(AffixPatternType type, UErrorCode &status) const {
return AffixUtils::containsType(UnicodeStringCharSequence(pattern), type, status);
bool ParsedPatternInfo::containsSymbolType(AffixPatternType type, UErrorCode& status) const {
return AffixUtils::containsType(pattern, type, status);
}
bool ParsedPatternInfo::hasBody() const {
@ -117,10 +132,14 @@ UChar32 ParsedPatternInfo::ParserState::next() {
return codePoint;
}
void ParsedPatternInfo::consumePattern(const UnicodeString& patternString, UErrorCode &status) {
void ParsedPatternInfo::consumePattern(const UnicodeString& patternString, UErrorCode& status) {
if (U_FAILURE(status)) { return; }
this->pattern = patternString;
// This class is not intended for writing twice!
// Use move assignment to overwrite instead.
U_ASSERT(state.offset == 0);
// pattern := subpattern (';' subpattern)?
currentSubpattern = &positive;
consumeSubpattern(status);
@ -141,7 +160,7 @@ void ParsedPatternInfo::consumePattern(const UnicodeString& patternString, UErro
}
}
void ParsedPatternInfo::consumeSubpattern(UErrorCode &status) {
void ParsedPatternInfo::consumeSubpattern(UErrorCode& status) {
// subpattern := literals? number exponent? literals?
consumePadding(PadPosition::UNUM_PAD_BEFORE_PREFIX, status);
if (U_FAILURE(status)) { return; }
@ -161,23 +180,24 @@ void ParsedPatternInfo::consumeSubpattern(UErrorCode &status) {
if (U_FAILURE(status)) { return; }
}
void ParsedPatternInfo::consumePadding(PadPosition paddingLocation, UErrorCode &status) {
void ParsedPatternInfo::consumePadding(PadPosition paddingLocation, UErrorCode& status) {
if (state.peek() != u'*') {
return;
}
if (!currentSubpattern->paddingLocation.isNull()) {
if (currentSubpattern->hasPadding) {
state.toParseException(u"Cannot have multiple pad specifiers");
status = U_MULTIPLE_PAD_SPECIFIERS;
return;
}
currentSubpattern->paddingLocation = paddingLocation;
currentSubpattern->hasPadding = true;
state.next(); // consume the '*'
currentSubpattern->paddingEndpoints.start = state.offset;
consumeLiteral(status);
currentSubpattern->paddingEndpoints.end = state.offset;
}
void ParsedPatternInfo::consumeAffix(Endpoints &endpoints, UErrorCode &status) {
void ParsedPatternInfo::consumeAffix(Endpoints& endpoints, UErrorCode& status) {
// literals := { literal }
endpoints.start = state.offset;
while (true) {
@ -233,7 +253,7 @@ void ParsedPatternInfo::consumeAffix(Endpoints &endpoints, UErrorCode &status) {
endpoints.end = state.offset;
}
void ParsedPatternInfo::consumeLiteral(UErrorCode &status) {
void ParsedPatternInfo::consumeLiteral(UErrorCode& status) {
if (state.peek() == -1) {
state.toParseException(u"Expected unquoted literal but found EOL");
status = U_PATTERN_SYNTAX_ERROR;
@ -256,7 +276,7 @@ void ParsedPatternInfo::consumeLiteral(UErrorCode &status) {
}
}
void ParsedPatternInfo::consumeFormat(UErrorCode &status) {
void ParsedPatternInfo::consumeFormat(UErrorCode& status) {
consumeIntegerFormat(status);
if (U_FAILURE(status)) { return; }
if (state.peek() == u'.') {
@ -268,9 +288,9 @@ void ParsedPatternInfo::consumeFormat(UErrorCode &status) {
}
}
void ParsedPatternInfo::consumeIntegerFormat(UErrorCode &status) {
void ParsedPatternInfo::consumeIntegerFormat(UErrorCode& status) {
// Convenience reference:
ParsedSubpatternInfo &result = *currentSubpattern;
ParsedSubpatternInfo& result = *currentSubpattern;
while (true) {
switch (state.peek()) {
@ -359,9 +379,9 @@ void ParsedPatternInfo::consumeIntegerFormat(UErrorCode &status) {
}
}
void ParsedPatternInfo::consumeFractionFormat(UErrorCode &status) {
void ParsedPatternInfo::consumeFractionFormat(UErrorCode& status) {
// Convenience reference:
ParsedSubpatternInfo &result = *currentSubpattern;
ParsedSubpatternInfo& result = *currentSubpattern;
int32_t zeroCounter = 0;
while (true) {
@ -407,9 +427,9 @@ void ParsedPatternInfo::consumeFractionFormat(UErrorCode &status) {
}
}
void ParsedPatternInfo::consumeExponent(UErrorCode &status) {
void ParsedPatternInfo::consumeExponent(UErrorCode& status) {
// Convenience reference:
ParsedSubpatternInfo &result = *currentSubpattern;
ParsedSubpatternInfo& result = *currentSubpattern;
if (state.peek() != u'E') {
return;
@ -437,9 +457,9 @@ void ParsedPatternInfo::consumeExponent(UErrorCode &status) {
/// END RECURSIVE DESCENT PARSER IMPLEMENTATION ///
///////////////////////////////////////////////////
void
PatternParser::parseToExistingPropertiesImpl(const UnicodeString& pattern, DecimalFormatProperties &properties,
IgnoreRounding ignoreRounding, UErrorCode &status) {
void PatternParser::parseToExistingPropertiesImpl(const UnicodeString& pattern,
DecimalFormatProperties& properties,
IgnoreRounding ignoreRounding, UErrorCode& status) {
if (pattern.length() == 0) {
// Backwards compatibility requires that we reset to the default values.
// TODO: Only overwrite the properties that "saveToProperties" normally touches?
@ -453,13 +473,13 @@ PatternParser::parseToExistingPropertiesImpl(const UnicodeString& pattern, Decim
patternInfoToProperties(properties, patternInfo, ignoreRounding, status);
}
void PatternParser::patternInfoToProperties(DecimalFormatProperties &properties,
ParsedPatternInfo& patternInfo,
IgnoreRounding _ignoreRounding, UErrorCode &status) {
void
PatternParser::patternInfoToProperties(DecimalFormatProperties& properties, ParsedPatternInfo& patternInfo,
IgnoreRounding _ignoreRounding, UErrorCode& status) {
// Translate from PatternParseResult to Properties.
// Note that most data from "negative" is ignored per the specification of DecimalFormat.
const ParsedSubpatternInfo &positive = patternInfo.positive;
const ParsedSubpatternInfo& positive = patternInfo.positive;
bool ignoreRounding;
if (_ignoreRounding == IGNORE_ROUNDING_NEVER) {
@ -477,8 +497,10 @@ void PatternParser::patternInfoToProperties(DecimalFormatProperties &properties,
auto grouping3 = static_cast<int16_t> ((positive.groupingSizes >> 32) & 0xffff);
if (grouping2 != -1) {
properties.groupingSize = grouping1;
properties.groupingUsed = true;
} else {
properties.groupingSize = -1;
properties.groupingUsed = false;
}
if (grouping3 != -1) {
properties.secondaryGroupingSize = grouping2;
@ -508,8 +530,7 @@ void PatternParser::patternInfoToProperties(DecimalFormatProperties &properties,
properties.maximumFractionDigits = -1;
properties.roundingIncrement = 0.0;
properties.minimumSignificantDigits = positive.integerAtSigns;
properties.maximumSignificantDigits =
positive.integerAtSigns + positive.integerTrailingHashSigns;
properties.maximumSignificantDigits = positive.integerAtSigns + positive.integerTrailingHashSigns;
} else if (!positive.rounding.isZero()) {
if (!ignoreRounding) {
properties.minimumFractionDigits = minFrac;
@ -568,11 +589,11 @@ void PatternParser::patternInfoToProperties(DecimalFormatProperties &properties,
UnicodeString posSuffix = patternInfo.getString(0);
// Padding settings
if (!positive.paddingLocation.isNull()) {
if (positive.hasPadding) {
// The width of the positive prefix and suffix templates are included in the padding
int paddingWidth =
positive.widthExceptAffixes + AffixUtils::estimateLength(UnicodeStringCharSequence(posPrefix), status) +
AffixUtils::estimateLength(UnicodeStringCharSequence(posSuffix), status);
int paddingWidth = positive.widthExceptAffixes +
AffixUtils::estimateLength(posPrefix, status) +
AffixUtils::estimateLength(posSuffix, status);
properties.formatWidth = paddingWidth;
UnicodeString rawPaddingString = patternInfo.getString(AffixPatternProvider::AFFIX_PADDING);
if (rawPaddingString.length() == 1) {
@ -622,8 +643,8 @@ void PatternParser::patternInfoToProperties(DecimalFormatProperties &properties,
/// End PatternStringParser.java; begin PatternStringUtils.java ///
///////////////////////////////////////////////////////////////////
UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatProperties &properties,
UErrorCode &status) {
UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatProperties& properties,
UErrorCode& status) {
UnicodeString sb;
// Convenience references
@ -656,7 +677,7 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP
if (!ppp.isBogus()) {
sb.append(ppp);
}
sb.append(AffixUtils::escape(UnicodeStringCharSequence(pp)));
sb.append(AffixUtils::escape(pp));
int afterPrefixPos = sb.length();
// Figure out the grouping sizes.
@ -695,11 +716,11 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP
}
} else if (roundingInterval != 0.0) {
// Rounding Interval.
digitsStringScale = minFrac;
digitsStringScale = -roundingutils::doubleFractionLength(roundingInterval);
// TODO: Check for DoS here?
DecimalQuantity incrementQuantity;
incrementQuantity.setToDouble(roundingInterval);
incrementQuantity.adjustMagnitude(minFrac);
incrementQuantity.adjustMagnitude(-digitsStringScale);
incrementQuantity.roundToMagnitude(0, kDefaultMode, status);
UnicodeString str = incrementQuantity.toPlainString();
if (str.charAt(0) == u'-') {
@ -753,7 +774,7 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP
if (!psp.isBogus()) {
sb.append(psp);
}
sb.append(AffixUtils::escape(UnicodeStringCharSequence(ps)));
sb.append(AffixUtils::escape(ps));
// Resolve Padding
if (paddingWidth != -1 && !paddingLocation.isNull()) {
@ -795,22 +816,25 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP
if (!npp.isBogus()) {
sb.append(npp);
}
sb.append(AffixUtils::escape(UnicodeStringCharSequence(np)));
sb.append(AffixUtils::escape(np));
// Copy the positive digit format into the negative.
// This is optional; the pattern is the same as if '#' were appended here instead.
sb.append(sb, afterPrefixPos, beforeSuffixPos);
// NOTE: It is not safe to append the UnicodeString to itself, so we need to copy.
// See http://bugs.icu-project.org/trac/ticket/13707
UnicodeString copy(sb);
sb.append(copy, afterPrefixPos, beforeSuffixPos - afterPrefixPos);
if (!nsp.isBogus()) {
sb.append(nsp);
}
sb.append(AffixUtils::escape(UnicodeStringCharSequence(ns)));
sb.append(AffixUtils::escape(ns));
}
return sb;
}
int PatternStringUtils::escapePaddingString(UnicodeString input, UnicodeString& output, int startIndex,
UErrorCode &status) {
(void)status;
UErrorCode& status) {
(void) status;
if (input.length() == 0) {
input.setTo(kFallbackPaddingString, -1);
}
@ -840,4 +864,207 @@ int PatternStringUtils::escapePaddingString(UnicodeString input, UnicodeString&
return output.length() - startLength;
}
UnicodeString
PatternStringUtils::convertLocalized(const UnicodeString& input, const DecimalFormatSymbols& symbols,
bool toLocalized, UErrorCode& status) {
// Construct a table of strings to be converted between localized and standard.
static constexpr int32_t LEN = 21;
UnicodeString table[LEN][2];
int standIdx = toLocalized ? 0 : 1;
int localIdx = toLocalized ? 1 : 0;
table[0][standIdx] = u"%";
table[0][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kPercentSymbol);
table[1][standIdx] = u"";
table[1][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kPerMillSymbol);
table[2][standIdx] = u".";
table[2][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol);
table[3][standIdx] = u",";
table[3][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol);
table[4][standIdx] = u"-";
table[4][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol);
table[5][standIdx] = u"+";
table[5][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol);
table[6][standIdx] = u";";
table[6][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol);
table[7][standIdx] = u"@";
table[7][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kSignificantDigitSymbol);
table[8][standIdx] = u"E";
table[8][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kExponentialSymbol);
table[9][standIdx] = u"*";
table[9][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kPadEscapeSymbol);
table[10][standIdx] = u"#";
table[10][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kDigitSymbol);
for (int i = 0; i < 10; i++) {
table[11 + i][standIdx] = u'0' + i;
table[11 + i][localIdx] = symbols.getConstDigitSymbol(i);
}
// Special case: quotes are NOT allowed to be in any localIdx strings.
// Substitute them with '' instead.
for (int32_t i = 0; i < LEN; i++) {
table[i][localIdx].findAndReplace(u'\'', u'');
}
// Iterate through the string and convert.
// State table:
// 0 => base state
// 1 => first char inside a quoted sequence in input and output string
// 2 => inside a quoted sequence in input and output string
// 3 => first char after a close quote in input string;
// close quote still needs to be written to output string
// 4 => base state in input string; inside quoted sequence in output string
// 5 => first char inside a quoted sequence in input string;
// inside quoted sequence in output string
UnicodeString result;
int state = 0;
for (int offset = 0; offset < input.length(); offset++) {
UChar ch = input.charAt(offset);
// Handle a quote character (state shift)
if (ch == u'\'') {
if (state == 0) {
result.append(u'\'');
state = 1;
continue;
} else if (state == 1) {
result.append(u'\'');
state = 0;
continue;
} else if (state == 2) {
state = 3;
continue;
} else if (state == 3) {
result.append(u'\'');
result.append(u'\'');
state = 1;
continue;
} else if (state == 4) {
state = 5;
continue;
} else {
U_ASSERT(state == 5);
result.append(u'\'');
result.append(u'\'');
state = 4;
continue;
}
}
if (state == 0 || state == 3 || state == 4) {
for (auto& pair : table) {
// Perform a greedy match on this symbol string
UnicodeString temp = input.tempSubString(offset, pair[0].length());
if (temp == pair[0]) {
// Skip ahead past this region for the next iteration
offset += pair[0].length() - 1;
if (state == 3 || state == 4) {
result.append(u'\'');
state = 0;
}
result.append(pair[1]);
goto continue_outer;
}
}
// No replacement found. Check if a special quote is necessary
for (auto& pair : table) {
UnicodeString temp = input.tempSubString(offset, pair[1].length());
if (temp == pair[1]) {
if (state == 0) {
result.append(u'\'');
state = 4;
}
result.append(ch);
goto continue_outer;
}
}
// Still nothing. Copy the char verbatim. (Add a close quote if necessary)
if (state == 3 || state == 4) {
result.append(u'\'');
state = 0;
}
result.append(ch);
} else {
U_ASSERT(state == 1 || state == 2 || state == 5);
result.append(ch);
state = 2;
}
continue_outer:;
}
// Resolve final quotes
if (state == 3 || state == 4) {
result.append(u'\'');
state = 0;
}
if (state != 0) {
// Malformed localized pattern: unterminated quote
status = U_PATTERN_SYNTAX_ERROR;
}
return result;
}
void PatternStringUtils::patternInfoToStringBuilder(const AffixPatternProvider& patternInfo, bool isPrefix,
int8_t signum, UNumberSignDisplay signDisplay,
StandardPlural::Form plural,
bool perMilleReplacesPercent, UnicodeString& output) {
// Should the output render '+' where '-' would normally appear in the pattern?
bool plusReplacesMinusSign = signum != -1 && (
signDisplay == UNUM_SIGN_ALWAYS || signDisplay == UNUM_SIGN_ACCOUNTING_ALWAYS || (
signum == 1 && (
signDisplay == UNUM_SIGN_EXCEPT_ZERO ||
signDisplay == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO))) &&
patternInfo.positiveHasPlusSign() == false;
// Should we use the affix from the negative subpattern? (If not, we will use the positive
// subpattern.)
bool useNegativeAffixPattern = patternInfo.hasNegativeSubpattern() && (
signum == -1 || (patternInfo.negativeHasMinusSign() && plusReplacesMinusSign));
// Resolve the flags for the affix pattern.
int flags = 0;
if (useNegativeAffixPattern) {
flags |= AffixPatternProvider::AFFIX_NEGATIVE_SUBPATTERN;
}
if (isPrefix) {
flags |= AffixPatternProvider::AFFIX_PREFIX;
}
if (plural != StandardPlural::Form::COUNT) {
U_ASSERT(plural == (AffixPatternProvider::AFFIX_PLURAL_MASK & plural));
flags |= plural;
}
// Should we prepend a sign to the pattern?
bool prependSign;
if (!isPrefix || useNegativeAffixPattern) {
prependSign = false;
} else if (signum == -1) {
prependSign = signDisplay != UNUM_SIGN_NEVER;
} else {
prependSign = plusReplacesMinusSign;
}
// Compute the length of the affix pattern.
int length = patternInfo.length(flags) + (prependSign ? 1 : 0);
// Finally, set the result into the StringBuilder.
output.remove();
for (int index = 0; index < length; index++) {
char16_t candidate;
if (prependSign && index == 0) {
candidate = u'-';
} else if (prependSign) {
candidate = patternInfo.charAt(flags, index - 1);
} else {
candidate = patternInfo.charAt(flags, index);
}
if (plusReplacesMinusSign && candidate == u'-') {
candidate = u'+';
}
if (perMilleReplacesPercent && candidate == u'%') {
candidate = u'';
}
output.append(candidate);
}
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#ifndef __NUMBER_PATTERNSTRING_H__
#define __NUMBER_PATTERNSTRING_H__
@ -41,7 +41,9 @@ struct U_I18N_API ParsedSubpatternInfo {
int32_t fractionTotal = 0; // for convenience
bool hasDecimal = false;
int32_t widthExceptAffixes = 0;
NullableValue<UNumberFormatPadPosition> paddingLocation;
// Note: NullableValue causes issues here with std::move.
bool hasPadding = false;
UNumberFormatPadPosition paddingLocation = UNUM_PAD_BEFORE_PREFIX;
DecimalQuantity rounding;
bool exponentHasPlusSign = false;
int32_t exponentZeros = 0;
@ -62,17 +64,21 @@ struct U_I18N_API ParsedPatternInfo : public AffixPatternProvider, public UMemor
ParsedSubpatternInfo positive;
ParsedSubpatternInfo negative;
ParsedPatternInfo() : state(this->pattern), currentSubpattern(nullptr) {}
ParsedPatternInfo()
: state(this->pattern), currentSubpattern(nullptr) {}
~ParsedPatternInfo() U_OVERRIDE = default;
static int32_t getLengthFromEndpoints(const Endpoints &endpoints);
// Need to declare this explicitly because of the destructor
ParsedPatternInfo& operator=(ParsedPatternInfo&& src) U_NOEXCEPT = default;
static int32_t getLengthFromEndpoints(const Endpoints& endpoints);
char16_t charAt(int32_t flags, int32_t index) const U_OVERRIDE;
int32_t length(int32_t flags) const U_OVERRIDE;
UnicodeString getString(int32_t flags) const;
UnicodeString getString(int32_t flags) const U_OVERRIDE;
bool positiveHasPlusSign() const U_OVERRIDE;
@ -82,16 +88,24 @@ struct U_I18N_API ParsedPatternInfo : public AffixPatternProvider, public UMemor
bool hasCurrencySign() const U_OVERRIDE;
bool containsSymbolType(AffixPatternType type, UErrorCode &status) const U_OVERRIDE;
bool containsSymbolType(AffixPatternType type, UErrorCode& status) const U_OVERRIDE;
bool hasBody() const U_OVERRIDE;
private:
struct U_I18N_API ParserState {
const UnicodeString &pattern; // reference to the parent
const UnicodeString& pattern; // reference to the parent
int32_t offset = 0;
explicit ParserState(const UnicodeString &_pattern) : pattern(_pattern) {};
explicit ParserState(const UnicodeString& _pattern)
: pattern(_pattern) {};
ParserState& operator=(ParserState&& src) U_NOEXCEPT {
// Leave pattern reference alone; it will continue to point to the same place in memory,
// which gets overwritten by ParsedPatternInfo's implicit move assignment.
offset = src.offset;
return *this;
}
UChar32 peek();
@ -99,45 +113,48 @@ struct U_I18N_API ParsedPatternInfo : public AffixPatternProvider, public UMemor
// TODO: We don't currently do anything with the message string.
// This method is here as a shell for Java compatibility.
inline void toParseException(const char16_t *message) { (void)message; }
}
state;
inline void toParseException(const char16_t* message) { (void) message; }
} state;
// NOTE: In Java, these are written as pure functions.
// In C++, they're written as methods.
// The behavior is the same.
// Mutable transient pointer:
ParsedSubpatternInfo *currentSubpattern;
ParsedSubpatternInfo* currentSubpattern;
// In Java, "negative == null" tells us whether or not we had a negative subpattern.
// In C++, we need to remember in another boolean.
bool fHasNegativeSubpattern = false;
const Endpoints &getEndpoints(int32_t flags) const;
const Endpoints& getEndpoints(int32_t flags) const;
/** Run the recursive descent parser. */
void consumePattern(const UnicodeString &patternString, UErrorCode &status);
void consumePattern(const UnicodeString& patternString, UErrorCode& status);
void consumeSubpattern(UErrorCode &status);
void consumeSubpattern(UErrorCode& status);
void consumePadding(PadPosition paddingLocation, UErrorCode &status);
void consumePadding(PadPosition paddingLocation, UErrorCode& status);
void consumeAffix(Endpoints &endpoints, UErrorCode &status);
void consumeAffix(Endpoints& endpoints, UErrorCode& status);
void consumeLiteral(UErrorCode &status);
void consumeLiteral(UErrorCode& status);
void consumeFormat(UErrorCode &status);
void consumeFormat(UErrorCode& status);
void consumeIntegerFormat(UErrorCode &status);
void consumeIntegerFormat(UErrorCode& status);
void consumeFractionFormat(UErrorCode &status);
void consumeFractionFormat(UErrorCode& status);
void consumeExponent(UErrorCode &status);
void consumeExponent(UErrorCode& status);
friend class PatternParser;
};
enum IgnoreRounding {
IGNORE_ROUNDING_NEVER = 0, IGNORE_ROUNDING_IF_CURRENCY = 1, IGNORE_ROUNDING_ALWAYS = 2
};
class U_I18N_API PatternParser {
public:
/**
@ -153,12 +170,8 @@ class U_I18N_API PatternParser {
* The LDML decimal format pattern (Excel-style pattern) to parse.
* @return The results of the parse.
*/
static void
parseToPatternInfo(const UnicodeString& patternString, ParsedPatternInfo &patternInfo, UErrorCode &status);
enum IgnoreRounding {
IGNORE_ROUNDING_NEVER = 0, IGNORE_ROUNDING_IF_CURRENCY = 1, IGNORE_ROUNDING_ALWAYS = 2
};
static void parseToPatternInfo(const UnicodeString& patternString, ParsedPatternInfo& patternInfo,
UErrorCode& status);
/**
* Parses a pattern string into a new property bag.
@ -173,8 +186,10 @@ class U_I18N_API PatternParser {
* @throws IllegalArgumentException
* If there is a syntax error in the pattern string.
*/
static DecimalFormatProperties
parseToProperties(const UnicodeString& pattern, IgnoreRounding ignoreRounding, UErrorCode &status);
static DecimalFormatProperties parseToProperties(const UnicodeString& pattern,
IgnoreRounding ignoreRounding, UErrorCode& status);
static DecimalFormatProperties parseToProperties(const UnicodeString& pattern, UErrorCode& status);
/**
* Parses a pattern string into an existing property bag. All properties that can be encoded into a pattern string
@ -190,18 +205,19 @@ class U_I18N_API PatternParser {
* @throws IllegalArgumentException
* If there was a syntax error in the pattern string.
*/
static void parseToExistingProperties(const UnicodeString& pattern, DecimalFormatProperties& properties,
IgnoreRounding ignoreRounding, UErrorCode &status);
static void parseToExistingProperties(const UnicodeString& pattern,
DecimalFormatProperties& properties,
IgnoreRounding ignoreRounding, UErrorCode& status);
private:
static void
parseToExistingPropertiesImpl(const UnicodeString& pattern, DecimalFormatProperties &properties,
IgnoreRounding ignoreRounding, UErrorCode &status);
static void parseToExistingPropertiesImpl(const UnicodeString& pattern,
DecimalFormatProperties& properties,
IgnoreRounding ignoreRounding, UErrorCode& status);
/** Finalizes the temporary data stored in the ParsedPatternInfo to the Properties. */
static void
patternInfoToProperties(DecimalFormatProperties &properties, ParsedPatternInfo& patternInfo,
IgnoreRounding _ignoreRounding, UErrorCode &status);
static void patternInfoToProperties(DecimalFormatProperties& properties,
ParsedPatternInfo& patternInfo, IgnoreRounding _ignoreRounding,
UErrorCode& status);
};
class U_I18N_API PatternStringUtils {
@ -217,8 +233,8 @@ class U_I18N_API PatternStringUtils {
* The property bag to serialize.
* @return A pattern string approximately serializing the property bag.
*/
static UnicodeString
propertiesToPatternString(const DecimalFormatProperties &properties, UErrorCode &status);
static UnicodeString propertiesToPatternString(const DecimalFormatProperties& properties,
UErrorCode& status);
/**
@ -248,14 +264,23 @@ class U_I18N_API PatternStringUtils {
* notation.
* @return The pattern expressed in the other notation.
*/
static UnicodeString
convertLocalized(UnicodeString input, DecimalFormatSymbols symbols, bool toLocalized,
UErrorCode &status);
static UnicodeString convertLocalized(const UnicodeString& input, const DecimalFormatSymbols& symbols,
bool toLocalized, UErrorCode& status);
/**
* This method contains the heart of the logic for rendering LDML affix strings. It handles
* sign-always-shown resolution, whether to use the positive or negative subpattern, permille
* substitution, and plural forms for CurrencyPluralInfo.
*/
static void patternInfoToStringBuilder(const AffixPatternProvider& patternInfo, bool isPrefix,
int8_t signum, UNumberSignDisplay signDisplay,
StandardPlural::Form plural, bool perMilleReplacesPercent,
UnicodeString& output);
private:
/** @return The number of chars inserted. */
static int
escapePaddingString(UnicodeString input, UnicodeString &output, int startIndex, UErrorCode &status);
static int escapePaddingString(UnicodeString input, UnicodeString& output, int startIndex,
UErrorCode& status);
};
} // namespace impl

View File

@ -3,17 +3,23 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#include "uassert.h"
#include "unicode/numberformatter.h"
#include "number_types.h"
#include "number_decimalquantity.h"
#include "double-conversion.h"
#include "number_roundingutils.h"
#include "putilimp.h"
using namespace icu;
using namespace icu::number;
using namespace icu::number::impl;
using double_conversion::DoubleToStringConverter;
namespace {
int32_t getRoundingMagnitudeFraction(int maxFrac) {
@ -46,6 +52,26 @@ int32_t getDisplayMagnitudeSignificant(const DecimalQuantity &value, int minSig)
}
digits_t roundingutils::doubleFractionLength(double input) {
char buffer[DoubleToStringConverter::kBase10MaximalLength + 1];
bool sign; // unused; always positive
int32_t length;
int32_t point;
DoubleToStringConverter::DoubleToAscii(
input,
DoubleToStringConverter::DtoaMode::SHORTEST,
0,
buffer,
sizeof(buffer),
&sign,
&length,
&point
);
return static_cast<digits_t>(length - point);
}
Rounder Rounder::unlimited() {
return Rounder(RND_NONE, {}, kDefaultMode);
}
@ -225,6 +251,9 @@ IncrementRounder Rounder::constructIncrement(double increment, int32_t minFrac)
IncrementSettings settings;
settings.fIncrement = increment;
settings.fMinFrac = static_cast<digits_t>(minFrac);
// One of the few pre-computed quantities:
// Note: it is possible for minFrac to be more than maxFrac... (misleading)
settings.fMaxFrac = roundingutils::doubleFractionLength(increment);
RounderUnion union_;
union_.increment = settings;
return {RND_INCREMENT, union_, kDefaultMode};
@ -314,6 +343,10 @@ void Rounder::apply(impl::DecimalQuantity &value, UErrorCode& status) const {
value.setFractionLength(
uprv_max(0, -getDisplayMagnitudeSignificant(value, fUnion.fracSig.fMinSig)),
INT32_MAX);
// Make sure that digits are displayed on zero.
if (value.isZero() && fUnion.fracSig.fMinSig > 0) {
value.setIntegerLength(1, INT32_MAX);
}
break;
case RND_FRACTION_SIGNIFICANT: {
@ -335,8 +368,11 @@ void Rounder::apply(impl::DecimalQuantity &value, UErrorCode& status) const {
case RND_INCREMENT:
value.roundToIncrement(
fUnion.increment.fIncrement, fRoundingMode, fUnion.increment.fMinFrac, status);
value.setFractionLength(fUnion.increment.fMinFrac, fUnion.increment.fMinFrac);
fUnion.increment.fIncrement,
fRoundingMode,
fUnion.increment.fMaxFrac,
status);
value.setFractionLength(fUnion.increment.fMinFrac, INT32_MAX);
break;
case RND_CURRENCY:

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#ifndef __NUMBER_ROUNDINGUTILS_H__
#define __NUMBER_ROUNDINGUTILS_H__
@ -131,6 +131,12 @@ inline bool roundsAtMidpoint(int roundingMode) {
}
}
/**
* Computes the number of fraction digits in a double. Used for computing maxFrac for an increment.
* Calls into the DoubleToStringConverter library to do so.
*/
digits_t doubleFractionLength(double input);
} // namespace roundingutils
} // namespace impl
} // namespace number

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#include <cstdlib>
#include "number_scientific.h"
@ -64,8 +64,13 @@ int32_t ScientificModifier::apply(NumberStringBuilder &output, int32_t /*leftInd
int32_t disp = std::abs(fExponent);
for (int j = 0; j < fHandler->fSettings.fMinExponentDigits || disp > 0; j++, disp /= 10) {
auto d = static_cast<int8_t>(disp % 10);
const UnicodeString &digitString = getDigitFromSymbols(d, *fHandler->fSymbols);
i += output.insert(i - j, digitString, UNUM_EXPONENT_FIELD, status);
i += utils::insertDigitFromSymbols(
output,
i - j,
d,
*fHandler->fSymbols,
UNUM_EXPONENT_FIELD,
status);
}
return i - rightIndex;
}
@ -117,6 +122,9 @@ void ScientificHandler::processQuantity(DecimalQuantity &quantity, MicroProps &m
ScientificModifier &mod = micros.helpers.scientificModifier;
mod.set(exponent, this);
micros.modInner = &mod;
// We already performed rounding. Do not perform it again.
micros.rounding = Rounder::constructPassThrough();
}
int32_t ScientificHandler::getMultiplier(int32_t magnitude) const {

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#ifndef __NUMBER_SCIENTIFIC_H__
#define __NUMBER_SCIENTIFIC_H__

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,318 @@
// © 2018 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#ifndef __SOURCE_NUMBER_SKELETONS_H__
#define __SOURCE_NUMBER_SKELETONS_H__
#include "number_types.h"
#include "numparse_types.h"
#include "unicode/ucharstrie.h"
using icu::numparse::impl::StringSegment;
U_NAMESPACE_BEGIN namespace number {
namespace impl {
// Forward-declaration
struct SeenMacroProps;
// namespace for enums and entrypoint functions
namespace skeleton {
///////////////////////////////////////////////////////////////////////////////////////
// NOTE: For an example of how to add a new stem to the number skeleton parser, see: //
// http://bugs.icu-project.org/trac/changeset/41193 //
///////////////////////////////////////////////////////////////////////////////////////
/**
* While parsing a skeleton, this enum records what type of option we expect to find next.
*/
enum ParseState {
// Section 0: We expect whitespace or a stem, but not an option:
STATE_NULL,
// Section 1: We might accept an option, but it is not required:
STATE_SCIENTIFIC,
STATE_ROUNDER,
STATE_FRACTION_ROUNDER,
// Section 2: An option is required:
STATE_INCREMENT_ROUNDER,
STATE_MEASURE_UNIT,
STATE_PER_MEASURE_UNIT,
STATE_CURRENCY_UNIT,
STATE_INTEGER_WIDTH,
STATE_NUMBERING_SYSTEM,
STATE_SCALE,
};
/**
* All possible stem literals have an entry in the StemEnum. The enum name is the kebab case stem
* string literal written in upper snake case.
*
* @see StemToObject
* @see #SERIALIZED_STEM_TRIE
*/
enum StemEnum {
// Section 1: Stems that do not require an option:
STEM_COMPACT_SHORT,
STEM_COMPACT_LONG,
STEM_SCIENTIFIC,
STEM_ENGINEERING,
STEM_NOTATION_SIMPLE,
STEM_BASE_UNIT,
STEM_PERCENT,
STEM_PERMILLE,
STEM_ROUND_INTEGER,
STEM_ROUND_UNLIMITED,
STEM_ROUND_CURRENCY_STANDARD,
STEM_ROUND_CURRENCY_CASH,
STEM_GROUP_OFF,
STEM_GROUP_MIN2,
STEM_GROUP_AUTO,
STEM_GROUP_ON_ALIGNED,
STEM_GROUP_THOUSANDS,
STEM_LATIN,
STEM_UNIT_WIDTH_NARROW,
STEM_UNIT_WIDTH_SHORT,
STEM_UNIT_WIDTH_FULL_NAME,
STEM_UNIT_WIDTH_ISO_CODE,
STEM_UNIT_WIDTH_HIDDEN,
STEM_SIGN_AUTO,
STEM_SIGN_ALWAYS,
STEM_SIGN_NEVER,
STEM_SIGN_ACCOUNTING,
STEM_SIGN_ACCOUNTING_ALWAYS,
STEM_SIGN_EXCEPT_ZERO,
STEM_SIGN_ACCOUNTING_EXCEPT_ZERO,
STEM_DECIMAL_AUTO,
STEM_DECIMAL_ALWAYS,
// Section 2: Stems that DO require an option:
STEM_ROUND_INCREMENT,
STEM_MEASURE_UNIT,
STEM_PER_MEASURE_UNIT,
STEM_CURRENCY,
STEM_INTEGER_WIDTH,
STEM_NUMBERING_SYSTEM,
STEM_SCALE,
};
/**
* Creates a NumberFormatter corresponding to the given skeleton string.
*
* @param skeletonString
* A number skeleton string, possibly not in its shortest form.
* @return An UnlocalizedNumberFormatter with behavior defined by the given skeleton string.
*/
UnlocalizedNumberFormatter create(const UnicodeString& skeletonString, UErrorCode& status);
/**
* Create a skeleton string corresponding to the given NumberFormatter.
*
* @param macros
* The NumberFormatter options object.
* @return A skeleton string in normalized form.
*/
UnicodeString generate(const MacroProps& macros, UErrorCode& status);
/**
* Converts from a skeleton string to a MacroProps. This method contains the primary parse loop.
*
* Internal: use the create() endpoint instead of this function.
*/
MacroProps parseSkeleton(const UnicodeString& skeletonString, UErrorCode& status);
/**
* Given that the current segment represents an stem, parse it and save the result.
*
* @return The next state after parsing this stem, corresponding to what subset of options to expect.
*/
ParseState parseStem(const StringSegment& segment, const UCharsTrie& stemTrie, SeenMacroProps& seen,
MacroProps& macros, UErrorCode& status);
/**
* Given that the current segment represents an option, parse it and save the result.
*
* @return The next state after parsing this option, corresponding to what subset of options to
* expect next.
*/
ParseState
parseOption(ParseState stem, const StringSegment& segment, MacroProps& macros, UErrorCode& status);
} // namespace skeleton
/**
* Namespace for utility methods that convert from StemEnum to corresponding objects or enums. This
* applies to only the "Section 1" stems, those that are well-defined without an option.
*/
namespace stem_to_object {
Notation notation(skeleton::StemEnum stem);
MeasureUnit unit(skeleton::StemEnum stem);
Rounder rounder(skeleton::StemEnum stem);
UGroupingStrategy groupingStrategy(skeleton::StemEnum stem);
UNumberUnitWidth unitWidth(skeleton::StemEnum stem);
UNumberSignDisplay signDisplay(skeleton::StemEnum stem);
UNumberDecimalSeparatorDisplay decimalSeparatorDisplay(skeleton::StemEnum stem);
} // namespace stem_to_object
/**
* Namespace for utility methods that convert from enums to stem strings. More complex object conversions
* take place in the object_to_stem_string namespace.
*/
namespace enum_to_stem_string {
void groupingStrategy(UGroupingStrategy value, UnicodeString& sb);
void unitWidth(UNumberUnitWidth value, UnicodeString& sb);
void signDisplay(UNumberSignDisplay value, UnicodeString& sb);
void decimalSeparatorDisplay(UNumberDecimalSeparatorDisplay value, UnicodeString& sb);
} // namespace enum_to_stem_string
/**
* Namespace for utility methods for processing stems and options that cannot be interpreted literally.
*/
namespace blueprint_helpers {
/** @return Whether we successfully found and parsed an exponent width option. */
bool parseExponentWidthOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
void generateExponentWidthOption(int32_t minExponentDigits, UnicodeString& sb, UErrorCode& status);
/** @return Whether we successfully found and parsed an exponent sign option. */
bool parseExponentSignOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
void parseCurrencyOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
void generateCurrencyOption(const CurrencyUnit& currency, UnicodeString& sb, UErrorCode& status);
void parseMeasureUnitOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
void generateMeasureUnitOption(const MeasureUnit& measureUnit, UnicodeString& sb, UErrorCode& status);
void parseMeasurePerUnitOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
void parseFractionStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
void generateFractionStem(int32_t minFrac, int32_t maxFrac, UnicodeString& sb, UErrorCode& status);
void parseDigitsStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
void generateDigitsStem(int32_t minSig, int32_t maxSig, UnicodeString& sb, UErrorCode& status);
/** @return Whether we successfully found and parsed a frac-sig option. */
bool parseFracSigOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
void parseIncrementOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
void
generateIncrementOption(double increment, int32_t trailingZeros, UnicodeString& sb, UErrorCode& status);
/** @return Whether we successfully found and parsed a rounding mode. */
bool parseRoundingModeOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
void generateRoundingModeOption(RoundingMode mode, UnicodeString& sb, UErrorCode& status);
void parseIntegerWidthOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
void generateIntegerWidthOption(int32_t minInt, int32_t maxInt, UnicodeString& sb, UErrorCode& status);
void parseNumberingSystemOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
void generateNumberingSystemOption(const NumberingSystem& ns, UnicodeString& sb, UErrorCode& status);
void parseScaleOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
void generateScaleOption(int32_t magnitude, const DecNum* arbitrary, UnicodeString& sb,
UErrorCode& status);
} // namespace blueprint_helpers
/**
* Class for utility methods for generating a token corresponding to each macro-prop. Each method
* returns whether or not a token was written to the string builder.
*
* This needs to be a class, not a namespace, so it can be friended.
*/
class GeneratorHelpers {
public:
/**
* Main skeleton generator function. Appends the normalized skeleton for the MacroProps to the given
* StringBuilder.
*
* Internal: use the create() endpoint instead of this function.
*/
static void generateSkeleton(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
private:
static bool notation(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
static bool unit(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
static bool perUnit(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
static bool rounding(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
static bool grouping(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
static bool integerWidth(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
static bool symbols(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
static bool unitWidth(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
static bool sign(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
static bool decimal(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
static bool scale(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
};
/**
* Struct for null-checking.
* In Java, we can just check the object reference. In C++, we need a different method.
*/
struct SeenMacroProps {
bool notation = false;
bool unit = false;
bool perUnit = false;
bool rounder = false;
bool grouper = false;
bool padder = false;
bool integerWidth = false;
bool symbols = false;
bool unitWidth = false;
bool sign = false;
bool decimal = false;
bool scale = false;
};
} // namespace impl
} // namespace number
U_NAMESPACE_END
#endif //__SOURCE_NUMBER_SKELETONS_H__
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#include "number_stringbuilder.h"
#include "unicode/utf16.h"
@ -337,6 +337,11 @@ UnicodeString NumberStringBuilder::toUnicodeString() const {
return UnicodeString(getCharPtr() + fZero, fLength);
}
const UnicodeString NumberStringBuilder::toTempUnicodeString() const {
// Readonly-alias constructor:
return UnicodeString(FALSE, getCharPtr() + fZero, fLength);
}
UnicodeString NumberStringBuilder::toDebugString() const {
UnicodeString sb;
sb.append(u"<NumberStringBuilder [", -1);
@ -454,7 +459,7 @@ void NumberStringBuilder::populateFieldPosition(FieldPosition &fp, int32_t offse
void NumberStringBuilder::populateFieldPositionIterator(FieldPositionIterator &fpi, UErrorCode &status) const {
// TODO: Set an initial capacity on uvec?
LocalPointer <UVector32> uvec(new UVector32(status));
LocalPointer <UVector32> uvec(new UVector32(status), status);
if (U_FAILURE(status)) {
return;
}

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#ifndef __NUMBER_STRINGBUILDER_H__
#define __NUMBER_STRINGBUILDER_H__
@ -84,8 +84,17 @@ class U_I18N_API NumberStringBuilder : public UMemory {
int32_t insert(int32_t index, const NumberStringBuilder &other, UErrorCode &status);
/**
* Gets a "safe" UnicodeString that can be used even after the NumberStringBuilder is destructed.
* */
UnicodeString toUnicodeString() const;
/**
* Gets an "unsafe" UnicodeString that is valid only as long as the NumberStringBuilder is alive and
* unchanged. Slightly faster than toUnicodeString().
*/
const UnicodeString toTempUnicodeString() const;
UnicodeString toDebugString() const;
const char16_t *chars() const;

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#ifndef __NUMBER_TYPES_H__
#define __NUMBER_TYPES_H__
@ -15,9 +15,9 @@
#include "unicode/utf16.h"
#include "uassert.h"
#include "unicode/platform.h"
#include "unicode/uniset.h"
U_NAMESPACE_BEGIN
namespace number {
U_NAMESPACE_BEGIN namespace number {
namespace impl {
// Typedef several enums for brevity and for easier comparison to Java.
@ -39,9 +39,6 @@ static constexpr RoundingMode kDefaultMode = RoundingMode::UNUM_FOUND_HALFEVEN;
// ICU4J Equivalent: Padder.FALLBACK_PADDING_STRING
static constexpr char16_t kFallbackPaddingString[] = u" ";
// ICU4J Equivalent: NumberFormatterImpl.DEFAULT_CURRENCY
static constexpr char16_t kDefaultCurrency[] = u"XXX";
// Forward declarations:
class Modifier;
@ -87,35 +84,10 @@ enum AffixPatternType {
};
enum CompactType {
TYPE_DECIMAL,
TYPE_CURRENCY
TYPE_DECIMAL, TYPE_CURRENCY
};
// TODO: Should this be moved somewhere else, maybe where other ICU classes can use it?
// Exported as U_I18N_API because it is a base class for other exported types
class U_I18N_API CharSequence {
public:
virtual ~CharSequence() = default;
virtual int32_t length() const = 0;
virtual char16_t charAt(int32_t index) const = 0;
virtual UChar32 codePointAt(int32_t index) const {
// Default implementation; can be overridden with a more efficient version
char16_t leading = charAt(index);
if (U16_IS_LEAD(leading) && length() > index + 1) {
char16_t trailing = charAt(index + 1);
return U16_GET_SUPPLEMENTARY(leading, trailing);
} else {
return leading;
}
}
virtual UnicodeString toUnicodeString() const = 0;
};
class U_I18N_API AffixPatternProvider {
public:
static const int32_t AFFIX_PLURAL_MASK = 0xff;
@ -123,12 +95,20 @@ class U_I18N_API AffixPatternProvider {
static const int32_t AFFIX_NEGATIVE_SUBPATTERN = 0x200;
static const int32_t AFFIX_PADDING = 0x400;
// Convenience compound flags
static const int32_t AFFIX_POS_PREFIX = AFFIX_PREFIX;
static const int32_t AFFIX_POS_SUFFIX = 0;
static const int32_t AFFIX_NEG_PREFIX = AFFIX_PREFIX | AFFIX_NEGATIVE_SUBPATTERN;
static const int32_t AFFIX_NEG_SUFFIX = AFFIX_NEGATIVE_SUBPATTERN;
virtual ~AffixPatternProvider() = default;
virtual char16_t charAt(int flags, int i) const = 0;
virtual int length(int flags) const = 0;
virtual UnicodeString getString(int flags) const = 0;
virtual bool hasCurrencySign() const = 0;
virtual bool positiveHasPlusSign() const = 0;
@ -137,7 +117,7 @@ class U_I18N_API AffixPatternProvider {
virtual bool negativeHasMinusSign() const = 0;
virtual bool containsSymbolType(AffixPatternType, UErrorCode &) const = 0;
virtual bool containsSymbolType(AffixPatternType, UErrorCode&) const = 0;
/**
* True if the pattern has a number placeholder like "0" or "#,##0.00"; false if the pattern does not
@ -173,8 +153,8 @@ class U_I18N_API Modifier {
* formatted.
* @return The number of characters (UTF-16 code units) that were added to the string builder.
*/
virtual int32_t
apply(NumberStringBuilder &output, int leftIndex, int rightIndex, UErrorCode &status) const = 0;
virtual int32_t apply(NumberStringBuilder& output, int leftIndex, int rightIndex,
UErrorCode& status) const = 0;
/**
* Gets the length of the prefix. This information can be used in combination with {@link #apply} to extract the
@ -187,7 +167,7 @@ class U_I18N_API Modifier {
/**
* Returns the number of code points in the modifier, prefix plus suffix.
*/
virtual int32_t getCodePointCount(UErrorCode &status) const = 0;
virtual int32_t getCodePointCount(UErrorCode& status) const = 0;
/**
* Whether this modifier is strong. If a modifier is strong, it should always be applied immediately and not allowed
@ -230,7 +210,8 @@ class U_I18N_API MicroPropsGenerator {
* The MicroProps instance to populate.
* @return A MicroProps instance resolved for the quantity.
*/
virtual void processQuantity(DecimalQuantity& quantity, MicroProps& micros, UErrorCode& status) const = 0;
virtual void processQuantity(DecimalQuantity& quantity, MicroProps& micros,
UErrorCode& status) const = 0;
};
/**
@ -255,24 +236,25 @@ class MultiplierProducer {
template<typename T>
class U_I18N_API NullableValue {
public:
NullableValue() : fNull(true) {}
NullableValue()
: fNull(true) {}
NullableValue(const NullableValue<T> &other) = default;
NullableValue(const NullableValue<T>& other) = default;
explicit NullableValue(const T &other) {
explicit NullableValue(const T& other) {
fValue = other;
fNull = false;
}
NullableValue<T> &operator=(const NullableValue<T> &other) = default;
NullableValue<T>& operator=(const NullableValue<T>& other) = default;
NullableValue<T> &operator=(const T &other) {
NullableValue<T>& operator=(const T& other) {
fValue = other;
fNull = false;
return *this;
}
bool operator==(const NullableValue &other) const {
bool operator==(const NullableValue& other) const {
// "fValue == other.fValue" returns UBool, not bool (causes compiler warnings)
return fNull ? other.fNull : (other.fNull ? false : static_cast<bool>(fValue == other.fValue));
}
@ -286,13 +268,21 @@ class U_I18N_API NullableValue {
return fNull;
}
T get(UErrorCode &status) const {
T get(UErrorCode& status) const {
if (fNull) {
status = U_UNDEFINED_VARIABLE;
}
return fValue;
}
T getNoError() const {
return fValue;
}
T getOrDefault(T defaultValue) const {
return fNull ? defaultValue : fValue;
}
private:
bool fNull;
T fValue;

View File

@ -0,0 +1,252 @@
// © 2018 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include <stdlib.h>
#include <cmath>
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
// Allow implicit conversion from char16_t* to UnicodeString for this file:
// Helpful in toString methods and elsewhere.
#define UNISTR_FROM_STRING_EXPLICIT
#include "number_types.h"
#include "number_utils.h"
#include "charstr.h"
#include "decContext.h"
#include "decNumber.h"
#include "double-conversion.h"
#include "uresimp.h"
#include "ureslocs.h"
using namespace icu;
using namespace icu::number;
using namespace icu::number::impl;
using icu::double_conversion::DoubleToStringConverter;
namespace {
const char16_t*
doGetPattern(UResourceBundle* res, const char* nsName, const char* patternKey, UErrorCode& publicStatus,
UErrorCode& localStatus) {
// Construct the path into the resource bundle
CharString key;
key.append("NumberElements/", publicStatus);
key.append(nsName, publicStatus);
key.append("/patterns/", publicStatus);
key.append(patternKey, publicStatus);
if (U_FAILURE(publicStatus)) {
return u"";
}
return ures_getStringByKeyWithFallback(res, key.data(), nullptr, &localStatus);
}
}
const char16_t* utils::getPatternForStyle(const Locale& locale, const char* nsName, CldrPatternStyle style,
UErrorCode& status) {
const char* patternKey;
switch (style) {
case CLDR_PATTERN_STYLE_DECIMAL:
patternKey = "decimalFormat";
break;
case CLDR_PATTERN_STYLE_CURRENCY:
patternKey = "currencyFormat";
break;
case CLDR_PATTERN_STYLE_ACCOUNTING:
patternKey = "accountingFormat";
break;
case CLDR_PATTERN_STYLE_PERCENT:
patternKey = "percentFormat";
break;
case CLDR_PATTERN_STYLE_SCIENTIFIC:
patternKey = "scientificFormat";
break;
default:
U_ASSERT(false);
}
LocalUResourceBundlePointer res(ures_open(nullptr, locale.getName(), &status));
if (U_FAILURE(status)) { return u""; }
// Attempt to get the pattern with the native numbering system.
UErrorCode localStatus = U_ZERO_ERROR;
const char16_t* pattern;
pattern = doGetPattern(res.getAlias(), nsName, patternKey, status, localStatus);
if (U_FAILURE(status)) { return u""; }
// Fall back to latn if native numbering system does not have the right pattern
if (U_FAILURE(localStatus) && uprv_strcmp("latn", nsName) != 0) {
localStatus = U_ZERO_ERROR;
pattern = doGetPattern(res.getAlias(), "latn", patternKey, status, localStatus);
if (U_FAILURE(status)) { return u""; }
}
return pattern;
}
DecNum::DecNum() {
uprv_decContextDefault(&fContext, DEC_INIT_BASE);
uprv_decContextSetRounding(&fContext, DEC_ROUND_HALF_EVEN);
fContext.traps = 0; // no traps, thank you (what does this even mean?)
}
DecNum::DecNum(const DecNum& other, UErrorCode& status)
: fContext(other.fContext) {
// Allocate memory for the new DecNum.
U_ASSERT(fContext.digits == other.fData.getCapacity());
if (fContext.digits > kDefaultDigits) {
void* p = fData.resize(fContext.digits, 0);
if (p == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
return;
}
}
// Copy the data from the old DecNum to the new one.
uprv_memcpy(fData.getAlias(), other.fData.getAlias(), sizeof(decNumber));
uprv_memcpy(fData.getArrayStart(),
other.fData.getArrayStart(),
other.fData.getArrayLimit() - other.fData.getArrayStart());
}
void DecNum::setTo(StringPiece str, UErrorCode& status) {
// We need NUL-terminated for decNumber; CharString guarantees this, but not StringPiece.
CharString cstr(str, status);
if (U_FAILURE(status)) { return; }
_setTo(cstr.data(), str.length(), status);
}
void DecNum::setTo(const char* str, UErrorCode& status) {
_setTo(str, static_cast<int32_t>(uprv_strlen(str)), status);
}
void DecNum::setTo(double d, UErrorCode& status) {
// Need to check for NaN and Infinity before going into DoubleToStringConverter
if (std::isnan(d) != 0 || std::isfinite(d) == 0) {
status = U_UNSUPPORTED_ERROR;
return;
}
// First convert from double to string, then string to DecNum.
// Allocate enough room for: all digits, "E-324", and NUL-terminator.
char buffer[DoubleToStringConverter::kBase10MaximalLength + 6];
bool sign; // unused; always positive
int32_t length;
int32_t point;
DoubleToStringConverter::DoubleToAscii(
d,
DoubleToStringConverter::DtoaMode::SHORTEST,
0,
buffer,
sizeof(buffer),
&sign,
&length,
&point
);
// Read initial result as a string.
_setTo(buffer, length, status);
// Set exponent and bitmask. Note that DoubleToStringConverter does not do negatives.
fData.getAlias()->exponent += point - length;
fData.getAlias()->bits |= static_cast<uint8_t>(std::signbit(d) ? DECNEG : 0);
}
void DecNum::_setTo(const char* str, int32_t maxDigits, UErrorCode& status) {
if (maxDigits > kDefaultDigits) {
fData.resize(maxDigits, 0);
fContext.digits = maxDigits;
} else {
fContext.digits = kDefaultDigits;
}
static_assert(DECDPUN == 1, "Assumes that DECDPUN is set to 1");
uprv_decNumberFromString(fData.getAlias(), str, &fContext);
// Check for invalid syntax and set the corresponding error code.
if ((fContext.status & DEC_Conversion_syntax) != 0) {
status = U_DECIMAL_NUMBER_SYNTAX_ERROR;
return;
} else if (fContext.status != 0) {
// Not a syntax error, but some other error, like an exponent that is too large.
status = U_UNSUPPORTED_ERROR;
return;
}
// For consistency with Java BigDecimal, no support for DecNum that is NaN or Infinity!
if (decNumberIsSpecial(fData.getAlias())) {
status = U_UNSUPPORTED_ERROR;
return;
}
}
void
DecNum::setTo(const uint8_t* bcd, int32_t length, int32_t scale, bool isNegative, UErrorCode& status) {
if (length > kDefaultDigits) {
fData.resize(length, 0);
fContext.digits = length;
} else {
fContext.digits = kDefaultDigits;
}
// "digits is of type int32_t, and must have a value in the range 1 through 999,999,999."
if (length < 1 || length > 999999999) {
// Too large for decNumber
status = U_UNSUPPORTED_ERROR;
return;
}
// "The exponent field holds the exponent of the number. Its range is limited by the requirement that
// "the range of the adjusted exponent of the number be balanced and fit within a whole number of
// "decimal digits (in this implementation, be 999,999,999 through +999,999,999). The adjusted
// "exponent is the exponent that would result if the number were expressed with a single digit before
// "the decimal point, and is therefore given by exponent+digits-1."
if (scale > 999999999 - length + 1 || scale < -999999999 - length + 1) {
// Too large for decNumber
status = U_UNSUPPORTED_ERROR;
return;
}
fData.getAlias()->digits = length;
fData.getAlias()->exponent = scale;
fData.getAlias()->bits = static_cast<uint8_t>(isNegative ? DECNEG : 0);
uprv_decNumberSetBCD(fData, bcd, static_cast<uint32_t>(length));
if (fContext.status != 0) {
// Some error occured while constructing the decNumber.
status = U_INTERNAL_PROGRAM_ERROR;
}
}
void DecNum::normalize() {
uprv_decNumberReduce(fData, fData, &fContext);
}
void DecNum::multiplyBy(const DecNum& rhs, UErrorCode& status) {
uprv_decNumberMultiply(fData, fData, rhs.fData, &fContext);
if (fContext.status != 0) {
status = U_INTERNAL_PROGRAM_ERROR;
}
}
void DecNum::divideBy(const DecNum& rhs, UErrorCode& status) {
uprv_decNumberDivide(fData, fData, rhs.fData, &fContext);
if (fContext.status != 0) {
status = U_INTERNAL_PROGRAM_ERROR;
}
}
bool DecNum::isNegative() const {
return decNumberIsNegative(fData.getAlias());
}
bool DecNum::isZero() const {
return decNumberIsZero(fData.getAlias());
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -3,7 +3,7 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#if !UCONFIG_NO_FORMATTING
#ifndef __NUMBER_UTILS_H__
#define __NUMBER_UTILS_H__
@ -13,43 +13,13 @@
#include "number_scientific.h"
#include "number_patternstring.h"
#include "number_modifiers.h"
#include "number_multiplier.h"
#include "decNumber.h"
#include "charstr.h"
U_NAMESPACE_BEGIN namespace number {
namespace impl {
class UnicodeStringCharSequence : public CharSequence {
public:
explicit UnicodeStringCharSequence(const UnicodeString &other) {
fStr = other;
}
~UnicodeStringCharSequence() U_OVERRIDE = default;
int32_t length() const U_OVERRIDE {
return fStr.length();
}
char16_t charAt(int32_t index) const U_OVERRIDE {
return fStr.charAt(index);
}
UChar32 codePointAt(int32_t index) const U_OVERRIDE {
return fStr.char32At(index);
}
UnicodeString toUnicodeString() const U_OVERRIDE {
// Allocate a UnicodeString of the correct length
UnicodeString output(length(), 0, -1);
for (int32_t i = 0; i < length(); i++) {
output.append(charAt(i));
}
return output;
}
private:
UnicodeString fStr;
};
struct MicroProps : public MicroPropsGenerator {
// NOTE: All of these fields are properly initialized in NumberFormatterImpl.
@ -62,10 +32,10 @@ struct MicroProps : public MicroPropsGenerator {
bool useCurrency;
// Note: This struct has no direct ownership of the following pointers.
const DecimalFormatSymbols *symbols;
const Modifier *modOuter;
const Modifier *modMiddle;
const Modifier *modInner;
const DecimalFormatSymbols* symbols;
const Modifier* modOuter;
const Modifier* modMiddle;
const Modifier* modInner;
// The following "helper" fields may optionally be used during the MicroPropsGenerator.
// They live here to retain memory.
@ -73,17 +43,18 @@ struct MicroProps : public MicroPropsGenerator {
ScientificModifier scientificModifier;
EmptyModifier emptyWeakModifier{false};
EmptyModifier emptyStrongModifier{true};
MultiplierFormatHandler multiplier;
} helpers;
MicroProps() = default;
MicroProps(const MicroProps &other) = default;
MicroProps(const MicroProps& other) = default;
MicroProps &operator=(const MicroProps &other) = default;
MicroProps& operator=(const MicroProps& other) = default;
void processQuantity(DecimalQuantity &, MicroProps &micros, UErrorCode &status) const U_OVERRIDE {
(void)status;
void processQuantity(DecimalQuantity&, MicroProps& micros, UErrorCode& status) const U_OVERRIDE {
(void) status;
if (this == &micros) {
// Unsafe path: no need to perform a copy.
U_ASSERT(!exhausted);
@ -100,27 +71,93 @@ struct MicroProps : public MicroPropsGenerator {
bool exhausted = false;
};
/**
* This struct provides the result of the number formatting pipeline to FormattedNumber.
*
* The DecimalQuantity is not currently being used by FormattedNumber, but at some point it could be used
* to add a toDecNumber() or similar method.
*/
struct NumberFormatterResults : public UMemory {
DecimalQuantity quantity;
NumberStringBuilder string;
enum CldrPatternStyle {
CLDR_PATTERN_STYLE_DECIMAL,
CLDR_PATTERN_STYLE_CURRENCY,
CLDR_PATTERN_STYLE_ACCOUNTING,
CLDR_PATTERN_STYLE_PERCENT,
CLDR_PATTERN_STYLE_SCIENTIFIC,
CLDR_PATTERN_STYLE_COUNT,
};
inline const UnicodeString getDigitFromSymbols(int8_t digit, const DecimalFormatSymbols &symbols) {
// TODO: Implement DecimalFormatSymbols.getCodePointZero()?
if (digit == 0) {
return symbols.getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kZeroDigitSymbol);
} else {
return symbols.getSymbol(static_cast<DecimalFormatSymbols::ENumberFormatSymbol>(
DecimalFormatSymbols::ENumberFormatSymbol::kOneDigitSymbol + digit - 1));
// Namespace for naked functions
namespace utils {
inline int32_t insertDigitFromSymbols(NumberStringBuilder& output, int32_t index, int8_t digit,
const DecimalFormatSymbols& symbols, Field field,
UErrorCode& status) {
if (symbols.getCodePointZero() != -1) {
return output.insertCodePoint(index, symbols.getCodePointZero() + digit, field, status);
}
return output.insert(index, symbols.getConstDigitSymbol(digit), field, status);
}
inline bool unitIsCurrency(const MeasureUnit& unit) {
return uprv_strcmp("currency", unit.getType()) == 0;
}
inline bool unitIsNoUnit(const MeasureUnit& unit) {
return uprv_strcmp("none", unit.getType()) == 0;
}
inline bool unitIsPercent(const MeasureUnit& unit) {
return uprv_strcmp("percent", unit.getSubtype()) == 0;
}
inline bool unitIsPermille(const MeasureUnit& unit) {
return uprv_strcmp("permille", unit.getSubtype()) == 0;
}
// NOTE: In Java, this method is in NumberFormat.java
const char16_t*
getPatternForStyle(const Locale& locale, const char* nsName, CldrPatternStyle style, UErrorCode& status);
} // namespace utils
/** A very thin C++ wrapper around decNumber.h */
class DecNum : public UMemory {
public:
DecNum(); // leaves object in valid but undefined state
// Copy-like constructor; use the default move operators.
DecNum(const DecNum& other, UErrorCode& status);
/** Sets the decNumber to the StringPiece. */
void setTo(StringPiece str, UErrorCode& status);
/** Sets the decNumber to the NUL-terminated char string. */
void setTo(const char* str, UErrorCode& status);
/** Uses double_conversion to set this decNumber to the given double. */
void setTo(double d, UErrorCode& status);
/** Sets the decNumber to the BCD representation. */
void setTo(const uint8_t* bcd, int32_t length, int32_t scale, bool isNegative, UErrorCode& status);
void normalize();
void multiplyBy(const DecNum& rhs, UErrorCode& status);
void divideBy(const DecNum& rhs, UErrorCode& status);
bool isNegative() const;
bool isZero() const;
inline const decNumber* getRawDecNumber() const {
return fData.getAlias();
}
private:
static constexpr int32_t kDefaultDigits = 34;
MaybeStackHeaderAndArray<decNumber, char, kDefaultDigits> fData;
decContext fContext;
void _setTo(const char* str, int32_t maxDigits, UErrorCode& status);
};
} // namespace impl
} // namespace number
U_NAMESPACE_END

View File

@ -0,0 +1,79 @@
// © 2018 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#ifndef __SOURCE_NUMBER_UTYPES_H__
#define __SOURCE_NUMBER_UTYPES_H__
#include "unicode/numberformatter.h"
#include "number_types.h"
#include "number_decimalquantity.h"
#include "number_stringbuilder.h"
U_NAMESPACE_BEGIN namespace number {
namespace impl {
/**
* Implementation class for UNumberFormatter with a magic number for safety.
*
* Wraps a LocalizedNumberFormatter by value.
*/
struct UNumberFormatterData : public UMemory {
// The magic number to identify incoming objects.
// Reads in ASCII as "NFR" (NumberFormatteR with room at the end)
static constexpr int32_t kMagic = 0x4E465200;
// Data members:
int32_t fMagic = kMagic;
LocalizedNumberFormatter fFormatter;
/** Convert from UNumberFormatter -> UNumberFormatterData. */
static UNumberFormatterData* validate(UNumberFormatter* input, UErrorCode& status);
/** Convert from UNumberFormatter -> UNumberFormatterData (const version). */
static const UNumberFormatterData* validate(const UNumberFormatter* input, UErrorCode& status);
/** Convert from UNumberFormatterData -> UNumberFormatter. */
UNumberFormatter* exportForC();
};
/**
* Implementation class for UFormattedNumber with magic number for safety.
*
* This struct is also held internally by the C++ version FormattedNumber since the member types are not
* declared in the public header file.
*
* The DecimalQuantity is not currently being used by FormattedNumber, but at some point it could be used
* to add a toDecNumber() or similar method.
*/
struct UFormattedNumberData : public UMemory {
// The magic number to identify incoming objects.
// Reads in ASCII as "FDN" (FormatteDNumber with room at the end)
static constexpr int32_t kMagic = 0x46444E00;
// Data members:
int32_t fMagic = kMagic;
DecimalQuantity quantity;
NumberStringBuilder string;
/** Convert from UFormattedNumber -> UFormattedNumberData. */
static UFormattedNumberData* validate(UFormattedNumber* input, UErrorCode& status);
/** Convert from UFormattedNumber -> UFormattedNumberData (const version). */
static const UFormattedNumberData* validate(const UFormattedNumber* input, UErrorCode& status);
/** Convert from UFormattedNumberData -> UFormattedNumber. */
UFormattedNumber* exportForC();
};
} // namespace impl
} // namespace number
U_NAMESPACE_END
#endif //__SOURCE_NUMBER_UTYPES_H__
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -51,10 +51,11 @@
#include "uassert.h"
#include "umutex.h"
#include "mutex.h"
#include "digitlst.h"
#include <float.h>
#include "sharednumberformat.h"
#include "unifiedcache.h"
#include "number_decimalquantity.h"
#include "number_utils.h"
//#define FMT_DEBUG
@ -129,31 +130,28 @@ static const UChar * const gLastResortNumberPatterns[UNUM_FORMAT_STYLE_COUNT] =
// Keys used for accessing resource bundles
static const char *gNumberElements = "NumberElements";
static const char *gLatn = "latn";
static const char *gPatterns = "patterns";
static const char *gFormatKeys[UNUM_FORMAT_STYLE_COUNT] = {
NULL, // UNUM_PATTERN_DECIMAL
"decimalFormat", // UNUM_DECIMAL
"currencyFormat", // UNUM_CURRENCY
"percentFormat", // UNUM_PERCENT
"scientificFormat", // UNUM_SCIENTIFIC
NULL, // UNUM_SPELLOUT
NULL, // UNUM_ORDINAL
NULL, // UNUM_DURATION
NULL, // UNUM_NUMBERING_SYSTEM
NULL, // UNUM_PATTERN_RULEBASED
static const icu::number::impl::CldrPatternStyle gFormatCldrStyles[UNUM_FORMAT_STYLE_COUNT] = {
/* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_PATTERN_DECIMAL
icu::number::impl::CLDR_PATTERN_STYLE_DECIMAL, // UNUM_DECIMAL
icu::number::impl::CLDR_PATTERN_STYLE_CURRENCY, // UNUM_CURRENCY
icu::number::impl::CLDR_PATTERN_STYLE_PERCENT, // UNUM_PERCENT
icu::number::impl::CLDR_PATTERN_STYLE_SCIENTIFIC, // UNUM_SCIENTIFIC
/* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_SPELLOUT
/* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_ORDINAL
/* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_DURATION
/* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_NUMBERING_SYSTEM
/* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_PATTERN_RULEBASED
// For UNUM_CURRENCY_ISO and UNUM_CURRENCY_PLURAL,
// the pattern is the same as the pattern of UNUM_CURRENCY
// except for replacing the single currency sign with
// double currency sign or triple currency sign.
"currencyFormat", // UNUM_CURRENCY_ISO
"currencyFormat", // UNUM_CURRENCY_PLURAL
"accountingFormat", // UNUM_CURRENCY_ACCOUNTING
"currencyFormat", // UNUM_CASH_CURRENCY
NULL, // UNUM_DECIMAL_COMPACT_SHORT
NULL, // UNUM_DECIMAL_COMPACT_LONG
"currencyFormat", // UNUM_CURRENCY_STANDARD
icu::number::impl::CLDR_PATTERN_STYLE_CURRENCY, // UNUM_CURRENCY_ISO
icu::number::impl::CLDR_PATTERN_STYLE_CURRENCY, // UNUM_CURRENCY_PLURAL
icu::number::impl::CLDR_PATTERN_STYLE_ACCOUNTING, // UNUM_CURRENCY_ACCOUNTING
icu::number::impl::CLDR_PATTERN_STYLE_CURRENCY, // UNUM_CASH_CURRENCY
/* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_DECIMAL_COMPACT_SHORT
/* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_DECIMAL_COMPACT_LONG
icu::number::impl::CLDR_PATTERN_STYLE_CURRENCY, // UNUM_CURRENCY_STANDARD
};
// Static hashtable cache of NumberingSystem objects used by NumberFormat
@ -524,7 +522,7 @@ ArgExtractor::ArgExtractor(const NumberFormat& /*nf*/, const Formattable& obj, U
ArgExtractor::~ArgExtractor() {
}
UnicodeString& NumberFormat::format(const DigitList &number,
UnicodeString& NumberFormat::format(const number::impl::DecimalQuantity &number,
UnicodeString& appendTo,
FieldPositionIterator* posIter,
UErrorCode& status) const {
@ -534,7 +532,7 @@ UnicodeString& NumberFormat::format(const DigitList &number,
if (U_FAILURE(status)) {
return appendTo;
}
double dnum = number.getDouble();
double dnum = number.toDouble();
format(dnum, appendTo, posIter, status);
return appendTo;
}
@ -542,7 +540,7 @@ UnicodeString& NumberFormat::format(const DigitList &number,
UnicodeString&
NumberFormat::format(const DigitList &number,
NumberFormat::format(const number::impl::DecimalQuantity &number,
UnicodeString& appendTo,
FieldPosition& pos,
UErrorCode &status) const {
@ -552,7 +550,7 @@ NumberFormat::format(const DigitList &number,
if (U_FAILURE(status)) {
return appendTo;
}
double dnum = number.getDouble();
double dnum = number.toDouble();
format(dnum, appendTo, pos, status);
return appendTo;
}
@ -578,7 +576,7 @@ NumberFormat::format(const Formattable& obj,
return cloneFmt->format(*n, appendTo, pos, status);
}
if (n->isNumeric() && n->getDigitList() != NULL) {
if (n->isNumeric() && n->getDecimalQuantity() != NULL) {
// Decimal Number. We will have a DigitList available if the value was
// set to a decimal number, or if the value originated with a parse.
//
@ -587,17 +585,17 @@ NumberFormat::format(const Formattable& obj,
// know about DigitList to continue to operate as they had.
//
// DecimalFormat overrides the DigitList formatting functions.
format(*n->getDigitList(), appendTo, pos, status);
format(*n->getDecimalQuantity(), appendTo, pos, status);
} else {
switch (n->getType()) {
case Formattable::kDouble:
format(n->getDouble(), appendTo, pos);
format(n->getDouble(), appendTo, pos, status);
break;
case Formattable::kLong:
format(n->getLong(), appendTo, pos);
format(n->getLong(), appendTo, pos, status);
break;
case Formattable::kInt64:
format(n->getInt64(), appendTo, pos);
format(n->getInt64(), appendTo, pos, status);
break;
default:
status = U_INVALID_FORMAT_ERROR;
@ -633,9 +631,9 @@ NumberFormat::format(const Formattable& obj,
return cloneFmt->format(*n, appendTo, posIter, status);
}
if (n->isNumeric() && n->getDigitList() != NULL) {
if (n->isNumeric() && n->getDecimalQuantity() != NULL) {
// Decimal Number
format(*n->getDigitList(), appendTo, posIter, status);
format(*n->getDecimalQuantity(), appendTo, posIter, status);
} else {
switch (n->getType()) {
case Formattable::kDouble:
@ -1400,27 +1398,13 @@ NumberFormat::makeInstance(const Locale& desiredLocale,
return NULL;
}
UResourceBundle *resource = ownedResource.orphan();
UResourceBundle *numElements = ures_getByKeyWithFallback(resource, gNumberElements, NULL, &status);
resource = ures_getByKeyWithFallback(numElements, ns->getName(), resource, &status);
resource = ures_getByKeyWithFallback(resource, gPatterns, resource, &status);
ownedResource.adoptInstead(resource);
int32_t patLen = 0;
const UChar *patResStr = ures_getStringByKeyWithFallback(resource, gFormatKeys[style], &patLen, &status);
// Didn't find a pattern specific to the numbering system, so fall back to "latn"
if ( status == U_MISSING_RESOURCE_ERROR && uprv_strcmp(gLatn,ns->getName())) {
status = U_ZERO_ERROR;
resource = ures_getByKeyWithFallback(numElements, gLatn, resource, &status);
resource = ures_getByKeyWithFallback(resource, gPatterns, resource, &status);
patResStr = ures_getStringByKeyWithFallback(resource, gFormatKeys[style], &patLen, &status);
}
ures_close(numElements);
// Creates the specified decimal format style of the desired locale.
pattern.setTo(TRUE, patResStr, patLen);
// Load the pattern from data using the common library function
const char16_t* patternPtr = number::impl::utils::getPatternForStyle(
desiredLocale,
ns->getName(),
gFormatCldrStyles[style],
status);
pattern = UnicodeString(TRUE, patternPtr, -1);
}
if (U_FAILURE(status)) {
return NULL;

Some files were not shown because too many files have changed in this diff Show More