1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 2010-2015, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
12 * Modification History:*
13 * Date Name Description
15 ********************************************************************************
18 #include "unicode/utypes.h"
19 #include "unicode/localpointer.h"
20 #include "unicode/uchar.h"
21 #include "unicode/unistr.h"
22 #include "unicode/ures.h"
23 #include "unicode/ustring.h"
24 #include "unicode/uloc.h"
25 #include "unicode/schriter.h"
26 #include "unicode/numsys.h"
32 #include "numsys_impl.h"
34 #if !UCONFIG_NO_FORMATTING
40 #define DEFAULT_DIGITS UNICODE_STRING_SIMPLE("0123456789");
41 static const char gNumberingSystems
[] = "numberingSystems";
42 static const char gNumberElements
[] = "NumberElements";
43 static const char gDefault
[] = "default";
44 static const char gNative
[] = "native";
45 static const char gTraditional
[] = "traditional";
46 static const char gFinance
[] = "finance";
47 static const char gDesc
[] = "desc";
48 static const char gRadix
[] = "radix";
49 static const char gAlgorithmic
[] = "algorithmic";
50 static const char gLatn
[] = "latn";
53 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumberingSystem
)
54 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumsysNameEnumeration
)
57 * Default Constructor.
62 NumberingSystem::NumberingSystem() {
65 UnicodeString defaultDigits
= DEFAULT_DIGITS
;
66 desc
.setTo(defaultDigits
);
67 uprv_strcpy(name
,gLatn
);
75 NumberingSystem::NumberingSystem(const NumberingSystem
& other
)
80 NumberingSystem
* U_EXPORT2
81 NumberingSystem::createInstance(int32_t radix_in
, UBool isAlgorithmic_in
, const UnicodeString
& desc_in
, UErrorCode
&status
) {
83 if (U_FAILURE(status
)) {
88 status
= U_ILLEGAL_ARGUMENT_ERROR
;
92 if ( !isAlgorithmic_in
) {
93 if ( desc_in
.countChar32() != radix_in
) {
94 status
= U_ILLEGAL_ARGUMENT_ERROR
;
99 LocalPointer
<NumberingSystem
> ns(new NumberingSystem(), status
);
100 if (U_FAILURE(status
)) {
104 ns
->setRadix(radix_in
);
105 ns
->setDesc(desc_in
);
106 ns
->setAlgorithmic(isAlgorithmic_in
);
107 ns
->setName(nullptr);
112 NumberingSystem
* U_EXPORT2
113 NumberingSystem::createInstance(const Locale
& inLocale
, UErrorCode
& status
) {
115 if (U_FAILURE(status
)) {
119 UBool nsResolved
= TRUE
;
120 UBool usingFallback
= FALSE
;
121 char buffer
[ULOC_KEYWORDS_CAPACITY
];
122 int32_t count
= inLocale
.getKeywordValue("numbers", buffer
, sizeof(buffer
), status
);
123 if (U_FAILURE(status
) || status
== U_STRING_NOT_TERMINATED_WARNING
) {
124 // the "numbers" keyword exceeds ULOC_KEYWORDS_CAPACITY; ignore and use default.
126 status
= U_ZERO_ERROR
;
128 if ( count
> 0 ) { // @numbers keyword was specified in the locale
129 U_ASSERT(count
< ULOC_KEYWORDS_CAPACITY
);
130 buffer
[count
] = '\0'; // Make sure it is null terminated.
131 if ( !uprv_strcmp(buffer
,gDefault
) || !uprv_strcmp(buffer
,gNative
) ||
132 !uprv_strcmp(buffer
,gTraditional
) || !uprv_strcmp(buffer
,gFinance
)) {
136 uprv_strcpy(buffer
, gDefault
);
140 if (!nsResolved
) { // Resolve the numbering system ( default, native, traditional or finance ) into a "real" numbering system
141 UErrorCode localStatus
= U_ZERO_ERROR
;
142 LocalUResourceBundlePointer
resource(ures_open(nullptr, inLocale
.getName(), &localStatus
));
143 LocalUResourceBundlePointer
numberElementsRes(ures_getByKey(resource
.getAlias(), gNumberElements
, nullptr, &localStatus
));
144 // Don't stomp on the catastrophic failure of OOM.
145 if (localStatus
== U_MEMORY_ALLOCATION_ERROR
) {
146 status
= U_MEMORY_ALLOCATION_ERROR
;
149 while (!nsResolved
) {
150 localStatus
= U_ZERO_ERROR
;
152 const UChar
*nsName
= ures_getStringByKeyWithFallback(numberElementsRes
.getAlias(), buffer
, &count
, &localStatus
);
153 // Don't stomp on the catastrophic failure of OOM.
154 if (localStatus
== U_MEMORY_ALLOCATION_ERROR
) {
155 status
= U_MEMORY_ALLOCATION_ERROR
;
158 if ( count
> 0 && count
< ULOC_KEYWORDS_CAPACITY
) { // numbering system found
159 u_UCharsToChars(nsName
, buffer
, count
);
160 buffer
[count
] = '\0'; // Make sure it is null terminated.
164 if (!nsResolved
) { // Fallback behavior per TR35 - traditional falls back to native, finance and native fall back to default
165 if (!uprv_strcmp(buffer
,gNative
) || !uprv_strcmp(buffer
,gFinance
)) {
166 uprv_strcpy(buffer
,gDefault
);
167 } else if (!uprv_strcmp(buffer
,gTraditional
)) {
168 uprv_strcpy(buffer
,gNative
);
169 } else { // If we get here we couldn't find even the default numbering system
170 usingFallback
= TRUE
;
178 status
= U_USING_FALLBACK_WARNING
;
179 NumberingSystem
*ns
= new NumberingSystem();
181 status
= U_MEMORY_ALLOCATION_ERROR
;
185 return NumberingSystem::createInstanceByName(buffer
, status
);
189 NumberingSystem
* U_EXPORT2
190 NumberingSystem::createInstance(UErrorCode
& status
) {
191 return NumberingSystem::createInstance(Locale::getDefault(), status
);
194 NumberingSystem
* U_EXPORT2
195 NumberingSystem::createInstanceByName(const char *name
, UErrorCode
& status
) {
197 int32_t algorithmic
= 0;
199 LocalUResourceBundlePointer
numberingSystemsInfo(ures_openDirect(nullptr, gNumberingSystems
, &status
));
200 LocalUResourceBundlePointer
nsCurrent(ures_getByKey(numberingSystemsInfo
.getAlias(), gNumberingSystems
, nullptr, &status
));
201 LocalUResourceBundlePointer
nsTop(ures_getByKey(nsCurrent
.getAlias(), name
, nullptr, &status
));
203 UnicodeString nsd
= ures_getUnicodeStringByKey(nsTop
.getAlias(), gDesc
, &status
);
205 ures_getByKey(nsTop
.getAlias(), gRadix
, nsCurrent
.getAlias(), &status
);
206 radix
= ures_getInt(nsCurrent
.getAlias(), &status
);
208 ures_getByKey(nsTop
.getAlias(), gAlgorithmic
, nsCurrent
.getAlias(), &status
);
209 algorithmic
= ures_getInt(nsCurrent
.getAlias(), &status
);
211 UBool isAlgorithmic
= ( algorithmic
== 1 );
213 if (U_FAILURE(status
)) {
214 // Don't stomp on the catastrophic failure of OOM.
215 if (status
!= U_MEMORY_ALLOCATION_ERROR
) {
216 status
= U_UNSUPPORTED_ERROR
;
221 LocalPointer
<NumberingSystem
> ns(NumberingSystem::createInstance(radix
, isAlgorithmic
, nsd
, status
), status
);
222 if (U_FAILURE(status
)) {
233 NumberingSystem::~NumberingSystem() {
236 int32_t NumberingSystem::getRadix() const {
240 UnicodeString
NumberingSystem::getDescription() const {
244 const char * NumberingSystem::getName() const {
248 void NumberingSystem::setRadix(int32_t r
) {
252 void NumberingSystem::setAlgorithmic(UBool c
) {
256 void NumberingSystem::setDesc(const UnicodeString
&d
) {
259 void NumberingSystem::setName(const char *n
) {
260 if ( n
== nullptr ) {
263 uprv_strncpy(name
,n
,kInternalNumSysNameCapacity
);
264 name
[kInternalNumSysNameCapacity
] = '\0'; // Make sure it is null terminated.
267 UBool
NumberingSystem::isAlgorithmic() const {
268 return ( algorithmic
);
273 UVector
* gNumsysNames
= nullptr;
274 UInitOnce gNumSysInitOnce
= U_INITONCE_INITIALIZER
;
276 U_CFUNC UBool U_CALLCONV
numSysCleanup() {
278 gNumsysNames
= nullptr;
279 gNumSysInitOnce
.reset();
283 U_CFUNC
void initNumsysNames(UErrorCode
&status
) {
284 U_ASSERT(gNumsysNames
== nullptr);
285 ucln_i18n_registerCleanup(UCLN_I18N_NUMSYS
, numSysCleanup
);
287 // TODO: Simple array of UnicodeString objects, based on length of table resource?
288 LocalPointer
<UVector
> numsysNames(new UVector(uprv_deleteUObject
, nullptr, status
), status
);
289 if (U_FAILURE(status
)) {
293 UErrorCode rbstatus
= U_ZERO_ERROR
;
294 UResourceBundle
*numberingSystemsInfo
= ures_openDirect(nullptr, "numberingSystems", &rbstatus
);
295 numberingSystemsInfo
=
296 ures_getByKey(numberingSystemsInfo
, "numberingSystems", numberingSystemsInfo
, &rbstatus
);
297 if (U_FAILURE(rbstatus
)) {
298 // Don't stomp on the catastrophic failure of OOM.
299 if (rbstatus
== U_MEMORY_ALLOCATION_ERROR
) {
302 status
= U_MISSING_RESOURCE_ERROR
;
304 ures_close(numberingSystemsInfo
);
308 while ( ures_hasNext(numberingSystemsInfo
) && U_SUCCESS(status
) ) {
309 LocalUResourceBundlePointer
nsCurrent(ures_getNextResource(numberingSystemsInfo
, nullptr, &rbstatus
));
310 if (rbstatus
== U_MEMORY_ALLOCATION_ERROR
) {
311 status
= rbstatus
; // we want to report OOM failure back to the caller.
314 const char *nsName
= ures_getKey(nsCurrent
.getAlias());
315 LocalPointer
<UnicodeString
> newElem(new UnicodeString(nsName
, -1, US_INV
), status
);
316 if (U_SUCCESS(status
)) {
317 numsysNames
->addElement(newElem
.getAlias(), status
);
318 if (U_SUCCESS(status
)) {
319 newElem
.orphan(); // on success, the numsysNames vector owns newElem.
324 ures_close(numberingSystemsInfo
);
325 if (U_SUCCESS(status
)) {
326 gNumsysNames
= numsysNames
.orphan();
331 } // end anonymous namespace
333 StringEnumeration
* NumberingSystem::getAvailableNames(UErrorCode
&status
) {
334 umtx_initOnce(gNumSysInitOnce
, &initNumsysNames
, status
);
335 LocalPointer
<StringEnumeration
> result(new NumsysNameEnumeration(status
), status
);
336 return result
.orphan();
339 NumsysNameEnumeration::NumsysNameEnumeration(UErrorCode
& status
) : pos(0) {
344 NumsysNameEnumeration::snext(UErrorCode
& status
) {
345 if (U_SUCCESS(status
) && (gNumsysNames
!= nullptr) && (pos
< gNumsysNames
->size())) {
346 return (const UnicodeString
*)gNumsysNames
->elementAt(pos
++);
352 NumsysNameEnumeration::reset(UErrorCode
& /*status*/) {
357 NumsysNameEnumeration::count(UErrorCode
& /*status*/) const {
358 return (gNumsysNames
==nullptr) ? 0 : gNumsysNames
->size();
361 NumsysNameEnumeration::~NumsysNameEnumeration() {
365 #endif /* #if !UCONFIG_NO_FORMATTING */