Coverage Report

Created: 2025-06-24 06:43

/src/icu/source/i18n/currpinf.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) 2009-2014, International Business Machines Corporation and
6
 * others. All Rights Reserved.
7
 *******************************************************************************
8
 */
9
10
#include "unicode/currpinf.h"
11
12
#if !UCONFIG_NO_FORMATTING
13
14
//#define CURRENCY_PLURAL_INFO_DEBUG 1
15
16
#ifdef CURRENCY_PLURAL_INFO_DEBUG
17
#include <iostream>
18
#endif
19
20
#include "unicode/locid.h"
21
#include "unicode/plurrule.h"
22
#include "unicode/strenum.h"
23
#include "unicode/ures.h"
24
#include "unicode/numsys.h"
25
#include "cstring.h"
26
#include "hash.h"
27
#include "uresimp.h"
28
#include "ureslocs.h"
29
30
U_NAMESPACE_BEGIN
31
32
static const UChar gNumberPatternSeparator = 0x3B; // ;
33
34
U_CDECL_BEGIN
35
36
/**
37
 * @internal ICU 4.2
38
 */
39
static UBool U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2);
40
41
UBool
42
0
U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2) {
43
0
    const UnicodeString* affix_1 = (UnicodeString*)val1.pointer;
44
0
    const UnicodeString* affix_2 = (UnicodeString*)val2.pointer;
45
0
    return  *affix_1 == *affix_2;
46
0
}
47
48
U_CDECL_END
49
50
51
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyPluralInfo)
52
53
static const UChar gDefaultCurrencyPluralPattern[] = {'0', '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4, 0};
54
static const UChar gTripleCurrencySign[] = {0xA4, 0xA4, 0xA4, 0};
55
static const UChar gPluralCountOther[] = {0x6F, 0x74, 0x68, 0x65, 0x72, 0};
56
static const UChar gPart0[] = {0x7B, 0x30, 0x7D, 0};
57
static const UChar gPart1[] = {0x7B, 0x31, 0x7D, 0};
58
59
static const char gNumberElementsTag[]="NumberElements";
60
static const char gLatnTag[]="latn";
61
static const char gPatternsTag[]="patterns";
62
static const char gDecimalFormatTag[]="decimalFormat";
63
static const char gCurrUnitPtnTag[]="CurrencyUnitPatterns";
64
65
CurrencyPluralInfo::CurrencyPluralInfo(UErrorCode& status)
66
0
:   fPluralCountToCurrencyUnitPattern(nullptr),
67
0
    fPluralRules(nullptr),
68
0
    fLocale(nullptr),
69
0
    fInternalStatus(U_ZERO_ERROR) {
70
0
    initialize(Locale::getDefault(), status);
71
0
}
72
73
CurrencyPluralInfo::CurrencyPluralInfo(const Locale& locale, UErrorCode& status)
74
0
:   fPluralCountToCurrencyUnitPattern(nullptr),
75
0
    fPluralRules(nullptr),
76
0
    fLocale(nullptr),
77
0
    fInternalStatus(U_ZERO_ERROR) {
78
0
    initialize(locale, status);
79
0
}
80
81
CurrencyPluralInfo::CurrencyPluralInfo(const CurrencyPluralInfo& info) 
82
0
:   UObject(info),
83
0
    fPluralCountToCurrencyUnitPattern(nullptr),
84
0
    fPluralRules(nullptr),
85
0
    fLocale(nullptr),
86
0
    fInternalStatus(U_ZERO_ERROR) {
87
0
    *this = info;
88
0
}
89
90
CurrencyPluralInfo&
91
0
CurrencyPluralInfo::operator=(const CurrencyPluralInfo& info) {
92
0
    if (this == &info) {
93
0
        return *this;
94
0
    }
95
96
0
    fInternalStatus = info.fInternalStatus;
97
0
    if (U_FAILURE(fInternalStatus)) {
98
        // bail out early if the object we were copying from was already 'invalid'.
99
0
        return *this;
100
0
    }
101
102
0
    deleteHash(fPluralCountToCurrencyUnitPattern);
103
0
    fPluralCountToCurrencyUnitPattern = initHash(fInternalStatus);
104
0
    copyHash(info.fPluralCountToCurrencyUnitPattern, 
105
0
             fPluralCountToCurrencyUnitPattern, fInternalStatus);
106
0
    if ( U_FAILURE(fInternalStatus) ) {
107
0
        return *this;
108
0
    }
109
110
0
    delete fPluralRules;
111
0
    fPluralRules = nullptr;
112
0
    delete fLocale;
113
0
    fLocale = nullptr;
114
115
0
    if (info.fPluralRules != nullptr) {
116
0
        fPluralRules = info.fPluralRules->clone();
117
0
        if (fPluralRules == nullptr) {
118
0
            fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
119
0
            return *this;
120
0
        }
121
0
    }
122
0
    if (info.fLocale != nullptr) {
123
0
        fLocale = info.fLocale->clone();
124
0
        if (fLocale == nullptr) {
125
            // Note: If clone had an error parameter, then we could check/set that instead.
126
0
            fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
127
0
            return *this;
128
0
        }
129
        // If the other locale wasn't bogus, but our clone'd locale is bogus, then OOM happened
130
        // during the call to clone().
131
0
        if (!info.fLocale->isBogus() && fLocale->isBogus()) {
132
0
            fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
133
0
            return *this;
134
0
        }
135
0
    }
136
0
    return *this;
137
0
}
138
139
0
CurrencyPluralInfo::~CurrencyPluralInfo() {
140
0
    deleteHash(fPluralCountToCurrencyUnitPattern);
141
0
    fPluralCountToCurrencyUnitPattern = nullptr;
142
0
    delete fPluralRules;
143
0
    delete fLocale;
144
0
    fPluralRules = nullptr;
145
0
    fLocale = nullptr;
146
0
}
147
148
bool
149
0
CurrencyPluralInfo::operator==(const CurrencyPluralInfo& info) const {
150
#ifdef CURRENCY_PLURAL_INFO_DEBUG
151
    if (*fPluralRules == *info.fPluralRules) {
152
        std::cout << "same plural rules\n";
153
    }
154
    if (*fLocale == *info.fLocale) {
155
        std::cout << "same locale\n";
156
    }
157
    if (fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern)) {
158
        std::cout << "same pattern\n";
159
    }
160
#endif
161
0
    return *fPluralRules == *info.fPluralRules &&
162
0
           *fLocale == *info.fLocale &&
163
0
           fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern);
