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:
commit
4c538a7d49
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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" />
|
||||
|
@ -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">
|
||||
|
@ -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" />
|
||||
|
201
icu4c/source/common/numparse_unisets.cpp
Normal file
201
icu4c/source/common/numparse_unisets.cpp
Normal 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 */
|
89
icu4c/source/common/numparse_unisets.h
Normal file
89
icu4c/source/common/numparse_unisets.h
Normal 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 */
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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, ¤cyNames, &total_currency_name_count, ¤cySymbols, &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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
15
icu4c/source/configure
vendored
15
icu4c/source/configure
vendored
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 ¤cyAffixInfo,
|
||||
PluralAffix &appendTo,
|
||||
UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
AffixPatternIterator iter;
|
||||
affixPattern.iterator(iter);
|
||||
UnicodeString literal;
|
||||
while (iter.nextToken()) {
|
||||
switch (iter.getTokenType()) {
|
||||
case AffixPattern::kPercent:
|
||||
appendTo.append(fPercent, UNUM_PERCENT_FIELD);
|
||||
break;
|
||||
case AffixPattern::kPerMill:
|
||||
appendTo.append(fPermill, UNUM_PERMILL_FIELD);
|
||||
break;
|
||||
case AffixPattern::kNegative:
|
||||
appendTo.append(fNegative, UNUM_SIGN_FIELD);
|
||||
break;
|
||||
case AffixPattern::kPositive:
|
||||
appendTo.append(fPositive, UNUM_SIGN_FIELD);
|
||||
break;
|
||||
case AffixPattern::kCurrency:
|
||||
switch (iter.getTokenLength()) {
|
||||
case 1:
|
||||
appendTo.append(
|
||||
currencyAffixInfo.getSymbol(), UNUM_CURRENCY_FIELD);
|
||||
break;
|
||||
case 2:
|
||||
appendTo.append(
|
||||
currencyAffixInfo.getISO(), UNUM_CURRENCY_FIELD);
|
||||
break;
|
||||
case 3:
|
||||
appendTo.append(
|
||||
currencyAffixInfo.getLong(), UNUM_CURRENCY_FIELD, status);
|
||||
break;
|
||||
default:
|
||||
U_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AffixPattern::kLiteral:
|
||||
appendTo.append(iter.getLiteral(literal));
|
||||
break;
|
||||
default:
|
||||
U_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
|
||||
U_NAMESPACE_END
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
@ -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 ¤cyAffixInfo,
|
||||
PluralAffix &appendTo,
|
||||
UErrorCode &status) const;
|
||||
|
||||
UBool equals(const AffixPatternParser &other) const {
|
||||
return (fPercent == other.fPercent)
|
||||
&& (fPermill == other.fPermill)
|
||||
&& (fNegative == other.fNegative)
|
||||
&& (fPositive == other.fPositive);
|
||||
}
|
||||
|
||||
private:
|
||||
UnicodeString fPercent;
|
||||
UnicodeString fPermill;
|
||||
UnicodeString fNegative;
|
||||
UnicodeString fPositive;
|
||||
};
|
||||
|
||||
|
||||
U_NAMESPACE_END
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
#endif // __AFFIX_PATTERN_PARSER_H__
|
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
@ -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
|
@ -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 */
|
@ -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
|
@ -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
@ -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
|
@ -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 */
|
@ -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__
|
@ -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 */
|
@ -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__
|
@ -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 */
|
@ -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__
|
@ -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
|
||||
|
@ -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__
|
@ -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
|
||||
|
@ -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
@ -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
|
50
icu4c/source/i18n/fieldposutil.h
Normal file
50
icu4c/source/i18n/fieldposutil.h
Normal 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 */
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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" />
|
||||
|
@ -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>
|
@ -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" />
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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};
|
||||
}
|
||||
};
|
||||
|
234
icu4c/source/i18n/number_capi.cpp
Normal file
234
icu4c/source/i18n/number_capi.cpp
Normal 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 */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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__
|
||||
|
||||
|
123
icu4c/source/i18n/number_currencysymbols.cpp
Normal file
123
icu4c/source/i18n/number_currencysymbols.cpp
Normal 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 */
|
64
icu4c/source/i18n/number_currencysymbols.h
Normal file
64
icu4c/source/i18n/number_currencysymbols.h
Normal 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 */
|
@ -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 */
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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 ¬ation) 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 ¬ation) 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 ¯os, 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;
|
||||
}
|
||||
|
@ -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 ¯os, UErrorCode &status) {
|
||||
NumberFormatterImpl* NumberFormatterImpl::fromMacros(const MacroProps& macros, UErrorCode& status) {
|
||||
return new NumberFormatterImpl(macros, true, status);
|
||||
}
|
||||
|
||||
void NumberFormatterImpl::applyStatic(const MacroProps ¯os, 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 ¯os, 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 ¯os, 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 ¯os, 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 ¯os, 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 ¯os, 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 ¯os, 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 ¯os, 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 ¯os, 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 ¯os, 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 ¯os, 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 ¯os, 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 µs, 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 µs, DecimalQua
|
||||
return length;
|
||||
}
|
||||
|
||||
int32_t NumberFormatterImpl::writeNumber(const MicroProps µs, 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 µs, DecimalQuanti
|
||||
return length;
|
||||
}
|
||||
|
||||
int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps µs, 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 µs, 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 µs, 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;
|
||||
}
|
||||
|
@ -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 ¯os, 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 ¯os, 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.
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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"
|
||||
|
@ -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__
|
||||
|
||||
|
500
icu4c/source/i18n/number_mapper.cpp
Normal file
500
icu4c/source/i18n/number_mapper.cpp
Normal 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 */
|
162
icu4c/source/i18n/number_mapper.h
Normal file
162
icu4c/source/i18n/number_mapper.h
Normal 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 */
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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__
|
||||
|
||||
|
172
icu4c/source/i18n/number_multiplier.cpp
Normal file
172
icu4c/source/i18n/number_multiplier.cpp
Normal 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 */
|
54
icu4c/source/i18n/number_multiplier.h
Normal file
54
icu4c/source/i18n/number_multiplier.h
Normal 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 */
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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 ¤cy,
|
||||
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 µs,
|
||||
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 µs, 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 µs, 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 µs,
|
||||
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);
|
||||
|
@ -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 µs, UErrorCode &status) const U_OVERRIDE;
|
||||
void processQuantity(DecimalQuantity&, MicroProps& micros, UErrorCode& status) const U_OVERRIDE;
|
||||
|
||||
void applyToMicros(MicroProps µs, 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 ¤cy, 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);
|
||||
};
|
||||
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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__
|
||||
|
||||
|
1440
icu4c/source/i18n/number_skeletons.cpp
Normal file
1440
icu4c/source/i18n/number_skeletons.cpp
Normal file
File diff suppressed because it is too large
Load Diff
318
icu4c/source/i18n/number_skeletons.h
Normal file
318
icu4c/source/i18n/number_skeletons.h
Normal 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 */
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
252
icu4c/source/i18n/number_utils.cpp
Normal file
252
icu4c/source/i18n/number_utils.cpp
Normal 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 */
|
@ -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 µs, UErrorCode &status) const U_OVERRIDE {
|
||||
(void)status;
|
||||
void processQuantity(DecimalQuantity&, MicroProps& micros, UErrorCode& status) const U_OVERRIDE {
|
||||
(void) status;
|
||||
if (this == µs) {
|
||||
// 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
|
||||
|
79
icu4c/source/i18n/number_utypes.h
Normal file
79
icu4c/source/i18n/number_utypes.h
Normal 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 */
|
@ -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
Loading…
Reference in New Issue
Block a user