Coverage Report

Created: 2025-06-24 06:43

/src/icu/source/i18n/numsys.cpp
Line
Count
Source (jump to first uncovered line)
1
// © 2016 and later: Unicode, Inc. and others.
2
// License & terms of use: http://www.unicode.org/copyright.html
3
/*
4
*******************************************************************************
5
* Copyright (C) 2010-2015, International Business Machines Corporation and
6
* others. All Rights Reserved.
7
*******************************************************************************
8
*
9
*
10
* File NUMSYS.CPP
11
*
12
* Modification History:*
13
*   Date        Name        Description
14
*
15
********************************************************************************
16
*/
17
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"
27
#include "cstring.h"
28
#include "uassert.h"
29
#include "ucln_in.h"
30
#include "umutex.h"
31
#include "uresimp.h"
32
#include "numsys_impl.h"
33
34
#if !UCONFIG_NO_FORMATTING
35
36
U_NAMESPACE_BEGIN
37
38
// Useful constants
39
40
0
#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";
51
52
53
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumberingSystem)
54
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumsysNameEnumeration)
55
56
    /**
57
     * Default Constructor.
58
     *
59
     * @draft ICU 4.2
60
     */
61
62
0
NumberingSystem::NumberingSystem() {
63
0
     radix = 10;
64
0
     algorithmic = FALSE;
65
0
     UnicodeString defaultDigits = DEFAULT_DIGITS;
66
0
     desc.setTo(defaultDigits);
67
0
     uprv_strcpy(name,gLatn);
68
0
}
69
70
    /**
71
     * Copy constructor.
72
     * @draft ICU 4.2
73
     */
74
75
NumberingSystem::NumberingSystem(const NumberingSystem& other) 
76
0
:  UObject(other) {
77
0
    *this=other;
78
0
}
79
80
NumberingSystem* U_EXPORT2
81
0
NumberingSystem::createInstance(int32_t radix_in, UBool isAlgorithmic_in, const UnicodeString & desc_in, UErrorCode &status) {
82
83
0
    if (U_FAILURE(status)) {
84
0
        return nullptr;
85
0
    }
86
87
0
    if ( radix_in < 2 ) {
88
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
89
0
        return nullptr;
90
0
    }
91
92
0
    if ( !isAlgorithmic_in ) {
93
0
       if ( desc_in.countChar32() != radix_in ) {
94
0
           status = U_ILLEGAL_ARGUMENT_ERROR;
95
0
           return nullptr;
96
0
       }
97
0
    }
98
99
0
    LocalPointer<NumberingSystem> ns(new NumberingSystem(), status);
100
0
    if (U_FAILURE(status)) {
101
0
        return nullptr;
102
0
    }
103
104
0
    ns->setRadix(radix_in);
105
0
    ns->setDesc(desc_in);
106
0
    ns->setAlgorithmic(isAlgorithmic_in);
107
0
    ns->setName(nullptr);
108
109
0
    return ns.orphan();
110
0
}
111
112
NumberingSystem* U_EXPORT2
113
0
NumberingSystem::createInstance(const Locale & inLocale, UErrorCode& status) {
114
115
0
    if (U_FAILURE(status)) {
116
0
        return nullptr;
117
0
    }
118
119
0
    UBool nsResolved = TRUE;
120
0
    UBool usingFallback = FALSE;
121
0
    char buffer[ULOC_KEYWORDS_CAPACITY] = "";
122
0
    int32_t count = inLocale.getKeywordValue("numbers", buffer, sizeof(buffer), status);
123
0
    if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) {
124
        // the "numbers" keyword exceeds ULOC_KEYWORDS_CAPACITY; ignore and use default.
125
0
        count = 0;
126
0
        status = U_ZERO_ERROR;
127
0
    }
128
0
    if ( count > 0 ) { // @numbers keyword was specified in the locale
129
0
        U_ASSERT(count < ULOC_KEYWORDS_CAPACITY);
130
0
        buffer[count] = '\0'; // Make sure it is null terminated.
131
0
        if ( !uprv_strcmp(buffer,gDefault) || !uprv_strcmp(buffer,gNative) || 
132
0
             !uprv_strcmp(buffer,gTraditional) || !uprv_strcmp(buffer,gFinance)) {
133
0
            nsResolved = FALSE;
134
0
        }
135
0
    } else {
136
0
        uprv_strcpy(buffer, gDefault);
137
0
        nsResolved = FALSE;
138
0
    }