164
0
}
165
166
167
CurrencyPluralInfo*
168
0
CurrencyPluralInfo::clone() const {
169
0
    CurrencyPluralInfo* newObj = new CurrencyPluralInfo(*this);
170
    // Since clone doesn't have a 'status' parameter, the best we can do is return nullptr
171
    // if the new object was not full constructed properly (an error occurred).
172
0
    if (newObj != nullptr && U_FAILURE(newObj->fInternalStatus)) {
173
0
        delete newObj;
174
0
        newObj = nullptr;
175
0
    }
176
0
    return newObj;
177
0
}
178
179
const PluralRules* 
180
0
CurrencyPluralInfo::getPluralRules() const {
181
0
    return fPluralRules;
182
0
}
183
184
UnicodeString&
185
CurrencyPluralInfo::getCurrencyPluralPattern(const UnicodeString&  pluralCount,
186
0
                                             UnicodeString& result) const {
187
0
    const UnicodeString* currencyPluralPattern = 
188
0
        (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(pluralCount);
189
0
    if (currencyPluralPattern == nullptr) {
190
        // fall back to "other"
191
0
        if (pluralCount.compare(gPluralCountOther, 5)) {
192
0
            currencyPluralPattern = 
193
0
                (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(UnicodeString(TRUE, gPluralCountOther, 5));
194
0
        }
195
0
        if (currencyPluralPattern == nullptr) {
196
            // no currencyUnitPatterns defined, 
197
            // fallback to predefined default.
198
            // This should never happen when ICU resource files are
199
            // available, since currencyUnitPattern of "other" is always
200
            // defined in root.
201
0
            result = UnicodeString(gDefaultCurrencyPluralPattern);
202
0
            return result;
203
0
        }
204
0
    }
205
0
    result = *currencyPluralPattern;
206
0
    return result;
207
0
}
208
209
const Locale&
210
0
CurrencyPluralInfo::getLocale() const {
211
0
    return *fLocale;
212
0
}
213
214
void
215
CurrencyPluralInfo::setPluralRules(const UnicodeString& ruleDescription,
216
0
                                   UErrorCode& status) {
217
0
    if (U_SUCCESS(status)) {
218
0
        delete fPluralRules;
219
0
        fPluralRules = PluralRules::createRules(ruleDescription, status);
220
0
    }
221
0
}
222
223
void
224
CurrencyPluralInfo::setCurrencyPluralPattern(const UnicodeString& pluralCount,
225
                                             const UnicodeString& pattern,
226
0
                                             UErrorCode& status) {
227
0
    if (U_SUCCESS(status)) {
228
0
        UnicodeString* oldValue = static_cast<UnicodeString*>(
229
0
            fPluralCountToCurrencyUnitPattern->get(pluralCount));
230
0
        delete oldValue;
231
0
        LocalPointer<UnicodeString> p(new UnicodeString(pattern), status);
232
0
        if (U_SUCCESS(status)) {
233
            // the p object allocated above will be owned by fPluralCountToCurrencyUnitPattern
234
            // after the call to put(), even if the method returns failure.
235
0
            fPluralCountToCurrencyUnitPattern->put(pluralCount, p.orphan(), status);
236
0
        }
237
0
    }
238
0
}
239
240
void
241
0
CurrencyPluralInfo::setLocale(const Locale& loc, UErrorCode& status) {
242
0
    initialize(loc, status);
243
0
}
244
245
void 
246
0
CurrencyPluralInfo::initialize(const Locale& loc, UErrorCode& status) {
247
0
    if (U_FAILURE(status)) {
248
0
        return;
249
0
    }
250
0
    delete fLocale;
251
0
    fLocale = nullptr;    
252
0
    delete fPluralRules;
253
0
    fPluralRules = nullptr;
254
255
0
    fLocale = loc.clone();
256
0
    if (fLocale == nullptr) {
257
0
        status = U_MEMORY_ALLOCATION_ERROR;
258
0
        return;
259
0
    }
260
    // If the locale passed in wasn't bogus, but our clone'd locale is bogus, then OOM happened
261
    // during the call to loc.clone().
262
0
    if (!loc.isBogus() && fLocale->isBogus()) {
263
0
        status = U_MEMORY_ALLOCATION_ERROR;
264
0
        return;
265
0
    }
266
0
    fPluralRules = PluralRules::forLocale(loc, status);
267
0
    setupCurrencyPluralPattern(loc, status);
268
0
}
269
   
270
void
271
0
CurrencyPluralInfo::setupCurrencyPluralPattern(const Locale& loc, UErrorCode& status) {
272
0
    if (U_FAILURE(status)) {
273
0
        return;
274
0
    }
275
276
0
    deleteHash(fPluralCountToCurrencyUnitPattern);
277
0
    fPluralCountToCurrencyUnitPattern = initHash(status);
278
0
    if (U_FAILURE(status)) {
279
0
        return;
280
0
    }
281
282
0
    LocalPointer<NumberingSystem> ns(NumberingSystem::createInstance(loc, status), status);
283
0
    if (U_FAILURE(status)) {
284
0
        return;
285
0
    }
286
0
    UErrorCode ec = U_ZERO_ERROR;
287
0
    LocalUResourceBundlePointer rb(ures_open(nullptr, loc.getName(), &ec));
288
0
    LocalUResourceBundlePointer numElements(ures_getByKeyWithFallback(rb.getAlias(), gNumberElementsTag, nullptr, &ec));
289
0
    ures_getByKeyWithFallback(numElements.getAlias(), ns->getName(), rb.getAlias(), &ec);
290
0
    ures_getByKeyWithFallback(rb.getAlias(), gPatternsTag, rb.getAlias(), &ec);
291
0
    int32_t ptnLen;
292
0
    const UChar* numberStylePattern = ures_getStringByKeyWithFallback(rb.getAlias(), gDecimalFormatTag, &ptnLen, &ec);
293
    // Fall back to "latn" if num sys specific pattern isn't there.
294
0
    if ( ec == U_MISSING_RESOURCE_ERROR && (uprv_strcmp(ns->getName(), gLatnTag) != 0)) {
295
0
        ec = U_ZERO_ERROR;
296
0
        ures_getByKeyWithFallback(numElements.getAlias(), gLatnTag, rb.getAlias(), &ec);
297
0
        ures_getByKeyWithFallback(rb.getAlias(), gPatternsTag, rb.getAlias(), &ec);
298
0
        numberStylePattern = ures_getStringByKeyWithFallback(rb.getAlias(), gDecimalFormatTag, &ptnLen, &ec);
299
0
    }
300
0
    int32_t numberStylePatternLen = ptnLen;
301
0
    const UChar* negNumberStylePattern = nullptr;
302
0
    int32_t negNumberStylePatternLen = 0;
303
    // TODO: Java
304
    // parse to check whether there is ";" separator in the numberStylePattern
305
0
    UBool hasSeparator = false;
306
0
    if (U_SUCCESS(ec)) {
307
0
        for (int32_t styleCharIndex = 0; styleCharIndex < ptnLen; ++styleCharIndex) {
308
0
            if (numberStylePattern[styleCharIndex] == gNumberPatternSeparator) {
309
0
                hasSeparator = true;
310
                // split the number style pattern into positive and negative
311
0
                negNumberStylePattern = numberStylePattern + styleCharIndex + 1;
312
0
                negNumberStylePatternLen = ptnLen - styleCharIndex - 1;
313
0
                numberStylePatternLen = styleCharIndex;
314
0
            }
315
0
        }
316
0
    }
317
318
0
    if (U_FAILURE(ec)) {
319
        // If OOM occurred during the above code, then we want to report that back to the caller.
320
0
        if (ec == U_MEMORY_ALLOCATION_ERROR) {
321
0
            status = ec;
322
0
        }
323
0
        return;
324
0
    }
325
326
0
    LocalUResourceBundlePointer currRb(ures_open(U_ICUDATA_CURR, loc.getName(), &ec));
327
0
    LocalUResourceBundlePointer currencyRes(ures_getByKeyWithFallback(currRb.getAlias(), gCurrUnitPtnTag, nullptr, &ec));
328
    
329
#ifdef CURRENCY_PLURAL_INFO_DEBUG
330
    std::cout << "in set up\n";
331
#endif
332
0
    LocalPointer<StringEnumeration> keywords(fPluralRules->getKeywords(ec), ec);
333
0
    if (U_SUCCESS(ec)) {
334
0
        const char* pluralCount;
335
0
        while (((pluralCount = keywords->next(nullptr, ec)) != nullptr) && U_SUCCESS(ec)) {
336
0
            int32_t ptnLength;
337
0
            UErrorCode err = U_ZERO_ERROR;
338
0
            const UChar* patternChars = ures_getStringByKeyWithFallback(currencyRes.getAlias(), pluralCount, &ptnLength, &err);
339
0
            if (err == U_MEMORY_ALLOCATION_ERROR || patternChars == nullptr) {
340
0
                ec = err;
341
0
                break;
342
0
            }
343
0
            if (U_SUCCESS(err) && ptnLength > 0) {
344
0
                UnicodeString* pattern = new UnicodeString(patternChars, ptnLength);
345
0
                if (pattern == nullptr) {
346
0
                    ec = U_MEMORY_ALLOCATION_ERROR;
347
0
                    break;
348
0
                }
349
#ifdef CURRENCY_PLURAL_INFO_DEBUG
350
                char result_1[1000];
351
                pattern->extract(0, pattern->length(), result_1, "UTF-8");
352
                std::cout << "pluralCount: " << pluralCount << "; pattern: " << result_1 << "\n";
353
#endif
354
0
                pattern->findAndReplace(UnicodeString(TRUE, gPart0, 3), 
355
0
                    UnicodeString(numberStylePattern, numberStylePatternLen));
356
0
                pattern->findAndReplace(UnicodeString(TRUE, gPart1, 3), UnicodeString(TRUE, gTripleCurrencySign, 3));
357
358
0
                if (hasSeparator) {
359
0
                    UnicodeString negPattern(patternChars, ptnLength);
360
0
                    negPattern.findAndReplace(UnicodeString(TRUE, gPart0, 3), 
361
0
                        UnicodeString(negNumberStylePattern, negNumberStylePatternLen));
362
0
                    negPattern.findAndReplace(UnicodeString(TRUE, gPart1, 3), UnicodeString(TRUE, gTripleCurrencySign, 3));
363
0
                    pattern->append(gNumberPatternSeparator);
364
0
                    pattern->append(negPattern);
365
0
                }
366
#ifdef CURRENCY_PLURAL_INFO_DEBUG
367
                pattern->extract(0, pattern->length(), result_1, "UTF-8");
368
                std::cout << "pluralCount: " << pluralCount << "; pattern: " << result_1 << "\n";
369
#endif
370
                // the 'pattern' object allocated above will be owned by the fPluralCountToCurrencyUnitPattern after the call to
371
                // put(), even if the method returns failure.
372
0
                fPluralCountToCurrencyUnitPattern->put(UnicodeString(pluralCount, -1, US_INV), pattern, status);
373
0
            }
374
0
        }
375
0
    }
376
    // If OOM occurred during the above code, then we want to report that back to the caller.
377
0
    if (ec == U_MEMORY_ALLOCATION_ERROR) {
378
0
        status = ec;
379
0
    }
380
0
}
381
382
void
383
0
CurrencyPluralInfo::deleteHash(Hashtable* hTable) {
384
0
    if ( hTable == nullptr ) {
385
0
        return;
386
0
    }
387
0
    int32_t pos = UHASH_FIRST;
388
0
    const UHashElement* element = nullptr;
389
0
    while ( (element = hTable->nextElement(pos)) != nullptr ) {
390
0
        const UHashTok valueTok = element->value;
391
0
        const UnicodeString* value = (UnicodeString*)valueTok.pointer;
392
0
        delete value;
393
0
    }
394
0
    delete hTable;
395
0
    hTable = nullptr;
396
0
}
397
398
Hashtable*
399
0
CurrencyPluralInfo::initHash(UErrorCode& status) {
400
0
    if (U_FAILURE(status)) {
401
0
        return nullptr;
402
0
    }
403
0
    LocalPointer<Hashtable> hTable(new Hashtable(TRUE, status), status);
404
0
    if (U_FAILURE(status)) {
405
0
        return nullptr;
406
0
    }
407
0
    hTable->setValueComparator(ValueComparator);
408
0
    return hTable.orphan();
409
0
}
410
411
void
412
CurrencyPluralInfo::copyHash(const Hashtable* source,
413
                           Hashtable* target,
414
0
                           UErrorCode& status) {
415
0
    if (U_FAILURE(status)) {
416
0
        return;
417
0
    }
418
0
    int32_t pos = UHASH_FIRST;
419
0
    const UHashElement* element = nullptr;
420
0
    if (source) {
421
0
        while ( (element = source->nextElement(pos)) != nullptr ) {
422
0
            const UHashTok keyTok = element->key;
423
0
            const UnicodeString* key = (UnicodeString*)keyTok.pointer;
424
0
            const UHashTok valueTok = element->value;
425
0
            const UnicodeString* value = (UnicodeString*)valueTok.pointer;
426
0
            LocalPointer<UnicodeString> copy(new UnicodeString(*value), status);
427
0
            if (U_FAILURE(status)) {
428
0
                return;
429
0
            }
430
            // The HashTable owns the 'copy' object after the call to put().
431
0
            target->put(UnicodeString(*key), copy.orphan(), status);
432
0
            if (U_FAILURE(status)) {
433
0
                return;
434
0
            }
435
0
        }
436
0
    }
437
0
}
438
439
U_NAMESPACE_END
440
441
#endif