139
140
0
    if (!nsResolved) { // Resolve the numbering system ( default, native, traditional or finance ) into a "real" numbering system
141
0
        UErrorCode localStatus = U_ZERO_ERROR;
142
0
        LocalUResourceBundlePointer resource(ures_open(nullptr, inLocale.getName(), &localStatus));
143
0
        LocalUResourceBundlePointer numberElementsRes(ures_getByKey(resource.getAlias(), gNumberElements, nullptr, &localStatus));
144
        // Don't stomp on the catastrophic failure of OOM.
145
0
        if (localStatus == U_MEMORY_ALLOCATION_ERROR) {
146
0
            status = U_MEMORY_ALLOCATION_ERROR;
147
0
            return nullptr;
148
0
        }
149
0
        while (!nsResolved) {
150
0
            localStatus = U_ZERO_ERROR;
151
0
            count = 0;
152
0
            const UChar *nsName = ures_getStringByKeyWithFallback(numberElementsRes.getAlias(), buffer, &count, &localStatus);
153
            // Don't stomp on the catastrophic failure of OOM.
154
0
            if (localStatus == U_MEMORY_ALLOCATION_ERROR) {
155
0
                status = U_MEMORY_ALLOCATION_ERROR;
156
0
                return nullptr;
157
0
            }
158
0
            if ( count > 0 && count < ULOC_KEYWORDS_CAPACITY ) { // numbering system found
159
0
                u_UCharsToChars(nsName, buffer, count);
160
0
                buffer[count] = '\0'; // Make sure it is null terminated.
161
0
                nsResolved = TRUE;
162
0
            } 
163
164
0
            if (!nsResolved) { // Fallback behavior per TR35 - traditional falls back to native, finance and native fall back to default
165
0
                if (!uprv_strcmp(buffer,gNative) || !uprv_strcmp(buffer,gFinance)) { 
166
0
                    uprv_strcpy(buffer,gDefault);
167
0
                } else if (!uprv_strcmp(buffer,gTraditional)) {
168
0
                    uprv_strcpy(buffer,gNative);
169
0
                } else { // If we get here we couldn't find even the default numbering system
170
0
                    usingFallback = TRUE;
171
0
                    nsResolved = TRUE;
172
0
                }
173
0
            }
174
0
        }
175
0
    }
176
177
0
    if (usingFallback) {
178
0
        status = U_USING_FALLBACK_WARNING;
179
0
        NumberingSystem *ns = new NumberingSystem();
180
0
        if (ns == nullptr) {
181
0
            status = U_MEMORY_ALLOCATION_ERROR;
182
0
        }
183
0
        return ns;
184
0
    } else {
185
0
        return NumberingSystem::createInstanceByName(buffer, status);
186
0
    }
187
0
 }
188
189
NumberingSystem* U_EXPORT2
190
0
NumberingSystem::createInstance(UErrorCode& status) {
191
0
    return NumberingSystem::createInstance(Locale::getDefault(), status);
192
0
}
193
194
NumberingSystem* U_EXPORT2
195
0
NumberingSystem::createInstanceByName(const char *name, UErrorCode& status) {
196
0
    int32_t radix = 10;
197
0
    int32_t algorithmic = 0;
198
199
0
    LocalUResourceBundlePointer numberingSystemsInfo(ures_openDirect(nullptr, gNumberingSystems, &status));
200
0
    LocalUResourceBundlePointer nsCurrent(ures_getByKey(numberingSystemsInfo.getAlias(), gNumberingSystems, nullptr, &status));
201
0
    LocalUResourceBundlePointer nsTop(ures_getByKey(nsCurrent.getAlias(), name, nullptr, &status));
202
203
0
    UnicodeString nsd = ures_getUnicodeStringByKey(nsTop.getAlias(), gDesc, &status);
204
205
0
    ures_getByKey(nsTop.getAlias(), gRadix, nsCurrent.getAlias(), &status);
206
0
    radix = ures_getInt(nsCurrent.getAlias(), &status);
207
208
0
    ures_getByKey(nsTop.getAlias(), gAlgorithmic, nsCurrent.getAlias(), &status);
209
0
    algorithmic = ures_getInt(nsCurrent.getAlias(), &status);
210
211
0
    UBool isAlgorithmic = ( algorithmic == 1 );
212
213
0
    if (U_FAILURE(status)) {
214
        // Don't stomp on the catastrophic failure of OOM.
215
0
        if (status != U_MEMORY_ALLOCATION_ERROR) {
216
0
            status = U_UNSUPPORTED_ERROR;
217
0
        }
218
0
        return nullptr;
219
0
    }
220
221
0
    LocalPointer<NumberingSystem> ns(NumberingSystem::createInstance(radix, isAlgorithmic, nsd, status), status);
222
0
    if (U_FAILURE(status)) {
223
0
        return nullptr;
224
0
    }
225
0
    ns->setName(name);
226
0
    return ns.orphan();
227
0
}
228
229
    /**
230
     * Destructor.
231
     * @draft ICU 4.2
232
     */
233
0
NumberingSystem::~NumberingSystem() {
234
0
}
235
236
0
int32_t NumberingSystem::getRadix() const {
237
0
    return radix;
238
0
}
239
240
0
UnicodeString NumberingSystem::getDescription() const {
241
0
    return desc;
242
0
}
243
244
0
const char * NumberingSystem::getName() const {
245
0
    return name;
246
0
}
247
248
0
void NumberingSystem::setRadix(int32_t r) {
249
0
    radix = r;
250
0
}
251
252
0
void NumberingSystem::setAlgorithmic(UBool c) {
253
0
    algorithmic = c;
254
0
}
255
256
0
void NumberingSystem::setDesc(const UnicodeString &d) {
257
0
    desc.setTo(d);
258
0
}
259
0
void NumberingSystem::setName(const char *n) {
260
0
    if ( n == nullptr ) {
261
0
        name[0] = (char) 0;
262
0
    } else {
263
0
        uprv_strncpy(name,n,kInternalNumSysNameCapacity);
264
0
        name[kInternalNumSysNameCapacity] = '\0'; // Make sure it is null terminated.
265
0
    }
266
0
}
267
0
UBool NumberingSystem::isAlgorithmic() const {
268
0
    return ( algorithmic );
269
0
}
270
271
namespace {
272
273
UVector* gNumsysNames = nullptr;
274
UInitOnce gNumSysInitOnce = U_INITONCE_INITIALIZER;
275
276
0
U_CFUNC UBool U_CALLCONV numSysCleanup() {
277
0
    delete gNumsysNames;
278
0
    gNumsysNames = nullptr;
279
0
    gNumSysInitOnce.reset();
280
0
    return true;
281
0
}
282
283
0
U_CFUNC void initNumsysNames(UErrorCode &status) {
284
0
    U_ASSERT(gNumsysNames == nullptr);
285
0
    ucln_i18n_registerCleanup(UCLN_I18N_NUMSYS, numSysCleanup);
286
287
    // TODO: Simple array of UnicodeString objects, based on length of table resource?
288
0
    LocalPointer<UVector> numsysNames(new UVector(uprv_deleteUObject, nullptr, status), status);
289
0
    if (U_FAILURE(status)) {
290
0
        return;
291
0
    }
292
293
0
    UErrorCode rbstatus = U_ZERO_ERROR;
294
0
    UResourceBundle *numberingSystemsInfo = ures_openDirect(nullptr, "numberingSystems", &rbstatus);
295
0
    numberingSystemsInfo =
296
0
            ures_getByKey(numberingSystemsInfo, "numberingSystems", numberingSystemsInfo, &rbstatus);
297
0
    if (U_FAILURE(rbstatus)) {
298
        // Don't stomp on the catastrophic failure of OOM.
299
0
        if (rbstatus == U_MEMORY_ALLOCATION_ERROR) {
300
0
            status = rbstatus;
301
0
        } else {
302
0
            status = U_MISSING_RESOURCE_ERROR;
303
0
        }
304
0
        ures_close(numberingSystemsInfo);
305
0
        return;
306
0
    }
307
308
0
    while ( ures_hasNext(numberingSystemsInfo) && U_SUCCESS(status) ) {
309
0
        LocalUResourceBundlePointer nsCurrent(ures_getNextResource(numberingSystemsInfo, nullptr, &rbstatus));
310
0
        if (rbstatus == U_MEMORY_ALLOCATION_ERROR) {
311
0
            status = rbstatus; // we want to report OOM failure back to the caller.
312
0
            break;
313
0
        }
314
0
        const char *nsName = ures_getKey(nsCurrent.getAlias());
315
0
        LocalPointer<UnicodeString> newElem(new UnicodeString(nsName, -1, US_INV), status);
316
0
        if (U_SUCCESS(status)) {
317
0
            numsysNames->addElementX(newElem.getAlias(), status);
318
0
            if (U_SUCCESS(status)) {
319
0
                newElem.orphan(); // on success, the numsysNames vector owns newElem.
320
0
            }
321
0
        }
322
0
    }
323
324
0
    ures_close(numberingSystemsInfo);
325
0
    if (U_SUCCESS(status)) {
326
0
        gNumsysNames = numsysNames.orphan();
327
0
    }
328
0
    return;
329
0
}
330
331
}   // end anonymous namespace
332
333
0
StringEnumeration* NumberingSystem::getAvailableNames(UErrorCode &status) {
334
0
    umtx_initOnce(gNumSysInitOnce, &initNumsysNames, status);
335
0
    LocalPointer<StringEnumeration> result(new NumsysNameEnumeration(status), status);
336
0
    return result.orphan();
337
0
}
338
339
0
NumsysNameEnumeration::NumsysNameEnumeration(UErrorCode& status) : pos(0) {
340
0
    (void)status;
341
0
}
342
343
const UnicodeString*
344
0
NumsysNameEnumeration::snext(UErrorCode& status) {
345
0
    if (U_SUCCESS(status) && (gNumsysNames != nullptr) && (pos < gNumsysNames->size())) {
346
0
        return (const UnicodeString*)gNumsysNames->elementAt(pos++);
347
0
    }
348
0
    return nullptr;
349
0
}
350
351
void
352
0
NumsysNameEnumeration::reset(UErrorCode& /*status*/) {
353
0
    pos=0;
354
0
}
355
356
int32_t
357
0
NumsysNameEnumeration::count(UErrorCode& /*status*/) const {
358
0
    return (gNumsysNames==nullptr) ? 0 : gNumsysNames->size();
359
0
}
360
361
0
NumsysNameEnumeration::~NumsysNameEnumeration() {
362
0
}
363
U_NAMESPACE_END
364
365
#endif /* #if !UCONFIG_NO_FORMATTING */
366
367
//eof